diff options
author | Bjorn Helgaas <bjorn.helgaas@hp.com> | 2009-11-13 19:34:49 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-11-24 18:30:14 -0500 |
commit | ff097ddd4aeac790fd51d013c79c2f18ec9a7117 (patch) | |
tree | 4a77f081e4ac7e93aa74a102bf372419699468b9 /arch/x86/pci/mmconfig-shared.c | |
parent | 987c367b4e93be6826394e7c9cc14d28bb5c8810 (diff) |
x86/PCI: MMCONFIG: manage pci_mmcfg_region as a list, not a table
This changes pci_mmcfg_region from a table to a list, to make it easier
to add and remove MMCONFIG regions for PCI host bridge hotplug.
Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'arch/x86/pci/mmconfig-shared.c')
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 106 |
1 files changed, 41 insertions, 65 deletions
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 6eeeac0d25f4..2709aa81801d 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/sfi_acpi.h> | 16 | #include <linux/sfi_acpi.h> |
17 | #include <linux/bitmap.h> | 17 | #include <linux/bitmap.h> |
18 | #include <linux/dmi.h> | 18 | #include <linux/dmi.h> |
19 | #include <linux/sort.h> | ||
20 | #include <asm/e820.h> | 19 | #include <asm/e820.h> |
21 | #include <asm/pci_x86.h> | 20 | #include <asm/pci_x86.h> |
22 | #include <asm/acpi.h> | 21 | #include <asm/acpi.h> |
@@ -26,53 +25,58 @@ | |||
26 | /* 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. */ |
27 | static int __initdata pci_mmcfg_resources_inserted; | 26 | static int __initdata pci_mmcfg_resources_inserted; |
28 | 27 | ||
28 | LIST_HEAD(pci_mmcfg_list); | ||
29 | |||
29 | static __init void free_all_mmcfg(void) | 30 | static __init void free_all_mmcfg(void) |
30 | { | 31 | { |
31 | int i; | 32 | struct pci_mmcfg_region *cfg, *tmp; |
32 | struct pci_mmcfg_region *cfg; | ||
33 | 33 | ||
34 | pci_mmcfg_arch_free(); | 34 | pci_mmcfg_arch_free(); |
35 | for (i = 0; i < pci_mmcfg_config_num; i++) { | 35 | list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) { |
36 | cfg = &pci_mmcfg_config[i]; | ||
37 | if (cfg->res.parent) | 36 | if (cfg->res.parent) |
38 | release_resource(&cfg->res); | 37 | release_resource(&cfg->res); |
38 | list_del(&cfg->list); | ||
39 | kfree(cfg); | ||
39 | } | 40 | } |
40 | pci_mmcfg_config_num = 0; | 41 | } |
41 | kfree(pci_mmcfg_config); | 42 | |
42 | pci_mmcfg_config = NULL; | 43 | static __init void list_add_sorted(struct pci_mmcfg_region *new) |
44 | { | ||
45 | struct pci_mmcfg_region *cfg; | ||
46 | |||
47 | /* keep list sorted by segment and starting bus number */ | ||
48 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | ||
49 | if (cfg->segment > new->segment || | ||
50 | (cfg->segment == new->segment && | ||
51 | cfg->start_bus >= new->start_bus)) { | ||
52 | list_add_tail(&new->list, &cfg->list); | ||
53 | return; | ||
54 | } | ||
55 | } | ||
56 | list_add_tail(&new->list, &pci_mmcfg_list); | ||
43 | } | 57 | } |
44 | 58 | ||
45 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | 59 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, |
46 | int end, u64 addr) | 60 | int end, u64 addr) |
47 | { | 61 | { |
48 | struct pci_mmcfg_region *new; | 62 | struct pci_mmcfg_region *new; |
49 | int new_num = pci_mmcfg_config_num + 1; | ||
50 | int i = pci_mmcfg_config_num; | ||
51 | int num_buses; | 63 | int num_buses; |
52 | struct resource *res; | 64 | struct resource *res; |
53 | 65 | ||
54 | if (addr == 0) | 66 | if (addr == 0) |
55 | return NULL; | 67 | return NULL; |
56 | 68 | ||
57 | new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL); | 69 | new = kzalloc(sizeof(*new), GFP_KERNEL); |
58 | if (!new) | 70 | if (!new) |
59 | return NULL; | 71 | return NULL; |
60 | 72 | ||
61 | if (pci_mmcfg_config) { | ||
62 | memcpy(new, pci_mmcfg_config, | ||
63 | sizeof(pci_mmcfg_config[0]) * new_num); | ||
64 | kfree(pci_mmcfg_config); | ||
65 | } | ||
66 | pci_mmcfg_config = new; | ||
67 | pci_mmcfg_config_num++; | ||
68 | |||
69 | new = &pci_mmcfg_config[i]; | ||
70 | |||
71 | new->address = addr; | 73 | new->address = addr; |
72 | new->segment = segment; | 74 | new->segment = segment; |
73 | new->start_bus = start; | 75 | new->start_bus = start; |
74 | new->end_bus = end; | 76 | new->end_bus = end; |
75 | 77 | ||
78 | list_add_sorted(new); | ||
79 | |||
76 | num_buses = end - start + 1; | 80 | num_buses = end - start + 1; |
77 | res = &new->res; | 81 | res = &new->res; |
78 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); | 82 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); |
@@ -82,7 +86,7 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
82 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); | 86 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); |
83 | res->name = new->name; | 87 | res->name = new->name; |
84 | 88 | ||
85 | return &pci_mmcfg_config[i]; | 89 | return new; |
86 | } | 90 | } |
87 | 91 | ||
88 | static const char __init *pci_mmcfg_e7520(void) | 92 | static const char __init *pci_mmcfg_e7520(void) |
@@ -214,7 +218,7 @@ static const char __init *pci_mmcfg_nvidia_mcp55(void) | |||
214 | /* | 218 | /* |
215 | * do check if amd fam10h already took over | 219 | * do check if amd fam10h already took over |
216 | */ | 220 | */ |
217 | if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked) | 221 | if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked) |
218 | return NULL; | 222 | return NULL; |
219 | 223 | ||
220 | mcp55_checked = true; | 224 | mcp55_checked = true; |
@@ -275,44 +279,26 @@ static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { | |||
275 | 0x0369, pci_mmcfg_nvidia_mcp55 }, | 279 | 0x0369, pci_mmcfg_nvidia_mcp55 }, |
276 | }; | 280 | }; |
277 | 281 | ||
278 | static int __init cmp_mmcfg(const void *x1, const void *x2) | ||
279 | { | ||
280 | const struct pci_mmcfg_region *m1 = x1; | ||
281 | const struct pci_mmcfg_region *m2 = x2; | ||
282 | int start1, start2; | ||
283 | |||
284 | start1 = m1->start_bus; | ||
285 | start2 = m2->start_bus; | ||
286 | |||
287 | return start1 - start2; | ||
288 | } | ||
289 | |||
290 | static void __init pci_mmcfg_check_end_bus_number(void) | 282 | static void __init pci_mmcfg_check_end_bus_number(void) |
291 | { | 283 | { |
292 | int i; | ||
293 | struct pci_mmcfg_region *cfg, *cfgx; | 284 | struct pci_mmcfg_region *cfg, *cfgx; |
294 | 285 | ||
295 | /* sort them at first */ | ||
296 | sort(pci_mmcfg_config, pci_mmcfg_config_num, | ||
297 | sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL); | ||
298 | |||
299 | /* last one*/ | 286 | /* last one*/ |
300 | if (pci_mmcfg_config_num > 0) { | 287 | cfg = list_entry(pci_mmcfg_list.prev, typeof(*cfg), list); |
301 | i = pci_mmcfg_config_num - 1; | 288 | if (cfg) |
302 | cfg = &pci_mmcfg_config[i]; | ||
303 | if (cfg->end_bus < cfg->start_bus) | 289 | if (cfg->end_bus < cfg->start_bus) |
304 | cfg->end_bus = 255; | 290 | cfg->end_bus = 255; |
305 | } | ||
306 | 291 | ||
307 | /* don't overlap please */ | 292 | if (list_is_singular(&pci_mmcfg_list)) |
308 | for (i = 0; i < pci_mmcfg_config_num - 1; i++) { | 293 | return; |
309 | cfg = &pci_mmcfg_config[i]; | ||
310 | cfgx = &pci_mmcfg_config[i+1]; | ||
311 | 294 | ||
295 | /* don't overlap please */ | ||
296 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | ||
312 | if (cfg->end_bus < cfg->start_bus) | 297 | if (cfg->end_bus < cfg->start_bus) |
313 | cfg->end_bus = 255; | 298 | cfg->end_bus = 255; |
314 | 299 | ||
315 | if (cfg->end_bus >= cfgx->start_bus) | 300 | cfgx = list_entry(cfg->list.next, typeof(*cfg), list); |
301 | if (cfg != cfgx && cfg->end_bus >= cfgx->start_bus) | ||
316 | cfg->end_bus = cfgx->start_bus - 1; | 302 | cfg->end_bus = cfgx->start_bus - 1; |
317 | } | 303 | } |
318 | } | 304 | } |
@@ -350,18 +336,15 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
350 | /* some end_bus_number is crazy, fix it */ | 336 | /* some end_bus_number is crazy, fix it */ |
351 | pci_mmcfg_check_end_bus_number(); | 337 | pci_mmcfg_check_end_bus_number(); |
352 | 338 | ||
353 | return pci_mmcfg_config_num != 0; | 339 | return !list_empty(&pci_mmcfg_list); |
354 | } | 340 | } |
355 | 341 | ||
356 | static void __init pci_mmcfg_insert_resources(void) | 342 | static void __init pci_mmcfg_insert_resources(void) |
357 | { | 343 | { |
358 | int i; | ||
359 | struct pci_mmcfg_region *cfg; | 344 | struct pci_mmcfg_region *cfg; |
360 | 345 | ||
361 | for (i = 0; i < pci_mmcfg_config_num; i++) { | 346 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
362 | cfg = &pci_mmcfg_config[i]; | ||
363 | insert_resource(&iomem_resource, &cfg->res); | 347 | insert_resource(&iomem_resource, &cfg->res); |
364 | } | ||
365 | 348 | ||
366 | /* Mark that the resources have been inserted. */ | 349 | /* Mark that the resources have been inserted. */ |
367 | pci_mmcfg_resources_inserted = 1; | 350 | pci_mmcfg_resources_inserted = 1; |
@@ -482,18 +465,15 @@ static void __init pci_mmcfg_reject_broken(int early) | |||
482 | struct pci_mmcfg_region *cfg; | 465 | struct pci_mmcfg_region *cfg; |
483 | int i; | 466 | int i; |
484 | 467 | ||
485 | if (pci_mmcfg_config_num == 0) | 468 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { |
486 | return; | ||
487 | |||
488 | for (i = 0; i < pci_mmcfg_config_num; i++) { | ||
489 | int valid = 0; | 469 | int valid = 0; |
490 | 470 | ||
491 | cfg = &pci_mmcfg_config[i]; | ||
492 | printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx " | 471 | printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx " |
493 | "segment %hu buses %u - %u\n", | 472 | "segment %hu buses %u - %u\n", |
494 | i, (unsigned long)cfg->address, cfg->segment, | 473 | i, (unsigned long)cfg->address, cfg->segment, |
495 | (unsigned int)cfg->start_bus, | 474 | (unsigned int)cfg->start_bus, |
496 | (unsigned int)cfg->end_bus); | 475 | (unsigned int)cfg->end_bus); |
476 | i++; | ||
497 | 477 | ||
498 | if (!early && !acpi_disabled) | 478 | if (!early && !acpi_disabled) |
499 | valid = is_mmconf_reserved(is_acpi_reserved, i, cfg, 0); | 479 | valid = is_mmconf_reserved(is_acpi_reserved, i, cfg, 0); |
@@ -524,10 +504,6 @@ reject: | |||
524 | 504 | ||
525 | static int __initdata known_bridge; | 505 | static int __initdata known_bridge; |
526 | 506 | ||
527 | /* The physical address of the MMCONFIG aperture. Set from ACPI tables. */ | ||
528 | struct pci_mmcfg_region *pci_mmcfg_config; | ||
529 | int pci_mmcfg_config_num; | ||
530 | |||
531 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, | 507 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, |
532 | struct acpi_mcfg_allocation *cfg) | 508 | struct acpi_mcfg_allocation *cfg) |
533 | { | 509 | { |
@@ -620,7 +596,7 @@ static void __init __pci_mmcfg_init(int early) | |||
620 | 596 | ||
621 | pci_mmcfg_reject_broken(early); | 597 | pci_mmcfg_reject_broken(early); |
622 | 598 | ||
623 | if (pci_mmcfg_config_num == 0) | 599 | if (list_empty(&pci_mmcfg_list)) |
624 | return; | 600 | return; |
625 | 601 | ||
626 | if (pci_mmcfg_arch_init()) | 602 | if (pci_mmcfg_arch_init()) |
@@ -652,7 +628,7 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
652 | */ | 628 | */ |
653 | if ((pci_mmcfg_resources_inserted == 1) || | 629 | if ((pci_mmcfg_resources_inserted == 1) || |
654 | (pci_probe & PCI_PROBE_MMCONF) == 0 || | 630 | (pci_probe & PCI_PROBE_MMCONF) == 0 || |
655 | (pci_mmcfg_config_num == 0)) | 631 | list_empty(&pci_mmcfg_list)) |
656 | return 1; | 632 | return 1; |
657 | 633 | ||
658 | /* | 634 | /* |