diff options
Diffstat (limited to 'arch/x86/pci/mmconfig-shared.c')
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 372 |
1 files changed, 248 insertions, 124 deletions
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 301e325992f..937bcece700 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/bitmap.h> | 17 | #include <linux/bitmap.h> |
18 | #include <linux/dmi.h> | 18 | #include <linux/dmi.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/mutex.h> | ||
21 | #include <linux/rculist.h> | ||
20 | #include <asm/e820.h> | 22 | #include <asm/e820.h> |
21 | #include <asm/pci_x86.h> | 23 | #include <asm/pci_x86.h> |
22 | #include <asm/acpi.h> | 24 | #include <asm/acpi.h> |
@@ -24,7 +26,9 @@ | |||
24 | #define PREFIX "PCI: " | 26 | #define PREFIX "PCI: " |
25 | 27 | ||
26 | /* Indicate if the mmcfg resources have been placed into the resource table. */ | 28 | /* Indicate if the mmcfg resources have been placed into the resource table. */ |
27 | static int __initdata pci_mmcfg_resources_inserted; | 29 | static bool pci_mmcfg_running_state; |
30 | static bool pci_mmcfg_arch_init_failed; | ||
31 | static DEFINE_MUTEX(pci_mmcfg_lock); | ||
28 | 32 | ||
29 | LIST_HEAD(pci_mmcfg_list); | 33 | LIST_HEAD(pci_mmcfg_list); |
30 | 34 | ||
@@ -45,24 +49,25 @@ static __init void free_all_mmcfg(void) | |||
45 | pci_mmconfig_remove(cfg); | 49 | pci_mmconfig_remove(cfg); |
46 | } | 50 | } |
47 | 51 | ||
48 | static __init void list_add_sorted(struct pci_mmcfg_region *new) | 52 | static __devinit void list_add_sorted(struct pci_mmcfg_region *new) |
49 | { | 53 | { |
50 | struct pci_mmcfg_region *cfg; | 54 | struct pci_mmcfg_region *cfg; |
51 | 55 | ||
52 | /* keep list sorted by segment and starting bus number */ | 56 | /* keep list sorted by segment and starting bus number */ |
53 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 57 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { |
54 | if (cfg->segment > new->segment || | 58 | if (cfg->segment > new->segment || |
55 | (cfg->segment == new->segment && | 59 | (cfg->segment == new->segment && |
56 | cfg->start_bus >= new->start_bus)) { | 60 | cfg->start_bus >= new->start_bus)) { |
57 | list_add_tail(&new->list, &cfg->list); | 61 | list_add_tail_rcu(&new->list, &cfg->list); |
58 | return; | 62 | return; |
59 | } | 63 | } |
60 | } | 64 | } |
61 | list_add_tail(&new->list, &pci_mmcfg_list); | 65 | list_add_tail_rcu(&new->list, &pci_mmcfg_list); |
62 | } | 66 | } |
63 | 67 | ||
64 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | 68 | static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, |
65 | int end, u64 addr) | 69 | int start, |
70 | int end, u64 addr) | ||
66 | { | 71 | { |
67 | struct pci_mmcfg_region *new; | 72 | struct pci_mmcfg_region *new; |
68 | struct resource *res; | 73 | struct resource *res; |
@@ -79,8 +84,6 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
79 | new->start_bus = start; | 84 | new->start_bus = start; |
80 | new->end_bus = end; | 85 | new->end_bus = end; |
81 | 86 | ||
82 | list_add_sorted(new); | ||
83 | |||
84 | res = &new->res; | 87 | res = &new->res; |
85 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); | 88 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); |
86 | res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; | 89 | res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; |
@@ -89,9 +92,25 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
89 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); | 92 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); |
90 | res->name = new->name; | 93 | res->name = new->name; |
91 | 94 | ||
92 | printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at " | 95 | return new; |
93 | "%pR (base %#lx)\n", segment, start, end, &new->res, | 96 | } |
94 | (unsigned long) addr); | 97 | |
98 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | ||
99 | int end, u64 addr) | ||
100 | { | ||
101 | struct pci_mmcfg_region *new; | ||
102 | |||
103 | new = pci_mmconfig_alloc(segment, start, end, addr); | ||
104 | if (new) { | ||
105 | mutex_lock(&pci_mmcfg_lock); | ||
106 | list_add_sorted(new); | ||
107 | mutex_unlock(&pci_mmcfg_lock); | ||
108 | |||
109 | pr_info(PREFIX | ||
110 | "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " | ||
111 | "(base %#lx)\n", | ||
112 | segment, start, end, &new->res, (unsigned long)addr); | ||
113 | } | ||
95 | 114 | ||
96 | return new; | 115 | return new; |
97 | } | 116 | } |
@@ -100,7 +119,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) | |||
100 | { | 119 | { |
101 | struct pci_mmcfg_region *cfg; | 120 | struct pci_mmcfg_region *cfg; |
102 | 121 | ||
103 | list_for_each_entry(cfg, &pci_mmcfg_list, list) | 122 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) |
104 | if (cfg->segment == segment && | 123 | if (cfg->segment == segment && |
105 | cfg->start_bus <= bus && bus <= cfg->end_bus) | 124 | cfg->start_bus <= bus && bus <= cfg->end_bus) |
106 | return cfg; | 125 | return cfg; |
@@ -343,8 +362,7 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
343 | name = pci_mmcfg_probes[i].probe(); | 362 | name = pci_mmcfg_probes[i].probe(); |
344 | 363 | ||
345 | if (name) | 364 | if (name) |
346 | printk(KERN_INFO PREFIX "%s with MMCONFIG support\n", | 365 | pr_info(PREFIX "%s with MMCONFIG support\n", name); |
347 | name); | ||
348 | } | 366 | } |
349 | 367 | ||
350 | /* some end_bus_number is crazy, fix it */ | 368 | /* some end_bus_number is crazy, fix it */ |
@@ -353,19 +371,8 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
353 | return !list_empty(&pci_mmcfg_list); | 371 | return !list_empty(&pci_mmcfg_list); |
354 | } | 372 | } |
355 | 373 | ||
356 | static void __init pci_mmcfg_insert_resources(void) | 374 | static acpi_status __devinit check_mcfg_resource(struct acpi_resource *res, |
357 | { | 375 | void *data) |
358 | struct pci_mmcfg_region *cfg; | ||
359 | |||
360 | list_for_each_entry(cfg, &pci_mmcfg_list, list) | ||
361 | insert_resource(&iomem_resource, &cfg->res); | ||
362 | |||
363 | /* Mark that the resources have been inserted. */ | ||
364 | pci_mmcfg_resources_inserted = 1; | ||
365 | } | ||
366 | |||
367 | static acpi_status __init check_mcfg_resource(struct acpi_resource *res, | ||
368 | void *data) | ||
369 | { | 376 | { |
370 | struct resource *mcfg_res = data; | 377 | struct resource *mcfg_res = data; |
371 | struct acpi_resource_address64 address; | 378 | struct acpi_resource_address64 address; |
@@ -401,8 +408,8 @@ static acpi_status __init check_mcfg_resource(struct acpi_resource *res, | |||
401 | return AE_OK; | 408 | return AE_OK; |
402 | } | 409 | } |
403 | 410 | ||
404 | static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, | 411 | static acpi_status __devinit find_mboard_resource(acpi_handle handle, u32 lvl, |
405 | void *context, void **rv) | 412 | void *context, void **rv) |
406 | { | 413 | { |
407 | struct resource *mcfg_res = context; | 414 | struct resource *mcfg_res = context; |
408 | 415 | ||
@@ -415,7 +422,7 @@ static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, | |||
415 | return AE_OK; | 422 | return AE_OK; |
416 | } | 423 | } |
417 | 424 | ||
418 | static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) | 425 | static int __devinit is_acpi_reserved(u64 start, u64 end, unsigned not_used) |
419 | { | 426 | { |
420 | struct resource mcfg_res; | 427 | struct resource mcfg_res; |
421 | 428 | ||
@@ -434,13 +441,15 @@ static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) | |||
434 | 441 | ||
435 | typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); | 442 | typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); |
436 | 443 | ||
437 | static int __init is_mmconf_reserved(check_reserved_t is_reserved, | 444 | static int __ref is_mmconf_reserved(check_reserved_t is_reserved, |
438 | struct pci_mmcfg_region *cfg, int with_e820) | 445 | struct pci_mmcfg_region *cfg, |
446 | struct device *dev, int with_e820) | ||
439 | { | 447 | { |
440 | u64 addr = cfg->res.start; | 448 | u64 addr = cfg->res.start; |
441 | u64 size = resource_size(&cfg->res); | 449 | u64 size = resource_size(&cfg->res); |
442 | u64 old_size = size; | 450 | u64 old_size = size; |
443 | int valid = 0, num_buses; | 451 | int num_buses; |
452 | char *method = with_e820 ? "E820" : "ACPI motherboard resources"; | ||
444 | 453 | ||
445 | while (!is_reserved(addr, addr + size, E820_RESERVED)) { | 454 | while (!is_reserved(addr, addr + size, E820_RESERVED)) { |
446 | size >>= 1; | 455 | size >>= 1; |
@@ -448,30 +457,76 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved, | |||
448 | break; | 457 | break; |
449 | } | 458 | } |
450 | 459 | ||
451 | if (size >= (16UL<<20) || size == old_size) { | 460 | if (size < (16UL<<20) && size != old_size) |
452 | printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n", | 461 | return 0; |
453 | &cfg->res, | 462 | |
454 | with_e820 ? "E820" : "ACPI motherboard resources"); | 463 | if (dev) |
455 | valid = 1; | 464 | dev_info(dev, "MMCONFIG at %pR reserved in %s\n", |
456 | 465 | &cfg->res, method); | |
457 | if (old_size != size) { | 466 | else |
458 | /* update end_bus */ | 467 | pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n", |
459 | cfg->end_bus = cfg->start_bus + ((size>>20) - 1); | 468 | &cfg->res, method); |
460 | num_buses = cfg->end_bus - cfg->start_bus + 1; | 469 | |
461 | cfg->res.end = cfg->res.start + | 470 | if (old_size != size) { |
462 | PCI_MMCFG_BUS_OFFSET(num_buses) - 1; | 471 | /* update end_bus */ |
463 | snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, | 472 | cfg->end_bus = cfg->start_bus + ((size>>20) - 1); |
464 | "PCI MMCONFIG %04x [bus %02x-%02x]", | 473 | num_buses = cfg->end_bus - cfg->start_bus + 1; |
465 | cfg->segment, cfg->start_bus, cfg->end_bus); | 474 | cfg->res.end = cfg->res.start + |
466 | printk(KERN_INFO PREFIX | 475 | PCI_MMCFG_BUS_OFFSET(num_buses) - 1; |
467 | "MMCONFIG for %04x [bus%02x-%02x] " | 476 | snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, |
468 | "at %pR (base %#lx) (size reduced!)\n", | 477 | "PCI MMCONFIG %04x [bus %02x-%02x]", |
469 | cfg->segment, cfg->start_bus, cfg->end_bus, | 478 | cfg->segment, cfg->start_bus, cfg->end_bus); |
470 | &cfg->res, (unsigned long) cfg->address); | 479 | |
471 | } | 480 | if (dev) |
481 | dev_info(dev, | ||
482 | "MMCONFIG " | ||
483 | "at %pR (base %#lx) (size reduced!)\n", | ||
484 | &cfg->res, (unsigned long) cfg->address); | ||
485 | else | ||
486 | pr_info(PREFIX | ||
487 | "MMCONFIG for %04x [bus%02x-%02x] " | ||
488 | "at %pR (base %#lx) (size reduced!)\n", | ||
489 | cfg->segment, cfg->start_bus, cfg->end_bus, | ||
490 | &cfg->res, (unsigned long) cfg->address); | ||
472 | } | 491 | } |
473 | 492 | ||
474 | return valid; | 493 | return 1; |
494 | } | ||
495 | |||
496 | static int __ref pci_mmcfg_check_reserved(struct device *dev, | ||
497 | struct pci_mmcfg_region *cfg, int early) | ||
498 | { | ||
499 | if (!early && !acpi_disabled) { | ||
500 | if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0)) | ||
501 | return 1; | ||
502 | |||
503 | if (dev) | ||
504 | dev_info(dev, FW_INFO | ||
505 | "MMCONFIG at %pR not reserved in " | ||
506 | "ACPI motherboard resources\n", | ||
507 | &cfg->res); | ||
508 | else | ||
509 | pr_info(FW_INFO PREFIX | ||
510 | "MMCONFIG at %pR not reserved in " | ||
511 | "ACPI motherboard resources\n", | ||
512 | &cfg->res); | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * e820_all_mapped() is marked as __init. | ||
517 | * All entries from ACPI MCFG table have been checked at boot time. | ||
518 | * For MCFG information constructed from hotpluggable host bridge's | ||
519 | * _CBA method, just assume it's reserved. | ||
520 | */ | ||
521 | if (pci_mmcfg_running_state) | ||
522 | return 1; | ||
523 | |||
524 | /* Don't try to do this check unless configuration | ||
525 | type 1 is available. how about type 2 ?*/ | ||
526 | if (raw_pci_ops) | ||
527 | return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1); | ||
528 | |||
529 | return 0; | ||
475 | } | 530 | } |
476 | 531 | ||
477 | static void __init pci_mmcfg_reject_broken(int early) | 532 | static void __init pci_mmcfg_reject_broken(int early) |
@@ -479,38 +534,14 @@ static void __init pci_mmcfg_reject_broken(int early) | |||
479 | struct pci_mmcfg_region *cfg; | 534 | struct pci_mmcfg_region *cfg; |
480 | 535 | ||
481 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 536 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { |
482 | int valid = 0; | 537 | if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) { |
483 | 538 | pr_info(PREFIX "not using MMCONFIG\n"); | |
484 | if (!early && !acpi_disabled) { | 539 | free_all_mmcfg(); |
485 | valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0); | 540 | return; |
486 | |||
487 | if (valid) | ||
488 | continue; | ||
489 | else | ||
490 | printk(KERN_ERR FW_BUG PREFIX | ||
491 | "MMCONFIG at %pR not reserved in " | ||
492 | "ACPI motherboard resources\n", | ||
493 | &cfg->res); | ||
494 | } | 541 | } |
495 | |||
496 | /* Don't try to do this check unless configuration | ||
497 | type 1 is available. how about type 2 ?*/ | ||
498 | if (raw_pci_ops) | ||
499 | valid = is_mmconf_reserved(e820_all_mapped, cfg, 1); | ||
500 | |||
501 | if (!valid) | ||
502 | goto reject; | ||
503 | } | 542 | } |
504 | |||
505 | return; | ||
506 | |||
507 | reject: | ||
508 | printk(KERN_INFO PREFIX "not using MMCONFIG\n"); | ||
509 | free_all_mmcfg(); | ||
510 | } | 543 | } |
511 | 544 | ||
512 | static int __initdata known_bridge; | ||
513 | |||
514 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, | 545 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, |
515 | struct acpi_mcfg_allocation *cfg) | 546 | struct acpi_mcfg_allocation *cfg) |
516 | { | 547 | { |
@@ -529,7 +560,7 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, | |||
529 | return 0; | 560 | return 0; |
530 | } | 561 | } |
531 | 562 | ||
532 | printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " | 563 | pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " |
533 | "is above 4GB, ignored\n", cfg->pci_segment, | 564 | "is above 4GB, ignored\n", cfg->pci_segment, |
534 | cfg->start_bus_number, cfg->end_bus_number, cfg->address); | 565 | cfg->start_bus_number, cfg->end_bus_number, cfg->address); |
535 | return -EINVAL; | 566 | return -EINVAL; |
@@ -556,7 +587,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
556 | i -= sizeof(struct acpi_mcfg_allocation); | 587 | i -= sizeof(struct acpi_mcfg_allocation); |
557 | }; | 588 | }; |
558 | if (entries == 0) { | 589 | if (entries == 0) { |
559 | printk(KERN_ERR PREFIX "MMCONFIG has no entries\n"); | 590 | pr_err(PREFIX "MMCONFIG has no entries\n"); |
560 | return -ENODEV; | 591 | return -ENODEV; |
561 | } | 592 | } |
562 | 593 | ||
@@ -570,8 +601,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
570 | 601 | ||
571 | if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, | 602 | if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, |
572 | cfg->end_bus_number, cfg->address) == NULL) { | 603 | cfg->end_bus_number, cfg->address) == NULL) { |
573 | printk(KERN_WARNING PREFIX | 604 | pr_warn(PREFIX "no memory for MCFG entries\n"); |
574 | "no memory for MCFG entries\n"); | ||
575 | free_all_mmcfg(); | 605 | free_all_mmcfg(); |
576 | return -ENOMEM; | 606 | return -ENOMEM; |
577 | } | 607 | } |
@@ -582,28 +612,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
582 | 612 | ||
583 | static void __init __pci_mmcfg_init(int early) | 613 | static void __init __pci_mmcfg_init(int early) |
584 | { | 614 | { |
585 | /* MMCONFIG disabled */ | ||
586 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | ||
587 | return; | ||
588 | |||
589 | /* MMCONFIG already enabled */ | ||
590 | if (!early && !(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF)) | ||
591 | return; | ||
592 | |||
593 | /* for late to exit */ | ||
594 | if (known_bridge) | ||
595 | return; | ||
596 | |||
597 | if (early) { | ||
598 | if (pci_mmcfg_check_hostbridge()) | ||
599 | known_bridge = 1; | ||
600 | } | ||
601 | |||
602 | if (!known_bridge) | ||
603 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
604 | |||
605 | pci_mmcfg_reject_broken(early); | 615 | pci_mmcfg_reject_broken(early); |
606 | |||
607 | if (list_empty(&pci_mmcfg_list)) | 616 | if (list_empty(&pci_mmcfg_list)) |
608 | return; | 617 | return; |
609 | 618 | ||
@@ -620,33 +629,48 @@ static void __init __pci_mmcfg_init(int early) | |||
620 | if (pci_mmcfg_arch_init()) | 629 | if (pci_mmcfg_arch_init()) |
621 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; | 630 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; |
622 | else { | 631 | else { |
623 | /* | 632 | free_all_mmcfg(); |
624 | * Signal not to attempt to insert mmcfg resources because | 633 | pci_mmcfg_arch_init_failed = true; |
625 | * the architecture mmcfg setup could not initialize. | ||
626 | */ | ||
627 | pci_mmcfg_resources_inserted = 1; | ||
628 | } | 634 | } |
629 | } | 635 | } |
630 | 636 | ||
637 | static int __initdata known_bridge; | ||
638 | |||
631 | void __init pci_mmcfg_early_init(void) | 639 | void __init pci_mmcfg_early_init(void) |
632 | { | 640 | { |
633 | __pci_mmcfg_init(1); | 641 | if (pci_probe & PCI_PROBE_MMCONF) { |
642 | if (pci_mmcfg_check_hostbridge()) | ||
643 | known_bridge = 1; | ||
644 | else | ||
645 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
646 | __pci_mmcfg_init(1); | ||
647 | } | ||
634 | } | 648 | } |
635 | 649 | ||
636 | void __init pci_mmcfg_late_init(void) | 650 | void __init pci_mmcfg_late_init(void) |
637 | { | 651 | { |
638 | __pci_mmcfg_init(0); | 652 | /* MMCONFIG disabled */ |
653 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | ||
654 | return; | ||
655 | |||
656 | if (known_bridge) | ||
657 | return; | ||
658 | |||
659 | /* MMCONFIG hasn't been enabled yet, try again */ | ||
660 | if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { | ||
661 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
662 | __pci_mmcfg_init(0); | ||
663 | } | ||
639 | } | 664 | } |
640 | 665 | ||
641 | static int __init pci_mmcfg_late_insert_resources(void) | 666 | static int __init pci_mmcfg_late_insert_resources(void) |
642 | { | 667 | { |
643 | /* | 668 | struct pci_mmcfg_region *cfg; |
644 | * If resources are already inserted or we are not using MMCONFIG, | 669 | |
645 | * don't insert the resources. | 670 | pci_mmcfg_running_state = true; |
646 | */ | 671 | |
647 | if ((pci_mmcfg_resources_inserted == 1) || | 672 | /* If we are not using MMCONFIG, don't insert the resources. */ |
648 | (pci_probe & PCI_PROBE_MMCONF) == 0 || | 673 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) |
649 | list_empty(&pci_mmcfg_list)) | ||
650 | return 1; | 674 | return 1; |
651 | 675 | ||
652 | /* | 676 | /* |
@@ -654,7 +678,9 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
654 | * marked so it won't cause request errors when __request_region is | 678 | * marked so it won't cause request errors when __request_region is |
655 | * called. | 679 | * called. |
656 | */ | 680 | */ |
657 | pci_mmcfg_insert_resources(); | 681 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
682 | if (!cfg->res.parent) | ||
683 | insert_resource(&iomem_resource, &cfg->res); | ||
658 | 684 | ||
659 | return 0; | 685 | return 0; |
660 | } | 686 | } |
@@ -665,3 +691,101 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
665 | * with other system resources. | 691 | * with other system resources. |
666 | */ | 692 | */ |
667 | late_initcall(pci_mmcfg_late_insert_resources); | 693 | late_initcall(pci_mmcfg_late_insert_resources); |
694 | |||
695 | /* Add MMCFG information for host bridges */ | ||
696 | int __devinit pci_mmconfig_insert(struct device *dev, | ||
697 | u16 seg, u8 start, u8 end, | ||
698 | phys_addr_t addr) | ||
699 | { | ||
700 | int rc; | ||
701 | struct resource *tmp = NULL; | ||
702 | struct pci_mmcfg_region *cfg; | ||
703 | |||
704 | if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) | ||
705 | return -ENODEV; | ||
706 | |||
707 | if (start > end) | ||
708 | return -EINVAL; | ||
709 | |||
710 | mutex_lock(&pci_mmcfg_lock); | ||
711 | cfg = pci_mmconfig_lookup(seg, start); | ||
712 | if (cfg) { | ||
713 | if (cfg->end_bus < end) | ||
714 | dev_info(dev, FW_INFO | ||
715 | "MMCONFIG for " | ||
716 | "domain %04x [bus %02x-%02x] " | ||
717 | "only partially covers this bridge\n", | ||
718 | cfg->segment, cfg->start_bus, cfg->end_bus); | ||
719 | mutex_unlock(&pci_mmcfg_lock); | ||
720 | return -EEXIST; | ||
721 | } | ||
722 | |||
723 | if (!addr) { | ||
724 | mutex_unlock(&pci_mmcfg_lock); | ||
725 | return -EINVAL; | ||
726 | } | ||
727 | |||
728 | rc = -EBUSY; | ||
729 | cfg = pci_mmconfig_alloc(seg, start, end, addr); | ||
730 | if (cfg == NULL) { | ||
731 | dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); | ||
732 | rc = -ENOMEM; | ||
733 | } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { | ||
734 | dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", | ||
735 | &cfg->res); | ||
736 | } else { | ||
737 | /* Insert resource if it's not in boot stage */ | ||
738 | if (pci_mmcfg_running_state) | ||
739 | tmp = insert_resource_conflict(&iomem_resource, | ||
740 | &cfg->res); | ||
741 | |||
742 | if (tmp) { | ||
743 | dev_warn(dev, | ||
744 | "MMCONFIG %pR conflicts with " | ||
745 | "%s %pR\n", | ||
746 | &cfg->res, tmp->name, tmp); | ||
747 | } else if (pci_mmcfg_arch_map(cfg)) { | ||
748 | dev_warn(dev, "fail to map MMCONFIG %pR.\n", | ||
749 | &cfg->res); | ||
750 | } else { | ||
751 | list_add_sorted(cfg); | ||
752 | dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", | ||
753 | &cfg->res, (unsigned long)addr); | ||
754 | cfg = NULL; | ||
755 | rc = 0; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | if (cfg) { | ||
760 | if (cfg->res.parent) | ||
761 | release_resource(&cfg->res); | ||
762 | kfree(cfg); | ||
763 | } | ||
764 | |||
765 | mutex_unlock(&pci_mmcfg_lock); | ||
766 | |||
767 | return rc; | ||
768 | } | ||
769 | |||
770 | /* Delete MMCFG information for host bridges */ | ||
771 | int pci_mmconfig_delete(u16 seg, u8 start, u8 end) | ||
772 | { | ||
773 | struct pci_mmcfg_region *cfg; | ||
774 | |||
775 | mutex_lock(&pci_mmcfg_lock); | ||
776 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) | ||
777 | if (cfg->segment == seg && cfg->start_bus == start && | ||
778 | cfg->end_bus == end) { | ||
779 | list_del_rcu(&cfg->list); | ||
780 | synchronize_rcu(); | ||
781 | pci_mmcfg_arch_unmap(cfg); | ||
782 | if (cfg->res.parent) | ||
783 | release_resource(&cfg->res); | ||
784 | mutex_unlock(&pci_mmcfg_lock); | ||
785 | kfree(cfg); | ||
786 | return 0; | ||
787 | } | ||
788 | mutex_unlock(&pci_mmcfg_lock); | ||
789 | |||
790 | return -ENOENT; | ||
791 | } | ||