* [PATCH v6 0/5] ACPI based PCI support for arm64 @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel This patchset provides a generic ACPI based PCI host controller implementation and uses it on arm64. The first patch moves the common code to handle MCFG ACPI table from arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset provides the generic implementation of an ACPI based PCI host controller with a new file drivers/acpi/pci_host_acpi.c. The other patches are to fix up arm64 and ACPI code to work with these two patches. The pci host controller implementation keeps a reference to pci_mmcfg_region entry so that config space access is done with a simple mapping and generic PCI config read/write. There is also an implementation of raw_pci_read/raw_pci_write provided by walking the pci_mmcfg_list The patchset is against 4.4, but it can be applied to pci/next with a very minor fixup. This is tested with arm64 QEMU and OVMF and on x86 with qemu. More testing or comments are welcome. The idea is to provide a simpler alternative for ACPI PCI support in arm64, any feedback from PCI or ACPI maintainers would be appreciated on this front. Thanks, JC. v5->v6: - fix arm64 with xen compile issue reported by kbuild bot v4->v5: - Fix arm build issue reported by the kbuild bot - Use config_enabled instead of ifdef in pci_mcfg.c v3->v4: - Handle suggestions from Arnd Bergmann <arnd@arndb.de> * move the implementation of host controller to drivers/acpi under config option CONFIG_ACPI_PCI_HOST_GENERIC * remove unnecessary arch hooks (this done as much as possible without affecting the scope of the patch, will need another separate patchset for the rest). * remove pcibios_assign_resources fs_initcall that was added - fixup domain_nr assignment code, and move ACPI companion set to PCI code. - Call map resource correctly in setup code - fix kbuild robot report - More testing v2->v3: - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c without changing x86 logic - use the pci_mmcfg_list in ARM64 implementation - provide raw_pci_read/raw_pci_write v1->v2: - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation of pci_mmcfg_late_init for arm64. - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c and it will save the MCFG table entries to an array. Earlier this was done with an arch_init call - remove unneeded pci_bus_add_devices call and fix MCFG saving code - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources Jayachandran C (5): APCI: MCFG: Move mmcfg_list management to drivers/acpi PCI: Handle ACPI companion and domain number ACPI: PCI: Support platforms that need pci_remap_iospace arm64: pci: Add ACPI support PCI: ACPI: Add a generic ACPI based host controller arch/arm64/Kconfig | 3 + arch/arm64/kernel/pci.c | 34 ++++- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 2 + drivers/acpi/pci_host_acpi.c | 186 ++++++++++++++++++++++++ drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 62 +++++++- drivers/acpi/resource.c | 2 + drivers/pci/pci.c | 15 +- drivers/pci/probe.c | 2 + drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 59 ++++++++ 17 files changed, 717 insertions(+), 269 deletions(-) create mode 100644 drivers/acpi/pci_host_acpi.c create mode 100644 drivers/acpi/pci_mcfg.c -- 1.9.1 ^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 0/5] ACPI based PCI support for arm64 @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel This patchset provides a generic ACPI based PCI host controller implementation and uses it on arm64. The first patch moves the common code to handle MCFG ACPI table from arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset provides the generic implementation of an ACPI based PCI host controller with a new file drivers/acpi/pci_host_acpi.c. The other patches are to fix up arm64 and ACPI code to work with these two patches. The pci host controller implementation keeps a reference to pci_mmcfg_region entry so that config space access is done with a simple mapping and generic PCI config read/write. There is also an implementation of raw_pci_read/raw_pci_write provided by walking the pci_mmcfg_list The patchset is against 4.4, but it can be applied to pci/next with a very minor fixup. This is tested with arm64 QEMU and OVMF and on x86 with qemu. More testing or comments are welcome. The idea is to provide a simpler alternative for ACPI PCI support in arm64, any feedback from PCI or ACPI maintainers would be appreciated on this front. Thanks, JC. v5->v6: - fix arm64 with xen compile issue reported by kbuild bot v4->v5: - Fix arm build issue reported by the kbuild bot - Use config_enabled instead of ifdef in pci_mcfg.c v3->v4: - Handle suggestions from Arnd Bergmann <arnd@arndb.de> * move the implementation of host controller to drivers/acpi under config option CONFIG_ACPI_PCI_HOST_GENERIC * remove unnecessary arch hooks (this done as much as possible without affecting the scope of the patch, will need another separate patchset for the rest). * remove pcibios_assign_resources fs_initcall that was added - fixup domain_nr assignment code, and move ACPI companion set to PCI code. - Call map resource correctly in setup code - fix kbuild robot report - More testing v2->v3: - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c without changing x86 logic - use the pci_mmcfg_list in ARM64 implementation - provide raw_pci_read/raw_pci_write v1->v2: - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation of pci_mmcfg_late_init for arm64. - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c and it will save the MCFG table entries to an array. Earlier this was done with an arch_init call - remove unneeded pci_bus_add_devices call and fix MCFG saving code - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources Jayachandran C (5): APCI: MCFG: Move mmcfg_list management to drivers/acpi PCI: Handle ACPI companion and domain number ACPI: PCI: Support platforms that need pci_remap_iospace arm64: pci: Add ACPI support PCI: ACPI: Add a generic ACPI based host controller arch/arm64/Kconfig | 3 + arch/arm64/kernel/pci.c | 34 ++++- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 2 + drivers/acpi/pci_host_acpi.c | 186 ++++++++++++++++++++++++ drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 62 +++++++- drivers/acpi/resource.c | 2 + drivers/pci/pci.c | 15 +- drivers/pci/probe.c | 2 + drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 59 ++++++++ 17 files changed, 717 insertions(+), 269 deletions(-) create mode 100644 drivers/acpi/pci_host_acpi.c create mode 100644 drivers/acpi/pci_mcfg.c -- 1.9.1 ^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 0/5] ACPI based PCI support for arm64 @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel This patchset provides a generic ACPI based PCI host controller implementation and uses it on arm64. The first patch moves the common code to handle MCFG ACPI table from arch/x86 to drivers/acpi/pci_mcfg.c. The last patch in the patchset provides the generic implementation of an ACPI based PCI host controller with a new file drivers/acpi/pci_host_acpi.c. The other patches are to fix up arm64 and ACPI code to work with these two patches. The pci host controller implementation keeps a reference to pci_mmcfg_region entry so that config space access is done with a simple mapping and generic PCI config read/write. There is also an implementation of raw_pci_read/raw_pci_write provided by walking the pci_mmcfg_list The patchset is against 4.4, but it can be applied to pci/next with a very minor fixup. This is tested with arm64 QEMU and OVMF and on x86 with qemu. More testing or comments are welcome. The idea is to provide a simpler alternative for ACPI PCI support in arm64, any feedback from PCI or ACPI maintainers would be appreciated on this front. Thanks, JC. v5->v6: - fix arm64 with xen compile issue reported by kbuild bot v4->v5: - Fix arm build issue reported by the kbuild bot - Use config_enabled instead of ifdef in pci_mcfg.c v3->v4: - Handle suggestions from Arnd Bergmann <arnd@arndb.de> * move the implementation of host controller to drivers/acpi under config option CONFIG_ACPI_PCI_HOST_GENERIC * remove unnecessary arch hooks (this done as much as possible without affecting the scope of the patch, will need another separate patchset for the rest). * remove pcibios_assign_resources fs_initcall that was added - fixup domain_nr assignment code, and move ACPI companion set to PCI code. - Call map resource correctly in setup code - fix kbuild robot report - More testing v2->v3: - Move maintenance of the pci_mmcfg_list to drivers/acpi/pci_mcfg.c without changing x86 logic - use the pci_mmcfg_list in ARM64 implementation - provide raw_pci_read/raw_pci_write v1->v2: - use CONFIG_PCI_MMCONFIG on arm64, provide a weak implementation of pci_mmcfg_late_init for arm64. - The real implementation of pci_mmcfg_late_init is in pci-host-acpi.c and it will save the MCFG table entries to an array. Earlier this was done with an arch_init call - remove unneeded pci_bus_add_devices call and fix MCFG saving code - Added a patch to ACPI pci_root.c to handle arm64 PCI IO resources Jayachandran C (5): APCI: MCFG: Move mmcfg_list management to drivers/acpi PCI: Handle ACPI companion and domain number ACPI: PCI: Support platforms that need pci_remap_iospace arm64: pci: Add ACPI support PCI: ACPI: Add a generic ACPI based host controller arch/arm64/Kconfig | 3 + arch/arm64/kernel/pci.c | 34 ++++- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 2 + drivers/acpi/pci_host_acpi.c | 186 ++++++++++++++++++++++++ drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 62 +++++++- drivers/acpi/resource.c | 2 + drivers/pci/pci.c | 15 +- drivers/pci/probe.c | 2 + drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 59 ++++++++ 17 files changed, 717 insertions(+), 269 deletions(-) create mode 100644 drivers/acpi/pci_host_acpi.c create mode 100644 drivers/acpi/pci_mcfg.c -- 1.9.1 ^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C (?) (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is to share the API and code with ARM64 later. The corresponding declarations are moved from asm/pci_x86.h to linux/pci-acpi.h As a part of this we introduce three functions that can be implemented by the arch code: pci_mmconfig_map_resource() to map a mcfg entry, pci_mmconfig_unmap_resource to do the corresponding unmap and pci_mmconfig_enabled to see if the arch setup of mcfg entries was successful. We also provide weak implementations of these, which will be used from ARM64. On x86, we retain the old logic by providing platform specific implementation. This patch is purely rearranging code, it should not have any impact on the logic of MCFG parsing or list handling. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Makefile | 1 + drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 33 +++++ 9 files changed, 386 insertions(+), 261 deletions(-) create mode 100644 drivers/acpi/pci_mcfg.c diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195d..83d74cb 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void); /* pci-mmconfig.c */ - -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) - -struct pci_mmcfg_region { - struct list_head list; - struct resource res; - u64 address; - char __iomem *virt; - u16 segment; - u8 start_bus; - u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; -}; - +struct pci_mmcfg_region; extern int __init pci_mmcfg_arch_init(void); extern void __init pci_mmcfg_arch_free(void); extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr); -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); - -extern struct list_head pci_mmcfg_list; - -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) /* * AMD Fam10h CPUs are buggy, and cannot access MMIO config space diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..626710b 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -12,13 +12,12 @@ #include <linux/pci.h> #include <linux/init.h> -#include <linux/sfi_acpi.h> #include <linux/bitmap.h> -#include <linux/dmi.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/rculist.h> #include <asm/e820.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> #include <asm/acpi.h> @@ -27,9 +26,6 @@ /* Indicate if the mmcfg resources have been placed into the resource table. */ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed; -static DEFINE_MUTEX(pci_mmcfg_lock); - -LIST_HEAD(pci_mmcfg_list); static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) pci_mmconfig_remove(cfg); } -static void list_add_sorted(struct pci_mmcfg_region *new) -{ - struct pci_mmcfg_region *cfg; - - /* keep list sorted by segment and starting bus number */ - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { - if (cfg->segment > new->segment || - (cfg->segment == new->segment && - cfg->start_bus >= new->start_bus)) { - list_add_tail_rcu(&new->list, &cfg->list); - return; - } - } - list_add_tail_rcu(&new->list, &pci_mmcfg_list); -} - -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - struct resource *res; - - if (addr == 0) - return NULL; - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) - return NULL; - - new->address = addr; - new->segment = segment; - new->start_bus = start; - new->end_bus = end; - - res = &new->res; - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); - res->name = new->name; - - return new; -} - -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - - new = pci_mmconfig_alloc(segment, start, end, addr); - if (new) { - mutex_lock(&pci_mmcfg_lock); - list_add_sorted(new); - mutex_unlock(&pci_mmcfg_lock); - - pr_info(PREFIX - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " - "(base %#lx)\n", - segment, start, end, &new->res, (unsigned long)addr); - } - - return new; -} - -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == segment && - cfg->start_bus <= bus && bus <= cfg->end_bus) - return cfg; - - return NULL; -} - static const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) } } -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - return -EINVAL; -} - -static int __init pci_parse_mcfg(struct acpi_table_header *header) -{ - struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *cfg_table, *cfg; - unsigned long i; - int entries; - - if (!header) - return -EINVAL; - - mcfg = (struct acpi_table_mcfg *)header; - - /* how many config structures do we have */ - free_all_mmcfg(); - entries = 0; - i = header->length - sizeof(struct acpi_table_mcfg); - while (i >= sizeof(struct acpi_mcfg_allocation)) { - entries++; - i -= sizeof(struct acpi_mcfg_allocation); - } - if (entries == 0) { - pr_err(PREFIX "MMCONFIG has no entries\n"); - return -ENODEV; - } - - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; - for (i = 0; i < entries; i++) { - cfg = &cfg_table[i]; - if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); - return -ENODEV; - } - - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { - pr_warn(PREFIX "no memory for MCFG entries\n"); - free_all_mmcfg(); - return -ENOMEM; - } - } - - return 0; -} - #ifdef CONFIG_ACPI_APEI extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, void *data), void *data); @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) static int __initdata known_bridge; +static void __init pci_mmcfg_list_setup(void) +{ + free_all_mmcfg(); + if (pci_mmconfig_parse_table()) + free_all_mmcfg(); +} + void __init pci_mmcfg_early_init(void) { if (pci_probe & PCI_PROBE_MMCONF) { if (pci_mmcfg_check_hostbridge()) known_bridge = 1; else - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(1); set_apei_filter(); @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) /* MMCONFIG hasn't been enabled yet, try again */ if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(0); } } @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources); -/* Add MMCFG information for host bridges */ -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr) +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) { - int rc; - struct resource *tmp = NULL; - struct pci_mmcfg_region *cfg; + struct resource *tmp; - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) - return -ENODEV; - - if (start > end) - return -EINVAL; - - mutex_lock(&pci_mmcfg_lock); - cfg = pci_mmconfig_lookup(seg, start); - if (cfg) { - if (cfg->end_bus < end) - dev_info(dev, FW_INFO - "MMCONFIG for " - "domain %04x [bus %02x-%02x] " - "only partially covers this bridge\n", - cfg->segment, cfg->start_bus, cfg->end_bus); - mutex_unlock(&pci_mmcfg_lock); - return -EEXIST; - } - - if (!addr) { - mutex_unlock(&pci_mmcfg_lock); - return -EINVAL; - } - - rc = -EBUSY; - cfg = pci_mmconfig_alloc(seg, start, end, addr); - if (cfg == NULL) { - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); - rc = -ENOMEM; - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", &cfg->res); - } else { - /* Insert resource if it's not in boot stage */ - if (pci_mmcfg_running_state) - tmp = insert_resource_conflict(&iomem_resource, - &cfg->res); - + return -EBUSY; + } + /* Insert resource if it's not in boot stage */ + if (pci_mmcfg_running_state) { + tmp = insert_resource_conflict(&iomem_resource, + &cfg->res); if (tmp) { - dev_warn(dev, - "MMCONFIG %pR conflicts with " - "%s %pR\n", - &cfg->res, tmp->name, tmp); - } else if (pci_mmcfg_arch_map(cfg)) { - dev_warn(dev, "fail to map MMCONFIG %pR.\n", - &cfg->res); - } else { - list_add_sorted(cfg); - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)addr); - cfg = NULL; - rc = 0; + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &cfg->res, tmp->name, tmp); + return -EBUSY; } } - - if (cfg) { - if (cfg->res.parent) - release_resource(&cfg->res); - kfree(cfg); + if (pci_mmcfg_arch_map(cfg)) { + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); + return -EBUSY; } - - mutex_unlock(&pci_mmcfg_lock); - - return rc; + return 0; } -/* Delete MMCFG information for host bridges */ -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) { - struct pci_mmcfg_region *cfg; - - mutex_lock(&pci_mmcfg_lock); - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == seg && cfg->start_bus == start && - cfg->end_bus == end) { - list_del_rcu(&cfg->list); - synchronize_rcu(); - pci_mmcfg_arch_unmap(cfg); - if (cfg->res.parent) - release_resource(&cfg->res); - mutex_unlock(&pci_mmcfg_lock); - kfree(cfg); - return 0; - } - mutex_unlock(&pci_mmcfg_lock); + pci_mmcfg_arch_unmap(cfg); + if (cfg->res.parent) + release_resource(&cfg->res); + cfg->res.parent = NULL; +} - return -ENOENT; +int pci_mmconfig_enabled(void) +{ + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..38a37f8 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -12,6 +12,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index bea5249..29253ec 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 2e565e6..c181eeb 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -14,6 +14,7 @@ */ #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> static u8 limit __read_mostly; diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf3..3f65311 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 0000000..ea84365 --- /dev/null +++ b/drivers/acpi/pci_mcfg.c @@ -0,0 +1,312 @@ +/* + * pci_mcfg.c + * + * Common code to maintain the MCFG areas and mappings + * + * This has been extracted from arch/x86/pci/mmconfig-shared.c + * and moved here so that other architectures can use this code. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/rculist.h> + +#define PREFIX "ACPI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); +LIST_HEAD(pci_mmcfg_list); + +static void list_add_sorted(struct pci_mmcfg_region *new) +{ + struct pci_mmcfg_region *cfg; + + /* keep list sorted by segment and starting bus number */ + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { + if (cfg->segment > new->segment || + (cfg->segment == new->segment && + cfg->start_bus >= new->start_bus)) { + list_add_tail_rcu(&new->list, &cfg->list); + return; + } + } + list_add_tail_rcu(&new->list, &pci_mmcfg_list); +} + +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + struct resource *res; + + if (addr == 0) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + new->address = addr; + new->segment = segment; + new->start_bus = start; + new->end_bus = end; + + res = &new->res; + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); + res->name = new->name; + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + + new = pci_mmconfig_alloc(segment, start, end, addr); + if (new) { + mutex_lock(&pci_mmcfg_lock); + list_add_sorted(new); + mutex_unlock(&pci_mmcfg_lock); + + pr_info(PREFIX + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " + "(base %#lx)\n", + segment, start, end, &new->res, (unsigned long)addr); + } + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == segment && + cfg->start_bus <= bus && bus <= cfg->end_bus) + return cfg; + + return NULL; +} + +/* + * Map a pci_mmcfg_region, can be overrriden by arch + */ +int __weak pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg) +{ + struct resource *tmp; + void __iomem *vaddr; + + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); + if (tmp) { + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &mcfg->res, tmp->name, tmp); + return -EBUSY; + } + + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); + if (!vaddr) { + release_resource(&mcfg->res); + return -ENOMEM; + } + + mcfg->virt = vaddr; + return 0; +} + +/* + * Unmap a pci_mmcfg_region, can be overrriden by arch + */ +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) +{ + if (mcfg->virt) { + iounmap(mcfg->virt); + mcfg->virt = NULL; + } + if (mcfg->res.parent) { + release_resource(&mcfg->res); + mcfg->res.parent = NULL; + } +} + +/* + * check if the mmconfig is enabled and configured + */ +int __weak pci_mmconfig_enabled(void) +{ + return 1; +} + +/* Add MMCFG information for host bridges */ +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr) +{ + struct pci_mmcfg_region *cfg; + int rc; + + if (!pci_mmconfig_enabled()) + return -ENODEV; + if (start > end) + return -EINVAL; + + mutex_lock(&pci_mmcfg_lock); + cfg = pci_mmconfig_lookup(seg, start); + if (cfg) { + if (cfg->end_bus < end) + dev_info(dev, FW_INFO + "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg->segment, cfg->start_bus, cfg->end_bus); + rc = -EEXIST; + goto err; + } + + if (!addr) { + rc = -EINVAL; + goto err; + } + + cfg = pci_mmconfig_alloc(seg, start, end, addr); + if (cfg == NULL) { + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); + rc = -ENOMEM; + goto err; + } + rc = pci_mmconfig_map_resource(dev, cfg); + if (!rc) { + list_add_sorted(cfg); + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)addr); + return 0; + } else { + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); + } + +err: + mutex_unlock(&pci_mmcfg_lock); + return rc; +} + +/* Delete MMCFG information for host bridges */ +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +{ + struct pci_mmcfg_region *cfg; + + mutex_lock(&pci_mmcfg_lock); + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == seg && cfg->start_bus == start && + cfg->end_bus == end) { + list_del_rcu(&cfg->list); + synchronize_rcu(); + pci_mmconfig_unmap_resource(cfg); + mutex_unlock(&pci_mmcfg_lock); + kfree(cfg); + return 0; + } + mutex_unlock(&pci_mmcfg_lock); + + return -ENOENT; +} + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (!config_enabled(CONFIG_X86)) + return 0; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +static int __init pci_parse_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *cfg_table, *cfg; + unsigned long i; + int entries; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + + /* how many config structures do we have */ + entries = 0; + i = header->length - sizeof(struct acpi_table_mcfg); + while (i >= sizeof(struct acpi_mcfg_allocation)) { + entries++; + i -= sizeof(struct acpi_mcfg_allocation); + } + if (entries == 0) { + pr_err(PREFIX "MMCONFIG has no entries\n"); + return -ENODEV; + } + + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; + for (i = 0; i < entries; i++) { + cfg = &cfg_table[i]; + if (acpi_mcfg_check_entry(mcfg, cfg)) + return -ENODEV; + + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + cfg->end_bus_number, cfg->address) == NULL) { + pr_warn(PREFIX "no memory for MCFG entries\n"); + return -ENOMEM; + } + } + + return 0; +} + +int __init pci_mmconfig_parse_table(void) +{ + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); +} + +void __weak __init pci_mmcfg_late_init(void) +{ + int err, n = 0; + struct pci_mmcfg_region *cfg; + + err = pci_mmconfig_parse_table(); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + return; + } + + list_for_each_entry(cfg, &pci_mmcfg_list, list) { + pci_mmconfig_map_resource(NULL, cfg); + n++; + } + + pr_info(PREFIX " MCFG table loaded %d entries\n", n); +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7494dbe..97aa9d3 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -27,9 +27,6 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include "../pci/pci.h" -#ifdef CONFIG_PCI_MMCONFIG -#include <asm/pci_x86.h> -#endif static bool __read_mostly pci_seg_supported = true; @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) if (!xen_initial_domain()) return 0; - if ((pci_probe & PCI_PROBE_MMCONF) == 0) + if (!pci_mmconfig_enabled()) return 0; if (list_empty(&pci_mmcfg_list)) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab057..e9450ef 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; #define RESET_DELAY_DSM 0x08 #define FUNCTION_DELAY_DSM 0x09 +/* common API to maintain list of MCFG regions */ +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) + +struct pci_mmcfg_region { + struct list_head list; + struct resource res; + u64 address; + char __iomem *virt; + u16 segment; + u8 start_bus; + u8 end_bus; + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; +}; + +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr); +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +extern int pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg); +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); +extern int pci_mmconfig_enabled(void); +extern int __init pci_mmconfig_parse_table(void); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is to share the API and code with ARM64 later. The corresponding declarations are moved from asm/pci_x86.h to linux/pci-acpi.h As a part of this we introduce three functions that can be implemented by the arch code: pci_mmconfig_map_resource() to map a mcfg entry, pci_mmconfig_unmap_resource to do the corresponding unmap and pci_mmconfig_enabled to see if the arch setup of mcfg entries was successful. We also provide weak implementations of these, which will be used from ARM64. On x86, we retain the old logic by providing platform specific implementation. This patch is purely rearranging code, it should not have any impact on the logic of MCFG parsing or list handling. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Makefile | 1 + drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 33 +++++ 9 files changed, 386 insertions(+), 261 deletions(-) create mode 100644 drivers/acpi/pci_mcfg.c diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195d..83d74cb 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void); /* pci-mmconfig.c */ - -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) - -struct pci_mmcfg_region { - struct list_head list; - struct resource res; - u64 address; - char __iomem *virt; - u16 segment; - u8 start_bus; - u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; -}; - +struct pci_mmcfg_region; extern int __init pci_mmcfg_arch_init(void); extern void __init pci_mmcfg_arch_free(void); extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr); -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); - -extern struct list_head pci_mmcfg_list; - -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) /* * AMD Fam10h CPUs are buggy, and cannot access MMIO config space diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..626710b 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -12,13 +12,12 @@ #include <linux/pci.h> #include <linux/init.h> -#include <linux/sfi_acpi.h> #include <linux/bitmap.h> -#include <linux/dmi.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/rculist.h> #include <asm/e820.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> #include <asm/acpi.h> @@ -27,9 +26,6 @@ /* Indicate if the mmcfg resources have been placed into the resource table. */ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed; -static DEFINE_MUTEX(pci_mmcfg_lock); - -LIST_HEAD(pci_mmcfg_list); static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) pci_mmconfig_remove(cfg); } -static void list_add_sorted(struct pci_mmcfg_region *new) -{ - struct pci_mmcfg_region *cfg; - - /* keep list sorted by segment and starting bus number */ - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { - if (cfg->segment > new->segment || - (cfg->segment == new->segment && - cfg->start_bus >= new->start_bus)) { - list_add_tail_rcu(&new->list, &cfg->list); - return; - } - } - list_add_tail_rcu(&new->list, &pci_mmcfg_list); -} - -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - struct resource *res; - - if (addr == 0) - return NULL; - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) - return NULL; - - new->address = addr; - new->segment = segment; - new->start_bus = start; - new->end_bus = end; - - res = &new->res; - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); - res->name = new->name; - - return new; -} - -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - - new = pci_mmconfig_alloc(segment, start, end, addr); - if (new) { - mutex_lock(&pci_mmcfg_lock); - list_add_sorted(new); - mutex_unlock(&pci_mmcfg_lock); - - pr_info(PREFIX - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " - "(base %#lx)\n", - segment, start, end, &new->res, (unsigned long)addr); - } - - return new; -} - -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == segment && - cfg->start_bus <= bus && bus <= cfg->end_bus) - return cfg; - - return NULL; -} - static const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) } } -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - return -EINVAL; -} - -static int __init pci_parse_mcfg(struct acpi_table_header *header) -{ - struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *cfg_table, *cfg; - unsigned long i; - int entries; - - if (!header) - return -EINVAL; - - mcfg = (struct acpi_table_mcfg *)header; - - /* how many config structures do we have */ - free_all_mmcfg(); - entries = 0; - i = header->length - sizeof(struct acpi_table_mcfg); - while (i >= sizeof(struct acpi_mcfg_allocation)) { - entries++; - i -= sizeof(struct acpi_mcfg_allocation); - } - if (entries == 0) { - pr_err(PREFIX "MMCONFIG has no entries\n"); - return -ENODEV; - } - - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; - for (i = 0; i < entries; i++) { - cfg = &cfg_table[i]; - if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); - return -ENODEV; - } - - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { - pr_warn(PREFIX "no memory for MCFG entries\n"); - free_all_mmcfg(); - return -ENOMEM; - } - } - - return 0; -} - #ifdef CONFIG_ACPI_APEI extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, void *data), void *data); @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) static int __initdata known_bridge; +static void __init pci_mmcfg_list_setup(void) +{ + free_all_mmcfg(); + if (pci_mmconfig_parse_table()) + free_all_mmcfg(); +} + void __init pci_mmcfg_early_init(void) { if (pci_probe & PCI_PROBE_MMCONF) { if (pci_mmcfg_check_hostbridge()) known_bridge = 1; else - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(1); set_apei_filter(); @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) /* MMCONFIG hasn't been enabled yet, try again */ if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(0); } } @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources); -/* Add MMCFG information for host bridges */ -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr) +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) { - int rc; - struct resource *tmp = NULL; - struct pci_mmcfg_region *cfg; + struct resource *tmp; - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) - return -ENODEV; - - if (start > end) - return -EINVAL; - - mutex_lock(&pci_mmcfg_lock); - cfg = pci_mmconfig_lookup(seg, start); - if (cfg) { - if (cfg->end_bus < end) - dev_info(dev, FW_INFO - "MMCONFIG for " - "domain %04x [bus %02x-%02x] " - "only partially covers this bridge\n", - cfg->segment, cfg->start_bus, cfg->end_bus); - mutex_unlock(&pci_mmcfg_lock); - return -EEXIST; - } - - if (!addr) { - mutex_unlock(&pci_mmcfg_lock); - return -EINVAL; - } - - rc = -EBUSY; - cfg = pci_mmconfig_alloc(seg, start, end, addr); - if (cfg == NULL) { - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); - rc = -ENOMEM; - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", &cfg->res); - } else { - /* Insert resource if it's not in boot stage */ - if (pci_mmcfg_running_state) - tmp = insert_resource_conflict(&iomem_resource, - &cfg->res); - + return -EBUSY; + } + /* Insert resource if it's not in boot stage */ + if (pci_mmcfg_running_state) { + tmp = insert_resource_conflict(&iomem_resource, + &cfg->res); if (tmp) { - dev_warn(dev, - "MMCONFIG %pR conflicts with " - "%s %pR\n", - &cfg->res, tmp->name, tmp); - } else if (pci_mmcfg_arch_map(cfg)) { - dev_warn(dev, "fail to map MMCONFIG %pR.\n", - &cfg->res); - } else { - list_add_sorted(cfg); - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)addr); - cfg = NULL; - rc = 0; + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &cfg->res, tmp->name, tmp); + return -EBUSY; } } - - if (cfg) { - if (cfg->res.parent) - release_resource(&cfg->res); - kfree(cfg); + if (pci_mmcfg_arch_map(cfg)) { + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); + return -EBUSY; } - - mutex_unlock(&pci_mmcfg_lock); - - return rc; + return 0; } -/* Delete MMCFG information for host bridges */ -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) { - struct pci_mmcfg_region *cfg; - - mutex_lock(&pci_mmcfg_lock); - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == seg && cfg->start_bus == start && - cfg->end_bus == end) { - list_del_rcu(&cfg->list); - synchronize_rcu(); - pci_mmcfg_arch_unmap(cfg); - if (cfg->res.parent) - release_resource(&cfg->res); - mutex_unlock(&pci_mmcfg_lock); - kfree(cfg); - return 0; - } - mutex_unlock(&pci_mmcfg_lock); + pci_mmcfg_arch_unmap(cfg); + if (cfg->res.parent) + release_resource(&cfg->res); + cfg->res.parent = NULL; +} - return -ENOENT; +int pci_mmconfig_enabled(void) +{ + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..38a37f8 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -12,6 +12,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index bea5249..29253ec 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 2e565e6..c181eeb 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -14,6 +14,7 @@ */ #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> static u8 limit __read_mostly; diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf3..3f65311 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 0000000..ea84365 --- /dev/null +++ b/drivers/acpi/pci_mcfg.c @@ -0,0 +1,312 @@ +/* + * pci_mcfg.c + * + * Common code to maintain the MCFG areas and mappings + * + * This has been extracted from arch/x86/pci/mmconfig-shared.c + * and moved here so that other architectures can use this code. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/rculist.h> + +#define PREFIX "ACPI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); +LIST_HEAD(pci_mmcfg_list); + +static void list_add_sorted(struct pci_mmcfg_region *new) +{ + struct pci_mmcfg_region *cfg; + + /* keep list sorted by segment and starting bus number */ + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { + if (cfg->segment > new->segment || + (cfg->segment == new->segment && + cfg->start_bus >= new->start_bus)) { + list_add_tail_rcu(&new->list, &cfg->list); + return; + } + } + list_add_tail_rcu(&new->list, &pci_mmcfg_list); +} + +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + struct resource *res; + + if (addr == 0) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + new->address = addr; + new->segment = segment; + new->start_bus = start; + new->end_bus = end; + + res = &new->res; + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); + res->name = new->name; + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + + new = pci_mmconfig_alloc(segment, start, end, addr); + if (new) { + mutex_lock(&pci_mmcfg_lock); + list_add_sorted(new); + mutex_unlock(&pci_mmcfg_lock); + + pr_info(PREFIX + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " + "(base %#lx)\n", + segment, start, end, &new->res, (unsigned long)addr); + } + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == segment && + cfg->start_bus <= bus && bus <= cfg->end_bus) + return cfg; + + return NULL; +} + +/* + * Map a pci_mmcfg_region, can be overrriden by arch + */ +int __weak pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg) +{ + struct resource *tmp; + void __iomem *vaddr; + + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); + if (tmp) { + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &mcfg->res, tmp->name, tmp); + return -EBUSY; + } + + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); + if (!vaddr) { + release_resource(&mcfg->res); + return -ENOMEM; + } + + mcfg->virt = vaddr; + return 0; +} + +/* + * Unmap a pci_mmcfg_region, can be overrriden by arch + */ +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) +{ + if (mcfg->virt) { + iounmap(mcfg->virt); + mcfg->virt = NULL; + } + if (mcfg->res.parent) { + release_resource(&mcfg->res); + mcfg->res.parent = NULL; + } +} + +/* + * check if the mmconfig is enabled and configured + */ +int __weak pci_mmconfig_enabled(void) +{ + return 1; +} + +/* Add MMCFG information for host bridges */ +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr) +{ + struct pci_mmcfg_region *cfg; + int rc; + + if (!pci_mmconfig_enabled()) + return -ENODEV; + if (start > end) + return -EINVAL; + + mutex_lock(&pci_mmcfg_lock); + cfg = pci_mmconfig_lookup(seg, start); + if (cfg) { + if (cfg->end_bus < end) + dev_info(dev, FW_INFO + "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg->segment, cfg->start_bus, cfg->end_bus); + rc = -EEXIST; + goto err; + } + + if (!addr) { + rc = -EINVAL; + goto err; + } + + cfg = pci_mmconfig_alloc(seg, start, end, addr); + if (cfg == NULL) { + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); + rc = -ENOMEM; + goto err; + } + rc = pci_mmconfig_map_resource(dev, cfg); + if (!rc) { + list_add_sorted(cfg); + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)addr); + return 0; + } else { + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); + } + +err: + mutex_unlock(&pci_mmcfg_lock); + return rc; +} + +/* Delete MMCFG information for host bridges */ +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +{ + struct pci_mmcfg_region *cfg; + + mutex_lock(&pci_mmcfg_lock); + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == seg && cfg->start_bus == start && + cfg->end_bus == end) { + list_del_rcu(&cfg->list); + synchronize_rcu(); + pci_mmconfig_unmap_resource(cfg); + mutex_unlock(&pci_mmcfg_lock); + kfree(cfg); + return 0; + } + mutex_unlock(&pci_mmcfg_lock); + + return -ENOENT; +} + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (!config_enabled(CONFIG_X86)) + return 0; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +static int __init pci_parse_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *cfg_table, *cfg; + unsigned long i; + int entries; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + + /* how many config structures do we have */ + entries = 0; + i = header->length - sizeof(struct acpi_table_mcfg); + while (i >= sizeof(struct acpi_mcfg_allocation)) { + entries++; + i -= sizeof(struct acpi_mcfg_allocation); + } + if (entries == 0) { + pr_err(PREFIX "MMCONFIG has no entries\n"); + return -ENODEV; + } + + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; + for (i = 0; i < entries; i++) { + cfg = &cfg_table[i]; + if (acpi_mcfg_check_entry(mcfg, cfg)) + return -ENODEV; + + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + cfg->end_bus_number, cfg->address) == NULL) { + pr_warn(PREFIX "no memory for MCFG entries\n"); + return -ENOMEM; + } + } + + return 0; +} + +int __init pci_mmconfig_parse_table(void) +{ + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); +} + +void __weak __init pci_mmcfg_late_init(void) +{ + int err, n = 0; + struct pci_mmcfg_region *cfg; + + err = pci_mmconfig_parse_table(); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + return; + } + + list_for_each_entry(cfg, &pci_mmcfg_list, list) { + pci_mmconfig_map_resource(NULL, cfg); + n++; + } + + pr_info(PREFIX " MCFG table loaded %d entries\n", n); +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7494dbe..97aa9d3 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -27,9 +27,6 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include "../pci/pci.h" -#ifdef CONFIG_PCI_MMCONFIG -#include <asm/pci_x86.h> -#endif static bool __read_mostly pci_seg_supported = true; @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) if (!xen_initial_domain()) return 0; - if ((pci_probe & PCI_PROBE_MMCONF) == 0) + if (!pci_mmconfig_enabled()) return 0; if (list_empty(&pci_mmcfg_list)) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab057..e9450ef 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; #define RESET_DELAY_DSM 0x08 #define FUNCTION_DELAY_DSM 0x09 +/* common API to maintain list of MCFG regions */ +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) + +struct pci_mmcfg_region { + struct list_head list; + struct resource res; + u64 address; + char __iomem *virt; + u16 segment; + u8 start_bus; + u8 end_bus; + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; +}; + +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr); +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +extern int pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg); +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); +extern int pci_mmconfig_enabled(void); +extern int __init pci_mmconfig_parse_table(void); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is to share the API and code with ARM64 later. The corresponding declarations are moved from asm/pci_x86.h to linux/pci-acpi.h As a part of this we introduce three functions that can be implemented by the arch code: pci_mmconfig_map_resource() to map a mcfg entry, pci_mmconfig_unmap_resource to do the corresponding unmap and pci_mmconfig_enabled to see if the arch setup of mcfg entries was successful. We also provide weak implementations of these, which will be used from ARM64. On x86, we retain the old logic by providing platform specific implementation. This patch is purely rearranging code, it should not have any impact on the logic of MCFG parsing or list handling. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Makefile | 1 + drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 33 +++++ 9 files changed, 386 insertions(+), 261 deletions(-) create mode 100644 drivers/acpi/pci_mcfg.c diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195d..83d74cb 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void); /* pci-mmconfig.c */ - -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) - -struct pci_mmcfg_region { - struct list_head list; - struct resource res; - u64 address; - char __iomem *virt; - u16 segment; - u8 start_bus; - u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; -}; - +struct pci_mmcfg_region; extern int __init pci_mmcfg_arch_init(void); extern void __init pci_mmcfg_arch_free(void); extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr); -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); - -extern struct list_head pci_mmcfg_list; - -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) /* * AMD Fam10h CPUs are buggy, and cannot access MMIO config space diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..626710b 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -12,13 +12,12 @@ #include <linux/pci.h> #include <linux/init.h> -#include <linux/sfi_acpi.h> #include <linux/bitmap.h> -#include <linux/dmi.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/rculist.h> #include <asm/e820.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> #include <asm/acpi.h> @@ -27,9 +26,6 @@ /* Indicate if the mmcfg resources have been placed into the resource table. */ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed; -static DEFINE_MUTEX(pci_mmcfg_lock); - -LIST_HEAD(pci_mmcfg_list); static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) pci_mmconfig_remove(cfg); } -static void list_add_sorted(struct pci_mmcfg_region *new) -{ - struct pci_mmcfg_region *cfg; - - /* keep list sorted by segment and starting bus number */ - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { - if (cfg->segment > new->segment || - (cfg->segment == new->segment && - cfg->start_bus >= new->start_bus)) { - list_add_tail_rcu(&new->list, &cfg->list); - return; - } - } - list_add_tail_rcu(&new->list, &pci_mmcfg_list); -} - -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - struct resource *res; - - if (addr == 0) - return NULL; - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) - return NULL; - - new->address = addr; - new->segment = segment; - new->start_bus = start; - new->end_bus = end; - - res = &new->res; - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); - res->name = new->name; - - return new; -} - -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - - new = pci_mmconfig_alloc(segment, start, end, addr); - if (new) { - mutex_lock(&pci_mmcfg_lock); - list_add_sorted(new); - mutex_unlock(&pci_mmcfg_lock); - - pr_info(PREFIX - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " - "(base %#lx)\n", - segment, start, end, &new->res, (unsigned long)addr); - } - - return new; -} - -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == segment && - cfg->start_bus <= bus && bus <= cfg->end_bus) - return cfg; - - return NULL; -} - static const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) } } -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - return -EINVAL; -} - -static int __init pci_parse_mcfg(struct acpi_table_header *header) -{ - struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *cfg_table, *cfg; - unsigned long i; - int entries; - - if (!header) - return -EINVAL; - - mcfg = (struct acpi_table_mcfg *)header; - - /* how many config structures do we have */ - free_all_mmcfg(); - entries = 0; - i = header->length - sizeof(struct acpi_table_mcfg); - while (i >= sizeof(struct acpi_mcfg_allocation)) { - entries++; - i -= sizeof(struct acpi_mcfg_allocation); - } - if (entries == 0) { - pr_err(PREFIX "MMCONFIG has no entries\n"); - return -ENODEV; - } - - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; - for (i = 0; i < entries; i++) { - cfg = &cfg_table[i]; - if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); - return -ENODEV; - } - - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { - pr_warn(PREFIX "no memory for MCFG entries\n"); - free_all_mmcfg(); - return -ENOMEM; - } - } - - return 0; -} - #ifdef CONFIG_ACPI_APEI extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, void *data), void *data); @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) static int __initdata known_bridge; +static void __init pci_mmcfg_list_setup(void) +{ + free_all_mmcfg(); + if (pci_mmconfig_parse_table()) + free_all_mmcfg(); +} + void __init pci_mmcfg_early_init(void) { if (pci_probe & PCI_PROBE_MMCONF) { if (pci_mmcfg_check_hostbridge()) known_bridge = 1; else - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(1); set_apei_filter(); @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) /* MMCONFIG hasn't been enabled yet, try again */ if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(0); } } @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources); -/* Add MMCFG information for host bridges */ -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr) +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) { - int rc; - struct resource *tmp = NULL; - struct pci_mmcfg_region *cfg; + struct resource *tmp; - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) - return -ENODEV; - - if (start > end) - return -EINVAL; - - mutex_lock(&pci_mmcfg_lock); - cfg = pci_mmconfig_lookup(seg, start); - if (cfg) { - if (cfg->end_bus < end) - dev_info(dev, FW_INFO - "MMCONFIG for " - "domain %04x [bus %02x-%02x] " - "only partially covers this bridge\n", - cfg->segment, cfg->start_bus, cfg->end_bus); - mutex_unlock(&pci_mmcfg_lock); - return -EEXIST; - } - - if (!addr) { - mutex_unlock(&pci_mmcfg_lock); - return -EINVAL; - } - - rc = -EBUSY; - cfg = pci_mmconfig_alloc(seg, start, end, addr); - if (cfg == NULL) { - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); - rc = -ENOMEM; - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", &cfg->res); - } else { - /* Insert resource if it's not in boot stage */ - if (pci_mmcfg_running_state) - tmp = insert_resource_conflict(&iomem_resource, - &cfg->res); - + return -EBUSY; + } + /* Insert resource if it's not in boot stage */ + if (pci_mmcfg_running_state) { + tmp = insert_resource_conflict(&iomem_resource, + &cfg->res); if (tmp) { - dev_warn(dev, - "MMCONFIG %pR conflicts with " - "%s %pR\n", - &cfg->res, tmp->name, tmp); - } else if (pci_mmcfg_arch_map(cfg)) { - dev_warn(dev, "fail to map MMCONFIG %pR.\n", - &cfg->res); - } else { - list_add_sorted(cfg); - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)addr); - cfg = NULL; - rc = 0; + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &cfg->res, tmp->name, tmp); + return -EBUSY; } } - - if (cfg) { - if (cfg->res.parent) - release_resource(&cfg->res); - kfree(cfg); + if (pci_mmcfg_arch_map(cfg)) { + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); + return -EBUSY; } - - mutex_unlock(&pci_mmcfg_lock); - - return rc; + return 0; } -/* Delete MMCFG information for host bridges */ -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) { - struct pci_mmcfg_region *cfg; - - mutex_lock(&pci_mmcfg_lock); - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == seg && cfg->start_bus == start && - cfg->end_bus == end) { - list_del_rcu(&cfg->list); - synchronize_rcu(); - pci_mmcfg_arch_unmap(cfg); - if (cfg->res.parent) - release_resource(&cfg->res); - mutex_unlock(&pci_mmcfg_lock); - kfree(cfg); - return 0; - } - mutex_unlock(&pci_mmcfg_lock); + pci_mmcfg_arch_unmap(cfg); + if (cfg->res.parent) + release_resource(&cfg->res); + cfg->res.parent = NULL; +} - return -ENOENT; +int pci_mmconfig_enabled(void) +{ + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..38a37f8 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -12,6 +12,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index bea5249..29253ec 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 2e565e6..c181eeb 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -14,6 +14,7 @@ */ #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> static u8 limit __read_mostly; diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf3..3f65311 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 0000000..ea84365 --- /dev/null +++ b/drivers/acpi/pci_mcfg.c @@ -0,0 +1,312 @@ +/* + * pci_mcfg.c + * + * Common code to maintain the MCFG areas and mappings + * + * This has been extracted from arch/x86/pci/mmconfig-shared.c + * and moved here so that other architectures can use this code. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/rculist.h> + +#define PREFIX "ACPI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); +LIST_HEAD(pci_mmcfg_list); + +static void list_add_sorted(struct pci_mmcfg_region *new) +{ + struct pci_mmcfg_region *cfg; + + /* keep list sorted by segment and starting bus number */ + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { + if (cfg->segment > new->segment || + (cfg->segment == new->segment && + cfg->start_bus >= new->start_bus)) { + list_add_tail_rcu(&new->list, &cfg->list); + return; + } + } + list_add_tail_rcu(&new->list, &pci_mmcfg_list); +} + +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + struct resource *res; + + if (addr == 0) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + new->address = addr; + new->segment = segment; + new->start_bus = start; + new->end_bus = end; + + res = &new->res; + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); + res->name = new->name; + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + + new = pci_mmconfig_alloc(segment, start, end, addr); + if (new) { + mutex_lock(&pci_mmcfg_lock); + list_add_sorted(new); + mutex_unlock(&pci_mmcfg_lock); + + pr_info(PREFIX + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " + "(base %#lx)\n", + segment, start, end, &new->res, (unsigned long)addr); + } + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == segment && + cfg->start_bus <= bus && bus <= cfg->end_bus) + return cfg; + + return NULL; +} + +/* + * Map a pci_mmcfg_region, can be overrriden by arch + */ +int __weak pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg) +{ + struct resource *tmp; + void __iomem *vaddr; + + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); + if (tmp) { + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &mcfg->res, tmp->name, tmp); + return -EBUSY; + } + + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); + if (!vaddr) { + release_resource(&mcfg->res); + return -ENOMEM; + } + + mcfg->virt = vaddr; + return 0; +} + +/* + * Unmap a pci_mmcfg_region, can be overrriden by arch + */ +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) +{ + if (mcfg->virt) { + iounmap(mcfg->virt); + mcfg->virt = NULL; + } + if (mcfg->res.parent) { + release_resource(&mcfg->res); + mcfg->res.parent = NULL; + } +} + +/* + * check if the mmconfig is enabled and configured + */ +int __weak pci_mmconfig_enabled(void) +{ + return 1; +} + +/* Add MMCFG information for host bridges */ +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr) +{ + struct pci_mmcfg_region *cfg; + int rc; + + if (!pci_mmconfig_enabled()) + return -ENODEV; + if (start > end) + return -EINVAL; + + mutex_lock(&pci_mmcfg_lock); + cfg = pci_mmconfig_lookup(seg, start); + if (cfg) { + if (cfg->end_bus < end) + dev_info(dev, FW_INFO + "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg->segment, cfg->start_bus, cfg->end_bus); + rc = -EEXIST; + goto err; + } + + if (!addr) { + rc = -EINVAL; + goto err; + } + + cfg = pci_mmconfig_alloc(seg, start, end, addr); + if (cfg == NULL) { + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); + rc = -ENOMEM; + goto err; + } + rc = pci_mmconfig_map_resource(dev, cfg); + if (!rc) { + list_add_sorted(cfg); + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)addr); + return 0; + } else { + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); + } + +err: + mutex_unlock(&pci_mmcfg_lock); + return rc; +} + +/* Delete MMCFG information for host bridges */ +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +{ + struct pci_mmcfg_region *cfg; + + mutex_lock(&pci_mmcfg_lock); + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == seg && cfg->start_bus == start && + cfg->end_bus == end) { + list_del_rcu(&cfg->list); + synchronize_rcu(); + pci_mmconfig_unmap_resource(cfg); + mutex_unlock(&pci_mmcfg_lock); + kfree(cfg); + return 0; + } + mutex_unlock(&pci_mmcfg_lock); + + return -ENOENT; +} + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (!config_enabled(CONFIG_X86)) + return 0; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +static int __init pci_parse_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *cfg_table, *cfg; + unsigned long i; + int entries; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + + /* how many config structures do we have */ + entries = 0; + i = header->length - sizeof(struct acpi_table_mcfg); + while (i >= sizeof(struct acpi_mcfg_allocation)) { + entries++; + i -= sizeof(struct acpi_mcfg_allocation); + } + if (entries == 0) { + pr_err(PREFIX "MMCONFIG has no entries\n"); + return -ENODEV; + } + + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; + for (i = 0; i < entries; i++) { + cfg = &cfg_table[i]; + if (acpi_mcfg_check_entry(mcfg, cfg)) + return -ENODEV; + + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + cfg->end_bus_number, cfg->address) == NULL) { + pr_warn(PREFIX "no memory for MCFG entries\n"); + return -ENOMEM; + } + } + + return 0; +} + +int __init pci_mmconfig_parse_table(void) +{ + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); +} + +void __weak __init pci_mmcfg_late_init(void) +{ + int err, n = 0; + struct pci_mmcfg_region *cfg; + + err = pci_mmconfig_parse_table(); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + return; + } + + list_for_each_entry(cfg, &pci_mmcfg_list, list) { + pci_mmconfig_map_resource(NULL, cfg); + n++; + } + + pr_info(PREFIX " MCFG table loaded %d entries\n", n); +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7494dbe..97aa9d3 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -27,9 +27,6 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include "../pci/pci.h" -#ifdef CONFIG_PCI_MMCONFIG -#include <asm/pci_x86.h> -#endif static bool __read_mostly pci_seg_supported = true; @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) if (!xen_initial_domain()) return 0; - if ((pci_probe & PCI_PROBE_MMCONF) == 0) + if (!pci_mmconfig_enabled()) return 0; if (list_empty(&pci_mmcfg_list)) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab057..e9450ef 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; #define RESET_DELAY_DSM 0x08 #define FUNCTION_DELAY_DSM 0x09 +/* common API to maintain list of MCFG regions */ +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) + +struct pci_mmcfg_region { + struct list_head list; + struct resource res; + u64 address; + char __iomem *virt; + u16 segment; + u8 start_bus; + u8 end_bus; + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; +}; + +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr); +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +extern int pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg); +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); +extern int pci_mmconfig_enabled(void); +extern int __init pci_mmconfig_parse_table(void); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is to share the API and code with ARM64 later. The corresponding declarations are moved from asm/pci_x86.h to linux/pci-acpi.h As a part of this we introduce three functions that can be implemented by the arch code: pci_mmconfig_map_resource() to map a mcfg entry, pci_mmconfig_unmap_resource to do the corresponding unmap and pci_mmconfig_enabled to see if the arch setup of mcfg entries was successful. We also provide weak implementations of these, which will be used from ARM64. On x86, we retain the old logic by providing platform specific implementation. This patch is purely rearranging code, it should not have any impact on the logic of MCFG parsing or list handling. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/x86/include/asm/pci_x86.h | 24 +--- arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Makefile | 1 + drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 5 +- include/linux/pci-acpi.h | 33 +++++ 9 files changed, 386 insertions(+), 261 deletions(-) create mode 100644 drivers/acpi/pci_mcfg.c diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195d..83d74cb 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void); /* pci-mmconfig.c */ - -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) - -struct pci_mmcfg_region { - struct list_head list; - struct resource res; - u64 address; - char __iomem *virt; - u16 segment; - u8 start_bus; - u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; -}; - +struct pci_mmcfg_region; extern int __init pci_mmcfg_arch_init(void); extern void __init pci_mmcfg_arch_free(void); extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr); -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); - -extern struct list_head pci_mmcfg_list; - -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) /* * AMD Fam10h CPUs are buggy, and cannot access MMIO config space diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..626710b 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -12,13 +12,12 @@ #include <linux/pci.h> #include <linux/init.h> -#include <linux/sfi_acpi.h> #include <linux/bitmap.h> -#include <linux/dmi.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/rculist.h> #include <asm/e820.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> #include <asm/acpi.h> @@ -27,9 +26,6 @@ /* Indicate if the mmcfg resources have been placed into the resource table. */ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed; -static DEFINE_MUTEX(pci_mmcfg_lock); - -LIST_HEAD(pci_mmcfg_list); static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) pci_mmconfig_remove(cfg); } -static void list_add_sorted(struct pci_mmcfg_region *new) -{ - struct pci_mmcfg_region *cfg; - - /* keep list sorted by segment and starting bus number */ - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { - if (cfg->segment > new->segment || - (cfg->segment == new->segment && - cfg->start_bus >= new->start_bus)) { - list_add_tail_rcu(&new->list, &cfg->list); - return; - } - } - list_add_tail_rcu(&new->list, &pci_mmcfg_list); -} - -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - struct resource *res; - - if (addr == 0) - return NULL; - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) - return NULL; - - new->address = addr; - new->segment = segment; - new->start_bus = start; - new->end_bus = end; - - res = &new->res; - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); - res->name = new->name; - - return new; -} - -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, - int end, u64 addr) -{ - struct pci_mmcfg_region *new; - - new = pci_mmconfig_alloc(segment, start, end, addr); - if (new) { - mutex_lock(&pci_mmcfg_lock); - list_add_sorted(new); - mutex_unlock(&pci_mmcfg_lock); - - pr_info(PREFIX - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " - "(base %#lx)\n", - segment, start, end, &new->res, (unsigned long)addr); - } - - return new; -} - -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == segment && - cfg->start_bus <= bus && bus <= cfg->end_bus) - return cfg; - - return NULL; -} - static const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) } } -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - return -EINVAL; -} - -static int __init pci_parse_mcfg(struct acpi_table_header *header) -{ - struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *cfg_table, *cfg; - unsigned long i; - int entries; - - if (!header) - return -EINVAL; - - mcfg = (struct acpi_table_mcfg *)header; - - /* how many config structures do we have */ - free_all_mmcfg(); - entries = 0; - i = header->length - sizeof(struct acpi_table_mcfg); - while (i >= sizeof(struct acpi_mcfg_allocation)) { - entries++; - i -= sizeof(struct acpi_mcfg_allocation); - } - if (entries == 0) { - pr_err(PREFIX "MMCONFIG has no entries\n"); - return -ENODEV; - } - - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; - for (i = 0; i < entries; i++) { - cfg = &cfg_table[i]; - if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); - return -ENODEV; - } - - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { - pr_warn(PREFIX "no memory for MCFG entries\n"); - free_all_mmcfg(); - return -ENOMEM; - } - } - - return 0; -} - #ifdef CONFIG_ACPI_APEI extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, void *data), void *data); @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) static int __initdata known_bridge; +static void __init pci_mmcfg_list_setup(void) +{ + free_all_mmcfg(); + if (pci_mmconfig_parse_table()) + free_all_mmcfg(); +} + void __init pci_mmcfg_early_init(void) { if (pci_probe & PCI_PROBE_MMCONF) { if (pci_mmcfg_check_hostbridge()) known_bridge = 1; else - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(1); set_apei_filter(); @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) /* MMCONFIG hasn't been enabled yet, try again */ if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + pci_mmcfg_list_setup(); __pci_mmcfg_init(0); } } @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources); -/* Add MMCFG information for host bridges */ -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, - phys_addr_t addr) +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) { - int rc; - struct resource *tmp = NULL; - struct pci_mmcfg_region *cfg; + struct resource *tmp; - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) - return -ENODEV; - - if (start > end) - return -EINVAL; - - mutex_lock(&pci_mmcfg_lock); - cfg = pci_mmconfig_lookup(seg, start); - if (cfg) { - if (cfg->end_bus < end) - dev_info(dev, FW_INFO - "MMCONFIG for " - "domain %04x [bus %02x-%02x] " - "only partially covers this bridge\n", - cfg->segment, cfg->start_bus, cfg->end_bus); - mutex_unlock(&pci_mmcfg_lock); - return -EEXIST; - } - - if (!addr) { - mutex_unlock(&pci_mmcfg_lock); - return -EINVAL; - } - - rc = -EBUSY; - cfg = pci_mmconfig_alloc(seg, start, end, addr); - if (cfg == NULL) { - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); - rc = -ENOMEM; - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", &cfg->res); - } else { - /* Insert resource if it's not in boot stage */ - if (pci_mmcfg_running_state) - tmp = insert_resource_conflict(&iomem_resource, - &cfg->res); - + return -EBUSY; + } + /* Insert resource if it's not in boot stage */ + if (pci_mmcfg_running_state) { + tmp = insert_resource_conflict(&iomem_resource, + &cfg->res); if (tmp) { - dev_warn(dev, - "MMCONFIG %pR conflicts with " - "%s %pR\n", - &cfg->res, tmp->name, tmp); - } else if (pci_mmcfg_arch_map(cfg)) { - dev_warn(dev, "fail to map MMCONFIG %pR.\n", - &cfg->res); - } else { - list_add_sorted(cfg); - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)addr); - cfg = NULL; - rc = 0; + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &cfg->res, tmp->name, tmp); + return -EBUSY; } } - - if (cfg) { - if (cfg->res.parent) - release_resource(&cfg->res); - kfree(cfg); + if (pci_mmcfg_arch_map(cfg)) { + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); + return -EBUSY; } - - mutex_unlock(&pci_mmcfg_lock); - - return rc; + return 0; } -/* Delete MMCFG information for host bridges */ -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) { - struct pci_mmcfg_region *cfg; - - mutex_lock(&pci_mmcfg_lock); - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) - if (cfg->segment == seg && cfg->start_bus == start && - cfg->end_bus == end) { - list_del_rcu(&cfg->list); - synchronize_rcu(); - pci_mmcfg_arch_unmap(cfg); - if (cfg->res.parent) - release_resource(&cfg->res); - mutex_unlock(&pci_mmcfg_lock); - kfree(cfg); - return 0; - } - mutex_unlock(&pci_mmcfg_lock); + pci_mmcfg_arch_unmap(cfg); + if (cfg->res.parent) + release_resource(&cfg->res); + cfg->res.parent = NULL; +} - return -ENOENT; +int pci_mmconfig_enabled(void) +{ + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..38a37f8 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -12,6 +12,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index bea5249..29253ec 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/pci-acpi.h> #include <asm/e820.h> #include <asm/pci_x86.h> diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 2e565e6..c181eeb 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -14,6 +14,7 @@ */ #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <asm/pci_x86.h> static u8 limit __read_mostly; diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 675eaf3..3f65311 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 0000000..ea84365 --- /dev/null +++ b/drivers/acpi/pci_mcfg.c @@ -0,0 +1,312 @@ +/* + * pci_mcfg.c + * + * Common code to maintain the MCFG areas and mappings + * + * This has been extracted from arch/x86/pci/mmconfig-shared.c + * and moved here so that other architectures can use this code. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/rculist.h> + +#define PREFIX "ACPI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); +LIST_HEAD(pci_mmcfg_list); + +static void list_add_sorted(struct pci_mmcfg_region *new) +{ + struct pci_mmcfg_region *cfg; + + /* keep list sorted by segment and starting bus number */ + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { + if (cfg->segment > new->segment || + (cfg->segment == new->segment && + cfg->start_bus >= new->start_bus)) { + list_add_tail_rcu(&new->list, &cfg->list); + return; + } + } + list_add_tail_rcu(&new->list, &pci_mmcfg_list); +} + +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + struct resource *res; + + if (addr == 0) + return NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + new->address = addr; + new->segment = segment; + new->start_bus = start; + new->end_bus = end; + + res = &new->res; + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); + res->name = new->name; + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr) +{ + struct pci_mmcfg_region *new; + + new = pci_mmconfig_alloc(segment, start, end, addr); + if (new) { + mutex_lock(&pci_mmcfg_lock); + list_add_sorted(new); + mutex_unlock(&pci_mmcfg_lock); + + pr_info(PREFIX + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " + "(base %#lx)\n", + segment, start, end, &new->res, (unsigned long)addr); + } + + return new; +} + +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == segment && + cfg->start_bus <= bus && bus <= cfg->end_bus) + return cfg; + + return NULL; +} + +/* + * Map a pci_mmcfg_region, can be overrriden by arch + */ +int __weak pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg) +{ + struct resource *tmp; + void __iomem *vaddr; + + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); + if (tmp) { + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", + &mcfg->res, tmp->name, tmp); + return -EBUSY; + } + + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); + if (!vaddr) { + release_resource(&mcfg->res); + return -ENOMEM; + } + + mcfg->virt = vaddr; + return 0; +} + +/* + * Unmap a pci_mmcfg_region, can be overrriden by arch + */ +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) +{ + if (mcfg->virt) { + iounmap(mcfg->virt); + mcfg->virt = NULL; + } + if (mcfg->res.parent) { + release_resource(&mcfg->res); + mcfg->res.parent = NULL; + } +} + +/* + * check if the mmconfig is enabled and configured + */ +int __weak pci_mmconfig_enabled(void) +{ + return 1; +} + +/* Add MMCFG information for host bridges */ +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr) +{ + struct pci_mmcfg_region *cfg; + int rc; + + if (!pci_mmconfig_enabled()) + return -ENODEV; + if (start > end) + return -EINVAL; + + mutex_lock(&pci_mmcfg_lock); + cfg = pci_mmconfig_lookup(seg, start); + if (cfg) { + if (cfg->end_bus < end) + dev_info(dev, FW_INFO + "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg->segment, cfg->start_bus, cfg->end_bus); + rc = -EEXIST; + goto err; + } + + if (!addr) { + rc = -EINVAL; + goto err; + } + + cfg = pci_mmconfig_alloc(seg, start, end, addr); + if (cfg == NULL) { + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); + rc = -ENOMEM; + goto err; + } + rc = pci_mmconfig_map_resource(dev, cfg); + if (!rc) { + list_add_sorted(cfg); + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)addr); + return 0; + } else { + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); + } + +err: + mutex_unlock(&pci_mmcfg_lock); + return rc; +} + +/* Delete MMCFG information for host bridges */ +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +{ + struct pci_mmcfg_region *cfg; + + mutex_lock(&pci_mmcfg_lock); + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + if (cfg->segment == seg && cfg->start_bus == start && + cfg->end_bus == end) { + list_del_rcu(&cfg->list); + synchronize_rcu(); + pci_mmconfig_unmap_resource(cfg); + mutex_unlock(&pci_mmcfg_lock); + kfree(cfg); + return 0; + } + mutex_unlock(&pci_mmcfg_lock); + + return -ENOENT; +} + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (!config_enabled(CONFIG_X86)) + return 0; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +static int __init pci_parse_mcfg(struct acpi_table_header *header) +{ + struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *cfg_table, *cfg; + unsigned long i; + int entries; + + if (!header) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *)header; + + /* how many config structures do we have */ + entries = 0; + i = header->length - sizeof(struct acpi_table_mcfg); + while (i >= sizeof(struct acpi_mcfg_allocation)) { + entries++; + i -= sizeof(struct acpi_mcfg_allocation); + } + if (entries == 0) { + pr_err(PREFIX "MMCONFIG has no entries\n"); + return -ENODEV; + } + + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; + for (i = 0; i < entries; i++) { + cfg = &cfg_table[i]; + if (acpi_mcfg_check_entry(mcfg, cfg)) + return -ENODEV; + + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + cfg->end_bus_number, cfg->address) == NULL) { + pr_warn(PREFIX "no memory for MCFG entries\n"); + return -ENOMEM; + } + } + + return 0; +} + +int __init pci_mmconfig_parse_table(void) +{ + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); +} + +void __weak __init pci_mmcfg_late_init(void) +{ + int err, n = 0; + struct pci_mmcfg_region *cfg; + + err = pci_mmconfig_parse_table(); + if (err) { + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); + return; + } + + list_for_each_entry(cfg, &pci_mmcfg_list, list) { + pci_mmconfig_map_resource(NULL, cfg); + n++; + } + + pr_info(PREFIX " MCFG table loaded %d entries\n", n); +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7494dbe..97aa9d3 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -27,9 +27,6 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include "../pci/pci.h" -#ifdef CONFIG_PCI_MMCONFIG -#include <asm/pci_x86.h> -#endif static bool __read_mostly pci_seg_supported = true; @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) if (!xen_initial_domain()) return 0; - if ((pci_probe & PCI_PROBE_MMCONF) == 0) + if (!pci_mmconfig_enabled()) return 0; if (list_empty(&pci_mmcfg_list)) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab057..e9450ef 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; #define RESET_DELAY_DSM 0x08 #define FUNCTION_DELAY_DSM 0x09 +/* common API to maintain list of MCFG regions */ +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) + +struct pci_mmcfg_region { + struct list_head list; + struct resource res; + u64 address; + char __iomem *virt; + u16 segment; + u8 start_bus; + u8 end_bus; + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; +}; + +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, + phys_addr_t addr); +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +extern int pci_mmconfig_map_resource(struct device *dev, + struct pci_mmcfg_region *mcfg); +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); +extern int pci_mmconfig_enabled(void); +extern int __init pci_mmconfig_parse_table(void); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C @ 2016-01-14 8:48 ` Dennis Chen -1 siblings, 0 replies; 30+ messages in thread From: Dennis Chen @ 2016-01-14 8:48 UTC (permalink / raw) To: Jayachandran C Cc: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h > > As a part of this we introduce three functions that can be > implemented by the arch code: pci_mmconfig_map_resource() to map a > mcfg entry, pci_mmconfig_unmap_resource to do the corresponding > unmap and pci_mmconfig_enabled to see if the arch setup of > mcfg entries was successful. We also provide weak implementations > of these, which will be used from ARM64. On x86, we retain the > old logic by providing platform specific implementation. > > This patch is purely rearranging code, it should not have any > impact on the logic of MCFG parsing or list handling. > > Signed-off-by: Jayachandran C <jchandra@broadcom.com> > --- > arch/x86/include/asm/pci_x86.h | 24 +--- > arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ > arch/x86/pci/mmconfig_32.c | 1 + > arch/x86/pci/mmconfig_64.c | 1 + > arch/x86/pci/numachip.c | 1 + > drivers/acpi/Makefile | 1 + > drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ > drivers/xen/pci.c | 5 +- > include/linux/pci-acpi.h | 33 +++++ > 9 files changed, 386 insertions(+), 261 deletions(-) > create mode 100644 drivers/acpi/pci_mcfg.c > > diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h > index fa1195d..83d74cb 100644 > --- a/arch/x86/include/asm/pci_x86.h > +++ b/arch/x86/include/asm/pci_x86.h > @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); > extern void pcibios_fixup_irqs(void); > > /* pci-mmconfig.c */ > - > -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > - > -struct pci_mmcfg_region { > - struct list_head list; > - struct resource res; > - u64 address; > - char __iomem *virt; > - u16 segment; > - u8 start_bus; > - u8 end_bus; > - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > -}; > - > +struct pci_mmcfg_region; > extern int __init pci_mmcfg_arch_init(void); > extern void __init pci_mmcfg_arch_free(void); > extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); > extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); > -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr); > -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > - > -extern struct list_head pci_mmcfg_list; > - > -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > > /* > * AMD Fam10h CPUs are buggy, and cannot access MMIO config space > diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c > index dd30b7e..626710b 100644 > --- a/arch/x86/pci/mmconfig-shared.c > +++ b/arch/x86/pci/mmconfig-shared.c > @@ -12,13 +12,12 @@ > > #include <linux/pci.h> > #include <linux/init.h> > -#include <linux/sfi_acpi.h> > #include <linux/bitmap.h> > -#include <linux/dmi.h> > #include <linux/slab.h> > #include <linux/mutex.h> > #include <linux/rculist.h> > #include <asm/e820.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > #include <asm/acpi.h> > > @@ -27,9 +26,6 @@ > /* Indicate if the mmcfg resources have been placed into the resource table. */ > static bool pci_mmcfg_running_state; > static bool pci_mmcfg_arch_init_failed; > -static DEFINE_MUTEX(pci_mmcfg_lock); > - > -LIST_HEAD(pci_mmcfg_list); > > static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) > { > @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) > pci_mmconfig_remove(cfg); > } > > -static void list_add_sorted(struct pci_mmcfg_region *new) > -{ > - struct pci_mmcfg_region *cfg; > - > - /* keep list sorted by segment and starting bus number */ > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > - if (cfg->segment > new->segment || > - (cfg->segment == new->segment && > - cfg->start_bus >= new->start_bus)) { > - list_add_tail_rcu(&new->list, &cfg->list); > - return; > - } > - } > - list_add_tail_rcu(&new->list, &pci_mmcfg_list); > -} > - > -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - struct resource *res; > - > - if (addr == 0) > - return NULL; > - > - new = kzalloc(sizeof(*new), GFP_KERNEL); > - if (!new) > - return NULL; > - > - new->address = addr; > - new->segment = segment; > - new->start_bus = start; > - new->end_bus = end; > - > - res = &new->res; > - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > - res->name = new->name; > - > - return new; > -} Hmm, long long story about this refactor of pci code under x86 folder. I think everyone who as long as has the brain in his head knows these are the common code, not arch specific. But those kind of patches repeats again and again and again in the mail list while our maintainers are selective blind to this. > - > -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - > - new = pci_mmconfig_alloc(segment, start, end, addr); > - if (new) { > - mutex_lock(&pci_mmcfg_lock); > - list_add_sorted(new); > - mutex_unlock(&pci_mmcfg_lock); > - > - pr_info(PREFIX > - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > - "(base %#lx)\n", > - segment, start, end, &new->res, (unsigned long)addr); > - } > - > - return new; > -} > - > -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > -{ > - struct pci_mmcfg_region *cfg; > - > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == segment && > - cfg->start_bus <= bus && bus <= cfg->end_bus) > - return cfg; > - > - return NULL; > -} > - > static const char *__init pci_mmcfg_e7520(void) > { > u32 win; > @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) > } > } > > -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > - struct acpi_mcfg_allocation *cfg) > -{ > - int year; > - > - if (cfg->address < 0xFFFFFFFF) > - return 0; > - > - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > - return 0; > - > - if (mcfg->header.revision >= 1) { > - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > - year >= 2010) > - return 0; > - } > - > - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > - "is above 4GB, ignored\n", cfg->pci_segment, > - cfg->start_bus_number, cfg->end_bus_number, cfg->address); > - return -EINVAL; > -} > - > -static int __init pci_parse_mcfg(struct acpi_table_header *header) > -{ > - struct acpi_table_mcfg *mcfg; > - struct acpi_mcfg_allocation *cfg_table, *cfg; > - unsigned long i; > - int entries; > - > - if (!header) > - return -EINVAL; > - > - mcfg = (struct acpi_table_mcfg *)header; > - > - /* how many config structures do we have */ > - free_all_mmcfg(); > - entries = 0; > - i = header->length - sizeof(struct acpi_table_mcfg); > - while (i >= sizeof(struct acpi_mcfg_allocation)) { > - entries++; > - i -= sizeof(struct acpi_mcfg_allocation); > - } > - if (entries == 0) { > - pr_err(PREFIX "MMCONFIG has no entries\n"); > - return -ENODEV; > - } > - > - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > - for (i = 0; i < entries; i++) { > - cfg = &cfg_table[i]; > - if (acpi_mcfg_check_entry(mcfg, cfg)) { > - free_all_mmcfg(); > - return -ENODEV; > - } > - > - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > - cfg->end_bus_number, cfg->address) == NULL) { > - pr_warn(PREFIX "no memory for MCFG entries\n"); > - free_all_mmcfg(); > - return -ENOMEM; > - } > - } > - > - return 0; > -} > - > #ifdef CONFIG_ACPI_APEI > extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, > void *data), void *data); > @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) > > static int __initdata known_bridge; > > +static void __init pci_mmcfg_list_setup(void) > +{ > + free_all_mmcfg(); > + if (pci_mmconfig_parse_table()) > + free_all_mmcfg(); > +} > + > void __init pci_mmcfg_early_init(void) > { > if (pci_probe & PCI_PROBE_MMCONF) { > if (pci_mmcfg_check_hostbridge()) > known_bridge = 1; > else > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(1); > > set_apei_filter(); > @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) > > /* MMCONFIG hasn't been enabled yet, try again */ > if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(0); > } > } > @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) > */ > late_initcall(pci_mmcfg_late_insert_resources); > > -/* Add MMCFG information for host bridges */ > -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr) > +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) > { > - int rc; > - struct resource *tmp = NULL; > - struct pci_mmcfg_region *cfg; > + struct resource *tmp; > > - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) > - return -ENODEV; > - > - if (start > end) > - return -EINVAL; > - > - mutex_lock(&pci_mmcfg_lock); > - cfg = pci_mmconfig_lookup(seg, start); > - if (cfg) { > - if (cfg->end_bus < end) > - dev_info(dev, FW_INFO > - "MMCONFIG for " > - "domain %04x [bus %02x-%02x] " > - "only partially covers this bridge\n", > - cfg->segment, cfg->start_bus, cfg->end_bus); > - mutex_unlock(&pci_mmcfg_lock); > - return -EEXIST; > - } > - > - if (!addr) { > - mutex_unlock(&pci_mmcfg_lock); > - return -EINVAL; > - } > - > - rc = -EBUSY; > - cfg = pci_mmconfig_alloc(seg, start, end, addr); > - if (cfg == NULL) { > - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > - rc = -ENOMEM; > - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", > &cfg->res); > - } else { > - /* Insert resource if it's not in boot stage */ > - if (pci_mmcfg_running_state) > - tmp = insert_resource_conflict(&iomem_resource, > - &cfg->res); > - > + return -EBUSY; > + } > + /* Insert resource if it's not in boot stage */ > + if (pci_mmcfg_running_state) { > + tmp = insert_resource_conflict(&iomem_resource, > + &cfg->res); > if (tmp) { > - dev_warn(dev, > - "MMCONFIG %pR conflicts with " > - "%s %pR\n", > - &cfg->res, tmp->name, tmp); > - } else if (pci_mmcfg_arch_map(cfg)) { > - dev_warn(dev, "fail to map MMCONFIG %pR.\n", > - &cfg->res); > - } else { > - list_add_sorted(cfg); > - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > - &cfg->res, (unsigned long)addr); > - cfg = NULL; > - rc = 0; > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &cfg->res, tmp->name, tmp); > + return -EBUSY; > } > } > - > - if (cfg) { > - if (cfg->res.parent) > - release_resource(&cfg->res); > - kfree(cfg); > + if (pci_mmcfg_arch_map(cfg)) { > + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); > + return -EBUSY; > } > - > - mutex_unlock(&pci_mmcfg_lock); > - > - return rc; > + return 0; > } > > -/* Delete MMCFG information for host bridges */ > -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) > { > - struct pci_mmcfg_region *cfg; > - > - mutex_lock(&pci_mmcfg_lock); > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == seg && cfg->start_bus == start && > - cfg->end_bus == end) { > - list_del_rcu(&cfg->list); > - synchronize_rcu(); > - pci_mmcfg_arch_unmap(cfg); > - if (cfg->res.parent) > - release_resource(&cfg->res); > - mutex_unlock(&pci_mmcfg_lock); > - kfree(cfg); > - return 0; > - } > - mutex_unlock(&pci_mmcfg_lock); > + pci_mmcfg_arch_unmap(cfg); > + if (cfg->res.parent) > + release_resource(&cfg->res); > + cfg->res.parent = NULL; > +} > > - return -ENOENT; > +int pci_mmconfig_enabled(void) > +{ > + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; > } > diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c > index 43984bc..38a37f8 100644 > --- a/arch/x86/pci/mmconfig_32.c > +++ b/arch/x86/pci/mmconfig_32.c > @@ -12,6 +12,7 @@ > #include <linux/pci.h> > #include <linux/init.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c > index bea5249..29253ec 100644 > --- a/arch/x86/pci/mmconfig_64.c > +++ b/arch/x86/pci/mmconfig_64.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/bitmap.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c > index 2e565e6..c181eeb 100644 > --- a/arch/x86/pci/numachip.c > +++ b/arch/x86/pci/numachip.c > @@ -14,6 +14,7 @@ > */ > > #include <linux/pci.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > > static u8 limit __read_mostly; > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index 675eaf3..3f65311 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > acpi-y += ec.o > acpi-$(CONFIG_ACPI_DOCK) += dock.o > acpi-y += pci_root.o pci_link.o pci_irq.o > +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o > acpi-y += acpi_lpss.o acpi_apd.o > acpi-y += acpi_platform.o > acpi-y += acpi_pnp.o > diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c > new file mode 100644 > index 0000000..ea84365 > --- /dev/null > +++ b/drivers/acpi/pci_mcfg.c > @@ -0,0 +1,312 @@ > +/* > + * pci_mcfg.c > + * > + * Common code to maintain the MCFG areas and mappings > + * > + * This has been extracted from arch/x86/pci/mmconfig-shared.c > + * and moved here so that other architectures can use this code. > + */ > + > +#include <linux/pci.h> > +#include <linux/init.h> > +#include <linux/dmi.h> > +#include <linux/pci-acpi.h> > +#include <linux/sfi_acpi.h> > +#include <linux/slab.h> > +#include <linux/mutex.h> > +#include <linux/rculist.h> > + > +#define PREFIX "ACPI: " > + > +static DEFINE_MUTEX(pci_mmcfg_lock); > +LIST_HEAD(pci_mmcfg_list); > + > +static void list_add_sorted(struct pci_mmcfg_region *new) > +{ > + struct pci_mmcfg_region *cfg; > + > + /* keep list sorted by segment and starting bus number */ > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > + if (cfg->segment > new->segment || > + (cfg->segment == new->segment && > + cfg->start_bus >= new->start_bus)) { > + list_add_tail_rcu(&new->list, &cfg->list); > + return; > + } > + } > + list_add_tail_rcu(&new->list, &pci_mmcfg_list); > +} > + > +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + struct resource *res; > + > + if (addr == 0) > + return NULL; > + > + new = kzalloc(sizeof(*new), GFP_KERNEL); > + if (!new) > + return NULL; > + > + new->address = addr; > + new->segment = segment; > + new->start_bus = start; > + new->end_bus = end; > + > + res = &new->res; > + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > + res->name = new->name; > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + > + new = pci_mmconfig_alloc(segment, start, end, addr); > + if (new) { > + mutex_lock(&pci_mmcfg_lock); > + list_add_sorted(new); > + mutex_unlock(&pci_mmcfg_lock); > + > + pr_info(PREFIX > + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > + "(base %#lx)\n", > + segment, start, end, &new->res, (unsigned long)addr); > + } > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > +{ > + struct pci_mmcfg_region *cfg; > + > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == segment && > + cfg->start_bus <= bus && bus <= cfg->end_bus) > + return cfg; > + > + return NULL; > +} > + > +/* > + * Map a pci_mmcfg_region, can be overrriden by arch > + */ > +int __weak pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg) > +{ > + struct resource *tmp; > + void __iomem *vaddr; > + > + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); > + if (tmp) { > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &mcfg->res, tmp->name, tmp); > + return -EBUSY; > + } > + > + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); > + if (!vaddr) { > + release_resource(&mcfg->res); > + return -ENOMEM; > + } > + > + mcfg->virt = vaddr; > + return 0; > +} > + > +/* > + * Unmap a pci_mmcfg_region, can be overrriden by arch > + */ > +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) > +{ > + if (mcfg->virt) { > + iounmap(mcfg->virt); > + mcfg->virt = NULL; > + } > + if (mcfg->res.parent) { > + release_resource(&mcfg->res); > + mcfg->res.parent = NULL; > + } > +} > + > +/* > + * check if the mmconfig is enabled and configured > + */ > +int __weak pci_mmconfig_enabled(void) > +{ > + return 1; > +} > + > +/* Add MMCFG information for host bridges */ > +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr) > +{ > + struct pci_mmcfg_region *cfg; > + int rc; > + > + if (!pci_mmconfig_enabled()) > + return -ENODEV; > + if (start > end) > + return -EINVAL; > + > + mutex_lock(&pci_mmcfg_lock); > + cfg = pci_mmconfig_lookup(seg, start); > + if (cfg) { > + if (cfg->end_bus < end) > + dev_info(dev, FW_INFO > + "MMCONFIG for " > + "domain %04x [bus %02x-%02x] " > + "only partially covers this bridge\n", > + cfg->segment, cfg->start_bus, cfg->end_bus); > + rc = -EEXIST; > + goto err; > + } > + > + if (!addr) { > + rc = -EINVAL; > + goto err; > + } > + > + cfg = pci_mmconfig_alloc(seg, start, end, addr); > + if (cfg == NULL) { > + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > + rc = -ENOMEM; > + goto err; > + } > + rc = pci_mmconfig_map_resource(dev, cfg); > + if (!rc) { > + list_add_sorted(cfg); > + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > + &cfg->res, (unsigned long)addr); > + return 0; > + } else { > + if (cfg->res.parent) > + release_resource(&cfg->res); > + kfree(cfg); > + } > + > +err: > + mutex_unlock(&pci_mmcfg_lock); > + return rc; > +} > + > +/* Delete MMCFG information for host bridges */ > +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +{ > + struct pci_mmcfg_region *cfg; > + > + mutex_lock(&pci_mmcfg_lock); > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == seg && cfg->start_bus == start && > + cfg->end_bus == end) { > + list_del_rcu(&cfg->list); > + synchronize_rcu(); > + pci_mmconfig_unmap_resource(cfg); > + mutex_unlock(&pci_mmcfg_lock); > + kfree(cfg); > + return 0; > + } > + mutex_unlock(&pci_mmcfg_lock); > + > + return -ENOENT; > +} > + > +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg) > +{ > + int year; > + > + if (!config_enabled(CONFIG_X86)) > + return 0; > + > + if (cfg->address < 0xFFFFFFFF) > + return 0; > + > + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > + return 0; > + > + if (mcfg->header.revision >= 1) { > + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > + year >= 2010) > + return 0; > + } > + > + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > + "is above 4GB, ignored\n", cfg->pci_segment, > + cfg->start_bus_number, cfg->end_bus_number, cfg->address); > + return -EINVAL; > +} > + > +static int __init pci_parse_mcfg(struct acpi_table_header *header) > +{ > + struct acpi_table_mcfg *mcfg; > + struct acpi_mcfg_allocation *cfg_table, *cfg; > + unsigned long i; > + int entries; > + > + if (!header) > + return -EINVAL; > + > + mcfg = (struct acpi_table_mcfg *)header; > + > + /* how many config structures do we have */ > + entries = 0; > + i = header->length - sizeof(struct acpi_table_mcfg); > + while (i >= sizeof(struct acpi_mcfg_allocation)) { > + entries++; > + i -= sizeof(struct acpi_mcfg_allocation); > + } > + if (entries == 0) { > + pr_err(PREFIX "MMCONFIG has no entries\n"); > + return -ENODEV; > + } > + > + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > + for (i = 0; i < entries; i++) { > + cfg = &cfg_table[i]; > + if (acpi_mcfg_check_entry(mcfg, cfg)) > + return -ENODEV; > + > + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > + cfg->end_bus_number, cfg->address) == NULL) { > + pr_warn(PREFIX "no memory for MCFG entries\n"); > + return -ENOMEM; > + } > + } > + > + return 0; > +} > + > +int __init pci_mmconfig_parse_table(void) > +{ > + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > +} > + > +void __weak __init pci_mmcfg_late_init(void) > +{ > + int err, n = 0; > + struct pci_mmcfg_region *cfg; > + > + err = pci_mmconfig_parse_table(); > + if (err) { > + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); > + return; > + } > + > + list_for_each_entry(cfg, &pci_mmcfg_list, list) { > + pci_mmconfig_map_resource(NULL, cfg); > + n++; > + } > + > + pr_info(PREFIX " MCFG table loaded %d entries\n", n); > +} > diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c > index 7494dbe..97aa9d3 100644 > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) > diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h > index 89ab057..e9450ef 100644 > --- a/include/linux/pci-acpi.h > +++ b/include/linux/pci-acpi.h > @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; > #define RESET_DELAY_DSM 0x08 > #define FUNCTION_DELAY_DSM 0x09 > > +/* common API to maintain list of MCFG regions */ > +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > + > +struct pci_mmcfg_region { > + struct list_head list; > + struct resource res; > + u64 address; > + char __iomem *virt; > + u16 segment; > + u8 start_bus; > + u8 end_bus; > + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > +}; > + > +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr); > +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > + > +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr); > +extern int pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg); > +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); > +extern int pci_mmconfig_enabled(void); > +extern int __init pci_mmconfig_parse_table(void); > + > +extern struct list_head pci_mmcfg_list; > + > +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) > + > #else /* CONFIG_ACPI */ > static inline void acpi_pci_add_bus(struct pci_bus *bus) { } > static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } > -- > 1.9.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi @ 2016-01-14 8:48 ` Dennis Chen 0 siblings, 0 replies; 30+ messages in thread From: Dennis Chen @ 2016-01-14 8:48 UTC (permalink / raw) To: linux-arm-kernel On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h > > As a part of this we introduce three functions that can be > implemented by the arch code: pci_mmconfig_map_resource() to map a > mcfg entry, pci_mmconfig_unmap_resource to do the corresponding > unmap and pci_mmconfig_enabled to see if the arch setup of > mcfg entries was successful. We also provide weak implementations > of these, which will be used from ARM64. On x86, we retain the > old logic by providing platform specific implementation. > > This patch is purely rearranging code, it should not have any > impact on the logic of MCFG parsing or list handling. > > Signed-off-by: Jayachandran C <jchandra@broadcom.com> > --- > arch/x86/include/asm/pci_x86.h | 24 +--- > arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ > arch/x86/pci/mmconfig_32.c | 1 + > arch/x86/pci/mmconfig_64.c | 1 + > arch/x86/pci/numachip.c | 1 + > drivers/acpi/Makefile | 1 + > drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ > drivers/xen/pci.c | 5 +- > include/linux/pci-acpi.h | 33 +++++ > 9 files changed, 386 insertions(+), 261 deletions(-) > create mode 100644 drivers/acpi/pci_mcfg.c > > diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h > index fa1195d..83d74cb 100644 > --- a/arch/x86/include/asm/pci_x86.h > +++ b/arch/x86/include/asm/pci_x86.h > @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); > extern void pcibios_fixup_irqs(void); > > /* pci-mmconfig.c */ > - > -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > - > -struct pci_mmcfg_region { > - struct list_head list; > - struct resource res; > - u64 address; > - char __iomem *virt; > - u16 segment; > - u8 start_bus; > - u8 end_bus; > - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > -}; > - > +struct pci_mmcfg_region; > extern int __init pci_mmcfg_arch_init(void); > extern void __init pci_mmcfg_arch_free(void); > extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); > extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); > -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr); > -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > - > -extern struct list_head pci_mmcfg_list; > - > -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > > /* > * AMD Fam10h CPUs are buggy, and cannot access MMIO config space > diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c > index dd30b7e..626710b 100644 > --- a/arch/x86/pci/mmconfig-shared.c > +++ b/arch/x86/pci/mmconfig-shared.c > @@ -12,13 +12,12 @@ > > #include <linux/pci.h> > #include <linux/init.h> > -#include <linux/sfi_acpi.h> > #include <linux/bitmap.h> > -#include <linux/dmi.h> > #include <linux/slab.h> > #include <linux/mutex.h> > #include <linux/rculist.h> > #include <asm/e820.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > #include <asm/acpi.h> > > @@ -27,9 +26,6 @@ > /* Indicate if the mmcfg resources have been placed into the resource table. */ > static bool pci_mmcfg_running_state; > static bool pci_mmcfg_arch_init_failed; > -static DEFINE_MUTEX(pci_mmcfg_lock); > - > -LIST_HEAD(pci_mmcfg_list); > > static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) > { > @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) > pci_mmconfig_remove(cfg); > } > > -static void list_add_sorted(struct pci_mmcfg_region *new) > -{ > - struct pci_mmcfg_region *cfg; > - > - /* keep list sorted by segment and starting bus number */ > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > - if (cfg->segment > new->segment || > - (cfg->segment == new->segment && > - cfg->start_bus >= new->start_bus)) { > - list_add_tail_rcu(&new->list, &cfg->list); > - return; > - } > - } > - list_add_tail_rcu(&new->list, &pci_mmcfg_list); > -} > - > -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - struct resource *res; > - > - if (addr == 0) > - return NULL; > - > - new = kzalloc(sizeof(*new), GFP_KERNEL); > - if (!new) > - return NULL; > - > - new->address = addr; > - new->segment = segment; > - new->start_bus = start; > - new->end_bus = end; > - > - res = &new->res; > - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > - res->name = new->name; > - > - return new; > -} Hmm, long long story about this refactor of pci code under x86 folder. I think everyone who as long as has the brain in his head knows these are the common code, not arch specific. But those kind of patches repeats again and again and again in the mail list while our maintainers are selective blind to this. > - > -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - > - new = pci_mmconfig_alloc(segment, start, end, addr); > - if (new) { > - mutex_lock(&pci_mmcfg_lock); > - list_add_sorted(new); > - mutex_unlock(&pci_mmcfg_lock); > - > - pr_info(PREFIX > - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > - "(base %#lx)\n", > - segment, start, end, &new->res, (unsigned long)addr); > - } > - > - return new; > -} > - > -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > -{ > - struct pci_mmcfg_region *cfg; > - > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == segment && > - cfg->start_bus <= bus && bus <= cfg->end_bus) > - return cfg; > - > - return NULL; > -} > - > static const char *__init pci_mmcfg_e7520(void) > { > u32 win; > @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) > } > } > > -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > - struct acpi_mcfg_allocation *cfg) > -{ > - int year; > - > - if (cfg->address < 0xFFFFFFFF) > - return 0; > - > - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > - return 0; > - > - if (mcfg->header.revision >= 1) { > - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > - year >= 2010) > - return 0; > - } > - > - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > - "is above 4GB, ignored\n", cfg->pci_segment, > - cfg->start_bus_number, cfg->end_bus_number, cfg->address); > - return -EINVAL; > -} > - > -static int __init pci_parse_mcfg(struct acpi_table_header *header) > -{ > - struct acpi_table_mcfg *mcfg; > - struct acpi_mcfg_allocation *cfg_table, *cfg; > - unsigned long i; > - int entries; > - > - if (!header) > - return -EINVAL; > - > - mcfg = (struct acpi_table_mcfg *)header; > - > - /* how many config structures do we have */ > - free_all_mmcfg(); > - entries = 0; > - i = header->length - sizeof(struct acpi_table_mcfg); > - while (i >= sizeof(struct acpi_mcfg_allocation)) { > - entries++; > - i -= sizeof(struct acpi_mcfg_allocation); > - } > - if (entries == 0) { > - pr_err(PREFIX "MMCONFIG has no entries\n"); > - return -ENODEV; > - } > - > - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > - for (i = 0; i < entries; i++) { > - cfg = &cfg_table[i]; > - if (acpi_mcfg_check_entry(mcfg, cfg)) { > - free_all_mmcfg(); > - return -ENODEV; > - } > - > - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > - cfg->end_bus_number, cfg->address) == NULL) { > - pr_warn(PREFIX "no memory for MCFG entries\n"); > - free_all_mmcfg(); > - return -ENOMEM; > - } > - } > - > - return 0; > -} > - > #ifdef CONFIG_ACPI_APEI > extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, > void *data), void *data); > @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) > > static int __initdata known_bridge; > > +static void __init pci_mmcfg_list_setup(void) > +{ > + free_all_mmcfg(); > + if (pci_mmconfig_parse_table()) > + free_all_mmcfg(); > +} > + > void __init pci_mmcfg_early_init(void) > { > if (pci_probe & PCI_PROBE_MMCONF) { > if (pci_mmcfg_check_hostbridge()) > known_bridge = 1; > else > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(1); > > set_apei_filter(); > @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) > > /* MMCONFIG hasn't been enabled yet, try again */ > if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(0); > } > } > @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) > */ > late_initcall(pci_mmcfg_late_insert_resources); > > -/* Add MMCFG information for host bridges */ > -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr) > +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) > { > - int rc; > - struct resource *tmp = NULL; > - struct pci_mmcfg_region *cfg; > + struct resource *tmp; > > - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) > - return -ENODEV; > - > - if (start > end) > - return -EINVAL; > - > - mutex_lock(&pci_mmcfg_lock); > - cfg = pci_mmconfig_lookup(seg, start); > - if (cfg) { > - if (cfg->end_bus < end) > - dev_info(dev, FW_INFO > - "MMCONFIG for " > - "domain %04x [bus %02x-%02x] " > - "only partially covers this bridge\n", > - cfg->segment, cfg->start_bus, cfg->end_bus); > - mutex_unlock(&pci_mmcfg_lock); > - return -EEXIST; > - } > - > - if (!addr) { > - mutex_unlock(&pci_mmcfg_lock); > - return -EINVAL; > - } > - > - rc = -EBUSY; > - cfg = pci_mmconfig_alloc(seg, start, end, addr); > - if (cfg == NULL) { > - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > - rc = -ENOMEM; > - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", > &cfg->res); > - } else { > - /* Insert resource if it's not in boot stage */ > - if (pci_mmcfg_running_state) > - tmp = insert_resource_conflict(&iomem_resource, > - &cfg->res); > - > + return -EBUSY; > + } > + /* Insert resource if it's not in boot stage */ > + if (pci_mmcfg_running_state) { > + tmp = insert_resource_conflict(&iomem_resource, > + &cfg->res); > if (tmp) { > - dev_warn(dev, > - "MMCONFIG %pR conflicts with " > - "%s %pR\n", > - &cfg->res, tmp->name, tmp); > - } else if (pci_mmcfg_arch_map(cfg)) { > - dev_warn(dev, "fail to map MMCONFIG %pR.\n", > - &cfg->res); > - } else { > - list_add_sorted(cfg); > - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > - &cfg->res, (unsigned long)addr); > - cfg = NULL; > - rc = 0; > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &cfg->res, tmp->name, tmp); > + return -EBUSY; > } > } > - > - if (cfg) { > - if (cfg->res.parent) > - release_resource(&cfg->res); > - kfree(cfg); > + if (pci_mmcfg_arch_map(cfg)) { > + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); > + return -EBUSY; > } > - > - mutex_unlock(&pci_mmcfg_lock); > - > - return rc; > + return 0; > } > > -/* Delete MMCFG information for host bridges */ > -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) > { > - struct pci_mmcfg_region *cfg; > - > - mutex_lock(&pci_mmcfg_lock); > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == seg && cfg->start_bus == start && > - cfg->end_bus == end) { > - list_del_rcu(&cfg->list); > - synchronize_rcu(); > - pci_mmcfg_arch_unmap(cfg); > - if (cfg->res.parent) > - release_resource(&cfg->res); > - mutex_unlock(&pci_mmcfg_lock); > - kfree(cfg); > - return 0; > - } > - mutex_unlock(&pci_mmcfg_lock); > + pci_mmcfg_arch_unmap(cfg); > + if (cfg->res.parent) > + release_resource(&cfg->res); > + cfg->res.parent = NULL; > +} > > - return -ENOENT; > +int pci_mmconfig_enabled(void) > +{ > + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; > } > diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c > index 43984bc..38a37f8 100644 > --- a/arch/x86/pci/mmconfig_32.c > +++ b/arch/x86/pci/mmconfig_32.c > @@ -12,6 +12,7 @@ > #include <linux/pci.h> > #include <linux/init.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c > index bea5249..29253ec 100644 > --- a/arch/x86/pci/mmconfig_64.c > +++ b/arch/x86/pci/mmconfig_64.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/bitmap.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c > index 2e565e6..c181eeb 100644 > --- a/arch/x86/pci/numachip.c > +++ b/arch/x86/pci/numachip.c > @@ -14,6 +14,7 @@ > */ > > #include <linux/pci.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > > static u8 limit __read_mostly; > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index 675eaf3..3f65311 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > acpi-y += ec.o > acpi-$(CONFIG_ACPI_DOCK) += dock.o > acpi-y += pci_root.o pci_link.o pci_irq.o > +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o > acpi-y += acpi_lpss.o acpi_apd.o > acpi-y += acpi_platform.o > acpi-y += acpi_pnp.o > diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c > new file mode 100644 > index 0000000..ea84365 > --- /dev/null > +++ b/drivers/acpi/pci_mcfg.c > @@ -0,0 +1,312 @@ > +/* > + * pci_mcfg.c > + * > + * Common code to maintain the MCFG areas and mappings > + * > + * This has been extracted from arch/x86/pci/mmconfig-shared.c > + * and moved here so that other architectures can use this code. > + */ > + > +#include <linux/pci.h> > +#include <linux/init.h> > +#include <linux/dmi.h> > +#include <linux/pci-acpi.h> > +#include <linux/sfi_acpi.h> > +#include <linux/slab.h> > +#include <linux/mutex.h> > +#include <linux/rculist.h> > + > +#define PREFIX "ACPI: " > + > +static DEFINE_MUTEX(pci_mmcfg_lock); > +LIST_HEAD(pci_mmcfg_list); > + > +static void list_add_sorted(struct pci_mmcfg_region *new) > +{ > + struct pci_mmcfg_region *cfg; > + > + /* keep list sorted by segment and starting bus number */ > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > + if (cfg->segment > new->segment || > + (cfg->segment == new->segment && > + cfg->start_bus >= new->start_bus)) { > + list_add_tail_rcu(&new->list, &cfg->list); > + return; > + } > + } > + list_add_tail_rcu(&new->list, &pci_mmcfg_list); > +} > + > +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + struct resource *res; > + > + if (addr == 0) > + return NULL; > + > + new = kzalloc(sizeof(*new), GFP_KERNEL); > + if (!new) > + return NULL; > + > + new->address = addr; > + new->segment = segment; > + new->start_bus = start; > + new->end_bus = end; > + > + res = &new->res; > + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > + res->name = new->name; > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + > + new = pci_mmconfig_alloc(segment, start, end, addr); > + if (new) { > + mutex_lock(&pci_mmcfg_lock); > + list_add_sorted(new); > + mutex_unlock(&pci_mmcfg_lock); > + > + pr_info(PREFIX > + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > + "(base %#lx)\n", > + segment, start, end, &new->res, (unsigned long)addr); > + } > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > +{ > + struct pci_mmcfg_region *cfg; > + > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == segment && > + cfg->start_bus <= bus && bus <= cfg->end_bus) > + return cfg; > + > + return NULL; > +} > + > +/* > + * Map a pci_mmcfg_region, can be overrriden by arch > + */ > +int __weak pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg) > +{ > + struct resource *tmp; > + void __iomem *vaddr; > + > + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); > + if (tmp) { > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &mcfg->res, tmp->name, tmp); > + return -EBUSY; > + } > + > + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); > + if (!vaddr) { > + release_resource(&mcfg->res); > + return -ENOMEM; > + } > + > + mcfg->virt = vaddr; > + return 0; > +} > + > +/* > + * Unmap a pci_mmcfg_region, can be overrriden by arch > + */ > +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) > +{ > + if (mcfg->virt) { > + iounmap(mcfg->virt); > + mcfg->virt = NULL; > + } > + if (mcfg->res.parent) { > + release_resource(&mcfg->res); > + mcfg->res.parent = NULL; > + } > +} > + > +/* > + * check if the mmconfig is enabled and configured > + */ > +int __weak pci_mmconfig_enabled(void) > +{ > + return 1; > +} > + > +/* Add MMCFG information for host bridges */ > +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr) > +{ > + struct pci_mmcfg_region *cfg; > + int rc; > + > + if (!pci_mmconfig_enabled()) > + return -ENODEV; > + if (start > end) > + return -EINVAL; > + > + mutex_lock(&pci_mmcfg_lock); > + cfg = pci_mmconfig_lookup(seg, start); > + if (cfg) { > + if (cfg->end_bus < end) > + dev_info(dev, FW_INFO > + "MMCONFIG for " > + "domain %04x [bus %02x-%02x] " > + "only partially covers this bridge\n", > + cfg->segment, cfg->start_bus, cfg->end_bus); > + rc = -EEXIST; > + goto err; > + } > + > + if (!addr) { > + rc = -EINVAL; > + goto err; > + } > + > + cfg = pci_mmconfig_alloc(seg, start, end, addr); > + if (cfg == NULL) { > + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > + rc = -ENOMEM; > + goto err; > + } > + rc = pci_mmconfig_map_resource(dev, cfg); > + if (!rc) { > + list_add_sorted(cfg); > + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > + &cfg->res, (unsigned long)addr); > + return 0; > + } else { > + if (cfg->res.parent) > + release_resource(&cfg->res); > + kfree(cfg); > + } > + > +err: > + mutex_unlock(&pci_mmcfg_lock); > + return rc; > +} > + > +/* Delete MMCFG information for host bridges */ > +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +{ > + struct pci_mmcfg_region *cfg; > + > + mutex_lock(&pci_mmcfg_lock); > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == seg && cfg->start_bus == start && > + cfg->end_bus == end) { > + list_del_rcu(&cfg->list); > + synchronize_rcu(); > + pci_mmconfig_unmap_resource(cfg); > + mutex_unlock(&pci_mmcfg_lock); > + kfree(cfg); > + return 0; > + } > + mutex_unlock(&pci_mmcfg_lock); > + > + return -ENOENT; > +} > + > +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg) > +{ > + int year; > + > + if (!config_enabled(CONFIG_X86)) > + return 0; > + > + if (cfg->address < 0xFFFFFFFF) > + return 0; > + > + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > + return 0; > + > + if (mcfg->header.revision >= 1) { > + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > + year >= 2010) > + return 0; > + } > + > + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > + "is above 4GB, ignored\n", cfg->pci_segment, > + cfg->start_bus_number, cfg->end_bus_number, cfg->address); > + return -EINVAL; > +} > + > +static int __init pci_parse_mcfg(struct acpi_table_header *header) > +{ > + struct acpi_table_mcfg *mcfg; > + struct acpi_mcfg_allocation *cfg_table, *cfg; > + unsigned long i; > + int entries; > + > + if (!header) > + return -EINVAL; > + > + mcfg = (struct acpi_table_mcfg *)header; > + > + /* how many config structures do we have */ > + entries = 0; > + i = header->length - sizeof(struct acpi_table_mcfg); > + while (i >= sizeof(struct acpi_mcfg_allocation)) { > + entries++; > + i -= sizeof(struct acpi_mcfg_allocation); > + } > + if (entries == 0) { > + pr_err(PREFIX "MMCONFIG has no entries\n"); > + return -ENODEV; > + } > + > + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > + for (i = 0; i < entries; i++) { > + cfg = &cfg_table[i]; > + if (acpi_mcfg_check_entry(mcfg, cfg)) > + return -ENODEV; > + > + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > + cfg->end_bus_number, cfg->address) == NULL) { > + pr_warn(PREFIX "no memory for MCFG entries\n"); > + return -ENOMEM; > + } > + } > + > + return 0; > +} > + > +int __init pci_mmconfig_parse_table(void) > +{ > + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > +} > + > +void __weak __init pci_mmcfg_late_init(void) > +{ > + int err, n = 0; > + struct pci_mmcfg_region *cfg; > + > + err = pci_mmconfig_parse_table(); > + if (err) { > + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); > + return; > + } > + > + list_for_each_entry(cfg, &pci_mmcfg_list, list) { > + pci_mmconfig_map_resource(NULL, cfg); > + n++; > + } > + > + pr_info(PREFIX " MCFG table loaded %d entries\n", n); > +} > diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c > index 7494dbe..97aa9d3 100644 > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) > diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h > index 89ab057..e9450ef 100644 > --- a/include/linux/pci-acpi.h > +++ b/include/linux/pci-acpi.h > @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; > #define RESET_DELAY_DSM 0x08 > #define FUNCTION_DELAY_DSM 0x09 > > +/* common API to maintain list of MCFG regions */ > +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > + > +struct pci_mmcfg_region { > + struct list_head list; > + struct resource res; > + u64 address; > + char __iomem *virt; > + u16 segment; > + u8 start_bus; > + u8 end_bus; > + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > +}; > + > +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr); > +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > + > +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr); > +extern int pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg); > +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); > +extern int pci_mmconfig_enabled(void); > +extern int __init pci_mmconfig_parse_table(void); > + > +extern struct list_head pci_mmcfg_list; > + > +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) > + > #else /* CONFIG_ACPI */ > static inline void acpi_pci_add_bus(struct pci_bus *bus) { } > static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } > -- > 1.9.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C ` (2 preceding siblings ...) (?) @ 2016-01-14 8:48 ` Dennis Chen -1 siblings, 0 replies; 30+ messages in thread From: Dennis Chen @ 2016-01-14 8:48 UTC (permalink / raw) To: Jayachandran C Cc: Lorenzo Pieralisi, Arnd Bergmann, linux-pci, Rafael J. Wysocki, linux-acpi, Tomasz Nowicki, Bjorn Helgaas, xen-devel, linux-arm-kernel On Thu, Jan 14, 2016 at 3:32 PM, Jayachandran C <jchandra@broadcom.com> wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h > > As a part of this we introduce three functions that can be > implemented by the arch code: pci_mmconfig_map_resource() to map a > mcfg entry, pci_mmconfig_unmap_resource to do the corresponding > unmap and pci_mmconfig_enabled to see if the arch setup of > mcfg entries was successful. We also provide weak implementations > of these, which will be used from ARM64. On x86, we retain the > old logic by providing platform specific implementation. > > This patch is purely rearranging code, it should not have any > impact on the logic of MCFG parsing or list handling. > > Signed-off-by: Jayachandran C <jchandra@broadcom.com> > --- > arch/x86/include/asm/pci_x86.h | 24 +--- > arch/x86/pci/mmconfig-shared.c | 269 +++++------------------------------ > arch/x86/pci/mmconfig_32.c | 1 + > arch/x86/pci/mmconfig_64.c | 1 + > arch/x86/pci/numachip.c | 1 + > drivers/acpi/Makefile | 1 + > drivers/acpi/pci_mcfg.c | 312 +++++++++++++++++++++++++++++++++++++++++ > drivers/xen/pci.c | 5 +- > include/linux/pci-acpi.h | 33 +++++ > 9 files changed, 386 insertions(+), 261 deletions(-) > create mode 100644 drivers/acpi/pci_mcfg.c > > diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h > index fa1195d..83d74cb 100644 > --- a/arch/x86/include/asm/pci_x86.h > +++ b/arch/x86/include/asm/pci_x86.h > @@ -122,33 +122,11 @@ extern int pci_legacy_init(void); > extern void pcibios_fixup_irqs(void); > > /* pci-mmconfig.c */ > - > -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > - > -struct pci_mmcfg_region { > - struct list_head list; > - struct resource res; > - u64 address; > - char __iomem *virt; > - u16 segment; > - u8 start_bus; > - u8 end_bus; > - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > -}; > - > +struct pci_mmcfg_region; > extern int __init pci_mmcfg_arch_init(void); > extern void __init pci_mmcfg_arch_free(void); > extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); > extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); > -extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr); > -extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > -extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > - > -extern struct list_head pci_mmcfg_list; > - > -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > > /* > * AMD Fam10h CPUs are buggy, and cannot access MMIO config space > diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c > index dd30b7e..626710b 100644 > --- a/arch/x86/pci/mmconfig-shared.c > +++ b/arch/x86/pci/mmconfig-shared.c > @@ -12,13 +12,12 @@ > > #include <linux/pci.h> > #include <linux/init.h> > -#include <linux/sfi_acpi.h> > #include <linux/bitmap.h> > -#include <linux/dmi.h> > #include <linux/slab.h> > #include <linux/mutex.h> > #include <linux/rculist.h> > #include <asm/e820.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > #include <asm/acpi.h> > > @@ -27,9 +26,6 @@ > /* Indicate if the mmcfg resources have been placed into the resource table. */ > static bool pci_mmcfg_running_state; > static bool pci_mmcfg_arch_init_failed; > -static DEFINE_MUTEX(pci_mmcfg_lock); > - > -LIST_HEAD(pci_mmcfg_list); > > static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) > { > @@ -48,83 +44,6 @@ static void __init free_all_mmcfg(void) > pci_mmconfig_remove(cfg); > } > > -static void list_add_sorted(struct pci_mmcfg_region *new) > -{ > - struct pci_mmcfg_region *cfg; > - > - /* keep list sorted by segment and starting bus number */ > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > - if (cfg->segment > new->segment || > - (cfg->segment == new->segment && > - cfg->start_bus >= new->start_bus)) { > - list_add_tail_rcu(&new->list, &cfg->list); > - return; > - } > - } > - list_add_tail_rcu(&new->list, &pci_mmcfg_list); > -} > - > -static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - struct resource *res; > - > - if (addr == 0) > - return NULL; > - > - new = kzalloc(sizeof(*new), GFP_KERNEL); > - if (!new) > - return NULL; > - > - new->address = addr; > - new->segment = segment; > - new->start_bus = start; > - new->end_bus = end; > - > - res = &new->res; > - res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > - res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > - snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > - res->name = new->name; > - > - return new; > -} Hmm, long long story about this refactor of pci code under x86 folder. I think everyone who as long as has the brain in his head knows these are the common code, not arch specific. But those kind of patches repeats again and again and again in the mail list while our maintainers are selective blind to this. > - > -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, > - int end, u64 addr) > -{ > - struct pci_mmcfg_region *new; > - > - new = pci_mmconfig_alloc(segment, start, end, addr); > - if (new) { > - mutex_lock(&pci_mmcfg_lock); > - list_add_sorted(new); > - mutex_unlock(&pci_mmcfg_lock); > - > - pr_info(PREFIX > - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > - "(base %#lx)\n", > - segment, start, end, &new->res, (unsigned long)addr); > - } > - > - return new; > -} > - > -struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > -{ > - struct pci_mmcfg_region *cfg; > - > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == segment && > - cfg->start_bus <= bus && bus <= cfg->end_bus) > - return cfg; > - > - return NULL; > -} > - > static const char *__init pci_mmcfg_e7520(void) > { > u32 win; > @@ -543,73 +462,6 @@ static void __init pci_mmcfg_reject_broken(int early) > } > } > > -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > - struct acpi_mcfg_allocation *cfg) > -{ > - int year; > - > - if (cfg->address < 0xFFFFFFFF) > - return 0; > - > - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > - return 0; > - > - if (mcfg->header.revision >= 1) { > - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > - year >= 2010) > - return 0; > - } > - > - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > - "is above 4GB, ignored\n", cfg->pci_segment, > - cfg->start_bus_number, cfg->end_bus_number, cfg->address); > - return -EINVAL; > -} > - > -static int __init pci_parse_mcfg(struct acpi_table_header *header) > -{ > - struct acpi_table_mcfg *mcfg; > - struct acpi_mcfg_allocation *cfg_table, *cfg; > - unsigned long i; > - int entries; > - > - if (!header) > - return -EINVAL; > - > - mcfg = (struct acpi_table_mcfg *)header; > - > - /* how many config structures do we have */ > - free_all_mmcfg(); > - entries = 0; > - i = header->length - sizeof(struct acpi_table_mcfg); > - while (i >= sizeof(struct acpi_mcfg_allocation)) { > - entries++; > - i -= sizeof(struct acpi_mcfg_allocation); > - } > - if (entries == 0) { > - pr_err(PREFIX "MMCONFIG has no entries\n"); > - return -ENODEV; > - } > - > - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > - for (i = 0; i < entries; i++) { > - cfg = &cfg_table[i]; > - if (acpi_mcfg_check_entry(mcfg, cfg)) { > - free_all_mmcfg(); > - return -ENODEV; > - } > - > - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > - cfg->end_bus_number, cfg->address) == NULL) { > - pr_warn(PREFIX "no memory for MCFG entries\n"); > - free_all_mmcfg(); > - return -ENOMEM; > - } > - } > - > - return 0; > -} > - > #ifdef CONFIG_ACPI_APEI > extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, > void *data), void *data); > @@ -662,13 +514,20 @@ static void __init __pci_mmcfg_init(int early) > > static int __initdata known_bridge; > > +static void __init pci_mmcfg_list_setup(void) > +{ > + free_all_mmcfg(); > + if (pci_mmconfig_parse_table()) > + free_all_mmcfg(); > +} > + > void __init pci_mmcfg_early_init(void) > { > if (pci_probe & PCI_PROBE_MMCONF) { > if (pci_mmcfg_check_hostbridge()) > known_bridge = 1; > else > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(1); > > set_apei_filter(); > @@ -686,7 +545,7 @@ void __init pci_mmcfg_late_init(void) > > /* MMCONFIG hasn't been enabled yet, try again */ > if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { > - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > + pci_mmcfg_list_setup(); > __pci_mmcfg_init(0); > } > } > @@ -720,99 +579,41 @@ static int __init pci_mmcfg_late_insert_resources(void) > */ > late_initcall(pci_mmcfg_late_insert_resources); > > -/* Add MMCFG information for host bridges */ > -int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > - phys_addr_t addr) > +int pci_mmconfig_map_resource(struct device *dev, struct pci_mmcfg_region *cfg) > { > - int rc; > - struct resource *tmp = NULL; > - struct pci_mmcfg_region *cfg; > + struct resource *tmp; > > - if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) > - return -ENODEV; > - > - if (start > end) > - return -EINVAL; > - > - mutex_lock(&pci_mmcfg_lock); > - cfg = pci_mmconfig_lookup(seg, start); > - if (cfg) { > - if (cfg->end_bus < end) > - dev_info(dev, FW_INFO > - "MMCONFIG for " > - "domain %04x [bus %02x-%02x] " > - "only partially covers this bridge\n", > - cfg->segment, cfg->start_bus, cfg->end_bus); > - mutex_unlock(&pci_mmcfg_lock); > - return -EEXIST; > - } > - > - if (!addr) { > - mutex_unlock(&pci_mmcfg_lock); > - return -EINVAL; > - } > - > - rc = -EBUSY; > - cfg = pci_mmconfig_alloc(seg, start, end, addr); > - if (cfg == NULL) { > - dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > - rc = -ENOMEM; > - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > + if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { > dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", > &cfg->res); > - } else { > - /* Insert resource if it's not in boot stage */ > - if (pci_mmcfg_running_state) > - tmp = insert_resource_conflict(&iomem_resource, > - &cfg->res); > - > + return -EBUSY; > + } > + /* Insert resource if it's not in boot stage */ > + if (pci_mmcfg_running_state) { > + tmp = insert_resource_conflict(&iomem_resource, > + &cfg->res); > if (tmp) { > - dev_warn(dev, > - "MMCONFIG %pR conflicts with " > - "%s %pR\n", > - &cfg->res, tmp->name, tmp); > - } else if (pci_mmcfg_arch_map(cfg)) { > - dev_warn(dev, "fail to map MMCONFIG %pR.\n", > - &cfg->res); > - } else { > - list_add_sorted(cfg); > - dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > - &cfg->res, (unsigned long)addr); > - cfg = NULL; > - rc = 0; > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &cfg->res, tmp->name, tmp); > + return -EBUSY; > } > } > - > - if (cfg) { > - if (cfg->res.parent) > - release_resource(&cfg->res); > - kfree(cfg); > + if (pci_mmcfg_arch_map(cfg)) { > + dev_warn(dev, "fail to map MMCONFIG %pR.\n", &cfg->res); > + return -EBUSY; > } > - > - mutex_unlock(&pci_mmcfg_lock); > - > - return rc; > + return 0; > } > > -/* Delete MMCFG information for host bridges */ > -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *cfg) > { > - struct pci_mmcfg_region *cfg; > - > - mutex_lock(&pci_mmcfg_lock); > - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > - if (cfg->segment == seg && cfg->start_bus == start && > - cfg->end_bus == end) { > - list_del_rcu(&cfg->list); > - synchronize_rcu(); > - pci_mmcfg_arch_unmap(cfg); > - if (cfg->res.parent) > - release_resource(&cfg->res); > - mutex_unlock(&pci_mmcfg_lock); > - kfree(cfg); > - return 0; > - } > - mutex_unlock(&pci_mmcfg_lock); > + pci_mmcfg_arch_unmap(cfg); > + if (cfg->res.parent) > + release_resource(&cfg->res); > + cfg->res.parent = NULL; > +} > > - return -ENOENT; > +int pci_mmconfig_enabled(void) > +{ > + return (pci_probe & PCI_PROBE_MMCONF) && !pci_mmcfg_arch_init_failed; > } > diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c > index 43984bc..38a37f8 100644 > --- a/arch/x86/pci/mmconfig_32.c > +++ b/arch/x86/pci/mmconfig_32.c > @@ -12,6 +12,7 @@ > #include <linux/pci.h> > #include <linux/init.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c > index bea5249..29253ec 100644 > --- a/arch/x86/pci/mmconfig_64.c > +++ b/arch/x86/pci/mmconfig_64.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/bitmap.h> > #include <linux/rcupdate.h> > +#include <linux/pci-acpi.h> > #include <asm/e820.h> > #include <asm/pci_x86.h> > > diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c > index 2e565e6..c181eeb 100644 > --- a/arch/x86/pci/numachip.c > +++ b/arch/x86/pci/numachip.c > @@ -14,6 +14,7 @@ > */ > > #include <linux/pci.h> > +#include <linux/pci-acpi.h> > #include <asm/pci_x86.h> > > static u8 limit __read_mostly; > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index 675eaf3..3f65311 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > acpi-y += ec.o > acpi-$(CONFIG_ACPI_DOCK) += dock.o > acpi-y += pci_root.o pci_link.o pci_irq.o > +acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o > acpi-y += acpi_lpss.o acpi_apd.o > acpi-y += acpi_platform.o > acpi-y += acpi_pnp.o > diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c > new file mode 100644 > index 0000000..ea84365 > --- /dev/null > +++ b/drivers/acpi/pci_mcfg.c > @@ -0,0 +1,312 @@ > +/* > + * pci_mcfg.c > + * > + * Common code to maintain the MCFG areas and mappings > + * > + * This has been extracted from arch/x86/pci/mmconfig-shared.c > + * and moved here so that other architectures can use this code. > + */ > + > +#include <linux/pci.h> > +#include <linux/init.h> > +#include <linux/dmi.h> > +#include <linux/pci-acpi.h> > +#include <linux/sfi_acpi.h> > +#include <linux/slab.h> > +#include <linux/mutex.h> > +#include <linux/rculist.h> > + > +#define PREFIX "ACPI: " > + > +static DEFINE_MUTEX(pci_mmcfg_lock); > +LIST_HEAD(pci_mmcfg_list); > + > +static void list_add_sorted(struct pci_mmcfg_region *new) > +{ > + struct pci_mmcfg_region *cfg; > + > + /* keep list sorted by segment and starting bus number */ > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { > + if (cfg->segment > new->segment || > + (cfg->segment == new->segment && > + cfg->start_bus >= new->start_bus)) { > + list_add_tail_rcu(&new->list, &cfg->list); > + return; > + } > + } > + list_add_tail_rcu(&new->list, &pci_mmcfg_list); > +} > + > +static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + struct resource *res; > + > + if (addr == 0) > + return NULL; > + > + new = kzalloc(sizeof(*new), GFP_KERNEL); > + if (!new) > + return NULL; > + > + new->address = addr; > + new->segment = segment; > + new->start_bus = start; > + new->end_bus = end; > + > + res = &new->res; > + res->start = addr + PCI_MMCFG_BUS_OFFSET(start); > + res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; > + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; > + snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, > + "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); > + res->name = new->name; > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr) > +{ > + struct pci_mmcfg_region *new; > + > + new = pci_mmconfig_alloc(segment, start, end, addr); > + if (new) { > + mutex_lock(&pci_mmcfg_lock); > + list_add_sorted(new); > + mutex_unlock(&pci_mmcfg_lock); > + > + pr_info(PREFIX > + "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " > + "(base %#lx)\n", > + segment, start, end, &new->res, (unsigned long)addr); > + } > + > + return new; > +} > + > +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) > +{ > + struct pci_mmcfg_region *cfg; > + > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == segment && > + cfg->start_bus <= bus && bus <= cfg->end_bus) > + return cfg; > + > + return NULL; > +} > + > +/* > + * Map a pci_mmcfg_region, can be overrriden by arch > + */ > +int __weak pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg) > +{ > + struct resource *tmp; > + void __iomem *vaddr; > + > + tmp = insert_resource_conflict(&iomem_resource, &mcfg->res); > + if (tmp) { > + dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n", > + &mcfg->res, tmp->name, tmp); > + return -EBUSY; > + } > + > + vaddr = ioremap(mcfg->res.start, resource_size(&mcfg->res)); > + if (!vaddr) { > + release_resource(&mcfg->res); > + return -ENOMEM; > + } > + > + mcfg->virt = vaddr; > + return 0; > +} > + > +/* > + * Unmap a pci_mmcfg_region, can be overrriden by arch > + */ > +void __weak pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg) > +{ > + if (mcfg->virt) { > + iounmap(mcfg->virt); > + mcfg->virt = NULL; > + } > + if (mcfg->res.parent) { > + release_resource(&mcfg->res); > + mcfg->res.parent = NULL; > + } > +} > + > +/* > + * check if the mmconfig is enabled and configured > + */ > +int __weak pci_mmconfig_enabled(void) > +{ > + return 1; > +} > + > +/* Add MMCFG information for host bridges */ > +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr) > +{ > + struct pci_mmcfg_region *cfg; > + int rc; > + > + if (!pci_mmconfig_enabled()) > + return -ENODEV; > + if (start > end) > + return -EINVAL; > + > + mutex_lock(&pci_mmcfg_lock); > + cfg = pci_mmconfig_lookup(seg, start); > + if (cfg) { > + if (cfg->end_bus < end) > + dev_info(dev, FW_INFO > + "MMCONFIG for " > + "domain %04x [bus %02x-%02x] " > + "only partially covers this bridge\n", > + cfg->segment, cfg->start_bus, cfg->end_bus); > + rc = -EEXIST; > + goto err; > + } > + > + if (!addr) { > + rc = -EINVAL; > + goto err; > + } > + > + cfg = pci_mmconfig_alloc(seg, start, end, addr); > + if (cfg == NULL) { > + dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); > + rc = -ENOMEM; > + goto err; > + } > + rc = pci_mmconfig_map_resource(dev, cfg); > + if (!rc) { > + list_add_sorted(cfg); > + dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", > + &cfg->res, (unsigned long)addr); > + return 0; > + } else { > + if (cfg->res.parent) > + release_resource(&cfg->res); > + kfree(cfg); > + } > + > +err: > + mutex_unlock(&pci_mmcfg_lock); > + return rc; > +} > + > +/* Delete MMCFG information for host bridges */ > +int pci_mmconfig_delete(u16 seg, u8 start, u8 end) > +{ > + struct pci_mmcfg_region *cfg; > + > + mutex_lock(&pci_mmcfg_lock); > + list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) > + if (cfg->segment == seg && cfg->start_bus == start && > + cfg->end_bus == end) { > + list_del_rcu(&cfg->list); > + synchronize_rcu(); > + pci_mmconfig_unmap_resource(cfg); > + mutex_unlock(&pci_mmcfg_lock); > + kfree(cfg); > + return 0; > + } > + mutex_unlock(&pci_mmcfg_lock); > + > + return -ENOENT; > +} > + > +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg) > +{ > + int year; > + > + if (!config_enabled(CONFIG_X86)) > + return 0; > + > + if (cfg->address < 0xFFFFFFFF) > + return 0; > + > + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > + return 0; > + > + if (mcfg->header.revision >= 1) { > + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > + year >= 2010) > + return 0; > + } > + > + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > + "is above 4GB, ignored\n", cfg->pci_segment, > + cfg->start_bus_number, cfg->end_bus_number, cfg->address); > + return -EINVAL; > +} > + > +static int __init pci_parse_mcfg(struct acpi_table_header *header) > +{ > + struct acpi_table_mcfg *mcfg; > + struct acpi_mcfg_allocation *cfg_table, *cfg; > + unsigned long i; > + int entries; > + > + if (!header) > + return -EINVAL; > + > + mcfg = (struct acpi_table_mcfg *)header; > + > + /* how many config structures do we have */ > + entries = 0; > + i = header->length - sizeof(struct acpi_table_mcfg); > + while (i >= sizeof(struct acpi_mcfg_allocation)) { > + entries++; > + i -= sizeof(struct acpi_mcfg_allocation); > + } > + if (entries == 0) { > + pr_err(PREFIX "MMCONFIG has no entries\n"); > + return -ENODEV; > + } > + > + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; > + for (i = 0; i < entries; i++) { > + cfg = &cfg_table[i]; > + if (acpi_mcfg_check_entry(mcfg, cfg)) > + return -ENODEV; > + > + if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, > + cfg->end_bus_number, cfg->address) == NULL) { > + pr_warn(PREFIX "no memory for MCFG entries\n"); > + return -ENOMEM; > + } > + } > + > + return 0; > +} > + > +int __init pci_mmconfig_parse_table(void) > +{ > + return acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); > +} > + > +void __weak __init pci_mmcfg_late_init(void) > +{ > + int err, n = 0; > + struct pci_mmcfg_region *cfg; > + > + err = pci_mmconfig_parse_table(); > + if (err) { > + pr_err(PREFIX " Failed to parse MCFG (%d)\n", err); > + return; > + } > + > + list_for_each_entry(cfg, &pci_mmcfg_list, list) { > + pci_mmconfig_map_resource(NULL, cfg); > + n++; > + } > + > + pr_info(PREFIX " MCFG table loaded %d entries\n", n); > +} > diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c > index 7494dbe..97aa9d3 100644 > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) > diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h > index 89ab057..e9450ef 100644 > --- a/include/linux/pci-acpi.h > +++ b/include/linux/pci-acpi.h > @@ -106,6 +106,39 @@ extern const u8 pci_acpi_dsm_uuid[]; > #define RESET_DELAY_DSM 0x08 > #define FUNCTION_DELAY_DSM 0x09 > > +/* common API to maintain list of MCFG regions */ > +/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > +#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > + > +struct pci_mmcfg_region { > + struct list_head list; > + struct resource res; > + u64 address; > + char __iomem *virt; > + u16 segment; > + u8 start_bus; > + u8 end_bus; > + char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > +}; > + > +extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr); > +extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); > + > +extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > +extern struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, > + int end, u64 addr); > +extern int pci_mmconfig_map_resource(struct device *dev, > + struct pci_mmcfg_region *mcfg); > +extern void pci_mmconfig_unmap_resource(struct pci_mmcfg_region *mcfg); > +extern int pci_mmconfig_enabled(void); > +extern int __init pci_mmconfig_parse_table(void); > + > +extern struct list_head pci_mmcfg_list; > + > +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > +#define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) > + > #else /* CONFIG_ACPI */ > static inline void acpi_pci_add_bus(struct pci_bus *bus) { } > static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } > -- > 1.9.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 10:33 ` David Vrabel -1 siblings, 0 replies; 30+ messages in thread From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw) To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel On 14/01/16 07:32, Jayachandran C wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h [...] > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) Xen parts: Acked-by: David Vrabel <david.vrabel@citrix.com> David ^ permalink raw reply [flat|nested] 30+ messages in thread
* [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi @ 2016-01-14 10:33 ` David Vrabel 0 siblings, 0 replies; 30+ messages in thread From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw) To: linux-arm-kernel On 14/01/16 07:32, Jayachandran C wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h [...] > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) Xen parts: Acked-by: David Vrabel <david.vrabel@citrix.com> David ^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Xen-devel] [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi @ 2016-01-14 10:33 ` David Vrabel 0 siblings, 0 replies; 30+ messages in thread From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw) To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel On 14/01/16 07:32, Jayachandran C wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h [...] > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) Xen parts: Acked-by: David Vrabel <david.vrabel@citrix.com> David ^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi 2016-01-14 7:32 ` Jayachandran C ` (4 preceding siblings ...) (?) @ 2016-01-14 10:33 ` David Vrabel -1 siblings, 0 replies; 30+ messages in thread From: David Vrabel @ 2016-01-14 10:33 UTC (permalink / raw) To: Jayachandran C, linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, xen-devel On 14/01/16 07:32, Jayachandran C wrote: > Move pci_mmcfg_list handling to a drivers/acpi/pci_mcfg.c. This is > to share the API and code with ARM64 later. The corresponding > declarations are moved from asm/pci_x86.h to linux/pci-acpi.h [...] > --- a/drivers/xen/pci.c > +++ b/drivers/xen/pci.c > @@ -27,9 +27,6 @@ > #include <asm/xen/hypervisor.h> > #include <asm/xen/hypercall.h> > #include "../pci/pci.h" > -#ifdef CONFIG_PCI_MMCONFIG > -#include <asm/pci_x86.h> > -#endif > > static bool __read_mostly pci_seg_supported = true; > > @@ -221,7 +218,7 @@ static int __init xen_mcfg_late(void) > if (!xen_initial_domain()) > return 0; > > - if ((pci_probe & PCI_PROBE_MMCONF) == 0) > + if (!pci_mmconfig_enabled()) > return 0; > > if (list_empty(&pci_mmcfg_list)) Xen parts: Acked-by: David Vrabel <david.vrabel@citrix.com> David ^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel pci_create_root_bus is called with NULL as parent in ACPI. This ends up calling pci_bus_assign_domain_nr with a NULL parent, which crashes when dereferencing parent. Fix this by providing a way to set the ACPI domain number and ACPI companion in PCI code. We define pci_acpi_set_companion() to set the ACPI companion pointer and acpi_pci_get_segment() and to get the PCI domain number. This is just defined for the generic ACPI PCI controller, using the corresponding sysdata. pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment() to get the domain number to set on the root bus, in case of ACPI. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/pci/pci.c | 15 ++++++++++++++- drivers/pci/probe.c | 2 ++ include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 314db8c..39d2bcc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/log2.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/pm_wakeup.h> #include <linux/interrupt.h> #include <linux/device.h> @@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void) void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) { static int use_dt_domains = -1; - int domain = of_get_pci_domain_nr(parent->of_node); + int domain; /* + * Handle ACPI early + * + * The companion is not set at this point, and ACPI sets parent to + * NULL, we have to try to get the segment from acpi root info. + */ + if (!parent || !parent->of_node) { + bus->domain_nr = acpi_pci_get_segment(bus->sysdata); + return; + } + + domain = of_get_pci_domain_nr(parent->of_node); + /* * Check DT domain and use_dt_domains values. * * If DT domain property is valid (domain >= 0) and diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984..5af66496 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/cpumask.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/aer.h> #include <linux/acpi.h> #include <asm-generic/pci-bridge.h> @@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + pci_acpi_set_companion(bridge); error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index e9450ef..f8d62e3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list; #define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) #define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) - #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } #endif /* CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC) +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + struct pci_bus *b = bridge->bus; + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata; + + ACPI_COMPANION_SET(&bridge->dev, ci->bridge); +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata; + + return ci->root->segment; +} +#else +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + /* leave it to the platform for now */ +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + return 0; +} +#endif + #ifdef CONFIG_ACPI_APEI extern bool aer_acpi_firmware_first(void); #else -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel pci_create_root_bus is called with NULL as parent in ACPI. This ends up calling pci_bus_assign_domain_nr with a NULL parent, which crashes when dereferencing parent. Fix this by providing a way to set the ACPI domain number and ACPI companion in PCI code. We define pci_acpi_set_companion() to set the ACPI companion pointer and acpi_pci_get_segment() and to get the PCI domain number. This is just defined for the generic ACPI PCI controller, using the corresponding sysdata. pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment() to get the domain number to set on the root bus, in case of ACPI. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/pci/pci.c | 15 ++++++++++++++- drivers/pci/probe.c | 2 ++ include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 314db8c..39d2bcc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/log2.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/pm_wakeup.h> #include <linux/interrupt.h> #include <linux/device.h> @@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void) void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) { static int use_dt_domains = -1; - int domain = of_get_pci_domain_nr(parent->of_node); + int domain; /* + * Handle ACPI early + * + * The companion is not set at this point, and ACPI sets parent to + * NULL, we have to try to get the segment from acpi root info. + */ + if (!parent || !parent->of_node) { + bus->domain_nr = acpi_pci_get_segment(bus->sysdata); + return; + } + + domain = of_get_pci_domain_nr(parent->of_node); + /* * Check DT domain and use_dt_domains values. * * If DT domain property is valid (domain >= 0) and diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984..5af66496 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/cpumask.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/aer.h> #include <linux/acpi.h> #include <asm-generic/pci-bridge.h> @@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + pci_acpi_set_companion(bridge); error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index e9450ef..f8d62e3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list; #define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) #define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) - #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } #endif /* CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC) +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + struct pci_bus *b = bridge->bus; + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata; + + ACPI_COMPANION_SET(&bridge->dev, ci->bridge); +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata; + + return ci->root->segment; +} +#else +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + /* leave it to the platform for now */ +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + return 0; +} +#endif + #ifdef CONFIG_ACPI_APEI extern bool aer_acpi_firmware_first(void); #else -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel pci_create_root_bus is called with NULL as parent in ACPI. This ends up calling pci_bus_assign_domain_nr with a NULL parent, which crashes when dereferencing parent. Fix this by providing a way to set the ACPI domain number and ACPI companion in PCI code. We define pci_acpi_set_companion() to set the ACPI companion pointer and acpi_pci_get_segment() and to get the PCI domain number. This is just defined for the generic ACPI PCI controller, using the corresponding sysdata. pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment() to get the domain number to set on the root bus, in case of ACPI. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/pci/pci.c | 15 ++++++++++++++- drivers/pci/probe.c | 2 ++ include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 314db8c..39d2bcc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/log2.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/pm_wakeup.h> #include <linux/interrupt.h> #include <linux/device.h> @@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void) void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) { static int use_dt_domains = -1; - int domain = of_get_pci_domain_nr(parent->of_node); + int domain; /* + * Handle ACPI early + * + * The companion is not set at this point, and ACPI sets parent to + * NULL, we have to try to get the segment from acpi root info. + */ + if (!parent || !parent->of_node) { + bus->domain_nr = acpi_pci_get_segment(bus->sysdata); + return; + } + + domain = of_get_pci_domain_nr(parent->of_node); + /* * Check DT domain and use_dt_domains values. * * If DT domain property is valid (domain >= 0) and diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984..5af66496 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/cpumask.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/aer.h> #include <linux/acpi.h> #include <asm-generic/pci-bridge.h> @@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + pci_acpi_set_companion(bridge); error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index e9450ef..f8d62e3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list; #define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) #define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) - #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } #endif /* CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC) +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + struct pci_bus *b = bridge->bus; + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata; + + ACPI_COMPANION_SET(&bridge->dev, ci->bridge); +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata; + + return ci->root->segment; +} +#else +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + /* leave it to the platform for now */ +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + return 0; +} +#endif + #ifdef CONFIG_ACPI_APEI extern bool aer_acpi_firmware_first(void); #else -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 2/5] PCI: Handle ACPI companion and domain number 2016-01-14 7:32 ` Jayachandran C ` (4 preceding siblings ...) (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel pci_create_root_bus is called with NULL as parent in ACPI. This ends up calling pci_bus_assign_domain_nr with a NULL parent, which crashes when dereferencing parent. Fix this by providing a way to set the ACPI domain number and ACPI companion in PCI code. We define pci_acpi_set_companion() to set the ACPI companion pointer and acpi_pci_get_segment() and to get the PCI domain number. This is just defined for the generic ACPI PCI controller, using the corresponding sysdata. pci_bus_assign_domain_nr is updated to call acpi_pci_get_segment() to get the domain number to set on the root bus, in case of ACPI. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/pci/pci.c | 15 ++++++++++++++- drivers/pci/probe.c | 2 ++ include/linux/pci-acpi.h | 28 +++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 314db8c..39d2bcc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/log2.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/pm_wakeup.h> #include <linux/interrupt.h> #include <linux/device.h> @@ -4772,9 +4773,21 @@ int pci_get_new_domain_nr(void) void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) { static int use_dt_domains = -1; - int domain = of_get_pci_domain_nr(parent->of_node); + int domain; /* + * Handle ACPI early + * + * The companion is not set at this point, and ACPI sets parent to + * NULL, we have to try to get the segment from acpi root info. + */ + if (!parent || !parent->of_node) { + bus->domain_nr = acpi_pci_get_segment(bus->sysdata); + return; + } + + domain = of_get_pci_domain_nr(parent->of_node); + /* * Check DT domain and use_dt_domains values. * * If DT domain property is valid (domain >= 0) and diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984..5af66496 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/cpumask.h> #include <linux/pci-aspm.h> +#include <linux/pci-acpi.h> #include <linux/aer.h> #include <linux/acpi.h> #include <asm-generic/pci-bridge.h> @@ -2105,6 +2106,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + pci_acpi_set_companion(bridge); error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index e9450ef..f8d62e3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -138,12 +138,38 @@ extern struct list_head pci_mmcfg_list; #define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) #define PCI_MMCFG_OFFSET(bus, devfn) ((bus) << 20 | (devfn) << 12) - #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } #endif /* CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_ACPI_PCI_HOST_GENERIC) +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + struct pci_bus *b = bridge->bus; + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)b->sysdata; + + ACPI_COMPANION_SET(&bridge->dev, ci->bridge); +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + struct acpi_pci_root_info *ci = (struct acpi_pci_root_info *)sysdata; + + return ci->root->segment; +} +#else +static inline void pci_acpi_set_companion(struct pci_host_bridge *bridge) +{ + /* leave it to the platform for now */ +} + +static inline u16 acpi_pci_get_segment(void *sysdata) +{ + return 0; +} +#endif + #ifdef CONFIG_ACPI_APEI extern bool aer_acpi_firmware_first(void); #else -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace 2016-01-14 7:32 ` Jayachandran C ` (5 preceding siblings ...) (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel On some platforms (in this case ARM64), the PCI iospace needs to be mapped with pci_remap_iospace and the resources have to be adjusted for the iospace physical address. This has to be done before acpi_pci_root_validate_resources() checks and removes resource windows. Handle this by adding a function acpi_pci_root_remap_iospace that is called in acpi_pci_probe_root_resources(), before the validate call. Also fix the address check in acpi_dev_ioresource_flags for similar platforms. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/acpi/resource.c | 2 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..fcaa484 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -720,6 +720,61 @@ next: } } +#ifdef PCI_IOBASE +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + struct resource_entry *entry; + struct resource iores; + resource_size_t iostart; + int err; + + iores.flags = IORESOURCE_IO; + iores.start = (resource_size_t)-1; + iores.end = 0; + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + iores.start = min(entry->res->start, iores.start); + iores.end = max(entry->res->end, iores.end); + } + } + if (iores.end == 0) + return 0; + iostart = iores.start; + + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + entry->res->start -= iostart; + entry->res->end -= iostart; + entry->offset -= iostart; + } + } + iores.start -= iostart; + iores.end -= iostart; + + err = pci_remap_iospace(&iores, iostart); + if (err) { + pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores); + return -ENODEV; + } + pr_info(PREFIX "Mapped %pR at %#lx for IO.\n", + &iores, (unsigned long)iostart); + return 0; +} +#else +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + return 0; +} +#endif /* PCI_IOBASE */ + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) { int ret; @@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else entry->res->name = info->name; } - acpi_pci_root_validate_resources(&device->dev, list, + ret = acpi_pci_root_remap_iospace(info); + if (ret >= 0) { + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_MEM); - acpi_pci_root_validate_resources(&device->dev, list, + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_IO); + } } return ret; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cdc5c25..530cad4 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#ifndef PCI_IOBASE if (res->end >= 0x10003) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#endif if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel On some platforms (in this case ARM64), the PCI iospace needs to be mapped with pci_remap_iospace and the resources have to be adjusted for the iospace physical address. This has to be done before acpi_pci_root_validate_resources() checks and removes resource windows. Handle this by adding a function acpi_pci_root_remap_iospace that is called in acpi_pci_probe_root_resources(), before the validate call. Also fix the address check in acpi_dev_ioresource_flags for similar platforms. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/acpi/resource.c | 2 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..fcaa484 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -720,6 +720,61 @@ next: } } +#ifdef PCI_IOBASE +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + struct resource_entry *entry; + struct resource iores; + resource_size_t iostart; + int err; + + iores.flags = IORESOURCE_IO; + iores.start = (resource_size_t)-1; + iores.end = 0; + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + iores.start = min(entry->res->start, iores.start); + iores.end = max(entry->res->end, iores.end); + } + } + if (iores.end == 0) + return 0; + iostart = iores.start; + + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + entry->res->start -= iostart; + entry->res->end -= iostart; + entry->offset -= iostart; + } + } + iores.start -= iostart; + iores.end -= iostart; + + err = pci_remap_iospace(&iores, iostart); + if (err) { + pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores); + return -ENODEV; + } + pr_info(PREFIX "Mapped %pR at %#lx for IO.\n", + &iores, (unsigned long)iostart); + return 0; +} +#else +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + return 0; +} +#endif /* PCI_IOBASE */ + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) { int ret; @@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else entry->res->name = info->name; } - acpi_pci_root_validate_resources(&device->dev, list, + ret = acpi_pci_root_remap_iospace(info); + if (ret >= 0) { + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_MEM); - acpi_pci_root_validate_resources(&device->dev, list, + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_IO); + } } return ret; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cdc5c25..530cad4 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#ifndef PCI_IOBASE if (res->end >= 0x10003) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#endif if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel On some platforms (in this case ARM64), the PCI iospace needs to be mapped with pci_remap_iospace and the resources have to be adjusted for the iospace physical address. This has to be done before acpi_pci_root_validate_resources() checks and removes resource windows. Handle this by adding a function acpi_pci_root_remap_iospace that is called in acpi_pci_probe_root_resources(), before the validate call. Also fix the address check in acpi_dev_ioresource_flags for similar platforms. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/acpi/resource.c | 2 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..fcaa484 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -720,6 +720,61 @@ next: } } +#ifdef PCI_IOBASE +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + struct resource_entry *entry; + struct resource iores; + resource_size_t iostart; + int err; + + iores.flags = IORESOURCE_IO; + iores.start = (resource_size_t)-1; + iores.end = 0; + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + iores.start = min(entry->res->start, iores.start); + iores.end = max(entry->res->end, iores.end); + } + } + if (iores.end == 0) + return 0; + iostart = iores.start; + + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + entry->res->start -= iostart; + entry->res->end -= iostart; + entry->offset -= iostart; + } + } + iores.start -= iostart; + iores.end -= iostart; + + err = pci_remap_iospace(&iores, iostart); + if (err) { + pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores); + return -ENODEV; + } + pr_info(PREFIX "Mapped %pR at %#lx for IO.\n", + &iores, (unsigned long)iostart); + return 0; +} +#else +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + return 0; +} +#endif /* PCI_IOBASE */ + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) { int ret; @@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else entry->res->name = info->name; } - acpi_pci_root_validate_resources(&device->dev, list, + ret = acpi_pci_root_remap_iospace(info); + if (ret >= 0) { + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_MEM); - acpi_pci_root_validate_resources(&device->dev, list, + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_IO); + } } return ret; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cdc5c25..530cad4 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#ifndef PCI_IOBASE if (res->end >= 0x10003) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#endif if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel On some platforms (in this case ARM64), the PCI iospace needs to be mapped with pci_remap_iospace and the resources have to be adjusted for the iospace physical address. This has to be done before acpi_pci_root_validate_resources() checks and removes resource windows. Handle this by adding a function acpi_pci_root_remap_iospace that is called in acpi_pci_probe_root_resources(), before the validate call. Also fix the address check in acpi_dev_ioresource_flags for similar platforms. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/pci_root.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/acpi/resource.c | 2 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..fcaa484 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -720,6 +720,61 @@ next: } } +#ifdef PCI_IOBASE +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + struct resource_entry *entry; + struct resource iores; + resource_size_t iostart; + int err; + + iores.flags = IORESOURCE_IO; + iores.start = (resource_size_t)-1; + iores.end = 0; + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + iores.start = min(entry->res->start, iores.start); + iores.end = max(entry->res->end, iores.end); + } + } + if (iores.end == 0) + return 0; + iostart = iores.start; + + resource_list_for_each_entry(entry, &ci->resources) { + if (entry->res->flags & IORESOURCE_IO) { + entry->res->start -= iostart; + entry->res->end -= iostart; + entry->offset -= iostart; + } + } + iores.start -= iostart; + iores.end -= iostart; + + err = pci_remap_iospace(&iores, iostart); + if (err) { + pr_err("PCI: ACPI: err %d mapping IO %pR\n", err, &iores); + return -ENODEV; + } + pr_info(PREFIX "Mapped %pR at %#lx for IO.\n", + &iores, (unsigned long)iostart); + return 0; +} +#else +/* + * The IO ports are mapped to a memory range, fixup IO resources to + * handle that + */ +static int acpi_pci_root_remap_iospace(struct acpi_pci_root_info *ci) +{ + return 0; +} +#endif /* PCI_IOBASE */ + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) { int ret; @@ -745,10 +800,13 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) else entry->res->name = info->name; } - acpi_pci_root_validate_resources(&device->dev, list, + ret = acpi_pci_root_remap_iospace(info); + if (ret >= 0) { + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_MEM); - acpi_pci_root_validate_resources(&device->dev, list, + acpi_pci_root_validate_resources(&device->dev, list, IORESOURCE_IO); + } } return ret; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index cdc5c25..530cad4 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -126,8 +126,10 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#ifndef PCI_IOBASE if (res->end >= 0x10003) res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; +#endif if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 4/5] arm64: pci: Add ACPI support 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Add functions needed for ACPI support. pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as weak so that it can be implemented by the generic ACPI PCI driver. pcibios_enable_device and pcibios_disable_device handle acpi irq enable and disable. And, pcibios_add_bus and pcibios_remove_bus have been added call the corresponding ACPI functions. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 871f217..8c93af2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC config PCI_SYSCALL def_bool PCI +config PCI_MMCONFIG + def_bool PCI && ACPI + source "drivers/pci/Kconfig" source "drivers/pci/pcie/Kconfig" source "drivers/pci/hotplug/Kconfig" diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index b3d098b..db0a6a9 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/of_pci.h> #include <linux/of_platform.h> +#include <linux/pci-acpi.h> #include <linux/slab.h> #include <asm/pci-bridge.h> @@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) if (pci_has_flag(PCI_PROBE_ONLY)) return 0; +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_enable(dev); +#endif + return pci_enable_resources(dev, mask); } +void pcibios_disable_device(struct pci_dev *dev) +{ +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_disable(dev); +#endif +} + /* * Try to assign the IRQ number from DT when adding a new device */ @@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev) } /* - * raw_pci_read/write - Platform-specific PCI config space access. + * ACPI uses these - leave it to the generic ACPI PCI driver */ -int raw_pci_read(unsigned int domain, unsigned int bus, +int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { return -ENXIO; } -int raw_pci_write(unsigned int domain, unsigned int bus, +int __weak raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { return -ENXIO; @@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus, #ifdef CONFIG_ACPI /* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ return NULL; } + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + acpi_pci_remove_bus(bus); +} + #endif -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 4/5] arm64: pci: Add ACPI support @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel Add functions needed for ACPI support. pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as weak so that it can be implemented by the generic ACPI PCI driver. pcibios_enable_device and pcibios_disable_device handle acpi irq enable and disable. And, pcibios_add_bus and pcibios_remove_bus have been added call the corresponding ACPI functions. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 871f217..8c93af2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC config PCI_SYSCALL def_bool PCI +config PCI_MMCONFIG + def_bool PCI && ACPI + source "drivers/pci/Kconfig" source "drivers/pci/pcie/Kconfig" source "drivers/pci/hotplug/Kconfig" diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index b3d098b..db0a6a9 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/of_pci.h> #include <linux/of_platform.h> +#include <linux/pci-acpi.h> #include <linux/slab.h> #include <asm/pci-bridge.h> @@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) if (pci_has_flag(PCI_PROBE_ONLY)) return 0; +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_enable(dev); +#endif + return pci_enable_resources(dev, mask); } +void pcibios_disable_device(struct pci_dev *dev) +{ +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_disable(dev); +#endif +} + /* * Try to assign the IRQ number from DT when adding a new device */ @@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev) } /* - * raw_pci_read/write - Platform-specific PCI config space access. + * ACPI uses these - leave it to the generic ACPI PCI driver */ -int raw_pci_read(unsigned int domain, unsigned int bus, +int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { return -ENXIO; } -int raw_pci_write(unsigned int domain, unsigned int bus, +int __weak raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { return -ENXIO; @@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus, #ifdef CONFIG_ACPI /* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ return NULL; } + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + acpi_pci_remove_bus(bus); +} + #endif -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 4/5] arm64: pci: Add ACPI support @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Add functions needed for ACPI support. pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as weak so that it can be implemented by the generic ACPI PCI driver. pcibios_enable_device and pcibios_disable_device handle acpi irq enable and disable. And, pcibios_add_bus and pcibios_remove_bus have been added call the corresponding ACPI functions. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 871f217..8c93af2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC config PCI_SYSCALL def_bool PCI +config PCI_MMCONFIG + def_bool PCI && ACPI + source "drivers/pci/Kconfig" source "drivers/pci/pcie/Kconfig" source "drivers/pci/hotplug/Kconfig" diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index b3d098b..db0a6a9 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/of_pci.h> #include <linux/of_platform.h> +#include <linux/pci-acpi.h> #include <linux/slab.h> #include <asm/pci-bridge.h> @@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) if (pci_has_flag(PCI_PROBE_ONLY)) return 0; +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_enable(dev); +#endif + return pci_enable_resources(dev, mask); } +void pcibios_disable_device(struct pci_dev *dev) +{ +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_disable(dev); +#endif +} + /* * Try to assign the IRQ number from DT when adding a new device */ @@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev) } /* - * raw_pci_read/write - Platform-specific PCI config space access. + * ACPI uses these - leave it to the generic ACPI PCI driver */ -int raw_pci_read(unsigned int domain, unsigned int bus, +int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { return -ENXIO; } -int raw_pci_write(unsigned int domain, unsigned int bus, +int __weak raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { return -ENXIO; @@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus, #ifdef CONFIG_ACPI /* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ return NULL; } + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + acpi_pci_remove_bus(bus); +} + #endif -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 4/5] arm64: pci: Add ACPI support 2016-01-14 7:32 ` Jayachandran C ` (8 preceding siblings ...) (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel Add functions needed for ACPI support. pci_acpi_scan_root(), raw_pci_read and raw_pci_write are marked as weak so that it can be implemented by the generic ACPI PCI driver. pcibios_enable_device and pcibios_disable_device handle acpi irq enable and disable. And, pcibios_add_bus and pcibios_remove_bus have been added call the corresponding ACPI functions. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- arch/arm64/Kconfig | 3 +++ arch/arm64/kernel/pci.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 871f217..8c93af2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -203,6 +203,9 @@ config PCI_DOMAINS_GENERIC config PCI_SYSCALL def_bool PCI +config PCI_MMCONFIG + def_bool PCI && ACPI + source "drivers/pci/Kconfig" source "drivers/pci/pcie/Kconfig" source "drivers/pci/hotplug/Kconfig" diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index b3d098b..db0a6a9 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -17,6 +17,7 @@ #include <linux/mm.h> #include <linux/of_pci.h> #include <linux/of_platform.h> +#include <linux/pci-acpi.h> #include <linux/slab.h> #include <asm/pci-bridge.h> @@ -48,9 +49,22 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) if (pci_has_flag(PCI_PROBE_ONLY)) return 0; +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_enable(dev); +#endif + return pci_enable_resources(dev, mask); } +void pcibios_disable_device(struct pci_dev *dev) +{ +#ifdef CONFIG_ACPI + if (acpi_find_root_bridge_handle(dev)) + acpi_pci_irq_disable(dev); +#endif +} + /* * Try to assign the IRQ number from DT when adding a new device */ @@ -62,15 +76,15 @@ int pcibios_add_device(struct pci_dev *dev) } /* - * raw_pci_read/write - Platform-specific PCI config space access. + * ACPI uses these - leave it to the generic ACPI PCI driver */ -int raw_pci_read(unsigned int domain, unsigned int bus, +int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { return -ENXIO; } -int raw_pci_write(unsigned int domain, unsigned int bus, +int __weak raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { return -ENXIO; @@ -78,9 +92,19 @@ int raw_pci_write(unsigned int domain, unsigned int bus, #ifdef CONFIG_ACPI /* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +struct pci_bus * __weak pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ return NULL; } + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + acpi_pci_remove_bus(bus); +} + #endif -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller 2016-01-14 7:32 ` Jayachandran C ` (9 preceding siblings ...) (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Tomasz Nowicki, Lorenzo Pieralisi, Jayachandran C, xen-devel Add a simple ACPI based PCI host controller. This is done by providing a simple implementation for pci_acpi_scan_root(). The pci_mmcfg_list handling is done by the ACPI code, we keep a reference to the pci_mmcfg_region which is already mapped. Provide implementations of raw_pci_read and raw_pci_write based on the pci_mmcfg_list This is enabled only for ARM64 now. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/acpi/pci_host_acpi.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5eef4cb..c5738be 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -283,6 +283,14 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 +config ACPI_PCI_HOST_GENERIC + bool "Generic ACPI PCI host controller" + depends on ARM64 && ACPI + help + Say Y here if you want to support a simple generic ACPI PCI host + controller. + + config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" default "" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3f65311..9fffdf2 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -41,6 +41,7 @@ acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o +acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c new file mode 100644 index 0000000..9dbdd81 --- /dev/null +++ b/drivers/acpi/pci_host_acpi.c @@ -0,0 +1,186 @@ +/* + * Generic PCI host controller driver for ACPI based systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (c) 2015 Broadcom Corporation + * + * Based on drivers/pci/host/pci-host-generic.c + * Copyright (C) 2014 ARM Limited + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> + +#define PREFIX "pci-host-acpi:" + +/* sysdata pointer is ->root_info */ +struct gen_acpi_root_info { + struct acpi_pci_root_info common; + struct pci_mmcfg_region *mcfg; + bool mcfg_added; +}; + +/* find mapping of a MCFG area */ +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct gen_acpi_root_info *pci = bus->sysdata; + struct pci_mmcfg_region *mcfg = pci->mcfg; + + if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus) + return NULL; + + return mcfg->virt + + PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) + + where; +} + +static struct pci_ops gen_acpi_pci_ops = { + .map_bus = gen_acpi_map_cfg_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +/* Insert the ECFG area for a root bus */ +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + struct device *dev = &ci->bridge->dev; + int err; + + info = container_of(ci, struct gen_acpi_root_info, common); + err = pci_mmconfig_insert(dev, root->segment, root->secondary.start, + root->secondary.end, root->mcfg_addr); + if (err && err != -EEXIST) + return err; + + info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + WARN_ON(info->mcfg == NULL); + info->mcfg_added = (err == -EEXIST); + return 0; +} + +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + + info = container_of(ci, struct gen_acpi_root_info, common); + if (info->mcfg_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); + info->mcfg = NULL; +} + +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &gen_acpi_pci_ops, + .init_info = pci_acpi_root_init_info, + .release_info = pci_acpi_root_release_info, +}; + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct acpi_device *device = root->device; + struct gen_acpi_root_info *ri; + struct pci_bus *bus, *child; + + /* allocate acpi_info/sysdata */ + ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL); + if (!ri) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, (int)root->secondary.start); + return NULL; + } + + bus = acpi_pci_root_create(root, &pci_acpi_root_ops, + &ri->common, ri); + if (!bus) { + dev_err(&device->dev, "Scanning rootbus failed"); + return NULL; + } + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +int raw_pci_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + *val = readb(addr + reg); + break; + case 2: + *val = readw(addr + reg); + break; + case 4: + *val = readl(addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} + +int raw_pci_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + writeb(val, addr + reg); + break; + case 2: + writew(val, addr + reg); + break; + case 4: + writel(val, addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller 2016-01-14 7:32 ` Jayachandran C (?) @ 2016-01-14 7:32 ` Jayachandran C -1 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Add a simple ACPI based PCI host controller. This is done by providing a simple implementation for pci_acpi_scan_root(). The pci_mmcfg_list handling is done by the ACPI code, we keep a reference to the pci_mmcfg_region which is already mapped. Provide implementations of raw_pci_read and raw_pci_write based on the pci_mmcfg_list This is enabled only for ARM64 now. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/acpi/pci_host_acpi.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5eef4cb..c5738be 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -283,6 +283,14 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 +config ACPI_PCI_HOST_GENERIC + bool "Generic ACPI PCI host controller" + depends on ARM64 && ACPI + help + Say Y here if you want to support a simple generic ACPI PCI host + controller. + + config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" default "" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3f65311..9fffdf2 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -41,6 +41,7 @@ acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o +acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c new file mode 100644 index 0000000..9dbdd81 --- /dev/null +++ b/drivers/acpi/pci_host_acpi.c @@ -0,0 +1,186 @@ +/* + * Generic PCI host controller driver for ACPI based systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (c) 2015 Broadcom Corporation + * + * Based on drivers/pci/host/pci-host-generic.c + * Copyright (C) 2014 ARM Limited + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> + +#define PREFIX "pci-host-acpi:" + +/* sysdata pointer is ->root_info */ +struct gen_acpi_root_info { + struct acpi_pci_root_info common; + struct pci_mmcfg_region *mcfg; + bool mcfg_added; +}; + +/* find mapping of a MCFG area */ +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct gen_acpi_root_info *pci = bus->sysdata; + struct pci_mmcfg_region *mcfg = pci->mcfg; + + if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus) + return NULL; + + return mcfg->virt + + PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) + + where; +} + +static struct pci_ops gen_acpi_pci_ops = { + .map_bus = gen_acpi_map_cfg_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +/* Insert the ECFG area for a root bus */ +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + struct device *dev = &ci->bridge->dev; + int err; + + info = container_of(ci, struct gen_acpi_root_info, common); + err = pci_mmconfig_insert(dev, root->segment, root->secondary.start, + root->secondary.end, root->mcfg_addr); + if (err && err != -EEXIST) + return err; + + info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + WARN_ON(info->mcfg == NULL); + info->mcfg_added = (err == -EEXIST); + return 0; +} + +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + + info = container_of(ci, struct gen_acpi_root_info, common); + if (info->mcfg_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); + info->mcfg = NULL; +} + +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &gen_acpi_pci_ops, + .init_info = pci_acpi_root_init_info, + .release_info = pci_acpi_root_release_info, +}; + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct acpi_device *device = root->device; + struct gen_acpi_root_info *ri; + struct pci_bus *bus, *child; + + /* allocate acpi_info/sysdata */ + ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL); + if (!ri) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, (int)root->secondary.start); + return NULL; + } + + bus = acpi_pci_root_create(root, &pci_acpi_root_ops, + &ri->common, ri); + if (!bus) { + dev_err(&device->dev, "Scanning rootbus failed"); + return NULL; + } + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +int raw_pci_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + *val = readb(addr + reg); + break; + case 2: + *val = readw(addr + reg); + break; + case 4: + *val = readl(addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} + +int raw_pci_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + writeb(val, addr + reg); + break; + case 2: + writew(val, addr + reg); + break; + case 4: + writel(val, addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-arm-kernel Add a simple ACPI based PCI host controller. This is done by providing a simple implementation for pci_acpi_scan_root(). The pci_mmcfg_list handling is done by the ACPI code, we keep a reference to the pci_mmcfg_region which is already mapped. Provide implementations of raw_pci_read and raw_pci_write based on the pci_mmcfg_list This is enabled only for ARM64 now. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/acpi/pci_host_acpi.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5eef4cb..c5738be 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -283,6 +283,14 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 +config ACPI_PCI_HOST_GENERIC + bool "Generic ACPI PCI host controller" + depends on ARM64 && ACPI + help + Say Y here if you want to support a simple generic ACPI PCI host + controller. + + config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" default "" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3f65311..9fffdf2 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -41,6 +41,7 @@ acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o +acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c new file mode 100644 index 0000000..9dbdd81 --- /dev/null +++ b/drivers/acpi/pci_host_acpi.c @@ -0,0 +1,186 @@ +/* + * Generic PCI host controller driver for ACPI based systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (c) 2015 Broadcom Corporation + * + * Based on drivers/pci/host/pci-host-generic.c + * Copyright (C) 2014 ARM Limited + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> + +#define PREFIX "pci-host-acpi:" + +/* sysdata pointer is ->root_info */ +struct gen_acpi_root_info { + struct acpi_pci_root_info common; + struct pci_mmcfg_region *mcfg; + bool mcfg_added; +}; + +/* find mapping of a MCFG area */ +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct gen_acpi_root_info *pci = bus->sysdata; + struct pci_mmcfg_region *mcfg = pci->mcfg; + + if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus) + return NULL; + + return mcfg->virt + + PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) + + where; +} + +static struct pci_ops gen_acpi_pci_ops = { + .map_bus = gen_acpi_map_cfg_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +/* Insert the ECFG area for a root bus */ +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + struct device *dev = &ci->bridge->dev; + int err; + + info = container_of(ci, struct gen_acpi_root_info, common); + err = pci_mmconfig_insert(dev, root->segment, root->secondary.start, + root->secondary.end, root->mcfg_addr); + if (err && err != -EEXIST) + return err; + + info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + WARN_ON(info->mcfg == NULL); + info->mcfg_added = (err == -EEXIST); + return 0; +} + +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + + info = container_of(ci, struct gen_acpi_root_info, common); + if (info->mcfg_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); + info->mcfg = NULL; +} + +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &gen_acpi_pci_ops, + .init_info = pci_acpi_root_init_info, + .release_info = pci_acpi_root_release_info, +}; + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct acpi_device *device = root->device; + struct gen_acpi_root_info *ri; + struct pci_bus *bus, *child; + + /* allocate acpi_info/sysdata */ + ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL); + if (!ri) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, (int)root->secondary.start); + return NULL; + } + + bus = acpi_pci_root_create(root, &pci_acpi_root_ops, + &ri->common, ri); + if (!bus) { + dev_err(&device->dev, "Scanning rootbus failed"); + return NULL; + } + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +int raw_pci_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + *val = readb(addr + reg); + break; + case 2: + *val = readw(addr + reg); + break; + case 4: + *val = readl(addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} + +int raw_pci_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + writeb(val, addr + reg); + break; + case 2: + writew(val, addr + reg); + break; + case 4: + writel(val, addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller @ 2016-01-14 7:32 ` Jayachandran C 0 siblings, 0 replies; 30+ messages in thread From: Jayachandran C @ 2016-01-14 7:32 UTC (permalink / raw) To: linux-pci, Bjorn Helgaas, linux-acpi, Arnd Bergmann, linux-arm-kernel, Rafael J. Wysocki Cc: Jayachandran C, Lorenzo Pieralisi, Tomasz Nowicki, xen-devel Add a simple ACPI based PCI host controller. This is done by providing a simple implementation for pci_acpi_scan_root(). The pci_mmcfg_list handling is done by the ACPI code, we keep a reference to the pci_mmcfg_region which is already mapped. Provide implementations of raw_pci_read and raw_pci_write based on the pci_mmcfg_list This is enabled only for ARM64 now. Signed-off-by: Jayachandran C <jchandra@broadcom.com> --- drivers/acpi/Kconfig | 8 ++ drivers/acpi/Makefile | 1 + drivers/acpi/pci_host_acpi.c | 186 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 drivers/acpi/pci_host_acpi.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5eef4cb..c5738be 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -283,6 +283,14 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 +config ACPI_PCI_HOST_GENERIC + bool "Generic ACPI PCI host controller" + depends on ARM64 && ACPI + help + Say Y here if you want to support a simple generic ACPI PCI host + controller. + + config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" default "" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3f65311..9fffdf2 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -41,6 +41,7 @@ acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o acpi-$(CONFIG_PCI_MMCONFIG) += pci_mcfg.o +acpi-$(CONFIG_ACPI_PCI_HOST_GENERIC) += pci_host_acpi.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_host_acpi.c b/drivers/acpi/pci_host_acpi.c new file mode 100644 index 0000000..9dbdd81 --- /dev/null +++ b/drivers/acpi/pci_host_acpi.c @@ -0,0 +1,186 @@ +/* + * Generic PCI host controller driver for ACPI based systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (c) 2015 Broadcom Corporation + * + * Based on drivers/pci/host/pci-host-generic.c + * Copyright (C) 2014 ARM Limited + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <linux/pci-acpi.h> +#include <linux/sfi_acpi.h> +#include <linux/slab.h> + +#define PREFIX "pci-host-acpi:" + +/* sysdata pointer is ->root_info */ +struct gen_acpi_root_info { + struct acpi_pci_root_info common; + struct pci_mmcfg_region *mcfg; + bool mcfg_added; +}; + +/* find mapping of a MCFG area */ +static void __iomem *gen_acpi_map_cfg_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct gen_acpi_root_info *pci = bus->sysdata; + struct pci_mmcfg_region *mcfg = pci->mcfg; + + if (bus->number < mcfg->start_bus || bus->number > mcfg->end_bus) + return NULL; + + return mcfg->virt + + PCI_MMCFG_OFFSET(bus->number - mcfg->start_bus, devfn) + + where; +} + +static struct pci_ops gen_acpi_pci_ops = { + .map_bus = gen_acpi_map_cfg_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +/* Insert the ECFG area for a root bus */ +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + struct device *dev = &ci->bridge->dev; + int err; + + info = container_of(ci, struct gen_acpi_root_info, common); + err = pci_mmconfig_insert(dev, root->segment, root->secondary.start, + root->secondary.end, root->mcfg_addr); + if (err && err != -EEXIST) + return err; + + info->mcfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + WARN_ON(info->mcfg == NULL); + info->mcfg_added = (err == -EEXIST); + return 0; +} + +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) +{ + struct gen_acpi_root_info *info; + struct acpi_pci_root *root = ci->root; + + info = container_of(ci, struct gen_acpi_root_info, common); + if (info->mcfg_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); + info->mcfg = NULL; +} + +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &gen_acpi_pci_ops, + .init_info = pci_acpi_root_init_info, + .release_info = pci_acpi_root_release_info, +}; + +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + struct acpi_device *device = root->device; + struct gen_acpi_root_info *ri; + struct pci_bus *bus, *child; + + /* allocate acpi_info/sysdata */ + ri = devm_kzalloc(&device->dev, sizeof(*ri), GFP_KERNEL); + if (!ri) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + root->segment, (int)root->secondary.start); + return NULL; + } + + bus = acpi_pci_root_create(root, &pci_acpi_root_ops, + &ri->common, ri); + if (!bus) { + dev_err(&device->dev, "Scanning rootbus failed"); + return NULL; + } + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +int raw_pci_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + *val = readb(addr + reg); + break; + case 2: + *val = readw(addr + reg); + break; + case 4: + *val = readl(addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} + +int raw_pci_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + struct pci_mmcfg_region *mcfg; + void __iomem *addr; + int err = -EINVAL; + + rcu_read_lock(); + mcfg = pci_mmconfig_lookup(seg, bus); + if (!mcfg || !mcfg->virt) + goto err; + + addr = mcfg->virt + PCI_MMCFG_OFFSET(bus, devfn); + switch (len) { + case 1: + writeb(val, addr + reg); + break; + case 2: + writew(val, addr + reg); + break; + case 4: + writel(val, addr + reg); + break; + } + err = 0; +err: + rcu_read_unlock(); + return err; +} -- 1.9.1 ^ permalink raw reply related [flat|nested] 30+ messages in thread
end of thread, other threads:[~2016-01-14 10:42 UTC | newest] Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-01-14 7:32 [PATCH v6 0/5] ACPI based PCI support for arm64 Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` [PATCH v6 1/5] APCI: MCFG: Move mmcfg_list management to drivers/acpi Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 8:48 ` Dennis Chen 2016-01-14 8:48 ` Dennis Chen 2016-01-14 8:48 ` Dennis Chen 2016-01-14 10:33 ` [Xen-devel] " David Vrabel 2016-01-14 10:33 ` David Vrabel 2016-01-14 10:33 ` David Vrabel 2016-01-14 10:33 ` David Vrabel 2016-01-14 7:32 ` [PATCH v6 2/5] PCI: Handle ACPI companion and domain number Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` [PATCH v6 3/5] ACPI: PCI: Support platforms that need pci_remap_iospace Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` [PATCH v6 4/5] arm64: pci: Add ACPI support Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` [PATCH v6 5/5] PCI: ACPI: Add a generic ACPI based host controller Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C 2016-01-14 7:32 ` Jayachandran C
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.