From the functionality point of view this series might be split into the
following logic parts: 1. Make MMCONFIG code arch-agnostic which allows all architectures to collect PCI config regions and used when necessary. 2. Move non-arch specific bits to the core code. 3. Use MMCONFIG code and implement generic ACPI based PCI host controller driver. 4. Enable above driver on ARM64
Patches has been built on top of 4.5-rc1 and can be found here: git@github.com:semihalf-nowicki-tomasz/linux.git (pci-acpi-v4)
NOTE, this patch set depends on Lorenzo's fixes: https://patchwork.ozlabs.org/patch/576450/ which can be found in pci-acpi-v4 branch.
This has been tested on Cavium ThunderX server, JunoR2, HP RX2660 IA64, x86, Hip05, X-Gene and QEMU-aarch64. Any help in reviewing and testing is very appreciated.
v3 -> v4 - dropped Jiang's fix http://lkml.iu.edu/hypermail/linux/kernel/1601.1/04318.html - added Lorenzo's fix patch 19/24 - ACPI PCI bus domain number assigning cleanup - changed resource management, we now claim and reassign resources - improvements for applying quirks - dropped Matthew's http://www.spinics.net/lists/linux-pci/msg45950.html dependency - rebased to 4.5-rc1
v2 -> v3 - fix legacy IRQ assigning and IO ports registration - remove reference to arch specific companion device for ia64 - move ACPI PCI host controller driver to pci_root.c - drop generic domain assignment for x86 and ia64 as I am not able to run all necessary test variants - drop patch which cleaned legacy IRQ assignment since it belongs to Mathew's series: https://patchwork.ozlabs.org/patch/557504/ - extend MCFG quirk code - rebased to 4.4
v1 -> v2 - moved non-arch specific piece of code to dirver/acpi/ directory - fixed IO resource handling - introduced PCI config accessors quirks matching - moved ACPI_COMPANION_SET to generic code
v1 - https://lkml.org/lkml/2015/10/27/504 v2 - https://lkml.org/lkml/2015/12/16/246 v3 - http://lkml.iu.edu/hypermail/linux/kernel/1601.1/04308.html
Lorenzo Pieralisi (1): drivers: pci: add generic code to claim bus resources
Tomasz Nowicki (22): x86, pci: Reorder logic of pci_mmconfig_insert() function x86, pci, acpi: Move arch-agnostic MMCONFIG (aka ECAM) and ACPI code out of arch/x86/ directory pci, acpi, mcfg: Provide generic implementation of MCFG code initialization. x86, pci: mmconfig_{32,64}.c code refactoring - remove code duplication. x86, pci, ecam: mmconfig_64.c becomes default implementation for ECAM driver. XEN / PCI: Remove the dependence on arch x86 when PCI_MMCONFIG=y pci, acpi, mcfg: Provide default RAW ACPI PCI config space accessors. arm64, acpi: Use MCFG and empty PCI config space accessors from mcfg.c file. pci, acpi, ecam: Add flag to indicate whether ECAM region was hot added or not. x86, pci: Cleanup platform specific MCFG data by using ECAM hot_added flag. pci, acpi: Move ACPI host bridge device companion assignment to core code. x86, ia64, pci: Remove ACPI companion device from platform specific data. pci, acpi: Provide generic way to assign bus domain number. x86, ia64: Include acpi_pci_{add|remove}_bus to the default pcibios_{add|remove}_bus implementation. acpi, mcfg: Implement two calls that might be used to inject/remove MCFG region. x86, acpi, pci: Use equivalent function from mcfg.c driver. acpi, mcfg: Add default PCI config accessors implementation and initial support for related quirks. pci, of: Move the PCI I/O space management to PCI core code. pci, acpi: Support for ACPI based generic PCI host controller init pci, acpi: Match PCI config space accessors against platfrom specific quirks. arm64, pci, acpi: Assign legacy IRQs once device is enable. arm64, pci, acpi: Start using ACPI based PCI host bridge driver for ARM64.
arch/arm64/Kconfig | 8 ++ arch/arm64/kernel/pci.c | 37 ++---- arch/ia64/hp/common/sba_iommu.c | 2 +- arch/ia64/include/asm/pci.h | 1 - arch/ia64/pci/pci.c | 26 ---- arch/ia64/sn/kernel/io_acpi_init.c | 4 +- arch/x86/Kconfig | 4 + arch/x86/include/asm/pci.h | 3 - arch/x86/include/asm/pci_x86.h | 28 +---- arch/x86/pci/acpi.c | 56 ++------- arch/x86/pci/common.c | 10 -- arch/x86/pci/mmconfig-shared.c | 250 ++++++------------------------------- arch/x86/pci/mmconfig_32.c | 11 +- arch/x86/pci/mmconfig_64.c | 67 +--------- arch/x86/pci/numachip.c | 1 + drivers/acpi/Kconfig | 7 ++ drivers/acpi/Makefile | 1 + drivers/acpi/mcfg.c | 201 +++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 134 +++++++++++++++++++- drivers/of/address.c | 116 +---------------- drivers/pci/Kconfig | 10 ++ drivers/pci/Makefile | 5 + drivers/pci/ecam.c | 234 ++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 126 ++++++++++++++++++- drivers/pci/probe.c | 5 + drivers/pci/setup-bus.c | 63 ++++++++++ drivers/xen/pci.c | 7 +- include/acpi/acpi_bus.h | 1 + include/asm-generic/vmlinux.lds.h | 7 ++ include/linux/acpi.h | 2 + include/linux/ecam.h | 62 +++++++++ include/linux/of_address.h | 9 -- include/linux/pci-acpi.h | 19 +++ include/linux/pci.h | 6 + 34 files changed, 962 insertions(+), 561 deletions(-) create mode 100644 drivers/acpi/mcfg.c create mode 100644 drivers/pci/ecam.c create mode 100644 include/linux/ecam.h
This patch is the first step for MMCONFIG refactoring process.
Code that uses pci_mmcfg_lock will be moved to common file and become accessible for all architectures. pci_mmconfig_insert() cannot be moved so easily since it is mixing generic mmconfig code with x86 specific logic inside of mutual exclusive block guarded by pci_mmcfg_lock.
To get rid of that constraint, we reorder actions as follow: 1. sanity check for mmconfig region presence, if we already have such region it doesn't make snese to alloc new mmconfig list entry 2. mmconfig entry allocation, no need to lock 3. insertion to iomem_resource has its own lock, no need to wrap it into mutex 4. insertion to mmconfig list can be done as the final step in separate function (candidate for further refactoring) and needs another mmconfig lookup to avoid race condition.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/pci/mmconfig-shared.c | 101 +++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 43 deletions(-)
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..c8bb9b0 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -720,6 +720,38 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources);
+static int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) +{ + struct pci_mmcfg_region *cfg_conflict; + int err = 0; + + mutex_lock(&pci_mmcfg_lock); + cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); + if (cfg_conflict) { + if (cfg_conflict->end_bus < cfg->end_bus) + pr_info(FW_INFO "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg_conflict->segment, cfg_conflict->start_bus, + cfg_conflict->end_bus); + err = -EEXIST; + goto out; + } + + if (pci_mmcfg_arch_map(cfg)) { + pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); + err = -ENOMEM; + goto out; + } else { + list_add_sorted(cfg); + pr_info("MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)cfg->address); + } +out: + mutex_unlock(&pci_mmcfg_lock); + return err; +} + /* Add MMCFG information for host bridges */ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, phys_addr_t addr) @@ -734,63 +766,46 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, if (start > end) return -EINVAL;
- mutex_lock(&pci_mmcfg_lock); + rcu_read_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); + rcu_read_unlock(); + if (cfg) return -EEXIST; - }
- if (!addr) { - mutex_unlock(&pci_mmcfg_lock); + if (!addr) return -EINVAL; - }
- rc = -EBUSY; cfg = pci_mmconfig_alloc(seg, start, end, addr); - if (cfg == NULL) { + if (!cfg) { dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); - rc = -ENOMEM; - } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { + return -ENOMEM; + } + + rc = -EBUSY; + 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); - - 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; - } + goto error; }
- if (cfg) { - if (cfg->res.parent) - release_resource(&cfg->res); - kfree(cfg); + /* 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); + goto error; }
- mutex_unlock(&pci_mmcfg_lock); + rc = pci_mmconfig_inject(cfg); + if (!rc) + return 0;
+error: + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); return rc; }
ECAM standard and MCFG table are architecture independent and it makes sense to share common code across all architectures. Both are going to corresponding files - ecam.c and mcfg.c
While we are here, rename pci_parse_mcfg to acpi_parse_mcfg. We already have acpi_parse_mcfg prototype which is used nowhere. At the same time, we need pci_parse_mcfg been global so acpi_parse_mcfg can be used perfectly here.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/Kconfig | 3 + arch/x86/include/asm/pci_x86.h | 21 ----- arch/x86/pci/acpi.c | 1 + arch/x86/pci/mmconfig-shared.c | 205 +---------------------------------------- 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/mcfg.c | 59 ++++++++++++ drivers/pci/Kconfig | 7 ++ drivers/pci/Makefile | 5 + drivers/pci/ecam.c | 175 +++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 1 + include/linux/acpi.h | 2 + include/linux/ecam.h | 37 ++++++++ 15 files changed, 299 insertions(+), 221 deletions(-) create mode 100644 drivers/acpi/mcfg.c create mode 100644 drivers/pci/ecam.c create mode 100644 include/linux/ecam.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 330e738..a74acce 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -131,6 +131,7 @@ config X86 select HAVE_MIXED_BREAKPOINTS_REGS select HAVE_OPROFILE select HAVE_OPTPROBES + select HAVE_PCI_ECAM select HAVE_PCSPKR_PLATFORM select HAVE_PERF_EVENTS select HAVE_PERF_EVENTS_NMI @@ -2399,6 +2400,7 @@ config PCI_DIRECT
config PCI_MMCONFIG def_bool y + select PCI_ECAM depends on X86_32 && PCI && (ACPI || SFI) && (PCI_GOMMCONFIG || PCI_GOANY)
config PCI_OLPC @@ -2416,6 +2418,7 @@ config PCI_DOMAINS
config PCI_MMCONFIG bool "Support mmconfig PCI config space access" + select PCI_ECAM depends on X86_64 && PCI && ACPI
config PCI_CNB20LE_QUIRK diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 46873fb..0281d2d 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,33 +122,12 @@ 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]; -}; - 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)
/* * On AMD Fam10h CPUs, all PCI MMIO configuration space accesses must use diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index e20dbe5..a997d7c 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -5,6 +5,7 @@ #include <linux/dmi.h> #include <linux/slab.h> #include <linux/pci-acpi.h> +#include <linux/ecam.h> #include <asm/numa.h> #include <asm/pci_x86.h>
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index c8bb9b0..ce2c2e4 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/rculist.h> +#include <linux/ecam.h> #include <asm/e820.h> #include <asm/pci_x86.h> #include <asm/acpi.h> @@ -27,103 +28,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) -{ - if (cfg->res.parent) - release_resource(&cfg->res); - list_del(&cfg->list); - kfree(cfg); -} - -static void __init free_all_mmcfg(void) -{ - struct pci_mmcfg_region *cfg, *tmp; - - pci_mmcfg_arch_free(); - list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) - 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) { @@ -543,8 +447,8 @@ 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 __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) { int year;
@@ -566,50 +470,6 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, 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); @@ -668,7 +528,7 @@ void __init pci_mmcfg_early_init(void) if (pci_mmcfg_check_hostbridge()) known_bridge = 1; else - acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); + acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); __pci_mmcfg_init(1);
set_apei_filter(); @@ -686,7 +546,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); + acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); __pci_mmcfg_init(0); } } @@ -720,38 +580,6 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources);
-static int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) -{ - struct pci_mmcfg_region *cfg_conflict; - int err = 0; - - mutex_lock(&pci_mmcfg_lock); - cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); - if (cfg_conflict) { - if (cfg_conflict->end_bus < cfg->end_bus) - pr_info(FW_INFO "MMCONFIG for " - "domain %04x [bus %02x-%02x] " - "only partially covers this bridge\n", - cfg_conflict->segment, cfg_conflict->start_bus, - cfg_conflict->end_bus); - err = -EEXIST; - goto out; - } - - if (pci_mmcfg_arch_map(cfg)) { - pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); - err = -ENOMEM; - goto out; - } else { - list_add_sorted(cfg); - pr_info("MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)cfg->address); - } -out: - mutex_unlock(&pci_mmcfg_lock); - return err; -} - /* Add MMCFG information for host bridges */ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, phys_addr_t addr) @@ -808,26 +636,3 @@ error: kfree(cfg); 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_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); - - return -ENOENT; -} diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..246f135 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/ecam.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..b14fcd3 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/ecam.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..55fbd18 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -13,6 +13,7 @@ * */
+#include <linux/ecam.h> #include <linux/pci.h> #include <asm/pci_x86.h>
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 7ea903d..e660805 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o +obj-$(CONFIG_PCI_MMCONFIG) += mcfg.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c new file mode 100644 index 0000000..5ecef20 --- /dev/null +++ b/drivers/acpi/mcfg.c @@ -0,0 +1,59 @@ +/* + * MCFG ACPI table parser. + * + * 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. + * + */ + +#include <linux/acpi.h> +#include <linux/ecam.h> + +#include <asm/pci_x86.h> /* Temp hack before refactoring arch-specific calls */ + +#define PREFIX "MCFG: " + +int __init acpi_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 "MCFG table 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; +} diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 73de4ef..9950248 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -26,6 +26,13 @@ config PCI_MSI_IRQ_DOMAIN depends on PCI_MSI select GENERIC_MSI_IRQ_DOMAIN
+config PCI_ECAM + bool "Enhanced Configuration Access Mechanism (ECAM)" + depends on PCI && HAVE_PCI_ECAM + +config HAVE_PCI_ECAM + bool + config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index be3f631..eb574f8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -42,6 +42,11 @@ obj-$(CONFIG_SPARC_LEON) += setup-irq.o obj-$(CONFIG_M68K) += setup-irq.o
# +# Enhanced Configuration Access Mechanism (ECAM) +# +obj-$(CONFIG_PCI_ECAM) += ecam.o + +# # ACPI Related PCI FW Functions # ACPI _DSM provided firmware instance and string name # diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c new file mode 100644 index 0000000..d221dba --- /dev/null +++ b/drivers/pci/ecam.c @@ -0,0 +1,175 @@ +/* + * Arch agnostic direct PCI config space access via + * ECAM (Enhanced Configuration Access Mechanism) + * + * Per-architecture code takes care of the mappings, region validation and + * accesses themselves. + * + * 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. + * + */ + +#include <linux/mutex.h> +#include <linux/rculist.h> +#include <linux/ecam.h> + +#include <asm/io.h> +#include <asm/pci_x86.h> /* Temp hack before refactoring arch-specific calls */ + +#define PREFIX "PCI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); + +LIST_HEAD(pci_mmcfg_list); + +static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) +{ + if (cfg->res.parent) + release_resource(&cfg->res); + list_del(&cfg->list); + kfree(cfg); +} + +void __init free_all_mmcfg(void) +{ + struct pci_mmcfg_region *cfg, *tmp; + + pci_mmcfg_arch_free(); + list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) + pci_mmconfig_remove(cfg); +} + +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); +} + +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; +} + +/* 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_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); + + return -ENOENT; +} + +int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) +{ + struct pci_mmcfg_region *cfg_conflict; + int err = 0; + + mutex_lock(&pci_mmcfg_lock); + cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); + if (cfg_conflict) { + if (cfg_conflict->end_bus < cfg->end_bus) + pr_info(FW_INFO "MMCONFIG for " + "domain %04x [bus %02x-%02x] " + "only partially covers this bridge\n", + cfg_conflict->segment, cfg_conflict->start_bus, + cfg_conflict->end_bus); + err = -EEXIST; + goto out; + } + + if (pci_mmcfg_arch_map(cfg)) { + pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); + err = -ENOMEM; + goto out; + } else { + list_add_sorted(cfg); + pr_info("MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)cfg->address); + + } +out: + mutex_unlock(&pci_mmcfg_lock); + return err; +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7494dbe..6785ebb 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -20,6 +20,7 @@ #include <linux/pci.h> #include <linux/acpi.h> #include <linux/pci-acpi.h> +#include <linux/ecam.h> #include <xen/xen.h> #include <xen/interface/physdev.h> #include <xen/interface/xen.h> diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 06ed7e5..c8130b3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -237,6 +237,8 @@ int acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries); int acpi_parse_mcfg (struct acpi_table_header *header); +int acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg); void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
/* the following four functions are architecture-dependent */ diff --git a/include/linux/ecam.h b/include/linux/ecam.h new file mode 100644 index 0000000..dec3b52 --- /dev/null +++ b/include/linux/ecam.h @@ -0,0 +1,37 @@ +#ifndef __ECAM_H +#define __ECAM_H +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <linux/acpi.h> + +/* "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 *pci_mmconfig_lookup(int segment, int bus); +struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr); +int pci_mmconfig_inject(struct pci_mmcfg_region *cfg); +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +void list_add_sorted(struct pci_mmcfg_region *new); +void free_all_mmcfg(void); +int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) + +#endif /* __KERNEL__ */ +#endif /* __ECAM_H */
First function acpi_mcfg_check_entry() does not apply any quirks by default.
Last two functions are required by ACPI subsystem to make PCI config space accessible. Generic code assume to do nothing for early init call but late init call does as follow: - parse MCFG table and add regions to ECAM resource list - map regions - add regions to iomem_resource
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/mcfg.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 5ecef20..fad9917 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -57,3 +57,29 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header)
return 0; } + +int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + return 0; +} + +void __init __weak pci_mmcfg_early_init(void) +{ + +} + +void __init __weak pci_mmcfg_late_init(void) +{ + struct pci_mmcfg_region *cfg; + + acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); + + if (list_empty(&pci_mmcfg_list)) + return; + if (!pci_mmcfg_arch_init()) + free_all_mmcfg(); + + list_for_each_entry(cfg, &pci_mmcfg_list, list) + insert_resource(&iomem_resource, &cfg->res); +}
mmconfig_64.c version is going to be default implementation for low-level operation on mmconfig regions. However, now it initializes raw_pci_ext_ops pointer which is specific for x86 only. Moreover, mmconfig_32.c is doing the same thing at the same time. So lets move it to mmconfig_shared.c so it becomes common for both and mmconfig_64.c turns out to be purely arch agnostic.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/include/asm/pci_x86.h | 5 +++++ arch/x86/pci/mmconfig-shared.c | 10 ++++++++-- arch/x86/pci/mmconfig_32.c | 10 ++-------- arch/x86/pci/mmconfig_64.c | 11 ++--------- 4 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 0281d2d..26e7dd4 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -129,6 +129,11 @@ 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);
+int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, + int reg, int len, u32 *value); +int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, + int reg, int len, u32 value); + /* * On AMD Fam10h CPUs, all PCI MMIO configuration space accesses must use * %eax. No other source or target registers may be used. The following diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index ce2c2e4..980f304 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -29,6 +29,11 @@ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed;
+const struct pci_raw_ops pci_mmcfg = { + .read = pci_mmcfg_read, + .write = pci_mmcfg_write, +}; + static const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -512,9 +517,10 @@ static void __init __pci_mmcfg_init(int early) } }
- if (pci_mmcfg_arch_init()) + if (pci_mmcfg_arch_init()) { + raw_pci_ext_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; - else { + } else { free_all_mmcfg(); pci_mmcfg_arch_init_failed = true; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 246f135..2ded56f 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -50,7 +50,7 @@ static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) } }
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus, +int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; @@ -89,7 +89,7 @@ err: *value = -1; return 0; }
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus, +int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; @@ -126,15 +126,9 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, return 0; }
-const struct pci_raw_ops pci_mmcfg = { - .read = pci_mmcfg_read, - .write = pci_mmcfg_write, -}; - int __init pci_mmcfg_arch_init(void) { printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n"); - raw_pci_ext_ops = &pci_mmcfg; return 1; }
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index b14fcd3..d0c48eb 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -25,7 +25,7 @@ static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned i return NULL; }
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus, +int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { char __iomem *addr; @@ -59,7 +59,7 @@ err: *value = -1; return 0; }
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus, +int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { char __iomem *addr; @@ -91,11 +91,6 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, return 0; }
-const struct pci_raw_ops pci_mmcfg = { - .read = pci_mmcfg_read, - .write = pci_mmcfg_write, -}; - static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg) { void __iomem *addr; @@ -121,8 +116,6 @@ int __init pci_mmcfg_arch_init(void) return 0; }
- raw_pci_ext_ops = &pci_mmcfg; - return 1; }
mmconfig_64.c functions responsible for ECAM regions mapping are generic enough to use them for other architectures, GENERIC_ECAM_MAP should be selected in that case (like 64bit x86). Some hosts may need arch-specific mapping logic, thus they are obligated to provide own implementation and resign GENERIC_ECAM_MAP feature (like 32bit x86 does).
Note, we leaved x86-specific PCI config accessors in the corresponding files.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/Kconfig | 1 + arch/x86/include/asm/pci_x86.h | 4 --- arch/x86/pci/mmconfig_64.c | 55 --------------------------------------- drivers/acpi/mcfg.c | 2 -- drivers/pci/Kconfig | 3 +++ drivers/pci/ecam.c | 59 +++++++++++++++++++++++++++++++++++++++++- include/linux/ecam.h | 6 +++++ 7 files changed, 68 insertions(+), 62 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a74acce..15674b9 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -65,6 +65,7 @@ config X86 select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE select GENERIC_EARLY_IOREMAP + select GENERIC_ECAM_MAP if X86_64 select GENERIC_FIND_FIRST_BIT select GENERIC_IOMAP select GENERIC_IRQ_PROBE diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 26e7dd4..6e14518 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,10 +122,6 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void);
/* pci-mmconfig.c */ -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);
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index d0c48eb..fd356cc 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -90,58 +90,3 @@ int pci_mmcfg_write(unsigned int seg, unsigned int bus,
return 0; } - -static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg) -{ - void __iomem *addr; - u64 start, size; - int num_buses; - - start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); - num_buses = cfg->end_bus - cfg->start_bus + 1; - size = PCI_MMCFG_BUS_OFFSET(num_buses); - addr = ioremap_nocache(start, size); - if (addr) - addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); - return addr; -} - -int __init pci_mmcfg_arch_init(void) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry(cfg, &pci_mmcfg_list, list) - if (pci_mmcfg_arch_map(cfg)) { - pci_mmcfg_arch_free(); - return 0; - } - - return 1; -} - -void __init pci_mmcfg_arch_free(void) -{ - struct pci_mmcfg_region *cfg; - - list_for_each_entry(cfg, &pci_mmcfg_list, list) - pci_mmcfg_arch_unmap(cfg); -} - -int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) -{ - cfg->virt = mcfg_ioremap(cfg); - if (!cfg->virt) { - pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res); - return -ENOMEM; - } - - return 0; -} - -void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) -{ - if (cfg && cfg->virt) { - iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); - cfg->virt = NULL; - } -} diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index fad9917..745b83e 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -10,8 +10,6 @@ #include <linux/acpi.h> #include <linux/ecam.h>
-#include <asm/pci_x86.h> /* Temp hack before refactoring arch-specific calls */ - #define PREFIX "MCFG: "
int __init acpi_parse_mcfg(struct acpi_table_header *header) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 9950248..dd26f39 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -33,6 +33,9 @@ config PCI_ECAM config HAVE_PCI_ECAM bool
+config GENERIC_ECAM_MAP + bool + config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index d221dba..7bc75af 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -16,7 +16,6 @@ #include <linux/ecam.h>
#include <asm/io.h> -#include <asm/pci_x86.h> /* Temp hack before refactoring arch-specific calls */
#define PREFIX "PCI: "
@@ -24,6 +23,64 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+#ifdef CONFIG_GENERIC_ECAM_MAP + +static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg) +{ + void __iomem *addr; + u64 start, size; + int num_buses; + + start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); + num_buses = cfg->end_bus - cfg->start_bus + 1; + size = PCI_MMCFG_BUS_OFFSET(num_buses); + addr = ioremap_nocache(start, size); + if (addr) + addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); + return addr; +} + +int __init pci_mmcfg_arch_init(void) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry(cfg, &pci_mmcfg_list, list) + if (pci_mmcfg_arch_map(cfg)) { + pci_mmcfg_arch_free(); + return 0; + } + + return 1; +} + +void __init pci_mmcfg_arch_free(void) +{ + struct pci_mmcfg_region *cfg; + + list_for_each_entry(cfg, &pci_mmcfg_list, list) + pci_mmcfg_arch_unmap(cfg); +} + +int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) +{ + cfg->virt = mcfg_ioremap(cfg); + if (!cfg->virt) { + pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res); + return -ENOMEM; + } + + return 0; +} + +void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) +{ + if (cfg && cfg->virt) { + iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); + cfg->virt = NULL; + } +} +#endif /* CONFIG_GENERIC_ECAM_MAP */ + static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { if (cfg->res.parent) diff --git a/include/linux/ecam.h b/include/linux/ecam.h index dec3b52..813acd1 100644 --- a/include/linux/ecam.h +++ b/include/linux/ecam.h @@ -29,6 +29,12 @@ void list_add_sorted(struct pci_mmcfg_region *new); void free_all_mmcfg(void); int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+/* Arch specific calls */ +int pci_mmcfg_arch_init(void); +void pci_mmcfg_arch_free(void); +int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); +void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); + extern struct list_head pci_mmcfg_list;
#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
In drivers/xen/pci.c, there are arch x86 dependent codes when CONFIG_PCI_MMCONFIG is enabled, since CONFIG_PCI_MMCONFIG depends on ACPI, so this will prevent XEN PCI running on other architectures using ACPI with PCI_MMCONFIG enabled (such as ARM64).
Fortunatly, it can be sloved in a simple way. In drivers/xen/pci.c, the only x86 dependent code is if ((pci_probe & PCI_PROBE_MMCONF) == 0), and it's defined in asm/pci_x86.h, the code means that if the PCI resource is not probed in PCI_PROBE_MMCONF way, just ingnore the xen mcfg init. Actually this is duplicate, because if PCI resource is not probed in PCI_PROBE_MMCONF way, the pci_mmconfig_list will be empty, and the if (list_empty()) after it will do the same job.
So just remove the arch related code and the head file, this will be no functional change for x86, and also makes xen/pci.c usable for other architectures.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org CC: Konrad Rzeszutek Wilk konrad.wilk@oracle.com CC: Boris Ostrovsky boris.ostrovsky@oracle.com Cc: Stefano Stabellini Stefano.Stabellini@eu.citrix.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org Reviewed-by: Boris Ostrovsky boris.ostrovsky@oracle.com Acked-by: Stefano Stabellini stefano.stabellini@eu.citrix.com --- drivers/xen/pci.c | 6 ------ 1 file changed, 6 deletions(-)
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 6785ebb..9a8dbe3 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -28,9 +28,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;
@@ -222,9 +219,6 @@ static int __init xen_mcfg_late(void) if (!xen_initial_domain()) return 0;
- if ((pci_probe & PCI_PROBE_MMCONF) == 0) - return 0; - if (list_empty(&pci_mmcfg_list)) return 0;
Lets keep RAW ACPI PCI config space accessors empty by default, since we are note sure if they are necessary accross all archs. Once we sort this out, we can provide generic version or let architectures to overwrite, like now x86.
Suggested-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/mcfg.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 745b83e..3e1e7be 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -9,9 +9,30 @@
#include <linux/acpi.h> #include <linux/ecam.h> +#include <linux/pci.h>
#define PREFIX "MCFG: "
+/* + * raw_pci_read/write - raw ACPI PCI config space accessors. + * + * By defauly (__weak) these accessors are empty and should be overwritten + * by architectures which support operations on ACPI PCI_Config regions, + * see osl.c file. + */ + +int __weak raw_pci_read(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int __weak raw_pci_write(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + return PCIBIOS_DEVICE_NOT_FOUND; +} + int __init acpi_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg;
We can now enable MCFG driver and its GENERIC_ECAM_MAP feature. Currently there is no RAW pci config accessor use case for ARM64, so lets use empty one for now. At the same time we can cleanup the old implementation of RAW accessors from arch/arm64/kernel/pci.c
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/arm64/Kconfig | 7 +++++++ arch/arm64/kernel/pci.c | 15 --------------- 2 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 8cc6228..4f45ea4 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -34,6 +34,7 @@ config ARM64 select GENERIC_CLOCKEVENTS_BROADCAST select GENERIC_CPU_AUTOPROBE select GENERIC_EARLY_IOREMAP + select GENERIC_ECAM_MAP select GENERIC_IDLE_POLL_SETUP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW @@ -96,6 +97,7 @@ config ARM64 select SYSCTL_EXCEPTION_TRACE select HAVE_CONTEXT_TRACKING select HAVE_ARM_SMCCC + select HAVE_PCI_ECAM help ARM 64-bit (AArch64) Linux support.
@@ -238,6 +240,11 @@ source "drivers/pci/Kconfig" source "drivers/pci/pcie/Kconfig" source "drivers/pci/hotplug/Kconfig"
+config PCI_MMCONFIG + def_bool y + select PCI_ECAM + depends on ACPI + endmenu
menu "Kernel Features" diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index b3d098b..023b983 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -61,21 +61,6 @@ int pcibios_add_device(struct pci_dev *dev) return 0; }
-/* - * raw_pci_read/write - Platform-specific PCI config space access. - */ -int 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, - unsigned int devfn, int reg, int len, u32 val) -{ - return -ENXIO; -} - #ifdef CONFIG_ACPI /* Root bridge scanning */ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
There are two ways we can get ECAM (aka MCFG) regions using ACPI, from MCFG static table and from _CBA method. We cannot remove static regions, however regions coming from _CBA should be removed while removing bridge device.
In the light of above we need flag to mark hot added ECAM entries so that user should use pci_mmconfig_inject while adding regions from _CBA method.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/pci/ecam.c | 2 ++ include/linux/ecam.h | 1 + 2 files changed, 3 insertions(+)
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index 7bc75af..fea5700 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -131,6 +131,7 @@ struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, new->segment = segment; new->start_bus = start; new->end_bus = end; + new->hot_added = false;
res = &new->res; res->start = addr + PCI_MMCFG_BUS_OFFSET(start); @@ -221,6 +222,7 @@ int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) err = -ENOMEM; goto out; } else { + cfg->hot_added = true; list_add_sorted(cfg); pr_info("MMCONFIG at %pR (base %#lx)\n", &cfg->res, (unsigned long)cfg->address); diff --git a/include/linux/ecam.h b/include/linux/ecam.h index 813acd1..e0f322e 100644 --- a/include/linux/ecam.h +++ b/include/linux/ecam.h @@ -17,6 +17,7 @@ struct pci_mmcfg_region { u8 start_bus; u8 end_bus; char name[PCI_MMCFG_RESOURCE_NAME_LEN]; + bool hot_added; };
struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
Because of two facts: 1. MCFG region has hot_added flag 2. struct acpi_pci_root has topology info (domain, bus number) we can get rid of arch specific mcfg data from x86 struct pci_root_info.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/pci/acpi.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-)
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index a997d7c..8e89921 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -12,11 +12,6 @@ struct pci_root_info { struct acpi_pci_root_info common; struct pci_sysdata sd; -#ifdef CONFIG_PCI_MMCONFIG - bool mcfg_added; - u8 start_bus; - u8 end_bus; -#endif };
static bool pci_use_crs = true; @@ -180,16 +175,13 @@ static int check_segment(u16 seg, struct device *dev, char *estr)
static int setup_mcfg_map(struct acpi_pci_root_info *ci) { - int result, seg; - struct pci_root_info *info; + int result, seg, start, end; struct acpi_pci_root *root = ci->root; struct device *dev = &ci->bridge->dev;
- info = container_of(ci, struct pci_root_info, common); - info->start_bus = (u8)root->secondary.start; - info->end_bus = (u8)root->secondary.end; - info->mcfg_added = false; - seg = info->sd.domain; + seg = root->segment; + start = root->secondary.start; + end = root->secondary.end;
/* return success if MMCFG is not in use */ if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg) @@ -198,13 +190,11 @@ static int setup_mcfg_map(struct acpi_pci_root_info *ci) if (!(pci_probe & PCI_PROBE_MMCONF)) return check_segment(seg, dev, "MMCONFIG is disabled,");
- result = pci_mmconfig_insert(dev, seg, info->start_bus, info->end_bus, - root->mcfg_addr); + result = pci_mmconfig_insert(dev, seg, start, end, root->mcfg_addr); if (result == 0) { /* enable MMCFG if it hasn't been enabled yet */ if (raw_pci_ext_ops == NULL) raw_pci_ext_ops = &pci_mmcfg; - info->mcfg_added = true; } else if (result != -EEXIST) return check_segment(seg, dev, "fail to add MMCONFIG information,"); @@ -214,14 +204,16 @@ static int setup_mcfg_map(struct acpi_pci_root_info *ci)
static void teardown_mcfg_map(struct acpi_pci_root_info *ci) { - struct pci_root_info *info; + struct acpi_pci_root *root = ci->root; + struct pci_mmcfg_region *cfg;
- info = container_of(ci, struct pci_root_info, common); - if (info->mcfg_added) { - pci_mmconfig_delete(info->sd.domain, - info->start_bus, info->end_bus); - info->mcfg_added = false; - } + cfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + if (!cfg) + return; + + if (cfg->hot_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); } #else static int setup_mcfg_map(struct acpi_pci_root_info *ci)
Currently we have two platforms (x86 & ia64) capable of PCI ACPI host bridge initialization. They both use sysdata to pass down parent device reference and both rely on NULL parent in pci_create_root_bus() to validate sysdata content.
It looks hacky and prevents us from getting some firmware specific info for PCI host controller e.g. PCI bus domain number. However, we overcome that blocker by passing down parent device via pci_create_root_bus parameter (as the ACPI device type) and using ACPI_COMPANION_SET in core code for ACPI boot method. ACPI_COMPANION_SET is safe to run for all cases DT, ACPI and DT&ACPI.
Suggested-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/pci_root.c | 5 ++++- drivers/pci/probe.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..a65c8c2 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -846,7 +846,10 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
pci_acpi_root_add_resources(info); pci_add_resource(&info->resources, &root->secondary); - bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, + + /* Root bridge device needs to be sure of parent ACPI type */ + ACPI_COMPANION_SET(&device->dev, device); + bus = pci_create_root_bus(&device->dev, busnum, ops->pci_ops, sysdata, &info->resources); if (!bus) goto out_release_info; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6d7ab9b..81dd3a2 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2100,6 +2100,8 @@ 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); + ACPI_COMPANION_SET(&bridge->dev, + parent ? to_acpi_device_node(parent->fwnode) : NULL); error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge);
On Thu, Feb 04, 2016 at 06:28:49PM +0100, Tomasz Nowicki wrote:
Currently we have two platforms (x86 & ia64) capable of PCI ACPI host bridge initialization. They both use sysdata to pass down parent device reference and both rely on NULL parent in pci_create_root_bus() to validate sysdata content.
It looks hacky and prevents us from getting some firmware specific info for PCI host controller e.g. PCI bus domain number. However, we overcome that blocker by passing down parent device via pci_create_root_bus parameter (as the ACPI device type) and using ACPI_COMPANION_SET in core code for ACPI boot method. ACPI_COMPANION_SET is safe to run for all cases DT, ACPI and DT&ACPI.
Suggested-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
drivers/acpi/pci_root.c | 5 ++++- drivers/pci/probe.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..a65c8c2 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -846,7 +846,10 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, pci_acpi_root_add_resources(info); pci_add_resource(&info->resources, &root->secondary);
- bus = pci_create_root_bus(NULL, busnum, ops->pci_ops,
- /* Root bridge device needs to be sure of parent ACPI type */
You can improve the comment.
"pci_create_root_bus() needs to detect the parent device type, so initialize its companion data accordingly".
- ACPI_COMPANION_SET(&device->dev, device);
- bus = pci_create_root_bus(&device->dev, busnum, ops->pci_ops, sysdata, &info->resources); if (!bus) goto out_release_info;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6d7ab9b..81dd3a2 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2100,6 +2100,8 @@ 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);
- ACPI_COMPANION_SET(&bridge->dev,
parent ? to_acpi_device_node(parent->fwnode) : NULL);
if (parent) ACPI_COMPANION_SET(&bridge->dev, ACPI_COMPANION(parent));
?
It seems fine to me, hopefully Rafael can have a look to countercheck.
Reviewed-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
error = pcibios_root_bridge_prepare(bridge); if (error) { kfree(bridge); -- 1.9.1
On 02/09/2016 07:02 PM, Lorenzo Pieralisi wrote:
On Thu, Feb 04, 2016 at 06:28:49PM +0100, Tomasz Nowicki wrote:
Currently we have two platforms (x86 & ia64) capable of PCI ACPI host bridge initialization. They both use sysdata to pass down parent device reference and both rely on NULL parent in pci_create_root_bus() to validate sysdata content.
It looks hacky and prevents us from getting some firmware specific info for PCI host controller e.g. PCI bus domain number. However, we overcome that blocker by passing down parent device via pci_create_root_bus parameter (as the ACPI device type) and using ACPI_COMPANION_SET in core code for ACPI boot method. ACPI_COMPANION_SET is safe to run for all cases DT, ACPI and DT&ACPI.
Suggested-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
drivers/acpi/pci_root.c | 5 ++++- drivers/pci/probe.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ae3fe4e..a65c8c2 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -846,7 +846,10 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, pci_acpi_root_add_resources(info); pci_add_resource(&info->resources, &root->secondary);
- bus = pci_create_root_bus(NULL, busnum, ops->pci_ops,
- /* Root bridge device needs to be sure of parent ACPI type */
You can improve the comment.
"pci_create_root_bus() needs to detect the parent device type, so initialize its companion data accordingly".
Agree, I will change comment.
- ACPI_COMPANION_SET(&device->dev, device);
- bus = pci_create_root_bus(&device->dev, busnum, ops->pci_ops, sysdata, &info->resources); if (!bus) goto out_release_info;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6d7ab9b..81dd3a2 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2100,6 +2100,8 @@ 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);
- ACPI_COMPANION_SET(&bridge->dev,
parent ? to_acpi_device_node(parent->fwnode) : NULL);
if (parent) ACPI_COMPANION_SET(&bridge->dev, ACPI_COMPANION(parent));
?
Looks better.
It seems fine to me, hopefully Rafael can have a look to countercheck.
Reviewed-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
Thanks, Tomasz
Since PCI core code is setting ACPI companion device for us now, platform specific ACPI companion device setting turns out to be dead now. Therefore we can get rid of it, including related companion reference from PCI sysdata structure.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/ia64/hp/common/sba_iommu.c | 2 +- arch/ia64/include/asm/pci.h | 1 - arch/ia64/pci/pci.c | 16 ---------------- arch/ia64/sn/kernel/io_acpi_init.c | 4 ++-- arch/x86/include/asm/pci.h | 3 --- arch/x86/pci/acpi.c | 17 ----------------- 6 files changed, 3 insertions(+), 40 deletions(-)
diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index a6d6190..78e4444 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -1981,7 +1981,7 @@ sba_connect_bus(struct pci_bus *bus) if (PCI_CONTROLLER(bus)->iommu) return;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion); + handle = acpi_device_handle(ACPI_COMPANION(bus->bridge)); if (!handle) return;
diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 07039d1..5050748 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -65,7 +65,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define pci_legacy_write platform_pci_legacy_write
struct pci_controller { - struct acpi_device *companion; void *iommu; int segment; int node; /* nearest node with memory or NUMA_NO_NODE for global allocation */ diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 8f6ac2f..978d6af 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -301,28 +301,12 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) }
info->controller.segment = root->segment; - info->controller.companion = device; info->controller.node = acpi_get_node(device->handle); INIT_LIST_HEAD(&info->io_resources); return acpi_pci_root_create(root, &pci_acpi_root_ops, &info->common, &info->controller); }
-int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -{ - /* - * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL - * here, pci_create_root_bus() has been called by someone else and - * sysdata is likely to be different from what we expect. Let it go in - * that case. - */ - if (!bridge->dev.parent) { - struct pci_controller *controller = bridge->bus->sysdata; - ACPI_COMPANION_SET(&bridge->dev, controller->companion); - } - return 0; -} - void pcibios_fixup_device_resources(struct pci_dev *dev) { int idx; diff --git a/arch/ia64/sn/kernel/io_acpi_init.c b/arch/ia64/sn/kernel/io_acpi_init.c index 0640739..bcfddc2 100644 --- a/arch/ia64/sn/kernel/io_acpi_init.c +++ b/arch/ia64/sn/kernel/io_acpi_init.c @@ -132,7 +132,7 @@ sn_get_bussoft_ptr(struct pci_bus *bus) struct acpi_resource_vendor_typed *vendor;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion); + handle = acpi_device_handle(ACPI_COMPANION(bus->bridge)); status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS, &sn_uuid, &buffer); if (ACPI_FAILURE(status)) { @@ -360,7 +360,7 @@ sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info, acpi_status status; struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- rootbus_handle = acpi_device_handle(PCI_CONTROLLER(dev)->companion); + rootbus_handle = acpi_device_handle(ACPI_COMPANION(dev->bus->bridge)); status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL, &segment); if (ACPI_SUCCESS(status)) { diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 4625943..a98c022 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -14,9 +14,6 @@ struct pci_sysdata { int domain; /* PCI domain */ int node; /* NUMA node */ -#ifdef CONFIG_ACPI - struct acpi_device *companion; /* ACPI companion device */ -#endif #ifdef CONFIG_X86_64 void *iommu; /* IOMMU private data */ #endif diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 8e89921..d5086d1 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -341,7 +341,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) struct pci_sysdata sd = { .domain = domain, .node = node, - .companion = root->device };
memcpy(bus->sysdata, &sd, sizeof(sd)); @@ -356,7 +355,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) else { info->sd.domain = domain; info->sd.node = node; - info->sd.companion = root->device; bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &info->common, &info->sd); } @@ -374,21 +372,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) return bus; }
-int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -{ - /* - * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL - * here, pci_create_root_bus() has been called by someone else and - * sysdata is likely to be different from what we expect. Let it go in - * that case. - */ - if (!bridge->dev.parent) { - struct pci_sysdata *sd = bridge->bus->sysdata; - ACPI_COMPANION_SET(&bridge->dev, sd->companion); - } - return 0; -} - int __init pci_acpi_init(void) { struct pci_dev *dev = NULL;
Tomasz,
On Thu, Feb 04, 2016 at 06:28:50PM +0100, Tomasz Nowicki wrote:
Since PCI core code is setting ACPI companion device for us now, platform specific ACPI companion device setting turns out to be dead now. Therefore we can get rid of it, including related companion reference from PCI sysdata structure.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
arch/ia64/hp/common/sba_iommu.c | 2 +- arch/ia64/include/asm/pci.h | 1 - arch/ia64/pci/pci.c | 16 ---------------- arch/ia64/sn/kernel/io_acpi_init.c | 4 ++-- arch/x86/include/asm/pci.h | 3 --- arch/x86/pci/acpi.c | 17 ----------------- 6 files changed, 3 insertions(+), 40 deletions(-)
This patch has to be squashed with the previous one otherwise it has bisectability issues (ie the previous patch passes the parent to the pci_create_root_bus() call so, without this patch applied, the companion initialization would fail).
Other than that:
Reviewed-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index a6d6190..78e4444 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -1981,7 +1981,7 @@ sba_connect_bus(struct pci_bus *bus) if (PCI_CONTROLLER(bus)->iommu) return;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
- handle = acpi_device_handle(ACPI_COMPANION(bus->bridge)); if (!handle) return;
diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 07039d1..5050748 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -65,7 +65,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define pci_legacy_write platform_pci_legacy_write struct pci_controller {
- struct acpi_device *companion; void *iommu; int segment; int node; /* nearest node with memory or NUMA_NO_NODE for global allocation */
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 8f6ac2f..978d6af 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -301,28 +301,12 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) } info->controller.segment = root->segment;
- info->controller.companion = device; info->controller.node = acpi_get_node(device->handle); INIT_LIST_HEAD(&info->io_resources); return acpi_pci_root_create(root, &pci_acpi_root_ops, &info->common, &info->controller);
} -int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -{
- /*
* We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
* here, pci_create_root_bus() has been called by someone else and
* sysdata is likely to be different from what we expect. Let it go in
* that case.
*/
- if (!bridge->dev.parent) {
struct pci_controller *controller = bridge->bus->sysdata;
ACPI_COMPANION_SET(&bridge->dev, controller->companion);
- }
- return 0;
-}
void pcibios_fixup_device_resources(struct pci_dev *dev) { int idx; diff --git a/arch/ia64/sn/kernel/io_acpi_init.c b/arch/ia64/sn/kernel/io_acpi_init.c index 0640739..bcfddc2 100644 --- a/arch/ia64/sn/kernel/io_acpi_init.c +++ b/arch/ia64/sn/kernel/io_acpi_init.c @@ -132,7 +132,7 @@ sn_get_bussoft_ptr(struct pci_bus *bus) struct acpi_resource_vendor_typed *vendor;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
- handle = acpi_device_handle(ACPI_COMPANION(bus->bridge)); status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS, &sn_uuid, &buffer); if (ACPI_FAILURE(status)) {
@@ -360,7 +360,7 @@ sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info, acpi_status status; struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- rootbus_handle = acpi_device_handle(PCI_CONTROLLER(dev)->companion);
- rootbus_handle = acpi_device_handle(ACPI_COMPANION(dev->bus->bridge)); status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL, &segment); if (ACPI_SUCCESS(status)) {
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 4625943..a98c022 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -14,9 +14,6 @@ struct pci_sysdata { int domain; /* PCI domain */ int node; /* NUMA node */ -#ifdef CONFIG_ACPI
- struct acpi_device *companion; /* ACPI companion device */
-#endif #ifdef CONFIG_X86_64 void *iommu; /* IOMMU private data */ #endif diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 8e89921..d5086d1 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -341,7 +341,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) struct pci_sysdata sd = { .domain = domain, .node = node,
};.companion = root->device
memcpy(bus->sysdata, &sd, sizeof(sd)); @@ -356,7 +355,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) else { info->sd.domain = domain; info->sd.node = node;
}info->sd.companion = root->device; bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &info->common, &info->sd);
@@ -374,21 +372,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) return bus; } -int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -{
- /*
* We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
* here, pci_create_root_bus() has been called by someone else and
* sysdata is likely to be different from what we expect. Let it go in
* that case.
*/
- if (!bridge->dev.parent) {
struct pci_sysdata *sd = bridge->bus->sysdata;
ACPI_COMPANION_SET(&bridge->dev, sd->companion);
- }
- return 0;
-}
int __init pci_acpi_init(void) { struct pci_dev *dev = NULL; -- 1.9.1
On 02/09/2016 06:28 PM, Lorenzo Pieralisi wrote:
This patch has to be squashed with the previous one otherwise it has bisectability issues (ie the previous patch passes the parent to the pci_create_root_bus() call so, without this patch applied, the companion initialization would fail).
I do not think so. The previous patch passes the parent to the pci_create_root_bus(), indeed, so pci_create_root_bus() set companion device before calling pcibios_root_bridge_prepare() (for both cases x86 and ia64). In turn, pcibios_root_bridge_prepare() will do nothing, since bridge->dev.parent != NULL. After all, pcibios_root_bridge_prepare() is dead code.
Other than that:
Reviewed-by: Lorenzo Pieralisilorenzo.pieralisi@arm.com
Thanks!
Tomasz
As we now have valid PCI host bridge device reference we can introduce code that is going to find its bus domain number using ACPI _SEG method.
Note that _SEG method is optional, therefore _SEG absence means that all PCI buses belong to domain 0.
While at it, for the sake of code clarity we put ACPI and DT domain assign methods into the corresponding helpers.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Reviewed-by: Liviu Dudau Liviu.Dudau@arm.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/pci_root.c | 18 ++++++++++++++++++ drivers/pci/pci.c | 11 +++++++++-- include/linux/pci-acpi.h | 2 ++ 3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index a65c8c2..cc2c73a 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -419,6 +419,24 @@ out: } EXPORT_SYMBOL(acpi_pci_osc_control_set);
+int acpi_pci_bus_domain_nr(struct device *parent) +{ + struct acpi_device *acpi_dev = to_acpi_device(parent); + unsigned long long segment = 0; + acpi_status status; + + /* + * If _SEG method does not exist, following ACPI spec (6.5.6) + * all PCI buses belong to domain 0. + */ + status = acpi_evaluate_integer(acpi_dev->handle, METHOD_NAME__SEG, NULL, + &segment); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) + dev_err(&acpi_dev->dev, "can't evaluate _SEG\n"); + + return segment; +} + static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) { u32 support, control, requested; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 602eb42..d6c768e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -19,6 +19,7 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/log2.h> +#include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/pm_wakeup.h> #include <linux/interrupt.h> @@ -4769,7 +4770,7 @@ int pci_get_new_domain_nr(void) }
#ifdef CONFIG_PCI_DOMAINS_GENERIC -void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +static int of_pci_bus_domain_nr(struct device *parent) { static int use_dt_domains = -1; int domain = of_get_pci_domain_nr(parent->of_node); @@ -4811,7 +4812,13 @@ void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) domain = -1; }
- bus->domain_nr = domain; + return domain; +} + +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +{ + bus->domain_nr = acpi_disabled ? of_pci_bus_domain_nr(parent) : + acpi_pci_bus_domain_nr(parent); } #endif #endif diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab057..a72e22d 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -22,6 +22,7 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) { return acpi_remove_pm_notifier(dev); } +extern int acpi_pci_bus_domain_nr(struct device *parent); extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) @@ -109,6 +110,7 @@ extern const u8 pci_acpi_dsm_uuid[]; #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) { } +static inline int acpi_pci_bus_domain_nr(struct device *parent) { return -1; } #endif /* CONFIG_ACPI */
#ifdef CONFIG_ACPI_APEI
x86 and ia64 are the only arches that implement pcibios_{add|remove}_bus hooks and implement them in the same way. Moreover ARM64 is going to do the same. So it seems that acpi_pci_{add|remove}_bus is generic enough to be default option for pcibios_{add|remove}_bus hooks. Also, it is always safe to run acpi_pci_{add|remove}_bus as they have empty stubs for !ACPI case and return if ACPI has been switched off in run time.
After all we can remove x86 and ia64 pcibios_{add|remove}_bus implementation.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Reviewed-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/ia64/pci/pci.c | 10 ---------- arch/x86/pci/common.c | 10 ---------- drivers/pci/probe.c | 3 +++ 3 files changed, 3 insertions(+), 20 deletions(-)
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 978d6af..be4c9ef 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -358,16 +358,6 @@ void pcibios_fixup_bus(struct pci_bus *b) platform_pci_fixup_bus(b); }
-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); -} - void pcibios_set_master (struct pci_dev *dev) { /* No special bus mastering setup handling */ diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 2879efc..5aa25f1 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -171,16 +171,6 @@ void pcibios_fixup_bus(struct pci_bus *b) pcibios_fixup_device_resources(dev); }
-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); -} - /* * Only use DMI information to set this if nothing was passed * on the kernel command line (which was parsed earlier). diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 81dd3a2..ef22a22 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/cpumask.h> +#include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/aer.h> #include <linux/acpi.h> @@ -2060,10 +2061,12 @@ int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
void __weak pcibios_add_bus(struct pci_bus *bus) { + acpi_pci_add_bus(bus); }
void __weak pcibios_remove_bus(struct pci_bus *bus) { + acpi_pci_remove_bus(bus); }
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
Lets abstract two calls which allow to inject and remove MCFG regions which may come from DSDT table. These calls will be used for x86 and ARM64 PCI host bridge driver in the later patches.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/mcfg.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 9 +++++++++ 2 files changed, 47 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 3e1e7be..dca4c4e 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/ecam.h> #include <linux/pci.h> +#include <linux/pci-acpi.h>
#define PREFIX "MCFG: "
@@ -77,6 +78,43 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) return 0; }
+int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) +{ + struct pci_mmcfg_region *cfg; + struct acpi_pci_root *root; + int seg, start, end, err; + + root = ci->root; + seg = root->segment; + start = root->secondary.start; + end = root->secondary.end; + + cfg = pci_mmconfig_lookup(seg, start); + if (cfg) + return 0; + + cfg = pci_mmconfig_alloc(seg, start, end, root->mcfg_addr); + if (!cfg) + return -ENOMEM; + + err = pci_mmconfig_inject(cfg); + return err; +} + +void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) +{ + struct acpi_pci_root *root = ci->root; + struct pci_mmcfg_region *cfg; + + cfg = pci_mmconfig_lookup(root->segment, root->secondary.start); + if (!cfg) + return; + + if (cfg->hot_added) + pci_mmconfig_delete(root->segment, root->secondary.start, + root->secondary.end); +} + int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, struct acpi_mcfg_allocation *cfg) { diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a72e22d..65b91f3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -80,6 +80,15 @@ extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus);
+#ifdef CONFIG_PCI_MMCONFIG +int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci); +void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci); +#else +static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) +{ return 0; } +static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { } +#endif + #ifdef CONFIG_ACPI_PCI_SLOT void acpi_pci_slot_init(void); void acpi_pci_slot_enumerate(struct pci_bus *bus);
On Thu, Feb 04, 2016 at 06:28:53PM +0100, Tomasz Nowicki wrote:
Lets abstract two calls which allow to inject and remove MCFG regions which may come from DSDT table. These calls will be used for x86 and ARM64 PCI host bridge driver in the later patches.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
drivers/acpi/mcfg.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 9 +++++++++ 2 files changed, 47 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 3e1e7be..dca4c4e 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/ecam.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #define PREFIX "MCFG: " @@ -77,6 +78,43 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) return 0; } +int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) +{
- struct pci_mmcfg_region *cfg;
- struct acpi_pci_root *root;
- int seg, start, end, err;
- root = ci->root;
- seg = root->segment;
- start = root->secondary.start;
- end = root->secondary.end;
- cfg = pci_mmconfig_lookup(seg, start);
- if (cfg)
return 0;
- cfg = pci_mmconfig_alloc(seg, start, end, root->mcfg_addr);
- if (!cfg)
return -ENOMEM;
- err = pci_mmconfig_inject(cfg);
- return err;
When you integrate Jayachandran's patch this whole function will become a pci_mmconfig_insert() and that's where hot_added should be set.
+}
+void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) +{
- struct acpi_pci_root *root = ci->root;
- struct pci_mmcfg_region *cfg;
- cfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
- if (!cfg)
return;
- if (cfg->hot_added)
Move the hot_added check in pci_mmconfig_delete() (that does the look up again), we do not want to carry out pci_mmconfig_lookup only to check that flag here (and we miss rcu locking for the look-up BTW).
Thanks, Lorenzo
pci_mmconfig_delete(root->segment, root->secondary.start,
root->secondary.end);
+}
int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, struct acpi_mcfg_allocation *cfg) { diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a72e22d..65b91f3 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -80,6 +80,15 @@ extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus); +#ifdef CONFIG_PCI_MMCONFIG +int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci); +void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci); +#else +static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) +{ return 0; } +static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { } +#endif
#ifdef CONFIG_ACPI_PCI_SLOT void acpi_pci_slot_init(void); void acpi_pci_slot_enumerate(struct pci_bus *bus); -- 1.9.1
On 10.02.2016 15:06, Lorenzo Pieralisi wrote:
On Thu, Feb 04, 2016 at 06:28:53PM +0100, Tomasz Nowicki wrote:
Lets abstract two calls which allow to inject and remove MCFG regions which may come from DSDT table. These calls will be used for x86 and ARM64 PCI host bridge driver in the later patches.
Signed-off-by: Tomasz Nowickitn@semihalf.com Tested-by: Duc Dangdhdang@apm.com Tested-by: Dongdong Liuliudongdong3@huawei.com Tested-by: Hanjun Guohanjun.guo@linaro.org Tested-by: Graeme Gregorygraeme.gregory@linaro.org Tested-by: Sinan Kayaokaya@codeaurora.org
drivers/acpi/mcfg.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 9 +++++++++ 2 files changed, 47 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 3e1e7be..dca4c4e 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/ecam.h> #include <linux/pci.h> +#include <linux/pci-acpi.h>
#define PREFIX "MCFG: "
@@ -77,6 +78,43 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) return 0; }
+int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) +{
- struct pci_mmcfg_region *cfg;
- struct acpi_pci_root *root;
- int seg, start, end, err;
- root = ci->root;
- seg = root->segment;
- start = root->secondary.start;
- end = root->secondary.end;
- cfg = pci_mmconfig_lookup(seg, start);
- if (cfg)
return 0;
- cfg = pci_mmconfig_alloc(seg, start, end, root->mcfg_addr);
- if (!cfg)
return -ENOMEM;
- err = pci_mmconfig_inject(cfg);
- return err;
When you integrate Jayachandran's patch this whole function will become a pci_mmconfig_insert() and that's where hot_added should be set.
+}
+void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) +{
- struct acpi_pci_root *root = ci->root;
- struct pci_mmcfg_region *cfg;
- cfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
- if (!cfg)
return;
- if (cfg->hot_added)
Move the hot_added check in pci_mmconfig_delete() (that does the look up again), we do not want to carry out pci_mmconfig_lookup only to check that flag here (and we miss rcu locking for the look-up BTW).
Makes sense to me, I will follow your suggestion.
Thanks, Tomasz
Since mcfg.c driver provides the same function lets use it and remove code duplication.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/x86/pci/acpi.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index d5086d1..b4e72b5 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -201,29 +201,11 @@ static int setup_mcfg_map(struct acpi_pci_root_info *ci)
return 0; } - -static void teardown_mcfg_map(struct acpi_pci_root_info *ci) -{ - struct acpi_pci_root *root = ci->root; - struct pci_mmcfg_region *cfg; - - cfg = pci_mmconfig_lookup(root->segment, root->secondary.start); - if (!cfg) - return; - - if (cfg->hot_added) - pci_mmconfig_delete(root->segment, root->secondary.start, - root->secondary.end); -} #else static int setup_mcfg_map(struct acpi_pci_root_info *ci) { return 0; } - -static void teardown_mcfg_map(struct acpi_pci_root_info *ci) -{ -} #endif
static int pci_acpi_root_get_node(struct acpi_pci_root *root) @@ -251,7 +233,7 @@ static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) { - teardown_mcfg_map(ci); + pci_mmcfg_teardown_map(ci); kfree(container_of(ci, struct pci_root_info, common)); }
We use generic accessors from access.c by default. However, we already know platforms that need special handling while accessing to PCI config space. These platforms will need different accessors set matched against platform ID, domain, bus touple. Therefore we are going to add (in future) DECLARE_ACPI_MCFG_FIXUP which will register platform specific custom accessors. For now, we let pci_mcfg_get_ops to take acpi_pci_root structure as an arguments and left some space for quirk matching algorithm.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/mcfg.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 8 ++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index dca4c4e..dfc2d14 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -34,6 +34,36 @@ int __weak raw_pci_write(unsigned int domain, unsigned int bus, return PCIBIOS_DEVICE_NOT_FOUND; }
+void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{ + struct pci_mmcfg_region *cfg; + + cfg = pci_mmconfig_lookup(pci_domain_nr(bus), bus->number); + if (cfg && cfg->virt) + return cfg->virt + + (PCI_MMCFG_BUS_OFFSET(bus->number) | (devfn << 12)) + + offset; + return NULL; +} + +/* Default generic PCI config accessors */ +static struct pci_ops default_pci_mcfg_ops = { + .map_bus = pci_mcfg_dev_base, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{ + /* + * TODO: Match against platform specific quirks and return + * corresponding PCI config space accessor set. + */ + + return &default_pci_mcfg_ops; +} + int __init acpi_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg; diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 65b91f3..c974586 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -83,10 +83,18 @@ void acpi_pci_remove_bus(struct pci_bus *bus); #ifdef CONFIG_PCI_MMCONFIG int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci); void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci); +struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); +void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset); #else static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) { return 0; } static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { } +static inline struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{ return NULL; } +static inline void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{ return NULL; } #endif
#ifdef CONFIG_ACPI_PCI_SLOT
On Thu, Feb 4, 2016 at 10:58 PM, Tomasz Nowicki tn@semihalf.com wrote:
We use generic accessors from access.c by default. However, we already know platforms that need special handling while accessing to PCI config space. These platforms will need different accessors set matched against platform ID, domain, bus touple. Therefore we are going to add (in future) DECLARE_ACPI_MCFG_FIXUP which will register platform specific custom accessors. For now, we let pci_mcfg_get_ops to take acpi_pci_root structure as an arguments and left some space for quirk matching algorithm.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
drivers/acpi/mcfg.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 8 ++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index dca4c4e..dfc2d14 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -34,6 +34,36 @@ int __weak raw_pci_write(unsigned int domain, unsigned int bus, return PCIBIOS_DEVICE_NOT_FOUND; }
+void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{
struct pci_mmcfg_region *cfg;
cfg = pci_mmconfig_lookup(pci_domain_nr(bus), bus->number);
In the existing code, calls to pci_mmconfig_lookup() is done inside an rcu_read_lock/rcu_read_unlock pair. Any reason that is not required here?
Also, you can avoid having to do the lookup every time by saving a cfg pointer see http://lists.infradead.org/pipermail/linux-arm-kernel/2016-January/396921.ht...
if (cfg && cfg->virt)
return cfg->virt +
(PCI_MMCFG_BUS_OFFSET(bus->number) | (devfn << 12)) +
offset;
PCI_MMCFG_OFFSET(bus->number, devfn) would be better
return NULL;
+}
+/* Default generic PCI config accessors */ +static struct pci_ops default_pci_mcfg_ops = {
.map_bus = pci_mcfg_dev_base,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
+};
+struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{
/*
* TODO: Match against platform specific quirks and return
* corresponding PCI config space accessor set.
*/
return &default_pci_mcfg_ops;
+}
Is it necessary to make these non-static, even in the next patch, the functions are only used from this file.
int __init acpi_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg; diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 65b91f3..c974586 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -83,10 +83,18 @@ void acpi_pci_remove_bus(struct pci_bus *bus); #ifdef CONFIG_PCI_MMCONFIG int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci); void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci); +struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); +void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset); #else static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) { return 0; } static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { } +static inline struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{ return NULL; } +static inline void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{ return NULL; } #endif #ifdef CONFIG_ACPI_PCI_SLOT
JC.
On Mon, Feb 29, 2016 at 01:33:41PM +0530, Jayachandran Chandrashekaran Nair wrote:
On Thu, Feb 4, 2016 at 10:58 PM, Tomasz Nowicki tn@semihalf.com wrote:
We use generic accessors from access.c by default. However, we already know platforms that need special handling while accessing to PCI config space. These platforms will need different accessors set matched against platform ID, domain, bus touple. Therefore we are going to add (in future) DECLARE_ACPI_MCFG_FIXUP which will register platform specific custom accessors. For now, we let pci_mcfg_get_ops to take acpi_pci_root structure as an arguments and left some space for quirk matching algorithm.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org
drivers/acpi/mcfg.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 8 ++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index dca4c4e..dfc2d14 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -34,6 +34,36 @@ int __weak raw_pci_write(unsigned int domain, unsigned int bus, return PCIBIOS_DEVICE_NOT_FOUND; }
+void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{
struct pci_mmcfg_region *cfg;
cfg = pci_mmconfig_lookup(pci_domain_nr(bus), bus->number);
In the existing code, calls to pci_mmconfig_lookup() is done inside an rcu_read_lock/rcu_read_unlock pair. Any reason that is not required here?
Also, you can avoid having to do the lookup every time by saving a cfg pointer see http://lists.infradead.org/pipermail/linux-arm-kernel/2016-January/396921.ht...
Yes, this is much better (and sysdata usage is self-contained, it does not trickle into other bits of the kernel - ie arch code), it should be integrated in the final version of the patchset.
if (cfg && cfg->virt)
return cfg->virt +
(PCI_MMCFG_BUS_OFFSET(bus->number) | (devfn << 12)) +
offset;
PCI_MMCFG_OFFSET(bus->number, devfn) would be better
Ditto.
Thanks, Lorenzo
return NULL;
+}
+/* Default generic PCI config accessors */ +static struct pci_ops default_pci_mcfg_ops = {
.map_bus = pci_mcfg_dev_base,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
+};
+struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{
/*
* TODO: Match against platform specific quirks and return
* corresponding PCI config space accessor set.
*/
return &default_pci_mcfg_ops;
+}
Is it necessary to make these non-static, even in the next patch, the functions are only used from this file.
int __init acpi_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg; diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 65b91f3..c974586 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -83,10 +83,18 @@ void acpi_pci_remove_bus(struct pci_bus *bus); #ifdef CONFIG_PCI_MMCONFIG int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci); void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci); +struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); +void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset); #else static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci) { return 0; } static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { } +static inline struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{ return NULL; } +static inline void __iomem * +pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) +{ return NULL; } #endif #ifdef CONFIG_ACPI_PCI_SLOT
JC.
No functional changes in this patch.
PCI I/O space mapping code does not depend on OF, therefore it can be moved to PCI core code. This way we will be able to use it e.g. in ACPI PCI code.
Suggested-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Tomasz Nowicki tn@semihalf.com CC: Arnd Bergmann arnd@arndb.de CC: Liviu Dudau Liviu.Dudau@arm.com CC: Lorenzo Pieralisi Lorenzo.Pieralisi@arm.com --- drivers/of/address.c | 116 +-------------------------------------------- drivers/pci/pci.c | 115 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_address.h | 9 ---- include/linux/pci.h | 5 ++ 4 files changed, 121 insertions(+), 124 deletions(-)
diff --git a/drivers/of/address.c b/drivers/of/address.c index 91a469d..0a553c0 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -4,6 +4,7 @@ #include <linux/ioport.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -673,121 +674,6 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address);
-#ifdef PCI_IOBASE -struct io_range { - struct list_head list; - phys_addr_t start; - resource_size_t size; -}; - -static LIST_HEAD(io_range_list); -static DEFINE_SPINLOCK(io_range_lock); -#endif - -/* - * Record the PCI IO range (expressed as CPU physical address + size). - * Return a negative value if an error has occured, zero otherwise - */ -int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) -{ - int err = 0; - -#ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - /* check if the range hasn't been previously recorded */ - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (addr >= range->start && addr + size <= range->start + size) { - /* range already registered, bail out */ - goto end_register; - } - allocated_size += range->size; - } - - /* range not registed yet, check for available space */ - if (allocated_size + size - 1 > IO_SPACE_LIMIT) { - /* if it's too big check if 64K space can be reserved */ - if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { - err = -E2BIG; - goto end_register; - } - - size = SZ_64K; - pr_warn("Requested IO range too big, new size set to 64K\n"); - } - - /* add the range to the list */ - range = kzalloc(sizeof(*range), GFP_ATOMIC); - if (!range) { - err = -ENOMEM; - goto end_register; - } - - range->start = addr; - range->size = size; - - list_add_tail(&range->list, &io_range_list); - -end_register: - spin_unlock(&io_range_lock); -#endif - - return err; -} - -phys_addr_t pci_pio_to_address(unsigned long pio) -{ - phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; - -#ifdef PCI_IOBASE - struct io_range *range; - resource_size_t allocated_size = 0; - - if (pio > IO_SPACE_LIMIT) - return address; - - spin_lock(&io_range_lock); - list_for_each_entry(range, &io_range_list, list) { - if (pio >= allocated_size && pio < allocated_size + range->size) { - address = range->start + pio - allocated_size; - break; - } - allocated_size += range->size; - } - spin_unlock(&io_range_lock); -#endif - - return address; -} - -unsigned long __weak pci_address_to_pio(phys_addr_t address) -{ -#ifdef PCI_IOBASE - struct io_range *res; - resource_size_t offset = 0; - unsigned long addr = -1; - - spin_lock(&io_range_lock); - list_for_each_entry(res, &io_range_list, list) { - if (address >= res->start && address < res->start + res->size) { - addr = address - res->start + offset; - break; - } - offset += res->size; - } - spin_unlock(&io_range_lock); - - return addr; -#else - if (address > IO_SPACE_LIMIT) - return (unsigned long)-1; - - return (unsigned long) address; -#endif -} - static int __of_address_to_resource(struct device_node *dev, const __be32 *addrp, u64 size, unsigned int flags, const char *name, struct resource *r) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d6c768e..3a516c0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3023,6 +3023,121 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) } EXPORT_SYMBOL(pci_request_regions_exclusive);
+#ifdef PCI_IOBASE +struct io_range { + struct list_head list; + phys_addr_t start; + resource_size_t size; +}; + +static LIST_HEAD(io_range_list); +static DEFINE_SPINLOCK(io_range_lock); +#endif + +/* + * Record the PCI IO range (expressed as CPU physical address + size). + * Return a negative value if an error has occured, zero otherwise + */ +int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) +{ + int err = 0; + +#ifdef PCI_IOBASE + struct io_range *range; + resource_size_t allocated_size = 0; + + /* check if the range hasn't been previously recorded */ + spin_lock(&io_range_lock); + list_for_each_entry(range, &io_range_list, list) { + if (addr >= range->start && addr + size <= range->start + size) { + /* range already registered, bail out */ + goto end_register; + } + allocated_size += range->size; + } + + /* range not registed yet, check for available space */ + if (allocated_size + size - 1 > IO_SPACE_LIMIT) { + /* if it's too big check if 64K space can be reserved */ + if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { + err = -E2BIG; + goto end_register; + } + + size = SZ_64K; + pr_warn("Requested IO range too big, new size set to 64K\n"); + } + + /* add the range to the list */ + range = kzalloc(sizeof(*range), GFP_ATOMIC); + if (!range) { + err = -ENOMEM; + goto end_register; + } + + range->start = addr; + range->size = size; + + list_add_tail(&range->list, &io_range_list); + +end_register: + spin_unlock(&io_range_lock); +#endif + + return err; +} + +phys_addr_t pci_pio_to_address(unsigned long pio) +{ + phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; + +#ifdef PCI_IOBASE + struct io_range *range; + resource_size_t allocated_size = 0; + + if (pio > IO_SPACE_LIMIT) + return address; + + spin_lock(&io_range_lock); + list_for_each_entry(range, &io_range_list, list) { + if (pio >= allocated_size && pio < allocated_size + range->size) { + address = range->start + pio - allocated_size; + break; + } + allocated_size += range->size; + } + spin_unlock(&io_range_lock); +#endif + + return address; +} + +unsigned long __weak pci_address_to_pio(phys_addr_t address) +{ +#ifdef PCI_IOBASE + struct io_range *res; + resource_size_t offset = 0; + unsigned long addr = -1; + + spin_lock(&io_range_lock); + list_for_each_entry(res, &io_range_list, list) { + if (address >= res->start && address < res->start + res->size) { + addr = address - res->start + offset; + break; + } + offset += res->size; + } + spin_unlock(&io_range_lock); + + return addr; +#else + if (address > IO_SPACE_LIMIT) + return (unsigned long)-1; + + return (unsigned long) address; +#endif +} + /** * pci_remap_iospace - Remap the memory mapped I/O space * @res: Resource describing the I/O space diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 01c0a55..3786473 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -47,10 +47,6 @@ void __iomem *of_io_request_and_map(struct device_node *device, extern const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags);
-extern int pci_register_io_range(phys_addr_t addr, resource_size_t size); -extern unsigned long pci_address_to_pio(phys_addr_t addr); -extern phys_addr_t pci_pio_to_address(unsigned long pio); - extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node); extern struct of_pci_range *of_pci_range_parser_one( @@ -86,11 +82,6 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index, return NULL; }
-static inline phys_addr_t pci_pio_to_address(unsigned long pio) -{ - return 0; -} - static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 27df4a6..dac677c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1168,6 +1168,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, void *alignf_data);
+int pci_register_io_range(phys_addr_t addr, resource_size_t size); +unsigned long pci_address_to_pio(phys_addr_t addr); +phys_addr_t pci_pio_to_address(unsigned long pio); int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar) @@ -1488,6 +1491,8 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name) { return -EIO; } static inline void pci_release_regions(struct pci_dev *dev) { }
+static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; } + static inline void pci_block_cfg_access(struct pci_dev *dev) { } static inline int pci_block_cfg_access_in_atomic(struct pci_dev *dev) { return 0; }
From: Lorenzo Pieralisi Lorenzo.Pieralisi@arm.com
PCI core code contains a set of functions, eg:
pci_assign_unassigned_bus_resources()
that allow to assign the PCI resources for a given bus after enumeration.
On systems where the PCI BARs are immutable (ie they must not and can not be assigned), PCI resources must be claimed in order to be validated and inserted in the PCI resources tree, but there is no generic PCI kernel function for that purpose and the resource claiming is implemented in an arch specific fashion which resulted in arches implementations that contain duplicated code.
This patch, based on the ia64 resource claiming arch implementation, implements a set of functions in core PCI code that provides a PCI core interface for resources claiming for a given PCI bus hierarchy, paving the way for further resource claiming consolidation across architectures.
Signed-off-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Cc: Arnd Bergmann arnd@arndb.de Cc: Bjorn Helgaas bhelgaas@google.com Cc: Yinghai Lu yinghai@kernel.org --- drivers/pci/setup-bus.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 64 insertions(+)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7796d0a..c959398 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1424,6 +1424,69 @@ void pci_bus_assign_resources(const struct pci_bus *bus) } EXPORT_SYMBOL(pci_bus_assign_resources);
+static void pci_claim_device_resources(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (!r->flags || r->parent) + continue; + + pci_claim_resource(dev, i); + } +} + +static void pci_claim_bridge_resources(struct pci_dev *dev) +{ + int i; + + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (!r->flags || r->parent) + continue; + + pci_claim_bridge_resource(dev, i); + } +} + +static void pci_bus_allocate_dev_resources(struct pci_bus *b) +{ + struct pci_dev *dev; + struct pci_bus *child; + + list_for_each_entry(dev, &b->devices, bus_list) { + pci_claim_device_resources(dev); + + child = dev->subordinate; + if (child) + pci_bus_allocate_dev_resources(child); + } +} + +static void pci_bus_allocate_resources(struct pci_bus *b) +{ + struct pci_bus *child; + + /* Depth-First Search on bus tree */ + if (b->self) { + pci_read_bridge_bases(b); + pci_claim_bridge_resources(b->self); + } + + list_for_each_entry(child, &b->children, node) + pci_bus_allocate_resources(child); +} + +void pci_bus_claim_resources(struct pci_bus *b) +{ + pci_bus_allocate_resources(b); + pci_bus_allocate_dev_resources(b); +} +EXPORT_SYMBOL(pci_bus_claim_resources); + static void __pci_bridge_assign_resources(const struct pci_dev *bridge, struct list_head *add_head, struct list_head *fail_head) diff --git a/include/linux/pci.h b/include/linux/pci.h index dac677c..6faf994 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1119,6 +1119,7 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx); void pci_bus_assign_resources(const struct pci_bus *bus); +void pci_bus_claim_resources(struct pci_bus *bus); void pci_bus_size_bridges(struct pci_bus *bus); int pci_claim_resource(struct pci_dev *, int); int pci_claim_bridge_resource(struct pci_dev *bridge, int i);
Because of two patch series: 1. Jiang Liu's common interface to support PCI host controller init 2. MMCONFIG refactoring (part of this patch set) now we can think about generic ACPI based PCI host controller init implementation out of arch/ directory.
These calls use information from MCFG table (PCI config space regions) and _CRS method (IO/irq resources) to initialize PCI hostbridge.
Signed-off-by: Tomasz Nowicki tn@semihalf.com Signed-off-by: Hanjun Guo hanjun.guo@linaro.org Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com CC: Arnd Bergmann arnd@arndb.de CC: Catalin Marinas catalin.marinas@arm.com CC: Liviu Dudau Liviu.Dudau@arm.com CC: Lorenzo Pieralisi Lorenzo.Pieralisi@arm.com CC: Will Deacon will.deacon@arm.com Tested-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com Tested-by: Jeremy Linton jeremy.linton@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/Kconfig | 7 +++ drivers/acpi/pci_root.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+)
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 183ffa3..1c7f57bd 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -346,6 +346,13 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N.
+config ACPI_PCI_HOST_GENERIC + bool + help + Select this config option from the architecture Kconfig, + if it is preferred to enable ACPI PCI host controller driver which + has no arch-specific assumptions. + config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index cc2c73a..acf0d53 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -532,6 +532,117 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) } }
+#ifdef CONFIG_ACPI_PCI_HOST_GENERIC +static void pci_mcfg_release_info(struct acpi_pci_root_info *ci) +{ + pci_mmcfg_teardown_map(ci); + kfree(ci); +} + +static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) +{ + struct list_head *list = &ci->resources; + struct acpi_device *device = ci->bridge; + struct resource_entry *entry, *tmp; + unsigned long flags; + int ret; + + flags = IORESOURCE_IO | IORESOURCE_MEM; + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)flags); + if (ret < 0) { + dev_warn(&device->dev, + "failed to parse _CRS method, error code %d\n", ret); + return ret; + } else if (ret == 0) + dev_dbg(&device->dev, + "no IO and memory resources present in _CRS\n"); + + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + struct resource *res = entry->res; + + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + else + res->name = ci->name; + + if (res->flags & IORESOURCE_IO) { + resource_size_t cpu_addr = res->start; + resource_size_t pci_addr = cpu_addr - entry->offset; + resource_size_t length = resource_size(res); + unsigned long port; + + if (pci_register_io_range(cpu_addr, length)) { + resource_list_destroy_entry(entry); + continue; + } + + port = pci_address_to_pio(cpu_addr); + if (port == (unsigned long)-1) { + resource_list_destroy_entry(entry); + continue; + } + + res->start = port; + res->end = port + length - 1; + entry->offset = port - pci_addr; + + if (pci_remap_iospace(res, cpu_addr) < 0) + resource_list_destroy_entry(entry); + } + } + return ret; +} + +static struct acpi_pci_root_ops acpi_pci_root_ops = { + .init_info = pci_mmcfg_setup_map, + .release_info = pci_mcfg_release_info, + .prepare_resources = pci_acpi_root_prepare_resources, +}; + +/* Root bridge scanning */ +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + int node = acpi_get_node(root->device->handle); + int domain = root->segment; + int busnum = root->secondary.start; + struct acpi_pci_root_info *info; + struct pci_bus *bus, *child; + + if (domain && !pci_domains_supported) { + pr_warn("PCI %04x:%02x: multiple domains not supported.\n", + domain, busnum); + return NULL; + } + + info = kzalloc_node(sizeof(*info), GFP_KERNEL, node); + if (!info) { + dev_err(&root->device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + domain, busnum); + return NULL; + } + + acpi_pci_root_ops.pci_ops = pci_mcfg_get_ops(root); + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, info, root); + if (!bus) + return NULL; + + pci_bus_claim_resources(bus); + pci_assign_unassigned_bus_resources(bus); + + /* + * After the PCI-E bus has been walked and all devices discovered, + * configure any settings of the fabric that might be necessary. + */ + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} +#endif /* CONFIG_ACPI_PCI_HOST_GENERIC */ + static int acpi_pci_root_add(struct acpi_device *device, const struct acpi_device_id *not_used) {
Some platforms may not be fully compliant with generic set of PCI config accessors. For these cases we implement the way to overwrite accessors set prior to PCI buses enumeration. Algorithm traverses available quirk list, matches against <platform ID (DMI), domain, bus number> tuple and returns corresponding accessors. All quirks can be defined using: DECLARE_ACPI_MCFG_FIXUP() and kept self contained. Example,
static const struct dmi_system_id foo_dmi[] = { { .ident = "<Platform ident string>", .callback = <handler>, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "<system vendor>"), DMI_MATCH(DMI_PRODUCT_NAME, "<product name>"), DMI_MATCH(DMI_PRODUCT_VERSION, "product version"), }, }, { } };
static struct pci_ops foo_ecam_pci_ops = { .map_bus = pci_mcfg_dev_base, .read = foo_ecam_config_read, .write = foo_ecam_config_write, }; DECLARE_ACPI_MCFG_FIXUP(foo_dmi, NULL, &foo_ecam_pci_ops, <domain_nr>, <bus_nr>);
More custom (non-DMI) matching can be done via an extra call. Note that there is possibility to assign quirk related private data to root->sysdata which will be available along read/wriate accessor, example:
static int boo_match(struct pci_mcfg_fixup *fixup, struct acpi_pci_root *root) { return [condition] ? 1 : 0; }
int boo_ecam_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { struct acpi_pci_root *root = bus->sysdata; struct boo_priv_data *boo_data = root->sysdata;
[..] }
static struct pci_ops boo_ecam_pci_ops = { .map_bus = pci_mcfg_dev_base, .read = boo_ecam_config_read, .write = boo_ecam_config_write, }; DECLARE_ACPI_MCFG_FIXUP(NULL, boo_match, &boo_ecam_pci_ops, <domain_nr>, <bus_nr>);
Signed-off-by: Tomasz Nowicki tn@semihalf.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- drivers/acpi/mcfg.c | 33 +++++++++++++++++++++++++++++++-- include/acpi/acpi_bus.h | 1 + include/asm-generic/vmlinux.lds.h | 7 +++++++ include/linux/ecam.h | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index dfc2d14..4663f70 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -8,6 +8,7 @@ */
#include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/ecam.h> #include <linux/pci.h> #include <linux/pci-acpi.h> @@ -34,6 +35,29 @@ int __weak raw_pci_write(unsigned int domain, unsigned int bus, return PCIBIOS_DEVICE_NOT_FOUND; }
+extern struct pci_mcfg_fixup __start_acpi_mcfg_fixups[]; +extern struct pci_mcfg_fixup __end_acpi_mcfg_fixups[]; + +static struct pci_ops *pci_mcfg_check_quirks(struct acpi_pci_root *root) +{ + struct pci_mcfg_fixup *f; + int bus_num = root->secondary.start; + int domain = root->segment; + + /* + * First match against PCI topology domain:bus then use DMI or + * custom match handler. + */ + for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) { + if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && + (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && + (f->system ? dmi_check_system(f->system) : 1 && + f->match ? f->match(f, root) : 1)) + return f->ops; + } + return NULL; +} + void __iomem * pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset) { @@ -56,10 +80,15 @@ static struct pci_ops default_pci_mcfg_ops = {
struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) { + struct pci_ops *pci_mcfg_ops_quirk; + /* - * TODO: Match against platform specific quirks and return - * corresponding PCI config space accessor set. + * Match against platform specific quirks and return corresponding + * PCI config space accessor set. */ + pci_mcfg_ops_quirk = pci_mcfg_check_quirks(root); + if (pci_mcfg_ops_quirk) + return pci_mcfg_ops_quirk;
return &default_pci_mcfg_ops; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 14362a8..0fc6f13 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -556,6 +556,7 @@ struct acpi_pci_root { struct pci_bus *bus; u16 segment; struct resource secondary; /* downstream bus range */ + void *sysdata;
u32 osc_support_set; /* _OSC state of support bits */ u32 osc_control_set; /* _OSC state of control bits */ diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c4bd0e2..c93fc97 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -298,6 +298,13 @@ VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ } \ \ + /* ACPI MCFG quirks */ \ + .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \ + *(.acpi_fixup_mcfg) \ + VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \ + } \ + \ /* Built-in firmware blobs */ \ .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_builtin_fw) = .; \ diff --git a/include/linux/ecam.h b/include/linux/ecam.h index e0f322e..21215be 100644 --- a/include/linux/ecam.h +++ b/include/linux/ecam.h @@ -20,6 +20,24 @@ struct pci_mmcfg_region { bool hot_added; };
+struct pci_mcfg_fixup { + const struct dmi_system_id *system; + int (*match)(struct pci_mcfg_fixup *, struct acpi_pci_root *); + struct pci_ops *ops; + int domain; + int bus_num; +}; + +#define PCI_MCFG_DOMAIN_ANY -1 +#define PCI_MCFG_BUS_ANY -1 + +/* Designate a routine to fix up buggy MCFG */ +#define DECLARE_ACPI_MCFG_FIXUP(system, match, ops, dom, bus) \ + static const struct pci_mcfg_fixup __mcfg_fixup_##system##dom##bus\ + __used __attribute__((__section__(".acpi_fixup_mcfg"), \ + aligned((sizeof(void *))))) = \ + { system, match, ops, dom, bus }; + struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, int end, u64 addr);
This is the last step before enabling generic ACPI PCI host controller for ARM64. We need to take care of legacy IRQ mapping for non-MSI(X) PCI devices. pcibios_enable_device() boot order is not sensitive to ACPI device enumeration, so it is the best place to assign device's IRQs.
NOTE: *This is going to be temporary solution*. There is ongoing work which aims for cleaning legacy IRQ allocation, see [1]. We can consider this patch as the necessary evil which will be removed once [1] series hits mailnline in the near future.
1. http://comments.gmane.org/gmane.linux.kernel.pci/46461
Signed-off-by: Tomasz Nowicki tn@semihalf.com --- arch/arm64/kernel/pci.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 023b983..d1a701f 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -39,16 +39,26 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, }
/** - * pcibios_enable_device - Enable I/O and memory. + * pcibios_enable_device - Enable I/O, memory and legacy IRQs for ACPI. * @dev: PCI device to be enabled * @mask: bitmask of BARs to enable */ int pcibios_enable_device(struct pci_dev *dev, int mask) { + int ret; + if (pci_has_flag(PCI_PROBE_ONLY)) return 0;
- return pci_enable_resources(dev, mask); + ret = pci_enable_resources(dev, mask); + if (ret < 0) + return ret; + +#ifdef CONFIG_ACPI + if (!pci_dev_msi_enabled(dev)) + return acpi_pci_irq_enable(dev); +#endif + return 0; }
/*
On Thu, Feb 04, 2016 at 06:29:00PM +0100, Tomasz Nowicki wrote:
This is the last step before enabling generic ACPI PCI host controller for ARM64. We need to take care of legacy IRQ mapping for non-MSI(X) PCI devices. pcibios_enable_device() boot order is not sensitive to ACPI device enumeration, so it is the best place to assign device's IRQs.
I guess you are referring to:
https://lists.linaro.org/pipermail/linaro-acpi/2015-October/005944.html
It is weird that the dependency can't be enforced, I will have a look into this, it would be nice to have DT and ACPI legacy IRQs mapping confined in pcibios_add_device() so that we can remove them in one go when Matthew's series is merged.
As for the MSI check, by reading commit history its need I think it harks back to bba6f6fc, which was supposed to be a quick hack and it has been in the kernel for 9 years :), is it really needed ?
NOTE: *This is going to be temporary solution*. There is ongoing work which aims for cleaning legacy IRQ allocation, see [1]. We can consider this patch as the necessary evil which will be removed once [1] series hits mailnline in the near future.
Signed-off-by: Tomasz Nowicki tn@semihalf.com
arch/arm64/kernel/pci.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 023b983..d1a701f 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -39,16 +39,26 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, } /**
- pcibios_enable_device - Enable I/O and memory.
*/
- pcibios_enable_device - Enable I/O, memory and legacy IRQs for ACPI.
- @dev: PCI device to be enabled
- @mask: bitmask of BARs to enable
int pcibios_enable_device(struct pci_dev *dev, int mask) {
- int ret;
- if (pci_has_flag(PCI_PROBE_ONLY)) return 0;
- return pci_enable_resources(dev, mask);
- ret = pci_enable_resources(dev, mask);
- if (ret < 0)
return ret;
+#ifdef CONFIG_ACPI
- if (!pci_dev_msi_enabled(dev))
return acpi_pci_irq_enable(dev);
You need a guard here anyway, we do not want to probe ACPI IRQ if it is disabled (ie acpi_pci_disabled). Same goes for DT in pcibios_add_device(), which is not making this code any nicer.
Lorenzo
+#endif
- return 0;
} /* -- 1.9.1
On Thu, Feb 11, 2016 at 11:58:53AM +0000, Lorenzo Pieralisi wrote:
On Thu, Feb 04, 2016 at 06:29:00PM +0100, Tomasz Nowicki wrote:
This is the last step before enabling generic ACPI PCI host controller for ARM64. We need to take care of legacy IRQ mapping for non-MSI(X) PCI devices. pcibios_enable_device() boot order is not sensitive to ACPI device enumeration, so it is the best place to assign device's IRQs.
I guess you are referring to:
https://lists.linaro.org/pipermail/linaro-acpi/2015-October/005944.html
It is weird that the dependency can't be enforced, I will have a look into this, it would be nice to have DT and ACPI legacy IRQs mapping confined in pcibios_add_device() so that we can remove them in one go when Matthew's series is merged.
One option, that is not ideal but has the merit of setting the stage for pcibios_enable_device() AND pcibios_add_device() removal, is to add IRQ mapping (by adding the call) in a pcibios_alloc_irq() callback (if Bjorn does not remove it from core code before we manage to add it, I think he is only reverting the x86 version).
https://lkml.org/lkml/2016/2/9/648
That's called at device probe time, it should not change DT probing path (unless we use the irq number before the device is probed, which I doubt) and should allow the ACPI scan handlers to be installed so that the ACPI IRQ mapping can be effectively carried out.
pcibios_alloc_irq() replaces pcibios_add_device().
When Matthew's patchset lands in mainline, pcibios_alloc_irq() will be removed too (at least we have a place where all legacy IRQ mappings are carried out and I can obliterate it easily).
Other option is to change ACPI core code, I see no other way.
Tested on KVM PCI host generic (that uses pci_fixup_irqs() so it does not really count for DT).
Here (on top of your series), ready for flak.
Lorenzo
-- >8 -- diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 0b53262..26ee291 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -45,28 +45,23 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, */ int pcibios_enable_device(struct pci_dev *dev, int mask) { - int ret; - if (pci_has_flag(PCI_PROBE_ONLY)) return 0;
- ret = pci_enable_resources(dev, mask); - if (ret < 0) - return ret; - -#ifdef CONFIG_ACPI - if (!pci_dev_msi_enabled(dev)) - return acpi_pci_irq_enable(dev); -#endif - return 0; + return pci_enable_resources(dev, mask); }
/* - * Try to assign the IRQ number from DT when adding a new device + * Try to assign the IRQ number when probing a new device */ -int pcibios_add_device(struct pci_dev *dev) +int pcibios_alloc_irq(struct pci_dev *dev) { - dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); + if (acpi_disabled) + dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); +#ifdef CONFIG_ACPI + else + return acpi_pci_irq_enable(dev); +#endif
return 0; }
On 11.02.2016 18:17, Lorenzo Pieralisi wrote:
Here (on top of your series), ready for flak.
Lorenzo
-- >8 -- diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 0b53262..26ee291 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -45,28 +45,23 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, */ int pcibios_enable_device(struct pci_dev *dev, int mask) {
int ret;
if (pci_has_flag(PCI_PROBE_ONLY)) return 0;
ret = pci_enable_resources(dev, mask);
if (ret < 0)
return ret;
-#ifdef CONFIG_ACPI
- if (!pci_dev_msi_enabled(dev))
return acpi_pci_irq_enable(dev);
-#endif
- return 0;
return pci_enable_resources(dev, mask); }
/*
- Try to assign the IRQ number from DT when adding a new device
*/
- Try to assign the IRQ number when probing a new device
-int pcibios_add_device(struct pci_dev *dev) +int pcibios_alloc_irq(struct pci_dev *dev) {
- dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
- if (acpi_disabled)
dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
+#ifdef CONFIG_ACPI
- else
return acpi_pci_irq_enable(dev);
+#endif
return 0; }
I miss that way of solving the problem. OK lets try this way. I will integrate it.
Tomasz
On 11.02.2016 12:58, Lorenzo Pieralisi wrote:
As for the MSI check, by reading commit history its need I think it harks back to bba6f6fc, which was supposed to be a quick hack and it has been in the kernel for 9 years:), is it really needed ?
IMO, yes it is needed. We need to chose MSI vs IRQ, and MSI is preferable option.
Tomasz
Lets get rid of empty PCI init stub, related ACPI header and go with full-blown PCI host bridge driver.
Signed-off-by: Tomasz Nowicki tn@semihalf.com CC: Arnd Bergmann arnd@arndb.de CC: Catalin Marinas catalin.marinas@arm.com CC: Liviu Dudau Liviu.Dudau@arm.com CC: Lorenzo Pieralisi Lorenzo.Pieralisi@arm.com CC: Will Deacon will.deacon@arm.com Tested-by: Duc Dang dhdang@apm.com Tested-by: Dongdong Liu liudongdong3@huawei.com Tested-by: Hanjun Guo hanjun.guo@linaro.org Tested-by: Graeme Gregory graeme.gregory@linaro.org Tested-by: Sinan Kaya okaya@codeaurora.org --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/pci.c | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4f45ea4..567523b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2,6 +2,7 @@ config ARM64 def_bool y select ACPI_CCA_REQUIRED if ACPI select ACPI_GENERIC_GSI if ACPI + select ACPI_PCI_HOST_GENERIC if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index d1a701f..0b53262 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -70,12 +70,3 @@ int pcibios_add_device(struct pci_dev *dev)
return 0; } - -#ifdef CONFIG_ACPI -/* Root bridge scanning */ -struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) -{ - /* TODO: Should be revisited when implementing PCI on ACPI */ - return NULL; -} -#endif