Commit | Line | Data |
---|---|---|
3f317499 YG |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * AMD Address Translation Library | |
4 | * | |
5 | * access.c : DF Indirect Access functions | |
6 | * | |
7 | * Copyright (c) 2023, Advanced Micro Devices, Inc. | |
8 | * All Rights Reserved. | |
9 | * | |
10 | * Author: Yazen Ghannam <Yazen.Ghannam@amd.com> | |
11 | */ | |
12 | ||
13 | #include "internal.h" | |
14 | ||
15 | /* Protect the PCI config register pairs used for DF indirect access. */ | |
16 | static DEFINE_MUTEX(df_indirect_mutex); | |
17 | ||
18 | /* | |
19 | * Data Fabric Indirect Access uses FICAA/FICAD. | |
20 | * | |
21 | * Fabric Indirect Configuration Access Address (FICAA): constructed based | |
22 | * on the device's Instance Id and the PCI function and register offset of | |
23 | * the desired register. | |
24 | * | |
25 | * Fabric Indirect Configuration Access Data (FICAD): there are FICAD | |
26 | * low and high registers but so far only the low register is needed. | |
27 | * | |
28 | * Use Instance Id 0xFF to indicate a broadcast read. | |
29 | */ | |
30 | #define DF_BROADCAST 0xFF | |
31 | ||
32 | #define DF_FICAA_INST_EN BIT(0) | |
33 | #define DF_FICAA_REG_NUM GENMASK(10, 1) | |
34 | #define DF_FICAA_FUNC_NUM GENMASK(13, 11) | |
35 | #define DF_FICAA_INST_ID GENMASK(23, 16) | |
36 | ||
37 | #define DF_FICAA_REG_NUM_LEGACY GENMASK(10, 2) | |
38 | ||
453f0ae7 M |
39 | static u16 get_accessible_node(u16 node) |
40 | { | |
41 | /* | |
42 | * On heterogeneous systems, not all AMD Nodes are accessible | |
43 | * through software-visible registers. The Node ID needs to be | |
44 | * adjusted for register accesses. But its value should not be | |
45 | * changed for the translation methods. | |
46 | */ | |
47 | if (df_cfg.flags.heterogeneous) { | |
48 | /* Only Node 0 is accessible on DF3.5 systems. */ | |
49 | if (df_cfg.rev == DF3p5) | |
50 | node = 0; | |
51 | ||
52 | /* | |
53 | * Only the first Node in each Socket is accessible on | |
54 | * DF4.5 systems, and this is visible to software as one | |
55 | * Fabric per Socket. The Socket ID can be derived from | |
56 | * the Node ID and global shift values. | |
57 | */ | |
58 | if (df_cfg.rev == DF4p5) | |
59 | node >>= df_cfg.socket_id_shift - df_cfg.node_id_shift; | |
60 | } | |
61 | ||
62 | return node; | |
63 | } | |
64 | ||
3f317499 YG |
65 | static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) |
66 | { | |
67 | u32 ficaa_addr = 0x8C, ficad_addr = 0xB8; | |
68 | struct pci_dev *F4; | |
69 | int err = -ENODEV; | |
70 | u32 ficaa = 0; | |
71 | ||
453f0ae7 | 72 | node = get_accessible_node(node); |
3f317499 YG |
73 | if (node >= amd_nb_num()) |
74 | goto out; | |
75 | ||
76 | F4 = node_to_amd_nb(node)->link; | |
77 | if (!F4) | |
78 | goto out; | |
79 | ||
80 | /* Enable instance-specific access. */ | |
81 | if (instance_id != DF_BROADCAST) { | |
82 | ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1); | |
83 | ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id); | |
84 | } | |
85 | ||
86 | /* | |
87 | * The two least-significant bits are masked when inputing the | |
88 | * register offset to FICAA. | |
89 | */ | |
90 | reg >>= 2; | |
91 | ||
92 | if (df_cfg.flags.legacy_ficaa) { | |
93 | ficaa_addr = 0x5C; | |
94 | ficad_addr = 0x98; | |
95 | ||
96 | ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg); | |
97 | } else { | |
98 | ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg); | |
99 | } | |
100 | ||
101 | ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func); | |
102 | ||
103 | mutex_lock(&df_indirect_mutex); | |
104 | ||
105 | err = pci_write_config_dword(F4, ficaa_addr, ficaa); | |
106 | if (err) { | |
107 | pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa); | |
108 | goto out_unlock; | |
109 | } | |
110 | ||
111 | err = pci_read_config_dword(F4, ficad_addr, lo); | |
112 | if (err) | |
113 | pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa); | |
114 | ||
115 | pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x", | |
116 | node, instance_id, func, reg << 2, *lo); | |
117 | ||
118 | out_unlock: | |
119 | mutex_unlock(&df_indirect_mutex); | |
120 | ||
121 | out: | |
122 | return err; | |
123 | } | |
124 | ||
125 | int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo) | |
126 | { | |
127 | return __df_indirect_read(node, func, reg, instance_id, lo); | |
128 | } | |
129 | ||
130 | int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo) | |
131 | { | |
132 | return __df_indirect_read(node, func, reg, DF_BROADCAST, lo); | |
133 | } |