Commit | Line | Data |
---|---|---|
3f317499 YG |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * AMD Address Translation Library | |
4 | * | |
5 | * system.c : Functions to read and save system-wide data | |
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 | int determine_node_id(struct addr_ctx *ctx, u8 socket_id, u8 die_id) | |
16 | { | |
17 | u16 socket_id_bits, die_id_bits; | |
18 | ||
19 | if (socket_id > 0 && df_cfg.socket_id_mask == 0) { | |
20 | atl_debug(ctx, "Invalid socket inputs: socket_id=%u socket_id_mask=0x%x", | |
21 | socket_id, df_cfg.socket_id_mask); | |
22 | return -EINVAL; | |
23 | } | |
24 | ||
25 | /* Do each step independently to avoid shift out-of-bounds issues. */ | |
26 | socket_id_bits = socket_id; | |
27 | socket_id_bits <<= df_cfg.socket_id_shift; | |
28 | socket_id_bits &= df_cfg.socket_id_mask; | |
29 | ||
30 | if (die_id > 0 && df_cfg.die_id_mask == 0) { | |
31 | atl_debug(ctx, "Invalid die inputs: die_id=%u die_id_mask=0x%x", | |
32 | die_id, df_cfg.die_id_mask); | |
33 | return -EINVAL; | |
34 | } | |
35 | ||
36 | /* Do each step independently to avoid shift out-of-bounds issues. */ | |
37 | die_id_bits = die_id; | |
38 | die_id_bits <<= df_cfg.die_id_shift; | |
39 | die_id_bits &= df_cfg.die_id_mask; | |
40 | ||
41 | ctx->node_id = (socket_id_bits | die_id_bits) >> df_cfg.node_id_shift; | |
42 | return 0; | |
43 | } | |
44 | ||
45 | static void df2_get_masks_shifts(u32 mask0) | |
46 | { | |
47 | df_cfg.socket_id_shift = FIELD_GET(DF2_SOCKET_ID_SHIFT, mask0); | |
48 | df_cfg.socket_id_mask = FIELD_GET(DF2_SOCKET_ID_MASK, mask0); | |
49 | df_cfg.die_id_shift = FIELD_GET(DF2_DIE_ID_SHIFT, mask0); | |
50 | df_cfg.die_id_mask = FIELD_GET(DF2_DIE_ID_MASK, mask0); | |
51 | df_cfg.node_id_shift = df_cfg.die_id_shift; | |
52 | df_cfg.node_id_mask = df_cfg.socket_id_mask | df_cfg.die_id_mask; | |
53 | df_cfg.component_id_mask = ~df_cfg.node_id_mask; | |
54 | } | |
55 | ||
56 | static void df3_get_masks_shifts(u32 mask0, u32 mask1) | |
57 | { | |
58 | df_cfg.component_id_mask = FIELD_GET(DF3_COMPONENT_ID_MASK, mask0); | |
59 | df_cfg.node_id_mask = FIELD_GET(DF3_NODE_ID_MASK, mask0); | |
60 | ||
61 | df_cfg.node_id_shift = FIELD_GET(DF3_NODE_ID_SHIFT, mask1); | |
62 | df_cfg.socket_id_shift = FIELD_GET(DF3_SOCKET_ID_SHIFT, mask1); | |
63 | df_cfg.socket_id_mask = FIELD_GET(DF3_SOCKET_ID_MASK, mask1); | |
64 | df_cfg.die_id_mask = FIELD_GET(DF3_DIE_ID_MASK, mask1); | |
65 | } | |
66 | ||
67 | static void df3p5_get_masks_shifts(u32 mask0, u32 mask1, u32 mask2) | |
68 | { | |
69 | df_cfg.component_id_mask = FIELD_GET(DF4_COMPONENT_ID_MASK, mask0); | |
70 | df_cfg.node_id_mask = FIELD_GET(DF4_NODE_ID_MASK, mask0); | |
71 | ||
72 | df_cfg.node_id_shift = FIELD_GET(DF3_NODE_ID_SHIFT, mask1); | |
73 | df_cfg.socket_id_shift = FIELD_GET(DF4_SOCKET_ID_SHIFT, mask1); | |
74 | ||
75 | df_cfg.socket_id_mask = FIELD_GET(DF4_SOCKET_ID_MASK, mask2); | |
76 | df_cfg.die_id_mask = FIELD_GET(DF4_DIE_ID_MASK, mask2); | |
77 | } | |
78 | ||
79 | static void df4_get_masks_shifts(u32 mask0, u32 mask1, u32 mask2) | |
80 | { | |
81 | df3p5_get_masks_shifts(mask0, mask1, mask2); | |
82 | ||
83 | if (!(df_cfg.flags.socket_id_shift_quirk && df_cfg.socket_id_shift == 1)) | |
84 | return; | |
85 | ||
86 | df_cfg.socket_id_shift = 0; | |
87 | df_cfg.socket_id_mask = 1; | |
88 | df_cfg.die_id_shift = 0; | |
89 | df_cfg.die_id_mask = 0; | |
90 | df_cfg.node_id_shift = 8; | |
91 | df_cfg.node_id_mask = 0x100; | |
92 | } | |
93 | ||
94 | static int df4_get_fabric_id_mask_registers(void) | |
95 | { | |
96 | u32 mask0, mask1, mask2; | |
97 | ||
98 | /* Read D18F4x1B0 (SystemFabricIdMask0) */ | |
99 | if (df_indirect_read_broadcast(0, 4, 0x1B0, &mask0)) | |
100 | return -EINVAL; | |
101 | ||
102 | /* Read D18F4x1B4 (SystemFabricIdMask1) */ | |
103 | if (df_indirect_read_broadcast(0, 4, 0x1B4, &mask1)) | |
104 | return -EINVAL; | |
105 | ||
106 | /* Read D18F4x1B8 (SystemFabricIdMask2) */ | |
107 | if (df_indirect_read_broadcast(0, 4, 0x1B8, &mask2)) | |
108 | return -EINVAL; | |
109 | ||
110 | df4_get_masks_shifts(mask0, mask1, mask2); | |
111 | return 0; | |
112 | } | |
113 | ||
114 | static int df4_determine_df_rev(u32 reg) | |
115 | { | |
116 | df_cfg.rev = FIELD_GET(DF_MINOR_REVISION, reg) < 5 ? DF4 : DF4p5; | |
117 | ||
118 | /* Check for special cases or quirks based on Device/Vendor IDs.*/ | |
119 | ||
120 | /* Read D18F0x000 (DeviceVendorId0) */ | |
121 | if (df_indirect_read_broadcast(0, 0, 0, ®)) | |
122 | return -EINVAL; | |
123 | ||
124 | if (reg == DF_FUNC0_ID_ZEN4_SERVER) | |
125 | df_cfg.flags.socket_id_shift_quirk = 1; | |
126 | ||
87a61237 | 127 | if (reg == DF_FUNC0_ID_MI300) { |
453f0ae7 M |
128 | df_cfg.flags.heterogeneous = 1; |
129 | ||
87a61237 YG |
130 | if (get_addr_hash_mi300()) |
131 | return -EINVAL; | |
132 | } | |
133 | ||
3f317499 YG |
134 | return df4_get_fabric_id_mask_registers(); |
135 | } | |
136 | ||
137 | static int determine_df_rev_legacy(void) | |
138 | { | |
139 | u32 fabric_id_mask0, fabric_id_mask1, fabric_id_mask2; | |
140 | ||
141 | /* | |
142 | * Check for DF3.5. | |
143 | * | |
144 | * Component ID Mask must be non-zero. Register D18F1x150 is | |
145 | * reserved pre-DF3.5, so value will be Read-as-Zero. | |
146 | */ | |
147 | ||
148 | /* Read D18F1x150 (SystemFabricIdMask0). */ | |
149 | if (df_indirect_read_broadcast(0, 1, 0x150, &fabric_id_mask0)) | |
150 | return -EINVAL; | |
151 | ||
152 | if (FIELD_GET(DF4_COMPONENT_ID_MASK, fabric_id_mask0)) { | |
153 | df_cfg.rev = DF3p5; | |
154 | ||
155 | /* Read D18F1x154 (SystemFabricIdMask1) */ | |
156 | if (df_indirect_read_broadcast(0, 1, 0x154, &fabric_id_mask1)) | |
157 | return -EINVAL; | |
158 | ||
159 | /* Read D18F1x158 (SystemFabricIdMask2) */ | |
160 | if (df_indirect_read_broadcast(0, 1, 0x158, &fabric_id_mask2)) | |
161 | return -EINVAL; | |
162 | ||
163 | df3p5_get_masks_shifts(fabric_id_mask0, fabric_id_mask1, fabric_id_mask2); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | /* | |
168 | * Check for DF3. | |
169 | * | |
170 | * Component ID Mask must be non-zero. Field is Read-as-Zero on DF2. | |
171 | */ | |
172 | ||
173 | /* Read D18F1x208 (SystemFabricIdMask). */ | |
174 | if (df_indirect_read_broadcast(0, 1, 0x208, &fabric_id_mask0)) | |
175 | return -EINVAL; | |
176 | ||
177 | if (FIELD_GET(DF3_COMPONENT_ID_MASK, fabric_id_mask0)) { | |
178 | df_cfg.rev = DF3; | |
179 | ||
180 | /* Read D18F1x20C (SystemFabricIdMask1) */ | |
181 | if (df_indirect_read_broadcast(0, 1, 0x20C, &fabric_id_mask1)) | |
182 | return -EINVAL; | |
183 | ||
184 | df3_get_masks_shifts(fabric_id_mask0, fabric_id_mask1); | |
185 | return 0; | |
186 | } | |
187 | ||
188 | /* Default to DF2. */ | |
189 | df_cfg.rev = DF2; | |
190 | df2_get_masks_shifts(fabric_id_mask0); | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static int determine_df_rev(void) | |
195 | { | |
196 | u32 reg; | |
197 | u8 rev; | |
198 | ||
199 | if (df_cfg.rev != UNKNOWN) | |
200 | return 0; | |
201 | ||
202 | /* Read D18F0x40 (FabricBlockInstanceCount). */ | |
203 | if (df_indirect_read_broadcast(0, 0, 0x40, ®)) | |
204 | return -EINVAL; | |
205 | ||
206 | /* | |
207 | * Revision fields added for DF4 and later. | |
208 | * | |
209 | * Major revision of '0' is found pre-DF4. Field is Read-as-Zero. | |
210 | */ | |
211 | rev = FIELD_GET(DF_MAJOR_REVISION, reg); | |
212 | if (!rev) | |
213 | return determine_df_rev_legacy(); | |
214 | ||
215 | /* | |
216 | * Fail out for major revisions other than '4'. | |
217 | * | |
218 | * Explicit support should be added for newer systems to avoid issues. | |
219 | */ | |
220 | if (rev == 4) | |
221 | return df4_determine_df_rev(reg); | |
222 | ||
223 | return -EINVAL; | |
224 | } | |
225 | ||
226 | static void get_num_maps(void) | |
227 | { | |
228 | switch (df_cfg.rev) { | |
229 | case DF2: | |
230 | case DF3: | |
231 | case DF3p5: | |
232 | df_cfg.num_coh_st_maps = 2; | |
233 | break; | |
234 | case DF4: | |
235 | case DF4p5: | |
236 | df_cfg.num_coh_st_maps = 4; | |
237 | break; | |
238 | default: | |
239 | atl_debug_on_bad_df_rev(); | |
240 | } | |
241 | } | |
242 | ||
243 | static void apply_node_id_shift(void) | |
244 | { | |
245 | if (df_cfg.rev == DF2) | |
246 | return; | |
247 | ||
248 | df_cfg.die_id_shift = df_cfg.node_id_shift; | |
249 | df_cfg.die_id_mask <<= df_cfg.node_id_shift; | |
250 | df_cfg.socket_id_mask <<= df_cfg.node_id_shift; | |
251 | df_cfg.socket_id_shift += df_cfg.node_id_shift; | |
252 | } | |
253 | ||
254 | static void dump_df_cfg(void) | |
255 | { | |
256 | pr_debug("rev=0x%x", df_cfg.rev); | |
257 | ||
258 | pr_debug("component_id_mask=0x%x", df_cfg.component_id_mask); | |
259 | pr_debug("die_id_mask=0x%x", df_cfg.die_id_mask); | |
260 | pr_debug("node_id_mask=0x%x", df_cfg.node_id_mask); | |
261 | pr_debug("socket_id_mask=0x%x", df_cfg.socket_id_mask); | |
262 | ||
263 | pr_debug("die_id_shift=0x%x", df_cfg.die_id_shift); | |
264 | pr_debug("node_id_shift=0x%x", df_cfg.node_id_shift); | |
265 | pr_debug("socket_id_shift=0x%x", df_cfg.socket_id_shift); | |
266 | ||
267 | pr_debug("num_coh_st_maps=%u", df_cfg.num_coh_st_maps); | |
268 | ||
269 | pr_debug("flags.legacy_ficaa=%u", df_cfg.flags.legacy_ficaa); | |
270 | pr_debug("flags.socket_id_shift_quirk=%u", df_cfg.flags.socket_id_shift_quirk); | |
271 | } | |
272 | ||
273 | int get_df_system_info(void) | |
274 | { | |
275 | if (determine_df_rev()) { | |
276 | pr_warn("amd_atl: Failed to determine DF Revision"); | |
277 | df_cfg.rev = UNKNOWN; | |
278 | return -EINVAL; | |
279 | } | |
280 | ||
281 | apply_node_id_shift(); | |
282 | ||
283 | get_num_maps(); | |
284 | ||
285 | dump_df_cfg(); | |
286 | ||
287 | return 0; | |
288 | } |