diff options
author | Yinghai Lu <yinghai@kernel.org> | 2009-03-19 23:55:35 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-03-20 14:40:22 -0400 |
commit | 068258bc15439c11a966e873f931cc8e513dca61 (patch) | |
tree | 956f247105e770adffc1fca68820668a73b484a9 | |
parent | fafad5bf06c3a3bb8b24b28b6f065367e7411872 (diff) |
x86/PCI: host mmconfig detect clean up
Fix mmconfig detection to not assume a single mmconfig space in the
northbridge, paving the way for AMD fam10h + mcp55 CPUs. On those, the
MSR has some range, but the mcp55 pci config will have another one.
Also helps the mcp55 + io55 case, where every one will have one range.
If it is mcp55, exclude the range that is used by CPU MSR, in other
words , if the CPU claims busses 0-255, the range in mcp55 is dropped,
because CPU HW will not route those ranges to mcp55 mmconfig to handle
it.
Signed-off-by: Yinghai Lu <yinghai.lu@kernel.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 163 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_64.c | 17 |
2 files changed, 120 insertions, 60 deletions
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index d68dc1bb01b2..905bb526b133 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/acpi.h> | 15 | #include <linux/acpi.h> |
16 | #include <linux/bitmap.h> | 16 | #include <linux/bitmap.h> |
17 | #include <linux/sort.h> | ||
17 | #include <asm/e820.h> | 18 | #include <asm/e820.h> |
18 | #include <asm/pci_x86.h> | 19 | #include <asm/pci_x86.h> |
19 | 20 | ||
@@ -24,24 +25,49 @@ | |||
24 | /* Indicate if the mmcfg resources have been placed into the resource table. */ | 25 | /* Indicate if the mmcfg resources have been placed into the resource table. */ |
25 | static int __initdata pci_mmcfg_resources_inserted; | 26 | static int __initdata pci_mmcfg_resources_inserted; |
26 | 27 | ||
28 | static __init int extend_mmcfg(int num) | ||
29 | { | ||
30 | struct acpi_mcfg_allocation *new; | ||
31 | int new_num = pci_mmcfg_config_num + num; | ||
32 | |||
33 | new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL); | ||
34 | if (!new) | ||
35 | return -1; | ||
36 | |||
37 | if (pci_mmcfg_config) { | ||
38 | memcpy(new, pci_mmcfg_config, | ||
39 | sizeof(pci_mmcfg_config[0]) * new_num); | ||
40 | kfree(pci_mmcfg_config); | ||
41 | } | ||
42 | pci_mmcfg_config = new; | ||
43 | |||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end) | ||
48 | { | ||
49 | int i = pci_mmcfg_config_num; | ||
50 | |||
51 | pci_mmcfg_config_num++; | ||
52 | pci_mmcfg_config[i].address = addr; | ||
53 | pci_mmcfg_config[i].pci_segment = segment; | ||
54 | pci_mmcfg_config[i].start_bus_number = start; | ||
55 | pci_mmcfg_config[i].end_bus_number = end; | ||
56 | } | ||
57 | |||
27 | static const char __init *pci_mmcfg_e7520(void) | 58 | static const char __init *pci_mmcfg_e7520(void) |
28 | { | 59 | { |
29 | u32 win; | 60 | u32 win; |
30 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); | 61 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); |
31 | 62 | ||
32 | win = win & 0xf000; | 63 | win = win & 0xf000; |
33 | if(win == 0x0000 || win == 0xf000) | 64 | if (win == 0x0000 || win == 0xf000) |
34 | pci_mmcfg_config_num = 0; | 65 | return NULL; |
35 | else { | 66 | |
36 | pci_mmcfg_config_num = 1; | 67 | if (extend_mmcfg(1) == -1) |
37 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL); | 68 | return NULL; |
38 | if (!pci_mmcfg_config) | 69 | |
39 | return NULL; | 70 | fill_one_mmcfg(win << 16, 0, 0, 255); |
40 | pci_mmcfg_config[0].address = win << 16; | ||
41 | pci_mmcfg_config[0].pci_segment = 0; | ||
42 | pci_mmcfg_config[0].start_bus_number = 0; | ||
43 | pci_mmcfg_config[0].end_bus_number = 255; | ||
44 | } | ||
45 | 71 | ||
46 | return "Intel Corporation E7520 Memory Controller Hub"; | 72 | return "Intel Corporation E7520 Memory Controller Hub"; |
47 | } | 73 | } |
@@ -50,13 +76,11 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
50 | { | 76 | { |
51 | u32 pciexbar, mask = 0, len = 0; | 77 | u32 pciexbar, mask = 0, len = 0; |
52 | 78 | ||
53 | pci_mmcfg_config_num = 1; | ||
54 | |||
55 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); | 79 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); |
56 | 80 | ||
57 | /* Enable bit */ | 81 | /* Enable bit */ |
58 | if (!(pciexbar & 1)) | 82 | if (!(pciexbar & 1)) |
59 | pci_mmcfg_config_num = 0; | 83 | return NULL; |
60 | 84 | ||
61 | /* Size bits */ | 85 | /* Size bits */ |
62 | switch ((pciexbar >> 1) & 3) { | 86 | switch ((pciexbar >> 1) & 3) { |
@@ -73,28 +97,23 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
73 | len = 0x04000000U; | 97 | len = 0x04000000U; |
74 | break; | 98 | break; |
75 | default: | 99 | default: |
76 | pci_mmcfg_config_num = 0; | 100 | return NULL; |
77 | } | 101 | } |
78 | 102 | ||
79 | /* Errata #2, things break when not aligned on a 256Mb boundary */ | 103 | /* Errata #2, things break when not aligned on a 256Mb boundary */ |
80 | /* Can only happen in 64M/128M mode */ | 104 | /* Can only happen in 64M/128M mode */ |
81 | 105 | ||
82 | if ((pciexbar & mask) & 0x0fffffffU) | 106 | if ((pciexbar & mask) & 0x0fffffffU) |
83 | pci_mmcfg_config_num = 0; | 107 | return NULL; |
84 | 108 | ||
85 | /* Don't hit the APIC registers and their friends */ | 109 | /* Don't hit the APIC registers and their friends */ |
86 | if ((pciexbar & mask) >= 0xf0000000U) | 110 | if ((pciexbar & mask) >= 0xf0000000U) |
87 | pci_mmcfg_config_num = 0; | 111 | return NULL; |
88 | 112 | ||
89 | if (pci_mmcfg_config_num) { | 113 | if (extend_mmcfg(1) == -1) |
90 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL); | 114 | return NULL; |
91 | if (!pci_mmcfg_config) | 115 | |
92 | return NULL; | 116 | fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1); |
93 | pci_mmcfg_config[0].address = pciexbar & mask; | ||
94 | pci_mmcfg_config[0].pci_segment = 0; | ||
95 | pci_mmcfg_config[0].start_bus_number = 0; | ||
96 | pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1; | ||
97 | } | ||
98 | 117 | ||
99 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; | 118 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; |
100 | } | 119 | } |
@@ -138,18 +157,11 @@ static const char __init *pci_mmcfg_amd_fam10h(void) | |||
138 | busnbits = 8; | 157 | busnbits = 8; |
139 | } | 158 | } |
140 | 159 | ||
141 | pci_mmcfg_config_num = (1 << segnbits); | 160 | if (extend_mmcfg(1 << segnbits) == -1) |
142 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]) * | ||
143 | pci_mmcfg_config_num, GFP_KERNEL); | ||
144 | if (!pci_mmcfg_config) | ||
145 | return NULL; | 161 | return NULL; |
146 | 162 | ||
147 | for (i = 0; i < (1 << segnbits); i++) { | 163 | for (i = 0; i < (1 << segnbits); i++) |
148 | pci_mmcfg_config[i].address = base + (1<<28) * i; | 164 | fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1); |
149 | pci_mmcfg_config[i].pci_segment = i; | ||
150 | pci_mmcfg_config[i].start_bus_number = 0; | ||
151 | pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1; | ||
152 | } | ||
153 | 165 | ||
154 | return "AMD Family 10h NB"; | 166 | return "AMD Family 10h NB"; |
155 | } | 167 | } |
@@ -237,6 +249,48 @@ static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { | |||
237 | 0x0369, pci_mmcfg_nvidia_mcp55 }, | 249 | 0x0369, pci_mmcfg_nvidia_mcp55 }, |
238 | }; | 250 | }; |
239 | 251 | ||
252 | static int __init cmp_mmcfg(const void *x1, const void *x2) | ||
253 | { | ||
254 | const typeof(pci_mmcfg_config[0]) *m1 = x1; | ||
255 | const typeof(pci_mmcfg_config[0]) *m2 = x2; | ||
256 | int start1, start2; | ||
257 | |||
258 | start1 = m1->start_bus_number; | ||
259 | start2 = m2->start_bus_number; | ||
260 | |||
261 | return start1 - start2; | ||
262 | } | ||
263 | |||
264 | static void __init pci_mmcfg_check_end_bus_number(void) | ||
265 | { | ||
266 | int i; | ||
267 | typeof(pci_mmcfg_config[0]) *cfg, *cfgx; | ||
268 | |||
269 | /* sort them at first */ | ||
270 | sort(pci_mmcfg_config, pci_mmcfg_config_num, | ||
271 | sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL); | ||
272 | |||
273 | /* last one*/ | ||
274 | if (pci_mmcfg_config_num > 0) { | ||
275 | i = pci_mmcfg_config_num - 1; | ||
276 | cfg = &pci_mmcfg_config[i]; | ||
277 | if (cfg->end_bus_number < cfg->start_bus_number) | ||
278 | cfg->end_bus_number = 255; | ||
279 | } | ||
280 | |||
281 | /* don't overlap please */ | ||
282 | for (i = 0; i < pci_mmcfg_config_num - 1; i++) { | ||
283 | cfg = &pci_mmcfg_config[i]; | ||
284 | cfgx = &pci_mmcfg_config[i+1]; | ||
285 | |||
286 | if (cfg->end_bus_number < cfg->start_bus_number) | ||
287 | cfg->end_bus_number = 255; | ||
288 | |||
289 | if (cfg->end_bus_number >= cfgx->start_bus_number) | ||
290 | cfg->end_bus_number = cfgx->start_bus_number - 1; | ||
291 | } | ||
292 | } | ||
293 | |||
240 | static int __init pci_mmcfg_check_hostbridge(void) | 294 | static int __init pci_mmcfg_check_hostbridge(void) |
241 | { | 295 | { |
242 | u32 l; | 296 | u32 l; |
@@ -250,31 +304,33 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
250 | 304 | ||
251 | pci_mmcfg_config_num = 0; | 305 | pci_mmcfg_config_num = 0; |
252 | pci_mmcfg_config = NULL; | 306 | pci_mmcfg_config = NULL; |
253 | name = NULL; | ||
254 | 307 | ||
255 | for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) { | 308 | for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) { |
256 | bus = pci_mmcfg_probes[i].bus; | 309 | bus = pci_mmcfg_probes[i].bus; |
257 | devfn = pci_mmcfg_probes[i].devfn; | 310 | devfn = pci_mmcfg_probes[i].devfn; |
258 | raw_pci_ops->read(0, bus, devfn, 0, 4, &l); | 311 | raw_pci_ops->read(0, bus, devfn, 0, 4, &l); |
259 | vendor = l & 0xffff; | 312 | vendor = l & 0xffff; |
260 | device = (l >> 16) & 0xffff; | 313 | device = (l >> 16) & 0xffff; |
261 | 314 | ||
315 | name = NULL; | ||
262 | if (pci_mmcfg_probes[i].vendor == vendor && | 316 | if (pci_mmcfg_probes[i].vendor == vendor && |
263 | pci_mmcfg_probes[i].device == device) | 317 | pci_mmcfg_probes[i].device == device) |
264 | name = pci_mmcfg_probes[i].probe(); | 318 | name = pci_mmcfg_probes[i].probe(); |
265 | } | ||
266 | 319 | ||
267 | if (name) { | 320 | if (name) |
268 | printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n", | 321 | printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n", |
269 | name, pci_mmcfg_config_num ? "with" : "without"); | 322 | name); |
270 | } | 323 | } |
271 | 324 | ||
272 | return name != NULL; | 325 | /* some end_bus_number is crazy, fix it */ |
326 | pci_mmcfg_check_end_bus_number(); | ||
327 | |||
328 | return pci_mmcfg_config_num != 0; | ||
273 | } | 329 | } |
274 | 330 | ||
275 | static void __init pci_mmcfg_insert_resources(void) | 331 | static void __init pci_mmcfg_insert_resources(void) |
276 | { | 332 | { |
277 | #define PCI_MMCFG_RESOURCE_NAME_LEN 19 | 333 | #define PCI_MMCFG_RESOURCE_NAME_LEN 24 |
278 | int i; | 334 | int i; |
279 | struct resource *res; | 335 | struct resource *res; |
280 | char *names; | 336 | char *names; |
@@ -292,9 +348,10 @@ static void __init pci_mmcfg_insert_resources(void) | |||
292 | struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i]; | 348 | struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i]; |
293 | num_buses = cfg->end_bus_number - cfg->start_bus_number + 1; | 349 | num_buses = cfg->end_bus_number - cfg->start_bus_number + 1; |
294 | res->name = names; | 350 | res->name = names; |
295 | snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u", | 351 | snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, |
296 | cfg->pci_segment); | 352 | "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment, |
297 | res->start = cfg->address; | 353 | cfg->start_bus_number, cfg->end_bus_number); |
354 | res->start = cfg->address + (cfg->start_bus_number << 20); | ||
298 | res->end = res->start + (num_buses << 20) - 1; | 355 | res->end = res->start + (num_buses << 20) - 1; |
299 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; | 356 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; |
300 | insert_resource(&iomem_resource, res); | 357 | insert_resource(&iomem_resource, res); |
@@ -418,8 +475,6 @@ static void __init pci_mmcfg_reject_broken(int early) | |||
418 | (pci_mmcfg_config[0].address == 0)) | 475 | (pci_mmcfg_config[0].address == 0)) |
419 | return; | 476 | return; |
420 | 477 | ||
421 | cfg = &pci_mmcfg_config[0]; | ||
422 | |||
423 | for (i = 0; i < pci_mmcfg_config_num; i++) { | 478 | for (i = 0; i < pci_mmcfg_config_num; i++) { |
424 | int valid = 0; | 479 | int valid = 0; |
425 | u64 addr, size; | 480 | u64 addr, size; |
@@ -487,10 +542,10 @@ static void __init __pci_mmcfg_init(int early) | |||
487 | known_bridge = 1; | 542 | known_bridge = 1; |
488 | } | 543 | } |
489 | 544 | ||
490 | if (!known_bridge) { | 545 | if (!known_bridge) |
491 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); | 546 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); |
492 | pci_mmcfg_reject_broken(early); | 547 | |
493 | } | 548 | pci_mmcfg_reject_broken(early); |
494 | 549 | ||
495 | if ((pci_mmcfg_config_num == 0) || | 550 | if ((pci_mmcfg_config_num == 0) || |
496 | (pci_mmcfg_config == NULL) || | 551 | (pci_mmcfg_config == NULL) || |
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index 30007ffc8e11..94349f8b2f96 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c | |||
@@ -112,13 +112,18 @@ static struct pci_raw_ops pci_mmcfg = { | |||
112 | static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg) | 112 | static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg) |
113 | { | 113 | { |
114 | void __iomem *addr; | 114 | void __iomem *addr; |
115 | u32 size; | 115 | u64 start, size; |
116 | 116 | ||
117 | size = (cfg->end_bus_number + 1) << 20; | 117 | start = cfg->start_bus_number; |
118 | addr = ioremap_nocache(cfg->address, size); | 118 | start <<= 20; |
119 | start += cfg->address; | ||
120 | size = cfg->end_bus_number + 1 - cfg->start_bus_number; | ||
121 | size <<= 20; | ||
122 | addr = ioremap_nocache(start, size); | ||
119 | if (addr) { | 123 | if (addr) { |
120 | printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", | 124 | printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", |
121 | cfg->address, cfg->address + size - 1); | 125 | start, start + size - 1); |
126 | addr -= cfg->start_bus_number << 20; | ||
122 | } | 127 | } |
123 | return addr; | 128 | return addr; |
124 | } | 129 | } |
@@ -157,7 +162,7 @@ void __init pci_mmcfg_arch_free(void) | |||
157 | 162 | ||
158 | for (i = 0; i < pci_mmcfg_config_num; ++i) { | 163 | for (i = 0; i < pci_mmcfg_config_num; ++i) { |
159 | if (pci_mmcfg_virt[i].virt) { | 164 | if (pci_mmcfg_virt[i].virt) { |
160 | iounmap(pci_mmcfg_virt[i].virt); | 165 | iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20)); |
161 | pci_mmcfg_virt[i].virt = NULL; | 166 | pci_mmcfg_virt[i].virt = NULL; |
162 | pci_mmcfg_virt[i].cfg = NULL; | 167 | pci_mmcfg_virt[i].cfg = NULL; |
163 | } | 168 | } |