diff options
author | Jiang Liu <jiang.liu@huawei.com> | 2012-06-22 02:55:15 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-22 17:16:45 -0400 |
commit | 9c95111b330d2ddf851444528a7608f267cbb50c (patch) | |
tree | 79c6eaa49d79c7ca5c1b9fdd0feb62db9852e887 /arch/x86 | |
parent | 95c5e92f4f691bbaba40bbf3decfc8e13b6ea897 (diff) |
x86/PCI: add pci_mmconfig_insert()/delete() for PCI root bridge hotplug
Introduce pci_mmconfig_insert()/pci_mmconfig_delete(), which will be used
to update MMCONFIG information when supporting PCI root bridge hotplug.
Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jiang Liu <liuj97@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/pci_x86.h | 4 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 109 |
2 files changed, 109 insertions, 4 deletions
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index df898ceab4d4..af5018f3d7c3 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h | |||
@@ -137,6 +137,10 @@ extern int __init pci_mmcfg_arch_init(void); | |||
137 | extern void __init pci_mmcfg_arch_free(void); | 137 | extern void __init pci_mmcfg_arch_free(void); |
138 | extern int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); | 138 | extern int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); |
139 | extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); | 139 | extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); |
140 | extern int __devinit pci_mmconfig_insert(struct device *dev, | ||
141 | u16 seg, u8 start, | ||
142 | u8 end, phys_addr_t addr); | ||
143 | extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); | ||
140 | extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); | 144 | extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); |
141 | 145 | ||
142 | extern struct list_head pci_mmcfg_list; | 146 | extern struct list_head pci_mmcfg_list; |
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 15a7abf5139c..19fc42b9f823 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -28,6 +28,7 @@ | |||
28 | /* 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. */ |
29 | static int __initdata pci_mmcfg_resources_inserted; | 29 | static int __initdata pci_mmcfg_resources_inserted; |
30 | static bool pci_mmcfg_running_state; | 30 | static bool pci_mmcfg_running_state; |
31 | static bool pci_mmcfg_arch_init_failed; | ||
31 | static DEFINE_MUTEX(pci_mmcfg_lock); | 32 | static DEFINE_MUTEX(pci_mmcfg_lock); |
32 | 33 | ||
33 | LIST_HEAD(pci_mmcfg_list); | 34 | LIST_HEAD(pci_mmcfg_list); |
@@ -92,10 +93,6 @@ static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, | |||
92 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); | 93 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); |
93 | res->name = new->name; | 94 | res->name = new->name; |
94 | 95 | ||
95 | printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at " | ||
96 | "%pR (base %#lx)\n", segment, start, end, &new->res, | ||
97 | (unsigned long) addr); | ||
98 | |||
99 | return new; | 96 | return new; |
100 | } | 97 | } |
101 | 98 | ||
@@ -109,6 +106,11 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
109 | mutex_lock(&pci_mmcfg_lock); | 106 | mutex_lock(&pci_mmcfg_lock); |
110 | list_add_sorted(new); | 107 | list_add_sorted(new); |
111 | mutex_unlock(&pci_mmcfg_lock); | 108 | mutex_unlock(&pci_mmcfg_lock); |
109 | |||
110 | printk(KERN_INFO PREFIX | ||
111 | "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " | ||
112 | "(base %#lx)\n", | ||
113 | segment, start, end, &new->res, (unsigned long)addr); | ||
112 | } | 114 | } |
113 | 115 | ||
114 | return new; | 116 | return new; |
@@ -671,6 +673,7 @@ static void __init __pci_mmcfg_init(int early) | |||
671 | * the architecture mmcfg setup could not initialize. | 673 | * the architecture mmcfg setup could not initialize. |
672 | */ | 674 | */ |
673 | pci_mmcfg_resources_inserted = 1; | 675 | pci_mmcfg_resources_inserted = 1; |
676 | pci_mmcfg_arch_init_failed = true; | ||
674 | } | 677 | } |
675 | } | 678 | } |
676 | 679 | ||
@@ -713,3 +716,101 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
713 | * with other system resources. | 716 | * with other system resources. |
714 | */ | 717 | */ |
715 | late_initcall(pci_mmcfg_late_insert_resources); | 718 | late_initcall(pci_mmcfg_late_insert_resources); |
719 | |||
720 | /* Add MMCFG information for host bridges */ | ||
721 | int __devinit pci_mmconfig_insert(struct device *dev, | ||
722 | u16 seg, u8 start, u8 end, | ||
723 | phys_addr_t addr) | ||
724 | { | ||
725 | int rc; | ||
726 | struct resource *tmp = NULL; | ||
727 | struct pci_mmcfg_region *cfg; | ||
728 | |||
729 | if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) | ||
730 | return -ENODEV; | ||
731 | |||
732 | if (start > end) | ||
733 | return -EINVAL; | ||
734 | |||
735 | mutex_lock(&pci_mmcfg_lock); | ||
736 | cfg = pci_mmconfig_lookup(seg, start); | ||
737 | if (cfg) { | ||
738 | if (cfg->end_bus < end) | ||
739 | dev_info(dev, FW_INFO | ||
740 | "MMCONFIG for " | ||
741 | "domain %04x [bus %02x-%02x] " | ||
742 | "only partially covers this bridge\n", | ||
743 | cfg->segment, cfg->start_bus, cfg->end_bus); | ||
744 | mutex_unlock(&pci_mmcfg_lock); | ||
745 | return -EEXIST; | ||
746 | } | ||
747 | |||
748 | if (!addr) { | ||
749 | mutex_unlock(&pci_mmcfg_lock); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | |||
753 | rc = -EBUSY; | ||
754 | cfg = pci_mmconfig_alloc(seg, start, end, addr); | ||
755 | if (cfg == NULL) { | ||
756 | dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); | ||
757 | rc = -ENOMEM; | ||
758 | } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { | ||
759 | dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", | ||
760 | &cfg->res); | ||
761 | } else { | ||
762 | /* Insert resource if it's not in boot stage */ | ||
763 | if (pci_mmcfg_running_state) | ||
764 | tmp = insert_resource_conflict(&iomem_resource, | ||
765 | &cfg->res); | ||
766 | |||
767 | if (tmp) { | ||
768 | dev_warn(dev, | ||
769 | "MMCONFIG %pR conflicts with " | ||
770 | "%s %pR\n", | ||
771 | &cfg->res, tmp->name, tmp); | ||
772 | } else if (pci_mmcfg_arch_map(cfg)) { | ||
773 | dev_warn(dev, "fail to map MMCONFIG %pR.\n", | ||
774 | &cfg->res); | ||
775 | } else { | ||
776 | list_add_sorted(cfg); | ||
777 | dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", | ||
778 | &cfg->res, (unsigned long)addr); | ||
779 | cfg = NULL; | ||
780 | rc = 0; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | if (cfg) { | ||
785 | if (cfg->res.parent) | ||
786 | release_resource(&cfg->res); | ||
787 | kfree(cfg); | ||
788 | } | ||
789 | |||
790 | mutex_unlock(&pci_mmcfg_lock); | ||
791 | |||
792 | return rc; | ||
793 | } | ||
794 | |||
795 | /* Delete MMCFG information for host bridges */ | ||
796 | int pci_mmconfig_delete(u16 seg, u8 start, u8 end) | ||
797 | { | ||
798 | struct pci_mmcfg_region *cfg; | ||
799 | |||
800 | mutex_lock(&pci_mmcfg_lock); | ||
801 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) | ||
802 | if (cfg->segment == seg && cfg->start_bus == start && | ||
803 | cfg->end_bus == end) { | ||
804 | list_del_rcu(&cfg->list); | ||
805 | synchronize_rcu(); | ||
806 | pci_mmcfg_arch_unmap(cfg); | ||
807 | if (cfg->res.parent) | ||
808 | release_resource(&cfg->res); | ||
809 | mutex_unlock(&pci_mmcfg_lock); | ||
810 | kfree(cfg); | ||
811 | return 0; | ||
812 | } | ||
813 | mutex_unlock(&pci_mmcfg_lock); | ||
814 | |||
815 | return -ENOENT; | ||
816 | } | ||