diff options
author | Yinghai Lu <Yinghai.Lu@Sun.COM> | 2008-02-19 06:21:20 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-04-26 17:41:04 -0400 |
commit | 30a18d6c3f1e774de656ebd8ff219d53e2ba4029 (patch) | |
tree | 22aad98a32fc182736f78193938a24175a168b81 /arch/x86/pci/k8-bus_64.c | |
parent | 35ddd068fb94b187e94a3fc497ccecf27bdda9ae (diff) |
x86: multi pci root bus with different io resource range, on 64-bit
scan AMD opteron io/mmio routing to make sure every pci root bus get correct
resource range. Thus later pci scan could assign correct resource to device
with unassigned resource.
this can fix a system without _CRS for multi pci root bus.
Signed-off-by: Yinghai Lu <yinghai.lu@sun.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/pci/k8-bus_64.c')
-rw-r--r-- | arch/x86/pci/k8-bus_64.c | 404 |
1 files changed, 354 insertions, 50 deletions
diff --git a/arch/x86/pci/k8-bus_64.c b/arch/x86/pci/k8-bus_64.c index dab38310ee97..5e8a9d105edd 100644 --- a/arch/x86/pci/k8-bus_64.c +++ b/arch/x86/pci/k8-bus_64.c | |||
@@ -7,23 +7,29 @@ | |||
7 | 7 | ||
8 | /* | 8 | /* |
9 | * This discovers the pcibus <-> node mapping on AMD K8. | 9 | * This discovers the pcibus <-> node mapping on AMD K8. |
10 | * | 10 | * also get peer root bus resource for io,mmio |
11 | * RED-PEN need to call this again on PCI hotplug | ||
12 | * RED-PEN empty cpus get reported wrong | ||
13 | */ | 11 | */ |
14 | 12 | ||
15 | #define NODE_ID(dword) ((dword>>4) & 0x07) | ||
16 | #define LDT_BUS_NUMBER_REGISTER_0 0xE0 | ||
17 | #define LDT_BUS_NUMBER_REGISTER_1 0xE4 | ||
18 | #define LDT_BUS_NUMBER_REGISTER_2 0xE8 | ||
19 | #define LDT_BUS_NUMBER_REGISTER_3 0xEC | ||
20 | #define NR_LDT_BUS_NUMBER_REGISTERS 4 | ||
21 | #define SECONDARY_LDT_BUS_NUMBER(dword) ((dword >> 16) & 0xFF) | ||
22 | #define SUBORDINATE_LDT_BUS_NUMBER(dword) ((dword >> 24) & 0xFF) | ||
23 | 13 | ||
24 | #define PCI_DEVICE_ID_K8HTCONFIG 0x1100 | 14 | /* |
25 | #define PCI_DEVICE_ID_K8_10H_HTCONFIG 0x1200 | 15 | * sub bus (transparent) will use entres from 3 to store extra from root, |
26 | #define PCI_DEVICE_ID_K8_11H_HTCONFIG 0x1300 | 16 | * so need to make sure have enought slot there, increase PCI_BUS_NUM_RESOURCES? |
17 | */ | ||
18 | #define RES_NUM 16 | ||
19 | struct 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 | ||
31 | static int pci_root_num; | ||
32 | static struct pci_root_info pci_root_info[PCI_ROOT_NR]; | ||
27 | 33 | ||
28 | #ifdef CONFIG_NUMA | 34 | #ifdef CONFIG_NUMA |
29 | 35 | ||
@@ -55,77 +61,375 @@ int get_mp_bus_to_node(int busnum) | |||
55 | 61 | ||
56 | return node; | 62 | return node; |
57 | } | 63 | } |
58 | |||
59 | #endif | 64 | #endif |
60 | 65 | ||
66 | void 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 | |||
100 | struct res_range { | ||
101 | size_t start; | ||
102 | size_t end; | ||
103 | }; | ||
104 | |||
105 | static 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 | |||
142 | static 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 | |||
165 | addit: | ||
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 | |||
180 | struct pci_hostbridge_probe { | ||
181 | u32 bus; | ||
182 | u32 slot; | ||
183 | u32 vendor; | ||
184 | u32 device; | ||
185 | }; | ||
186 | |||
187 | static 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 | |||
61 | /** | 194 | /** |
62 | * early_fill_mp_bus_to_node() | 195 | * early_fill_mp_bus_to_node() |
63 | * called before pcibios_scan_root and pci_scan_bus | 196 | * called before pcibios_scan_root and pci_scan_bus |
64 | * fills the mp_bus_to_cpumask array based according to the LDT Bus Number | 197 | * fills the mp_bus_to_cpumask array based according to the LDT Bus Number |
65 | * Registers found in the K8 northbridge | 198 | * Registers found in the K8 northbridge |
66 | */ | 199 | */ |
67 | __init static int | 200 | static int __init early_fill_mp_bus_info(void) |
68 | early_fill_mp_bus_to_node(void) | ||
69 | { | 201 | { |
70 | #ifdef CONFIG_NUMA | 202 | int i; |
71 | int i, j; | 203 | int j; |
204 | unsigned bus; | ||
72 | unsigned slot; | 205 | unsigned slot; |
73 | u32 ldtbus; | 206 | int found; |
74 | u32 id; | ||
75 | int node; | 207 | int node; |
76 | u16 deviceid; | 208 | int link; |
77 | u16 vendorid; | 209 | int def_node; |
78 | int min_bus; | 210 | int def_link; |
79 | int max_bus; | 211 | struct pci_root_info *info; |
80 | 212 | u32 reg; | |
81 | static int lbnr[NR_LDT_BUS_NUMBER_REGISTERS] = { | 213 | struct resource *res; |
82 | LDT_BUS_NUMBER_REGISTER_0, | 214 | size_t start; |
83 | LDT_BUS_NUMBER_REGISTER_1, | 215 | size_t end; |
84 | LDT_BUS_NUMBER_REGISTER_2, | 216 | struct res_range range[RANGE_NUM]; |
85 | LDT_BUS_NUMBER_REGISTER_3 | 217 | u64 val; |
86 | }; | 218 | u32 address; |
87 | 219 | ||
220 | #ifdef CONFIG_NUMA | ||
88 | for (i = 0; i < BUS_NR; i++) | 221 | for (i = 0; i < BUS_NR; i++) |
89 | mp_bus_to_node[i] = -1; | 222 | mp_bus_to_node[i] = -1; |
223 | #endif | ||
90 | 224 | ||
91 | if (!early_pci_allowed()) | 225 | if (!early_pci_allowed()) |
92 | return -1; | 226 | return -1; |
93 | 227 | ||
94 | slot = 0x18; | 228 | found = 0; |
95 | id = read_pci_config(0, slot, 0, PCI_VENDOR_ID); | 229 | for (i = 0; i < ARRAY_SIZE(pci_probes); i++) { |
230 | u32 id; | ||
231 | u16 device; | ||
232 | u16 vendor; | ||
96 | 233 | ||
97 | vendorid = id & 0xffff; | 234 | bus = pci_probes[i].bus; |
98 | if (vendorid != PCI_VENDOR_ID_AMD) | 235 | slot = pci_probes[i].slot; |
99 | goto out; | 236 | id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID); |
100 | 237 | ||
101 | deviceid = (id>>16) & 0xffff; | 238 | vendor = id & 0xffff; |
102 | if ((deviceid != PCI_DEVICE_ID_K8HTCONFIG) && | 239 | device = (id>>16) & 0xffff; |
103 | (deviceid != PCI_DEVICE_ID_K8_10H_HTCONFIG) && | 240 | if (pci_probes[i].vendor == vendor && |
104 | (deviceid != PCI_DEVICE_ID_K8_11H_HTCONFIG)) | 241 | pci_probes[i].device == device) { |
105 | goto out; | 242 | found = 1; |
243 | break; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | if (!found) | ||
248 | return 0; | ||
106 | 249 | ||
107 | for (i = 0; i < NR_LDT_BUS_NUMBER_REGISTERS; i++) { | 250 | pci_root_num = 0; |
108 | ldtbus = read_pci_config(0, slot, 1, lbnr[i]); | 251 | for (i = 0; i < 4; i++) { |
252 | int min_bus; | ||
253 | int max_bus; | ||
254 | reg = read_pci_config(bus, slot, 1, 0xe0 + (i << 2)); | ||
109 | 255 | ||
110 | /* Check if that register is enabled for bus range */ | 256 | /* Check if that register is enabled for bus range */ |
111 | if ((ldtbus & 7) != 3) | 257 | if ((reg & 7) != 3) |
112 | continue; | 258 | continue; |
113 | 259 | ||
114 | min_bus = SECONDARY_LDT_BUS_NUMBER(ldtbus); | 260 | min_bus = (reg >> 16) & 0xff; |
115 | max_bus = SUBORDINATE_LDT_BUS_NUMBER(ldtbus); | 261 | max_bus = (reg >> 24) & 0xff; |
116 | node = NODE_ID(ldtbus); | 262 | node = (reg >> 4) & 0x07; |
263 | #ifdef CONFIG_NUMA | ||
117 | for (j = min_bus; j <= max_bus; j++) | 264 | for (j = min_bus; j <= max_bus; j++) |
118 | mp_bus_to_node[j] = (unsigned char) node; | 265 | mp_bus_to_node[j] = (unsigned char) node; |
266 | #endif | ||
267 | link = (reg >> 8) & 0x03; | ||
268 | |||
269 | info = &pci_root_info[pci_root_num]; | ||
270 | info->bus_min = min_bus; | ||
271 | info->bus_max = max_bus; | ||
272 | info->node = node; | ||
273 | info->link = link; | ||
274 | sprintf(info->name, "PCI Bus #%02x", min_bus); | ||
275 | pci_root_num++; | ||
119 | } | 276 | } |
120 | 277 | ||
121 | out: | 278 | /* get the default node and link for left over res */ |
279 | reg = read_pci_config(bus, slot, 0, 0x60); | ||
280 | def_node = (reg >> 8) & 0x07; | ||
281 | reg = read_pci_config(bus, slot, 0, 0x64); | ||
282 | def_link = (reg >> 8) & 0x03; | ||
283 | |||
284 | memset(range, 0, sizeof(range)); | ||
285 | range[0].end = 0xffff; | ||
286 | /* io port resource */ | ||
287 | for (i = 0; i < 4; i++) { | ||
288 | reg = read_pci_config(bus, slot, 1, 0xc0 + (i << 3)); | ||
289 | if (!(reg & 3)) | ||
290 | continue; | ||
291 | |||
292 | start = reg & 0xfff000; | ||
293 | reg = read_pci_config(bus, slot, 1, 0xc4 + (i << 3)); | ||
294 | node = reg & 0x07; | ||
295 | link = (reg >> 4) & 0x03; | ||
296 | end = (reg & 0xfff000) | 0xfff; | ||
297 | |||
298 | /* find the position */ | ||
299 | for (j = 0; j < pci_root_num; j++) { | ||
300 | info = &pci_root_info[j]; | ||
301 | if (info->node == node && info->link == link) | ||
302 | break; | ||
303 | } | ||
304 | if (j == pci_root_num) | ||
305 | continue; /* not found */ | ||
306 | |||
307 | info = &pci_root_info[j]; | ||
308 | update_res(info, start, end, IORESOURCE_IO, 0); | ||
309 | update_range(range, start, end); | ||
310 | } | ||
311 | /* add left over io port range to def node/link, [0, 0xffff] */ | ||
312 | /* find the position */ | ||
313 | for (j = 0; j < pci_root_num; j++) { | ||
314 | info = &pci_root_info[j]; | ||
315 | if (info->node == def_node && info->link == def_link) | ||
316 | break; | ||
317 | } | ||
318 | if (j < pci_root_num) { | ||
319 | info = &pci_root_info[j]; | ||
320 | for (i = 0; i < RANGE_NUM; i++) { | ||
321 | if (!range[i].end) | ||
322 | continue; | ||
323 | |||
324 | update_res(info, range[i].start, range[i].end, | ||
325 | IORESOURCE_IO, 1); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | memset(range, 0, sizeof(range)); | ||
330 | /* 0xfd00000000-0xffffffffff for HT */ | ||
331 | /* 0xfc00000000-0xfcffffffff for Family 10h mmconfig*/ | ||
332 | range[0].end = 0xfbffffffffULL; | ||
333 | |||
334 | /* need to take out [0, TOM) for RAM*/ | ||
335 | address = MSR_K8_TOP_MEM1; | ||
336 | rdmsrl(address, val); | ||
337 | end = (val & 0xffffff8000000ULL); | ||
338 | printk(KERN_INFO "TOM: %016lx aka %ldM\n", end, end>>20); | ||
339 | if (end < (1ULL<<32)) | ||
340 | update_range(range, 0, end - 1); | ||
341 | |||
342 | /* mmio resource */ | ||
343 | for (i = 0; i < 8; i++) { | ||
344 | reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3)); | ||
345 | if (!(reg & 3)) | ||
346 | continue; | ||
347 | |||
348 | start = reg & 0xffffff00; /* 39:16 on 31:8*/ | ||
349 | start <<= 8; | ||
350 | reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3)); | ||
351 | node = reg & 0x07; | ||
352 | link = (reg >> 4) & 0x03; | ||
353 | end = (reg & 0xffffff00); | ||
354 | end <<= 8; | ||
355 | end |= 0xffff; | ||
356 | |||
357 | /* find the position */ | ||
358 | for (j = 0; j < pci_root_num; j++) { | ||
359 | info = &pci_root_info[j]; | ||
360 | if (info->node == node && info->link == link) | ||
361 | break; | ||
362 | } | ||
363 | if (j == pci_root_num) | ||
364 | continue; /* not found */ | ||
365 | |||
366 | info = &pci_root_info[j]; | ||
367 | update_res(info, start, end, IORESOURCE_MEM, 0); | ||
368 | update_range(range, start, end); | ||
369 | } | ||
370 | |||
371 | /* need to take out [4G, TOM2) for RAM*/ | ||
372 | /* SYS_CFG */ | ||
373 | address = MSR_K8_SYSCFG; | ||
374 | rdmsrl(address, val); | ||
375 | /* TOP_MEM2 is enabled? */ | ||
376 | if (val & (1<<21)) { | ||
377 | /* TOP_MEM2 */ | ||
378 | address = MSR_K8_TOP_MEM2; | ||
379 | rdmsrl(address, val); | ||
380 | end = (val & 0xffffff8000000ULL); | ||
381 | printk(KERN_INFO "TOM2: %016lx aka %ldM\n", end, end>>20); | ||
382 | update_range(range, 1ULL<<32, end - 1); | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * add left over mmio range to def node/link ? | ||
387 | * that is tricky, just record range in from start_min to 4G | ||
388 | */ | ||
389 | for (j = 0; j < pci_root_num; j++) { | ||
390 | info = &pci_root_info[j]; | ||
391 | if (info->node == def_node && info->link == def_link) | ||
392 | break; | ||
393 | } | ||
394 | if (j < pci_root_num) { | ||
395 | info = &pci_root_info[j]; | ||
396 | |||
397 | for (i = 0; i < RANGE_NUM; i++) { | ||
398 | if (!range[i].end) | ||
399 | continue; | ||
400 | |||
401 | update_res(info, range[i].start, range[i].end, | ||
402 | IORESOURCE_MEM, 1); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | #ifdef CONFIG_NUMA | ||
122 | for (i = 0; i < BUS_NR; i++) { | 407 | for (i = 0; i < BUS_NR; i++) { |
123 | node = mp_bus_to_node[i]; | 408 | node = mp_bus_to_node[i]; |
124 | if (node >= 0) | 409 | if (node >= 0) |
125 | printk(KERN_DEBUG "bus: %02x to node: %02x\n", i, node); | 410 | printk(KERN_DEBUG "bus: %02x to node: %02x\n", i, node); |
126 | } | 411 | } |
127 | #endif | 412 | #endif |
413 | |||
414 | for (i = 0; i < pci_root_num; i++) { | ||
415 | int res_num; | ||
416 | int busnum; | ||
417 | |||
418 | info = &pci_root_info[i]; | ||
419 | res_num = info->res_num; | ||
420 | busnum = info->bus_min; | ||
421 | printk(KERN_DEBUG "bus: [%02x,%02x] on node %x link %x\n", | ||
422 | info->bus_min, info->bus_max, info->node, info->link); | ||
423 | for (j = 0; j < res_num; j++) { | ||
424 | res = &info->res[j]; | ||
425 | printk(KERN_DEBUG "bus: %02x index %x %s: [%llx, %llx]\n", | ||
426 | busnum, j, | ||
427 | (res->flags & IORESOURCE_IO)?"io port":"mmio", | ||
428 | res->start, res->end); | ||
429 | } | ||
430 | } | ||
431 | |||
128 | return 0; | 432 | return 0; |
129 | } | 433 | } |
130 | 434 | ||
131 | postcore_initcall(early_fill_mp_bus_to_node); | 435 | postcore_initcall(early_fill_mp_bus_info); |