]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | #include <linux/init.h> |
3 | #include <linux/pci.h> | |
d199a048 | 4 | #include <linux/topology.h> |
91ede005 | 5 | #include <linux/cpu.h> |
27811d8c YL |
6 | #include <linux/range.h> |
7 | ||
24d9b70b | 8 | #include <asm/amd_nb.h> |
82487711 | 9 | #include <asm/pci_x86.h> |
3a27dd1c | 10 | |
871d5f8d | 11 | #include <asm/pci-direct.h> |
1da177e4 | 12 | |
99935a7a YL |
13 | #include "bus_numa.h" |
14 | ||
9e7f7231 SS |
15 | #define AMD_NB_F0_NODE_ID 0x60 |
16 | #define AMD_NB_F0_UNIT_ID 0x64 | |
17 | #define AMD_NB_F1_CONFIG_MAP_REG 0xe0 | |
18 | ||
19 | #define RANGE_NUM 16 | |
20 | #define AMD_NB_F1_CONFIG_MAP_RANGES 4 | |
1da177e4 | 21 | |
9e7f7231 | 22 | struct amd_hostbridge { |
30a18d6c YL |
23 | u32 bus; |
24 | u32 slot; | |
30a18d6c YL |
25 | u32 device; |
26 | }; | |
27 | ||
9e7f7231 SS |
28 | /* |
29 | * IMPORTANT NOTE: | |
30 | * hb_probes[] and early_root_info_init() is in maintenance mode. | |
31 | * It only supports K8, Fam10h, Fam11h, and Fam15h_00h-0fh . | |
32 | * Future processor will rely on information in ACPI. | |
33 | */ | |
34 | static struct amd_hostbridge hb_probes[] __initdata = { | |
35 | { 0, 0x18, 0x1100 }, /* K8 */ | |
36 | { 0, 0x18, 0x1200 }, /* Family10h */ | |
37 | { 0xff, 0, 0x1200 }, /* Family10h */ | |
38 | { 0, 0x18, 0x1300 }, /* Family11h */ | |
39 | { 0, 0x18, 0x1600 }, /* Family15h */ | |
30a18d6c YL |
40 | }; |
41 | ||
d28e5ac2 YL |
42 | static struct pci_root_info __init *find_pci_root_info(int node, int link) |
43 | { | |
44 | struct pci_root_info *info; | |
45 | ||
46 | /* find the position */ | |
47 | list_for_each_entry(info, &pci_root_infos, list) | |
48 | if (info->node == node && info->link == link) | |
49 | return info; | |
50 | ||
51 | return NULL; | |
52 | } | |
53 | ||
1da177e4 | 54 | /** |
9e7f7231 | 55 | * early_root_info_init() |
871d5f8d | 56 | * called before pcibios_scan_root and pci_scan_bus |
9e7f7231 SS |
57 | * fills the mp_bus_to_cpumask array based according |
58 | * to the LDT Bus Number Registers found in the northbridge. | |
1da177e4 | 59 | */ |
9e7f7231 | 60 | static int __init early_root_info_init(void) |
1da177e4 | 61 | { |
30a18d6c | 62 | int i; |
30a18d6c | 63 | unsigned bus; |
871d5f8d | 64 | unsigned slot; |
35ddd068 | 65 | int node; |
30a18d6c YL |
66 | int link; |
67 | int def_node; | |
68 | int def_link; | |
69 | struct pci_root_info *info; | |
70 | u32 reg; | |
97445c3b YL |
71 | u64 start; |
72 | u64 end; | |
27811d8c | 73 | struct range range[RANGE_NUM]; |
30a18d6c YL |
74 | u64 val; |
75 | u32 address; | |
3e3da00c | 76 | bool found; |
24d25dbf BH |
77 | struct resource fam10h_mmconf_res, *fam10h_mmconf; |
78 | u64 fam10h_mmconf_start; | |
79 | u64 fam10h_mmconf_end; | |
1da177e4 | 80 | |
871d5f8d YL |
81 | if (!early_pci_allowed()) |
82 | return -1; | |
83 | ||
3e3da00c | 84 | found = false; |
9e7f7231 | 85 | for (i = 0; i < ARRAY_SIZE(hb_probes); i++) { |
30a18d6c YL |
86 | u32 id; |
87 | u16 device; | |
88 | u16 vendor; | |
35ddd068 | 89 | |
9e7f7231 SS |
90 | bus = hb_probes[i].bus; |
91 | slot = hb_probes[i].slot; | |
30a18d6c | 92 | id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID); |
30a18d6c YL |
93 | vendor = id & 0xffff; |
94 | device = (id>>16) & 0xffff; | |
9e7f7231 SS |
95 | |
96 | if (vendor != PCI_VENDOR_ID_AMD) | |
97 | continue; | |
98 | ||
99 | if (hb_probes[i].device == device) { | |
3e3da00c | 100 | found = true; |
30a18d6c YL |
101 | break; |
102 | } | |
103 | } | |
104 | ||
3e3da00c | 105 | if (!found) |
30a18d6c | 106 | return 0; |
35ddd068 | 107 | |
94d4bb5b SS |
108 | /* |
109 | * We should learn topology and routing information from _PXM and | |
110 | * _CRS methods in the ACPI namespace. We extract node numbers | |
111 | * here to work around BIOSes that don't supply _PXM. | |
112 | */ | |
9e7f7231 | 113 | for (i = 0; i < AMD_NB_F1_CONFIG_MAP_RANGES; i++) { |
30a18d6c YL |
114 | int min_bus; |
115 | int max_bus; | |
9e7f7231 SS |
116 | reg = read_pci_config(bus, slot, 1, |
117 | AMD_NB_F1_CONFIG_MAP_REG + (i << 2)); | |
35ddd068 YL |
118 | |
119 | /* Check if that register is enabled for bus range */ | |
30a18d6c | 120 | if ((reg & 7) != 3) |
35ddd068 YL |
121 | continue; |
122 | ||
30a18d6c YL |
123 | min_bus = (reg >> 16) & 0xff; |
124 | max_bus = (reg >> 24) & 0xff; | |
125 | node = (reg >> 4) & 0x07; | |
30a18d6c YL |
126 | link = (reg >> 8) & 0x03; |
127 | ||
d28e5ac2 | 128 | info = alloc_pci_root_info(min_bus, max_bus, node, link); |
1da177e4 LT |
129 | } |
130 | ||
94d4bb5b SS |
131 | /* |
132 | * The following code extracts routing information for use on old | |
133 | * systems where Linux doesn't automatically use host bridge _CRS | |
134 | * methods (or when the user specifies "pci=nocrs"). | |
135 | * | |
136 | * We only do this through Fam11h, because _CRS should be enough on | |
137 | * newer systems. | |
138 | */ | |
139 | if (boot_cpu_data.x86 > 0x11) | |
140 | return 0; | |
141 | ||
30a18d6c | 142 | /* get the default node and link for left over res */ |
9e7f7231 | 143 | reg = read_pci_config(bus, slot, 0, AMD_NB_F0_NODE_ID); |
30a18d6c | 144 | def_node = (reg >> 8) & 0x07; |
9e7f7231 | 145 | reg = read_pci_config(bus, slot, 0, AMD_NB_F0_UNIT_ID); |
30a18d6c YL |
146 | def_link = (reg >> 8) & 0x03; |
147 | ||
148 | memset(range, 0, sizeof(range)); | |
e9a0064a | 149 | add_range(range, RANGE_NUM, 0, 0, 0xffff + 1); |
30a18d6c YL |
150 | /* io port resource */ |
151 | for (i = 0; i < 4; i++) { | |
152 | reg = read_pci_config(bus, slot, 1, 0xc0 + (i << 3)); | |
153 | if (!(reg & 3)) | |
154 | continue; | |
155 | ||
156 | start = reg & 0xfff000; | |
157 | reg = read_pci_config(bus, slot, 1, 0xc4 + (i << 3)); | |
158 | node = reg & 0x07; | |
159 | link = (reg >> 4) & 0x03; | |
160 | end = (reg & 0xfff000) | 0xfff; | |
161 | ||
d28e5ac2 YL |
162 | info = find_pci_root_info(node, link); |
163 | if (!info) | |
30a18d6c YL |
164 | continue; /* not found */ |
165 | ||
6e184f29 | 166 | printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", |
97445c3b | 167 | node, link, start, end); |
e8ee6f0a YL |
168 | |
169 | /* kernel only handle 16 bit only */ | |
170 | if (end > 0xffff) | |
171 | end = 0xffff; | |
172 | update_res(info, start, end, IORESOURCE_IO, 1); | |
e9a0064a | 173 | subtract_range(range, RANGE_NUM, start, end + 1); |
30a18d6c YL |
174 | } |
175 | /* add left over io port range to def node/link, [0, 0xffff] */ | |
176 | /* find the position */ | |
d28e5ac2 YL |
177 | info = find_pci_root_info(def_node, def_link); |
178 | if (info) { | |
30a18d6c YL |
179 | for (i = 0; i < RANGE_NUM; i++) { |
180 | if (!range[i].end) | |
181 | continue; | |
182 | ||
e9a0064a | 183 | update_res(info, range[i].start, range[i].end - 1, |
30a18d6c YL |
184 | IORESOURCE_IO, 1); |
185 | } | |
186 | } | |
187 | ||
188 | memset(range, 0, sizeof(range)); | |
189 | /* 0xfd00000000-0xffffffffff for HT */ | |
e9a0064a YL |
190 | end = cap_resource((0xfdULL<<32) - 1); |
191 | end++; | |
192 | add_range(range, RANGE_NUM, 0, 0, end); | |
30a18d6c YL |
193 | |
194 | /* need to take out [0, TOM) for RAM*/ | |
195 | address = MSR_K8_TOP_MEM1; | |
196 | rdmsrl(address, val); | |
8004dd96 | 197 | end = (val & 0xffffff800000ULL); |
97445c3b | 198 | printk(KERN_INFO "TOM: %016llx aka %lldM\n", end, end>>20); |
30a18d6c | 199 | if (end < (1ULL<<32)) |
e9a0064a | 200 | subtract_range(range, RANGE_NUM, 0, end); |
30a18d6c | 201 | |
6e184f29 | 202 | /* get mmconfig */ |
24d25dbf | 203 | fam10h_mmconf = amd_get_mmconfig_range(&fam10h_mmconf_res); |
6e184f29 | 204 | /* need to take out mmconf range */ |
24d25dbf BH |
205 | if (fam10h_mmconf) { |
206 | printk(KERN_DEBUG "Fam 10h mmconf %pR\n", fam10h_mmconf); | |
207 | fam10h_mmconf_start = fam10h_mmconf->start; | |
208 | fam10h_mmconf_end = fam10h_mmconf->end; | |
e9a0064a YL |
209 | subtract_range(range, RANGE_NUM, fam10h_mmconf_start, |
210 | fam10h_mmconf_end + 1); | |
24d25dbf BH |
211 | } else { |
212 | fam10h_mmconf_start = 0; | |
213 | fam10h_mmconf_end = 0; | |
6e184f29 YL |
214 | } |
215 | ||
30a18d6c YL |
216 | /* mmio resource */ |
217 | for (i = 0; i < 8; i++) { | |
218 | reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3)); | |
219 | if (!(reg & 3)) | |
220 | continue; | |
221 | ||
222 | start = reg & 0xffffff00; /* 39:16 on 31:8*/ | |
223 | start <<= 8; | |
224 | reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3)); | |
225 | node = reg & 0x07; | |
226 | link = (reg >> 4) & 0x03; | |
227 | end = (reg & 0xffffff00); | |
228 | end <<= 8; | |
229 | end |= 0xffff; | |
230 | ||
d28e5ac2 | 231 | info = find_pci_root_info(node, link); |
30a18d6c | 232 | |
d28e5ac2 YL |
233 | if (!info) |
234 | continue; | |
6e184f29 YL |
235 | |
236 | printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]", | |
97445c3b | 237 | node, link, start, end); |
6e184f29 YL |
238 | /* |
239 | * some sick allocation would have range overlap with fam10h | |
240 | * mmconf range, so need to update start and end. | |
241 | */ | |
242 | if (fam10h_mmconf_end) { | |
243 | int changed = 0; | |
244 | u64 endx = 0; | |
245 | if (start >= fam10h_mmconf_start && | |
246 | start <= fam10h_mmconf_end) { | |
247 | start = fam10h_mmconf_end + 1; | |
248 | changed = 1; | |
249 | } | |
250 | ||
251 | if (end >= fam10h_mmconf_start && | |
252 | end <= fam10h_mmconf_end) { | |
253 | end = fam10h_mmconf_start - 1; | |
254 | changed = 1; | |
255 | } | |
256 | ||
257 | if (start < fam10h_mmconf_start && | |
258 | end > fam10h_mmconf_end) { | |
259 | /* we got a hole */ | |
260 | endx = fam10h_mmconf_start - 1; | |
261 | update_res(info, start, endx, IORESOURCE_MEM, 0); | |
e9a0064a YL |
262 | subtract_range(range, RANGE_NUM, start, |
263 | endx + 1); | |
97445c3b | 264 | printk(KERN_CONT " ==> [%llx, %llx]", start, endx); |
6e184f29 YL |
265 | start = fam10h_mmconf_end + 1; |
266 | changed = 1; | |
267 | } | |
268 | if (changed) { | |
269 | if (start <= end) { | |
97445c3b | 270 | printk(KERN_CONT " %s [%llx, %llx]", endx ? "and" : "==>", start, end); |
6e184f29 YL |
271 | } else { |
272 | printk(KERN_CONT "%s\n", endx?"":" ==> none"); | |
273 | continue; | |
274 | } | |
275 | } | |
276 | } | |
277 | ||
9ad3f2c7 YL |
278 | update_res(info, cap_resource(start), cap_resource(end), |
279 | IORESOURCE_MEM, 1); | |
e9a0064a | 280 | subtract_range(range, RANGE_NUM, start, end + 1); |
6e184f29 | 281 | printk(KERN_CONT "\n"); |
30a18d6c YL |
282 | } |
283 | ||
284 | /* need to take out [4G, TOM2) for RAM*/ | |
285 | /* SYS_CFG */ | |
286 | address = MSR_K8_SYSCFG; | |
287 | rdmsrl(address, val); | |
288 | /* TOP_MEM2 is enabled? */ | |
289 | if (val & (1<<21)) { | |
290 | /* TOP_MEM2 */ | |
291 | address = MSR_K8_TOP_MEM2; | |
292 | rdmsrl(address, val); | |
8004dd96 | 293 | end = (val & 0xffffff800000ULL); |
97445c3b | 294 | printk(KERN_INFO "TOM2: %016llx aka %lldM\n", end, end>>20); |
e9a0064a | 295 | subtract_range(range, RANGE_NUM, 1ULL<<32, end); |
30a18d6c YL |
296 | } |
297 | ||
298 | /* | |
299 | * add left over mmio range to def node/link ? | |
300 | * that is tricky, just record range in from start_min to 4G | |
301 | */ | |
d28e5ac2 YL |
302 | info = find_pci_root_info(def_node, def_link); |
303 | if (info) { | |
30a18d6c YL |
304 | for (i = 0; i < RANGE_NUM; i++) { |
305 | if (!range[i].end) | |
306 | continue; | |
307 | ||
9ad3f2c7 | 308 | update_res(info, cap_resource(range[i].start), |
e9a0064a | 309 | cap_resource(range[i].end - 1), |
30a18d6c YL |
310 | IORESOURCE_MEM, 1); |
311 | } | |
312 | } | |
313 | ||
d28e5ac2 | 314 | list_for_each_entry(info, &pci_root_infos, list) { |
30a18d6c | 315 | int busnum; |
d28e5ac2 | 316 | struct pci_root_res *root_res; |
30a18d6c | 317 | |
a10bb128 YL |
318 | busnum = info->busn.start; |
319 | printk(KERN_DEBUG "bus: %pR on node %x link %x\n", | |
320 | &info->busn, info->node, info->link); | |
d28e5ac2 YL |
321 | list_for_each_entry(root_res, &info->resources, list) |
322 | printk(KERN_DEBUG "bus: %02x %pR\n", | |
323 | busnum, &root_res->res); | |
30a18d6c YL |
324 | } |
325 | ||
1da177e4 LT |
326 | return 0; |
327 | } | |
328 | ||
3a27dd1c RR |
329 | #define ENABLE_CF8_EXT_CFG (1ULL << 46) |
330 | ||
c8b877a5 | 331 | static int amd_bus_cpu_online(unsigned int cpu) |
3a27dd1c RR |
332 | { |
333 | u64 reg; | |
c8b877a5 | 334 | |
3a27dd1c RR |
335 | rdmsrl(MSR_AMD64_NB_CFG, reg); |
336 | if (!(reg & ENABLE_CF8_EXT_CFG)) { | |
337 | reg |= ENABLE_CF8_EXT_CFG; | |
338 | wrmsrl(MSR_AMD64_NB_CFG, reg); | |
339 | } | |
c8b877a5 | 340 | return 0; |
3a27dd1c RR |
341 | } |
342 | ||
24d9b70b JB |
343 | static void __init pci_enable_pci_io_ecs(void) |
344 | { | |
345 | #ifdef CONFIG_AMD_NB | |
346 | unsigned int i, n; | |
347 | ||
348 | for (n = i = 0; !n && amd_nb_bus_dev_ranges[i].dev_limit; ++i) { | |
349 | u8 bus = amd_nb_bus_dev_ranges[i].bus; | |
350 | u8 slot = amd_nb_bus_dev_ranges[i].dev_base; | |
351 | u8 limit = amd_nb_bus_dev_ranges[i].dev_limit; | |
352 | ||
353 | for (; slot < limit; ++slot) { | |
354 | u32 val = read_pci_config(bus, slot, 3, 0); | |
355 | ||
356 | if (!early_is_amd_nb(val)) | |
357 | continue; | |
358 | ||
359 | val = read_pci_config(bus, slot, 3, 0x8c); | |
360 | if (!(val & (ENABLE_CF8_EXT_CFG >> 32))) { | |
361 | val |= ENABLE_CF8_EXT_CFG >> 32; | |
362 | write_pci_config(bus, slot, 3, 0x8c, val); | |
363 | } | |
364 | ++n; | |
365 | } | |
366 | } | |
24d9b70b JB |
367 | #endif |
368 | } | |
369 | ||
91ede005 RR |
370 | static int __init pci_io_ecs_init(void) |
371 | { | |
c8b877a5 | 372 | int ret; |
91ede005 | 373 | |
3a27dd1c | 374 | /* assume all cpus from fam10h have IO ECS */ |
9e7f7231 | 375 | if (boot_cpu_data.x86 < 0x10) |
3a27dd1c | 376 | return 0; |
91ede005 | 377 | |
24d9b70b JB |
378 | /* Try the PCI method first. */ |
379 | if (early_pci_allowed()) | |
380 | pci_enable_pci_io_ecs(); | |
381 | ||
c8b877a5 SAS |
382 | ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "pci/amd_bus:online", |
383 | amd_bus_cpu_online, NULL); | |
384 | WARN_ON(ret < 0); | |
9f668f66 | 385 | |
3a27dd1c | 386 | pci_probe |= PCI_HAS_IO_ECS; |
91ede005 | 387 | |
3a27dd1c RR |
388 | return 0; |
389 | } | |
390 | ||
9b4e27b5 RR |
391 | static int __init amd_postcore_init(void) |
392 | { | |
393 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | |
394 | return 0; | |
395 | ||
9e7f7231 | 396 | early_root_info_init(); |
91ede005 | 397 | pci_io_ecs_init(); |
9b4e27b5 RR |
398 | |
399 | return 0; | |
400 | } | |
401 | ||
402 | postcore_initcall(amd_postcore_init); |