On 04/06/2012 07:16 PM, Taku Izumi wrote: > > This patch introduces the configuration for the base address of > the memory mapped configuration space (MMCFG) for hotpluggable PCI > hostbridges on x86 and x86_64. > > The MMCFG for hotpluggable host bridges must be described by using > ACPI _CBA method. This patch adds implementation for _CBA method > on ACPI pci_root driver and MMCFG manipulating functons for x86 and > x86_64. > > Signed-off-by: Taku Izumi <izumi.taku@xxxxxxxxxxxxxx> > --- > arch/x86/pci/mmconfig-shared.c | 61 ++++++++++++++++++++++++++++++++++------- > drivers/acpi/pci_root.c | 28 ++++++++++++++++++ > drivers/pci/pci.c | 31 ++++++++++++++++++++ > include/acpi/acnames.h | 1 > include/linux/pci.h | 3 ++ > 5 files changed, 113 insertions(+), 11 deletions(-) > > Index: linux-next-build/drivers/acpi/pci_root.c > =================================================================== > --- linux-next-build.orig/drivers/acpi/pci_root.c > +++ linux-next-build/drivers/acpi/pci_root.c > @@ -451,7 +451,7 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set); > > static int __devinit acpi_pci_root_add(struct acpi_device *device) > { > - unsigned long long segment, bus; > + unsigned long long segment, bus, base_addr; > acpi_status status; > int result; > struct acpi_pci_root *root; > @@ -506,6 +506,28 @@ static int __devinit acpi_pci_root_add(s > device->driver_data = root; > > /* > + * Check _CBA for hot pluggable host bridge > + */ > + base_addr = 0; > + status = acpi_evaluate_integer(device->handle, METHOD_NAME__CBA, NULL, > + &base_addr); > + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { > + printk(KERN_ERR PREFIX "can't evaluate _CBA\n"); > + result = -ENODEV; > + goto end; > + } > + if (base_addr) { > + result = pci_add_mmcfg_region(root->segment, > + root->secondary.start, > + root->secondary.end, > + base_addr); > + if (result) { > + printk(KERN_ERR PREFIX "can't add MMCFG entry\n"); > + goto end; > + } The MMCFG information for host bridges present at boot time should have already been added by __pci_mmcfg_init() when acpi_pci_root_add() is called. So it would be better for pci_add_mmcfg_region() to return -EEXIST for host bridges present at boot time. > + } > + > + /* > * All supported architectures that use ACPI have support for > * PCI domains, so we indicate this in _OSC support capabilities. > */ > @@ -625,6 +647,8 @@ static int __devinit acpi_pci_root_add(s > return 0; > > end: > + if (base_addr) > + pci_remove_mmcfg_region(root->segment, root->secondary.start); > if (!list_empty(&root->node)) > list_del(&root->node); > kfree(root); > @@ -646,6 +670,8 @@ static int acpi_pci_root_remove(struct a > device_set_run_wake(root->bus->bridge, false); > pci_acpi_remove_bus_pm_notifier(device); > > + pci_remove_mmcfg_region(root->segment, root->secondary.start); > + It would be better to only call pci_remove_mmcfg_region() if the host bridge has _CBA method available, otherwise we have no way to recover this MMCFG information and can't rebind the pci_root driver to a pci host bridge after an unbinding operation. > kfree(root); > return 0; > } > Index: linux-next-build/include/acpi/acnames.h > =================================================================== > --- linux-next-build.orig/include/acpi/acnames.h > +++ linux-next-build/include/acpi/acnames.h > @@ -61,6 +61,7 @@ > #define METHOD_NAME__AEI "_AEI" > #define METHOD_NAME__PRW "_PRW" > #define METHOD_NAME__SRS "_SRS" > +#define METHOD_NAME__CBA "_CBA" > > /* Method names - these methods must appear at the namespace root */ > > Index: linux-next-build/include/linux/pci.h > =================================================================== > --- linux-next-build.orig/include/linux/pci.h > +++ linux-next-build/include/linux/pci.h > @@ -1460,6 +1460,9 @@ void pcibios_disable_device(struct pci_d > void pcibios_set_master(struct pci_dev *dev); > int pcibios_set_pcie_reset_state(struct pci_dev *dev, > enum pcie_reset_state state); > +int pci_add_mmcfg_region(int segment, int start, > + int end, u64 addr); > +void pci_remove_mmcfg_region(int segment, int bus); > > #ifdef CONFIG_PCI_MMCONFIG > extern void __init pci_mmcfg_early_init(void); > Index: linux-next-build/arch/x86/pci/mmconfig-shared.c > =================================================================== > --- linux-next-build.orig/arch/x86/pci/mmconfig-shared.c > +++ linux-next-build/arch/x86/pci/mmconfig-shared.c > @@ -28,7 +28,7 @@ static int __initdata pci_mmcfg_resource > > LIST_HEAD(pci_mmcfg_list); > > -static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg) > +static __devinit void pci_mmconfig_remove(struct pci_mmcfg_region *cfg) > { > if (cfg->res.parent) > release_resource(&cfg->res); > @@ -45,7 +45,7 @@ static __init void free_all_mmcfg(void) > pci_mmconfig_remove(cfg); > } > > -static __init void list_add_sorted(struct pci_mmcfg_region *new) > +static __devinit void list_add_sorted(struct pci_mmcfg_region *new) > { > struct pci_mmcfg_region *cfg; > > @@ -61,8 +61,8 @@ static __init void list_add_sorted(struc > list_add_tail(&new->list, &pci_mmcfg_list); > } > > -static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > - int end, u64 addr) > +static __devinit struct pci_mmcfg_region *pci_mmconfig_add(int segment, > + int start, int end, u64 addr) > { > struct pci_mmcfg_region *new; > struct resource *res; > @@ -357,8 +357,10 @@ static void __init pci_mmcfg_insert_reso > { > struct pci_mmcfg_region *cfg; > > - list_for_each_entry(cfg, &pci_mmcfg_list, list) > - insert_resource(&iomem_resource, &cfg->res); > + list_for_each_entry(cfg, &pci_mmcfg_list, list) { > + if (!cfg->res.parent) > + insert_resource(&iomem_resource, &cfg->res); > + } > > /* Mark that the resources have been inserted. */ > pci_mmcfg_resources_inserted = 1; > @@ -401,7 +403,7 @@ static acpi_status __init check_mcfg_res > return AE_OK; > } > > -static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, > +static acpi_status __devinit find_mboard_resource(acpi_handle handle, u32 lvl, > void *context, void **rv) > { > struct resource *mcfg_res = context; > @@ -415,7 +417,7 @@ static acpi_status __init find_mboard_re > return AE_OK; > } > > -static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) > +static int __devinit is_acpi_reserved(u64 start, u64 end, unsigned not_used) > { > struct resource mcfg_res; > > @@ -434,8 +436,9 @@ static int __init is_acpi_reserved(u64 s > > typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); > > -static int __init is_mmconf_reserved(check_reserved_t is_reserved, > - struct pci_mmcfg_region *cfg, int with_e820) > +static int __devinit is_mmconf_reserved(check_reserved_t is_reserved, > + struct pci_mmcfg_region *cfg, > + int with_e820) > { > u64 addr = cfg->res.start; > u64 size = resource_size(&cfg->res); > @@ -580,6 +583,44 @@ static int __init pci_parse_mcfg(struct > return 0; > } > > +int pci_add_mmcfg_region(int segment, int start, int end, u64 addr) > +{ > + struct pci_mmcfg_region *cfg; > + int valid; > + > + cfg = pci_mmconfig_add(segment, start, end, addr); > + if (!cfg) { > + printk(KERN_WARNING PREFIX > + "no memory for MMCFG entry\n"); > + return -ENOMEM; > + } > + > + valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0); > + if (!valid) { > + printk(KERN_ERR FW_BUG PREFIX > + "MMCONFIG at %pR not reserved in " > + "ACPI motherboard resources\n", > + &cfg->res); > + pci_mmconfig_remove(cfg); > + return -EINVAL; > + } > + > + insert_resource(&iomem_resource, &cfg->res); > + > + return 0; > +} > + > +void pci_remove_mmcfg_region(int segment, int bus) > +{ > + struct pci_mmcfg_region *cfg; > + > + cfg = pci_mmconfig_lookup(segment, bus); > + if (!cfg) > + return; > + > + pci_mmconfig_remove(cfg); > +} > + > static void __init __pci_mmcfg_init(int early) > { > /* MMCONFIG disabled */ > Index: linux-next-build/drivers/pci/pci.c > =================================================================== > --- linux-next-build.orig/drivers/pci/pci.c > +++ linux-next-build/drivers/pci/pci.c > @@ -79,6 +79,37 @@ unsigned long pci_hotplug_mem_size = DEF > > enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF; > > +/** > + * > + * pci_add_mmcfg_region > + * @segment: segment number > + * @start: start bus number > + * @end: end bus number > + * @addr: base address > + * > + * Add MMCONFIG entry for specified segment or bus range group > + * > + * This is the default implementation. Architecture implementations > + * can override this. > + */ > +int __attribute__ ((weak)) pci_add_mmcfg_region(int segment, int start, > + int end, u64 addr) > +{ > + return -EINVAL; > +} > + > +/** > + * pci_remove_mmcfg_region > + * @segment: segment number > + * @bus: bus number > + * > + * Remove MMCONFIG entry for specified segment or bus range group > + * > + * This is the default implementation. Architecture implementations > + * can override this. > + */ > +void __attribute__ ((weak)) pci_remove_mmcfg_region(int segment, int bus) {} > + > /* > * The default CLS is used if arch didn't set CLS explicitly and not > * all pci devices agree on the same value. Arch can override either > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html