Message ID | 1425987320-15020-5-git-send-email-tomasz.nowicki@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
On Tue, 2015-03-10 at 12:35 +0100, Tomasz Nowicki wrote: > 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 <tomasz.nowicki@linaro.org> > --- > arch/x86/Kconfig | 2 + > arch/x86/include/asm/pci_x86.h | 33 ----- > arch/x86/pci/acpi.c | 1 + > arch/x86/pci/mmconfig-shared.c | 296 +---------------------------------------- > 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 | 81 +++++++++++ > drivers/pci/Kconfig | 4 + > drivers/pci/Makefile | 5 + > drivers/pci/ecam.c | 243 +++++++++++++++++++++++++++++++++ > include/linux/ecam.h | 51 +++++++ > 13 files changed, 394 insertions(+), 326 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 c2fb8a8..dd926f4 100644 > --- a/arch/x86/Kconfig > +++ b/arch/x86/Kconfig > @@ -2276,6 +2276,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 > @@ -2293,6 +2294,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 c57c225..e8a237f 100644 > --- a/arch/x86/include/asm/pci_x86.h > +++ b/arch/x86/include/asm/pci_x86.h > @@ -122,41 +122,8 @@ extern int pci_legacy_init(void); > extern void pcibios_fixup_irqs(void); > > /* pci-mmconfig.c */ > - > -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ > -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) > - > -struct pci_mmcfg_region { > - struct list_head list; > - struct resource res; > - u64 address; > - char __iomem *virt; > - u16 segment; > - u8 start_bus; > - u8 end_bus; > - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; > -}; > - > -struct pci_mmcfg_mmio_ops { > - u32 (*read)(int len, void __iomem *addr); > - void (*write)(int len, void __iomem *addr, u32 value); > -}; > - > -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 u32 pci_mmio_read(int len, void __iomem *addr); > -extern void pci_mmio_write(int len, void __iomem *addr, u32 value); > -extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); > - > -extern struct list_head pci_mmcfg_list; > - > -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > > /* > * AMD Fam10h CPUs are buggy, and cannot access MMIO config space > diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c > index 6ac2738..5dfccef 100644 > --- a/arch/x86/pci/acpi.c > +++ b/arch/x86/pci/acpi.c > @@ -4,6 +4,7 @@ > #include <linux/irq.h> > #include <linux/dmi.h> > #include <linux/slab.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 685cd4c..5064302 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,52 +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 u32 > -pci_mmconfig_generic_read(int len, void __iomem *addr) > -{ > - u32 data = 0; > - > - switch (len) { > - case 1: > - data = readb(addr); > - break; > - case 2: > - data = readw(addr); > - break; > - case 4: > - data = readl(addr); > - break; > - } > - > - return data; > -} > - > -static void > -pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) > -{ > - switch (len) { > - case 1: > - writeb(value, addr); > - break; > - case 2: > - writew(value, addr); > - break; > - case 4: > - writel(value, addr); > - break; > - } > -} > - > -static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { > - .read = pci_mmconfig_generic_read, > - .write = pci_mmconfig_generic_write, > -}; > - > -static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; > > static u32 > pci_mmconfig_amd_read(int len, void __iomem *addr) > @@ -115,128 +70,6 @@ static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { > .write = pci_mmconfig_amd_write, > }; > > -void > -pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) > -{ > - pci_mmcfg_mmio = ops; > -} > - > -u32 > -pci_mmio_read(int len, void __iomem *addr) > -{ > - if (!pci_mmcfg_mmio) { > - pr_err("PCI config space has no accessors !"); > - return 0; > - } > - > - return pci_mmcfg_mmio->read(len, addr); > -} > - > -void > -pci_mmio_write(int len, void __iomem *addr, u32 value) > -{ > - if (!pci_mmcfg_mmio) { > - pr_err("PCI config space has no accessors !"); > - return; > - } > - > - pci_mmcfg_mmio->write(len, addr, value); > -} > - > -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) > { > u32 win; > @@ -657,73 +490,6 @@ static void __init pci_mmcfg_reject_broken(int early) > } > } > > -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > - struct acpi_mcfg_allocation *cfg) > -{ > - int year; > - > - if (cfg->address < 0xFFFFFFFF) > - return 0; > - > - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > - return 0; > - > - if (mcfg->header.revision >= 1) { > - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > - year >= 2010) > - return 0; > - } > - > - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > - "is above 4GB, ignored\n", cfg->pci_segment, > - cfg->start_bus_number, cfg->end_bus_number, cfg->address); > - return -EINVAL; > -} This looks very arch-specific. Shouldn't this be only generic checks with some hook for architectures to add arch-specific checks? > - > -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); > @@ -782,7 +548,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(); > @@ -800,7 +566,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); > } > } > @@ -834,39 +600,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) > @@ -915,26 +648,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 4b3d025..5cf6291 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 032593d..b62ff18 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 5047e9b..01868b6 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 623b117..39498a1 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -67,6 +67,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-y += 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..a2e2326 > --- /dev/null > +++ b/drivers/acpi/mcfg.c > @@ -0,0 +1,81 @@ > +/* > + * 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/dmi.h> > +#include <linux/ecam.h> > + > +#define PREFIX "MCFG: " > + > +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg) > +{ > + int year; > + > + if (cfg->address < 0xFFFFFFFF) > + return 0; > + > + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) > + return 0; > + > + if (mcfg->header.revision >= 1) { > + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && > + year >= 2010) > + return 0; > + } > + > + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " > + "is above 4GB, ignored\n", cfg->pci_segment, > + cfg->start_bus_number, cfg->end_bus_number, cfg->address); > + return -EINVAL; > +} > + > +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 7a8f1c5..e2eb982 100644 > --- a/drivers/pci/Kconfig > +++ b/drivers/pci/Kconfig > @@ -22,6 +22,10 @@ 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 > + > config PCI_DEBUG > bool "PCI Debugging" > depends on PCI && DEBUG_KERNEL > diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile > index 73e4af4..ce7b630 100644 > --- a/drivers/pci/Makefile > +++ b/drivers/pci/Makefile > @@ -41,6 +41,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..f540184 > --- /dev/null > +++ b/drivers/pci/ecam.c > @@ -0,0 +1,243 @@ > +/* > + * 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> > + > +#define PREFIX "PCI: " > + > +static DEFINE_MUTEX(pci_mmcfg_lock); > + > +LIST_HEAD(pci_mmcfg_list); > + > +static u32 > +pci_mmconfig_generic_read(int len, void __iomem *addr) > +{ > + u32 data = 0; > + > + switch (len) { > + case 1: > + data = readb(addr); > + break; > + case 2: > + data = readw(addr); > + break; > + case 4: > + data = readl(addr); > + break; > + } > + > + return data; > +} > + > +static void > +pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) > +{ > + switch (len) { > + case 1: > + writeb(value, addr); > + break; > + case 2: > + writew(value, addr); > + break; > + case 4: > + writel(value, addr); > + break; > + } > +} > + > +static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { > + .read = pci_mmconfig_generic_read, > + .write = pci_mmconfig_generic_write, > +}; > + > +static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; > + > +void > +pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) > +{ > + pci_mmcfg_mmio = ops; > +} > + > +u32 > +pci_mmio_read(int len, void __iomem *addr) > +{ > + if (!pci_mmcfg_mmio) { > + pr_err("PCI config space has no accessors !"); > + return 0; > + } > + > + return pci_mmcfg_mmio->read(len, addr); > +} > + > +void > +pci_mmio_write(int len, void __iomem *addr, u32 value) > +{ > + if (!pci_mmcfg_mmio) { > + pr_err("PCI config space has no accessors !"); > + return; > + } > + > + pci_mmcfg_mmio->write(len, addr, value); > +} > + > +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/include/linux/ecam.h b/include/linux/ecam.h > new file mode 100644 > index 0000000..2387df5 > --- /dev/null > +++ b/include/linux/ecam.h > @@ -0,0 +1,51 @@ > +#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_mmio_ops { > + u32 (*read)(int len, void __iomem *addr); > + void (*write)(int len, void __iomem *addr, u32 value); > +}; > + > +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); > + > +/* 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 u32 pci_mmio_read(int len, void __iomem *addr); > +extern void pci_mmio_write(int len, void __iomem *addr, u32 value); > +extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); > + > +extern struct list_head pci_mmcfg_list; > + > +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) > + > +#endif /* __KERNEL__ */ > +#endif /* __ECAM_H */ -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 13.03.2015 17:35, Mark Salter wrote: > On Tue, 2015-03-10 at 12:35 +0100, Tomasz Nowicki wrote: >> 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 <tomasz.nowicki@linaro.org> >> --- >> arch/x86/Kconfig | 2 + >> arch/x86/include/asm/pci_x86.h | 33 ----- >> arch/x86/pci/acpi.c | 1 + >> arch/x86/pci/mmconfig-shared.c | 296 +---------------------------------------- >> 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 | 81 +++++++++++ >> drivers/pci/Kconfig | 4 + >> drivers/pci/Makefile | 5 + >> drivers/pci/ecam.c | 243 +++++++++++++++++++++++++++++++++ >> include/linux/ecam.h | 51 +++++++ >> 13 files changed, 394 insertions(+), 326 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 c2fb8a8..dd926f4 100644 >> --- a/arch/x86/Kconfig >> +++ b/arch/x86/Kconfig >> @@ -2276,6 +2276,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 >> @@ -2293,6 +2294,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 c57c225..e8a237f 100644 >> --- a/arch/x86/include/asm/pci_x86.h >> +++ b/arch/x86/include/asm/pci_x86.h >> @@ -122,41 +122,8 @@ extern int pci_legacy_init(void); >> extern void pcibios_fixup_irqs(void); >> >> /* pci-mmconfig.c */ >> - >> -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ >> -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) >> - >> -struct pci_mmcfg_region { >> - struct list_head list; >> - struct resource res; >> - u64 address; >> - char __iomem *virt; >> - u16 segment; >> - u8 start_bus; >> - u8 end_bus; >> - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; >> -}; >> - >> -struct pci_mmcfg_mmio_ops { >> - u32 (*read)(int len, void __iomem *addr); >> - void (*write)(int len, void __iomem *addr, u32 value); >> -}; >> - >> -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 u32 pci_mmio_read(int len, void __iomem *addr); >> -extern void pci_mmio_write(int len, void __iomem *addr, u32 value); >> -extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); >> - >> -extern struct list_head pci_mmcfg_list; >> - >> -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) >> >> /* >> * AMD Fam10h CPUs are buggy, and cannot access MMIO config space >> diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c >> index 6ac2738..5dfccef 100644 >> --- a/arch/x86/pci/acpi.c >> +++ b/arch/x86/pci/acpi.c >> @@ -4,6 +4,7 @@ >> #include <linux/irq.h> >> #include <linux/dmi.h> >> #include <linux/slab.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 685cd4c..5064302 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,52 +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 u32 >> -pci_mmconfig_generic_read(int len, void __iomem *addr) >> -{ >> - u32 data = 0; >> - >> - switch (len) { >> - case 1: >> - data = readb(addr); >> - break; >> - case 2: >> - data = readw(addr); >> - break; >> - case 4: >> - data = readl(addr); >> - break; >> - } >> - >> - return data; >> -} >> - >> -static void >> -pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) >> -{ >> - switch (len) { >> - case 1: >> - writeb(value, addr); >> - break; >> - case 2: >> - writew(value, addr); >> - break; >> - case 4: >> - writel(value, addr); >> - break; >> - } >> -} >> - >> -static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { >> - .read = pci_mmconfig_generic_read, >> - .write = pci_mmconfig_generic_write, >> -}; >> - >> -static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; >> >> static u32 >> pci_mmconfig_amd_read(int len, void __iomem *addr) >> @@ -115,128 +70,6 @@ static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { >> .write = pci_mmconfig_amd_write, >> }; >> >> -void >> -pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) >> -{ >> - pci_mmcfg_mmio = ops; >> -} >> - >> -u32 >> -pci_mmio_read(int len, void __iomem *addr) >> -{ >> - if (!pci_mmcfg_mmio) { >> - pr_err("PCI config space has no accessors !"); >> - return 0; >> - } >> - >> - return pci_mmcfg_mmio->read(len, addr); >> -} >> - >> -void >> -pci_mmio_write(int len, void __iomem *addr, u32 value) >> -{ >> - if (!pci_mmcfg_mmio) { >> - pr_err("PCI config space has no accessors !"); >> - return; >> - } >> - >> - pci_mmcfg_mmio->write(len, addr, value); >> -} >> - >> -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) >> { >> u32 win; >> @@ -657,73 +490,6 @@ static void __init pci_mmcfg_reject_broken(int early) >> } >> } >> >> -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, >> - struct acpi_mcfg_allocation *cfg) >> -{ >> - int year; >> - >> - if (cfg->address < 0xFFFFFFFF) >> - return 0; >> - >> - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) >> - return 0; >> - >> - if (mcfg->header.revision >= 1) { >> - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && >> - year >= 2010) >> - return 0; >> - } >> - >> - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " >> - "is above 4GB, ignored\n", cfg->pci_segment, >> - cfg->start_bus_number, cfg->end_bus_number, cfg->address); >> - return -EINVAL; >> -} > > This looks very arch-specific. Shouldn't this be only generic checks > with some hook for architectures to add arch-specific checks? Yes, you are right it should go to arch specific call. Thanks. Tomasz -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index c2fb8a8..dd926f4 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2276,6 +2276,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 @@ -2293,6 +2294,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 c57c225..e8a237f 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,41 +122,8 @@ extern int pci_legacy_init(void); extern void pcibios_fixup_irqs(void); /* pci-mmconfig.c */ - -/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) - -struct pci_mmcfg_region { - struct list_head list; - struct resource res; - u64 address; - char __iomem *virt; - u16 segment; - u8 start_bus; - u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; -}; - -struct pci_mmcfg_mmio_ops { - u32 (*read)(int len, void __iomem *addr); - void (*write)(int len, void __iomem *addr, u32 value); -}; - -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 u32 pci_mmio_read(int len, void __iomem *addr); -extern void pci_mmio_write(int len, void __iomem *addr, u32 value); -extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); - -extern struct list_head pci_mmcfg_list; - -#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) /* * AMD Fam10h CPUs are buggy, and cannot access MMIO config space diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 6ac2738..5dfccef 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -4,6 +4,7 @@ #include <linux/irq.h> #include <linux/dmi.h> #include <linux/slab.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 685cd4c..5064302 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,52 +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 u32 -pci_mmconfig_generic_read(int len, void __iomem *addr) -{ - u32 data = 0; - - switch (len) { - case 1: - data = readb(addr); - break; - case 2: - data = readw(addr); - break; - case 4: - data = readl(addr); - break; - } - - return data; -} - -static void -pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) -{ - switch (len) { - case 1: - writeb(value, addr); - break; - case 2: - writew(value, addr); - break; - case 4: - writel(value, addr); - break; - } -} - -static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { - .read = pci_mmconfig_generic_read, - .write = pci_mmconfig_generic_write, -}; - -static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; static u32 pci_mmconfig_amd_read(int len, void __iomem *addr) @@ -115,128 +70,6 @@ static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { .write = pci_mmconfig_amd_write, }; -void -pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) -{ - pci_mmcfg_mmio = ops; -} - -u32 -pci_mmio_read(int len, void __iomem *addr) -{ - if (!pci_mmcfg_mmio) { - pr_err("PCI config space has no accessors !"); - return 0; - } - - return pci_mmcfg_mmio->read(len, addr); -} - -void -pci_mmio_write(int len, void __iomem *addr, u32 value) -{ - if (!pci_mmcfg_mmio) { - pr_err("PCI config space has no accessors !"); - return; - } - - pci_mmcfg_mmio->write(len, addr, value); -} - -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) { u32 win; @@ -657,73 +490,6 @@ static void __init pci_mmcfg_reject_broken(int early) } } -static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - return -EINVAL; -} - -static int __init pci_parse_mcfg(struct acpi_table_header *header) -{ - struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *cfg_table, *cfg; - unsigned long i; - int entries; - - if (!header) - return -EINVAL; - - mcfg = (struct acpi_table_mcfg *)header; - - /* how many config structures do we have */ - free_all_mmcfg(); - entries = 0; - i = header->length - sizeof(struct acpi_table_mcfg); - while (i >= sizeof(struct acpi_mcfg_allocation)) { - entries++; - i -= sizeof(struct acpi_mcfg_allocation); - } - if (entries == 0) { - pr_err(PREFIX "MMCONFIG has no entries\n"); - return -ENODEV; - } - - cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; - for (i = 0; i < entries; i++) { - cfg = &cfg_table[i]; - if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); - return -ENODEV; - } - - if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { - pr_warn(PREFIX "no memory for MCFG entries\n"); - free_all_mmcfg(); - return -ENOMEM; - } - } - - return 0; -} - #ifdef CONFIG_ACPI_APEI extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, void *data), void *data); @@ -782,7 +548,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(); @@ -800,7 +566,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); } } @@ -834,39 +600,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) @@ -915,26 +648,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 4b3d025..5cf6291 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 032593d..b62ff18 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 5047e9b..01868b6 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 623b117..39498a1 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -67,6 +67,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-y += 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..a2e2326 --- /dev/null +++ b/drivers/acpi/mcfg.c @@ -0,0 +1,81 @@ +/* + * 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/dmi.h> +#include <linux/ecam.h> + +#define PREFIX "MCFG: " + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +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 7a8f1c5..e2eb982 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -22,6 +22,10 @@ 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 + config PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 73e4af4..ce7b630 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -41,6 +41,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..f540184 --- /dev/null +++ b/drivers/pci/ecam.c @@ -0,0 +1,243 @@ +/* + * 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> + +#define PREFIX "PCI: " + +static DEFINE_MUTEX(pci_mmcfg_lock); + +LIST_HEAD(pci_mmcfg_list); + +static u32 +pci_mmconfig_generic_read(int len, void __iomem *addr) +{ + u32 data = 0; + + switch (len) { + case 1: + data = readb(addr); + break; + case 2: + data = readw(addr); + break; + case 4: + data = readl(addr); + break; + } + + return data; +} + +static void +pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) +{ + switch (len) { + case 1: + writeb(value, addr); + break; + case 2: + writew(value, addr); + break; + case 4: + writel(value, addr); + break; + } +} + +static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { + .read = pci_mmconfig_generic_read, + .write = pci_mmconfig_generic_write, +}; + +static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; + +void +pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) +{ + pci_mmcfg_mmio = ops; +} + +u32 +pci_mmio_read(int len, void __iomem *addr) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return 0; + } + + return pci_mmcfg_mmio->read(len, addr); +} + +void +pci_mmio_write(int len, void __iomem *addr, u32 value) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return; + } + + pci_mmcfg_mmio->write(len, addr, value); +} + +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/include/linux/ecam.h b/include/linux/ecam.h new file mode 100644 index 0000000..2387df5 --- /dev/null +++ b/include/linux/ecam.h @@ -0,0 +1,51 @@ +#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_mmio_ops { + u32 (*read)(int len, void __iomem *addr); + void (*write)(int len, void __iomem *addr, u32 value); +}; + +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); + +/* 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 u32 pci_mmio_read(int len, void __iomem *addr); +extern void pci_mmio_write(int len, void __iomem *addr, u32 value); +extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) + +#endif /* __KERNEL__ */ +#endif /* __ECAM_H */
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 <tomasz.nowicki@linaro.org> --- arch/x86/Kconfig | 2 + arch/x86/include/asm/pci_x86.h | 33 ----- arch/x86/pci/acpi.c | 1 + arch/x86/pci/mmconfig-shared.c | 296 +---------------------------------------- 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 | 81 +++++++++++ drivers/pci/Kconfig | 4 + drivers/pci/Makefile | 5 + drivers/pci/ecam.c | 243 +++++++++++++++++++++++++++++++++ include/linux/ecam.h | 51 +++++++ 13 files changed, 394 insertions(+), 326 deletions(-) create mode 100644 drivers/acpi/mcfg.c create mode 100644 drivers/pci/ecam.c create mode 100644 include/linux/ecam.h