blob: 84b2030fc2f098fb23677c9d9d138f1e4abaa9f7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/init.h>
2#include <linux/pci.h>
Yinghai Lu871d5f82008-02-19 03:20:09 -08003#include <asm/pci-direct.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004#include <asm/mpspec.h>
5#include <linux/cpumask.h>
Yinghai Lu871d5f82008-02-19 03:20:09 -08006#include <linux/topology.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007
8/*
9 * This discovers the pcibus <-> node mapping on AMD K8.
Yinghai Lu30a18d62008-02-19 03:21:20 -080010 * also get peer root bus resource for io,mmio
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 */
12
Yinghai Lu35ddd062008-02-19 03:15:08 -080013
Yinghai Lu30a18d62008-02-19 03:21:20 -080014/*
15 * sub bus (transparent) will use entres from 3 to store extra from root,
16 * so need to make sure have enought slot there, increase PCI_BUS_NUM_RESOURCES?
17 */
18#define RES_NUM 16
19struct pci_root_info {
20 char name[12];
21 unsigned int res_num;
22 struct resource res[RES_NUM];
23 int bus_min;
24 int bus_max;
25 int node;
26 int link;
27};
28
29/* 4 at this time, it may become to 32 */
30#define PCI_ROOT_NR 4
31static int pci_root_num;
32static struct pci_root_info pci_root_info[PCI_ROOT_NR];
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Yinghai Lu871d5f82008-02-19 03:20:09 -080034#ifdef CONFIG_NUMA
35
36#define BUS_NR 256
37
38static int mp_bus_to_node[BUS_NR];
39
40void set_mp_bus_to_node(int busnum, int node)
41{
42 if (busnum >= 0 && busnum < BUS_NR)
43 mp_bus_to_node[busnum] = node;
44}
45
46int get_mp_bus_to_node(int busnum)
47{
48 int node = -1;
49
50 if (busnum < 0 || busnum > (BUS_NR - 1))
51 return node;
52
53 node = mp_bus_to_node[busnum];
54
55 /*
56 * let numa_node_id to decide it later in dma_alloc_pages
57 * if there is no ram on that node
58 */
59 if (node != -1 && !node_online(node))
60 node = -1;
61
62 return node;
63}
Yinghai Lu871d5f82008-02-19 03:20:09 -080064#endif
65
Yinghai Lu30a18d62008-02-19 03:21:20 -080066void set_pci_bus_resources_arch_default(struct pci_bus *b)
67{
68 int i;
69 int j;
70 struct pci_root_info *info;
71
72 if (!pci_root_num)
73 return;
74
75 for (i = 0; i < pci_root_num; i++) {
76 if (pci_root_info[i].bus_min == b->number)
77 break;
78 }
79
80 if (i == pci_root_num)
81 return;
82
83 info = &pci_root_info[i];
84 for (j = 0; j < info->res_num; j++) {
85 struct resource *res;
86 struct resource *root;
87
88 res = &info->res[j];
89 b->resource[j] = res;
90 if (res->flags & IORESOURCE_IO)
91 root = &ioport_resource;
92 else
93 root = &iomem_resource;
94 insert_resource(root, res);
95 }
96}
97
98#define RANGE_NUM 16
99
100struct res_range {
101 size_t start;
102 size_t end;
103};
104
105static void __init update_range(struct res_range *range, size_t start,
106 size_t end)
107{
108 int i;
109 int j;
110
111 for (j = 0; j < RANGE_NUM; j++) {
112 if (!range[j].end)
113 continue;
114 if (start == range[j].start && end < range[j].end) {
115 range[j].start = end + 1;
116 break;
117 } else if (start == range[j].start && end == range[j].end) {
118 range[j].start = 0;
119 range[j].end = 0;
120 break;
121 } else if (start > range[j].start && end == range[j].end) {
122 range[j].end = start - 1;
123 break;
124 } else if (start > range[j].start && end < range[j].end) {
125 /* find the new spare */
126 for (i = 0; i < RANGE_NUM; i++) {
127 if (range[i].end == 0)
128 break;
129 }
130 if (i < RANGE_NUM) {
131 range[i].end = range[j].end;
132 range[i].start = end + 1;
133 } else {
134 printk(KERN_ERR "run of slot in ranges\n");
135 }
136 range[j].end = start - 1;
137 break;
138 }
139 }
140}
141
142static void __init update_res(struct pci_root_info *info, size_t start,
143 size_t end, unsigned long flags, int merge)
144{
145 int i;
146 struct resource *res;
147
148 if (!merge)
149 goto addit;
150
151 /* try to merge it with old one */
152 for (i = 0; i < info->res_num; i++) {
153 res = &info->res[i];
154 if (res->flags != flags)
155 continue;
156 if (res->end + 1 == start) {
157 res->end = end;
158 return;
159 } else if (end + 1 == res->start) {
160 res->start = start;
161 return;
162 }
163 }
164
165addit:
166
167 /* need to add that */
168 if (info->res_num >= RES_NUM)
169 return;
170
171 res = &info->res[info->res_num];
172 res->name = info->name;
173 res->flags = flags;
174 res->start = start;
175 res->end = end;
176 res->child = NULL;
177 info->res_num++;
178}
179
180struct pci_hostbridge_probe {
181 u32 bus;
182 u32 slot;
183 u32 vendor;
184 u32 device;
185};
186
187static struct pci_hostbridge_probe pci_probes[] __initdata = {
188 { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1100 },
189 { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 },
190 { 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 },
191 { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1300 },
192};
193
Yinghai Lu6e184f22008-03-06 01:15:31 -0800194static u64 __initdata fam10h_mmconf_start;
195static u64 __initdata fam10h_mmconf_end;
196static void __init get_pci_mmcfg_amd_fam10h_range(void)
197{
198 u32 address;
199 u64 base, msr;
200 unsigned segn_busn_bits;
201
202 /* assume all cpus from fam10h have mmconf */
203 if (boot_cpu_data.x86 < 0x10)
204 return;
205
206 address = MSR_FAM10H_MMIO_CONF_BASE;
207 rdmsrl(address, msr);
208
209 /* mmconfig is not enable */
210 if (!(msr & FAM10H_MMIO_CONF_ENABLE))
211 return;
212
213 base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
214
215 segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
216 FAM10H_MMIO_CONF_BUSRANGE_MASK;
217
218 fam10h_mmconf_start = base;
219 fam10h_mmconf_end = base + (1ULL<<(segn_busn_bits + 20)) - 1;
220}
221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222/**
Yinghai Lu871d5f82008-02-19 03:20:09 -0800223 * early_fill_mp_bus_to_node()
224 * called before pcibios_scan_root and pci_scan_bus
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 * fills the mp_bus_to_cpumask array based according to the LDT Bus Number
226 * Registers found in the K8 northbridge
227 */
Yinghai Lu30a18d62008-02-19 03:21:20 -0800228static int __init early_fill_mp_bus_info(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
Yinghai Lu30a18d62008-02-19 03:21:20 -0800230 int i;
231 int j;
232 unsigned bus;
Yinghai Lu871d5f82008-02-19 03:20:09 -0800233 unsigned slot;
Yinghai Lu30a18d62008-02-19 03:21:20 -0800234 int found;
Yinghai Lu35ddd062008-02-19 03:15:08 -0800235 int node;
Yinghai Lu30a18d62008-02-19 03:21:20 -0800236 int link;
237 int def_node;
238 int def_link;
239 struct pci_root_info *info;
240 u32 reg;
241 struct resource *res;
242 size_t start;
243 size_t end;
244 struct res_range range[RANGE_NUM];
245 u64 val;
246 u32 address;
Yinghai Lu35ddd062008-02-19 03:15:08 -0800247
Yinghai Lu30a18d62008-02-19 03:21:20 -0800248#ifdef CONFIG_NUMA
Yinghai Lu871d5f82008-02-19 03:20:09 -0800249 for (i = 0; i < BUS_NR; i++)
250 mp_bus_to_node[i] = -1;
Yinghai Lu30a18d62008-02-19 03:21:20 -0800251#endif
Yinghai Lu871d5f82008-02-19 03:20:09 -0800252
253 if (!early_pci_allowed())
254 return -1;
255
Yinghai Lu30a18d62008-02-19 03:21:20 -0800256 found = 0;
257 for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
258 u32 id;
259 u16 device;
260 u16 vendor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Yinghai Lu30a18d62008-02-19 03:21:20 -0800262 bus = pci_probes[i].bus;
263 slot = pci_probes[i].slot;
264 id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID);
Yinghai Lu35ddd062008-02-19 03:15:08 -0800265
Yinghai Lu30a18d62008-02-19 03:21:20 -0800266 vendor = id & 0xffff;
267 device = (id>>16) & 0xffff;
268 if (pci_probes[i].vendor == vendor &&
269 pci_probes[i].device == device) {
270 found = 1;
271 break;
272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 }
274
Yinghai Lu30a18d62008-02-19 03:21:20 -0800275 if (!found)
276 return 0;
277
278 pci_root_num = 0;
279 for (i = 0; i < 4; i++) {
280 int min_bus;
281 int max_bus;
282 reg = read_pci_config(bus, slot, 1, 0xe0 + (i << 2));
283
284 /* Check if that register is enabled for bus range */
285 if ((reg & 7) != 3)
286 continue;
287
288 min_bus = (reg >> 16) & 0xff;
289 max_bus = (reg >> 24) & 0xff;
290 node = (reg >> 4) & 0x07;
291#ifdef CONFIG_NUMA
292 for (j = min_bus; j <= max_bus; j++)
293 mp_bus_to_node[j] = (unsigned char) node;
294#endif
295 link = (reg >> 8) & 0x03;
296
297 info = &pci_root_info[pci_root_num];
298 info->bus_min = min_bus;
299 info->bus_max = max_bus;
300 info->node = node;
301 info->link = link;
302 sprintf(info->name, "PCI Bus #%02x", min_bus);
303 pci_root_num++;
304 }
305
306 /* get the default node and link for left over res */
307 reg = read_pci_config(bus, slot, 0, 0x60);
308 def_node = (reg >> 8) & 0x07;
309 reg = read_pci_config(bus, slot, 0, 0x64);
310 def_link = (reg >> 8) & 0x03;
311
312 memset(range, 0, sizeof(range));
313 range[0].end = 0xffff;
314 /* io port resource */
315 for (i = 0; i < 4; i++) {
316 reg = read_pci_config(bus, slot, 1, 0xc0 + (i << 3));
317 if (!(reg & 3))
318 continue;
319
320 start = reg & 0xfff000;
321 reg = read_pci_config(bus, slot, 1, 0xc4 + (i << 3));
322 node = reg & 0x07;
323 link = (reg >> 4) & 0x03;
324 end = (reg & 0xfff000) | 0xfff;
325
326 /* find the position */
327 for (j = 0; j < pci_root_num; j++) {
328 info = &pci_root_info[j];
329 if (info->node == node && info->link == link)
330 break;
331 }
332 if (j == pci_root_num)
333 continue; /* not found */
334
335 info = &pci_root_info[j];
Yinghai Lu6e184f22008-03-06 01:15:31 -0800336 printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n",
337 node, link, (u64)start, (u64)end);
Yinghai Lu30a18d62008-02-19 03:21:20 -0800338 update_res(info, start, end, IORESOURCE_IO, 0);
339 update_range(range, start, end);
340 }
341 /* add left over io port range to def node/link, [0, 0xffff] */
342 /* find the position */
343 for (j = 0; j < pci_root_num; j++) {
344 info = &pci_root_info[j];
345 if (info->node == def_node && info->link == def_link)
346 break;
347 }
348 if (j < pci_root_num) {
349 info = &pci_root_info[j];
350 for (i = 0; i < RANGE_NUM; i++) {
351 if (!range[i].end)
352 continue;
353
354 update_res(info, range[i].start, range[i].end,
355 IORESOURCE_IO, 1);
356 }
357 }
358
359 memset(range, 0, sizeof(range));
360 /* 0xfd00000000-0xffffffffff for HT */
Yinghai Lu6e184f22008-03-06 01:15:31 -0800361 range[0].end = (0xfdULL<<32) - 1;
Yinghai Lu30a18d62008-02-19 03:21:20 -0800362
363 /* need to take out [0, TOM) for RAM*/
364 address = MSR_K8_TOP_MEM1;
365 rdmsrl(address, val);
366 end = (val & 0xffffff8000000ULL);
367 printk(KERN_INFO "TOM: %016lx aka %ldM\n", end, end>>20);
368 if (end < (1ULL<<32))
369 update_range(range, 0, end - 1);
370
Yinghai Lu6e184f22008-03-06 01:15:31 -0800371 /* get mmconfig */
372 get_pci_mmcfg_amd_fam10h_range();
373 /* need to take out mmconf range */
374 if (fam10h_mmconf_end) {
375 printk(KERN_DEBUG "Fam 10h mmconf [%llx, %llx]\n", fam10h_mmconf_start, fam10h_mmconf_end);
376 update_range(range, fam10h_mmconf_start, fam10h_mmconf_end);
377 }
378
Yinghai Lu30a18d62008-02-19 03:21:20 -0800379 /* mmio resource */
380 for (i = 0; i < 8; i++) {
381 reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3));
382 if (!(reg & 3))
383 continue;
384
385 start = reg & 0xffffff00; /* 39:16 on 31:8*/
386 start <<= 8;
387 reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3));
388 node = reg & 0x07;
389 link = (reg >> 4) & 0x03;
390 end = (reg & 0xffffff00);
391 end <<= 8;
392 end |= 0xffff;
393
394 /* find the position */
395 for (j = 0; j < pci_root_num; j++) {
396 info = &pci_root_info[j];
397 if (info->node == node && info->link == link)
398 break;
399 }
400 if (j == pci_root_num)
401 continue; /* not found */
402
403 info = &pci_root_info[j];
Yinghai Lu6e184f22008-03-06 01:15:31 -0800404
405 printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]",
406 node, link, (u64)start, (u64)end);
407 /*
408 * some sick allocation would have range overlap with fam10h
409 * mmconf range, so need to update start and end.
410 */
411 if (fam10h_mmconf_end) {
412 int changed = 0;
413 u64 endx = 0;
414 if (start >= fam10h_mmconf_start &&
415 start <= fam10h_mmconf_end) {
416 start = fam10h_mmconf_end + 1;
417 changed = 1;
418 }
419
420 if (end >= fam10h_mmconf_start &&
421 end <= fam10h_mmconf_end) {
422 end = fam10h_mmconf_start - 1;
423 changed = 1;
424 }
425
426 if (start < fam10h_mmconf_start &&
427 end > fam10h_mmconf_end) {
428 /* we got a hole */
429 endx = fam10h_mmconf_start - 1;
430 update_res(info, start, endx, IORESOURCE_MEM, 0);
431 update_range(range, start, endx);
432 printk(KERN_CONT " ==> [%llx, %llx]", (u64)start, endx);
433 start = fam10h_mmconf_end + 1;
434 changed = 1;
435 }
436 if (changed) {
437 if (start <= end) {
438 printk(KERN_CONT " %s [%llx, %llx]", endx?"and":"==>", (u64)start, (u64)end);
439 } else {
440 printk(KERN_CONT "%s\n", endx?"":" ==> none");
441 continue;
442 }
443 }
444 }
445
Yinghai Lu30a18d62008-02-19 03:21:20 -0800446 update_res(info, start, end, IORESOURCE_MEM, 0);
447 update_range(range, start, end);
Yinghai Lu6e184f22008-03-06 01:15:31 -0800448 printk(KERN_CONT "\n");
Yinghai Lu30a18d62008-02-19 03:21:20 -0800449 }
450
451 /* need to take out [4G, TOM2) for RAM*/
452 /* SYS_CFG */
453 address = MSR_K8_SYSCFG;
454 rdmsrl(address, val);
455 /* TOP_MEM2 is enabled? */
456 if (val & (1<<21)) {
457 /* TOP_MEM2 */
458 address = MSR_K8_TOP_MEM2;
459 rdmsrl(address, val);
460 end = (val & 0xffffff8000000ULL);
461 printk(KERN_INFO "TOM2: %016lx aka %ldM\n", end, end>>20);
462 update_range(range, 1ULL<<32, end - 1);
463 }
464
465 /*
466 * add left over mmio range to def node/link ?
467 * that is tricky, just record range in from start_min to 4G
468 */
469 for (j = 0; j < pci_root_num; j++) {
470 info = &pci_root_info[j];
471 if (info->node == def_node && info->link == def_link)
472 break;
473 }
474 if (j < pci_root_num) {
475 info = &pci_root_info[j];
476
477 for (i = 0; i < RANGE_NUM; i++) {
478 if (!range[i].end)
479 continue;
480
481 update_res(info, range[i].start, range[i].end,
482 IORESOURCE_MEM, 1);
483 }
484 }
485
486#ifdef CONFIG_NUMA
Yinghai Lu871d5f82008-02-19 03:20:09 -0800487 for (i = 0; i < BUS_NR; i++) {
Yinghai Lu35ddd062008-02-19 03:15:08 -0800488 node = mp_bus_to_node[i];
Yinghai Lu871d5f82008-02-19 03:20:09 -0800489 if (node >= 0)
490 printk(KERN_DEBUG "bus: %02x to node: %02x\n", i, node);
491 }
492#endif
Yinghai Lu30a18d62008-02-19 03:21:20 -0800493
494 for (i = 0; i < pci_root_num; i++) {
495 int res_num;
496 int busnum;
497
498 info = &pci_root_info[i];
499 res_num = info->res_num;
500 busnum = info->bus_min;
501 printk(KERN_DEBUG "bus: [%02x,%02x] on node %x link %x\n",
502 info->bus_min, info->bus_max, info->node, info->link);
503 for (j = 0; j < res_num; j++) {
504 res = &info->res[j];
505 printk(KERN_DEBUG "bus: %02x index %x %s: [%llx, %llx]\n",
506 busnum, j,
507 (res->flags & IORESOURCE_IO)?"io port":"mmio",
508 res->start, res->end);
509 }
510 }
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 return 0;
513}
514
Yinghai Lu30a18d62008-02-19 03:21:20 -0800515postcore_initcall(early_fill_mp_bus_info);