On Wed, Nov 19, 2014 at 05:04:47PM +0100, Tomasz Nowicki wrote: > MMCFG table seems to be architecture independent and it makes sense > to share common code across all architectures. The ones that may need > architectural specific actions have default prototype (__weak). > > Signed-off-by: Tomasz Nowicki <tomasz.nowicki@xxxxxxxxxx> > Tested-by: Hanjun Guo <hanjun.guo@xxxxxxxxxx> > --- > arch/x86/include/asm/pci_x86.h | 29 ----- > arch/x86/pci/acpi.c | 1 + > arch/x86/pci/init.c | 1 + > arch/x86/pci/mmconfig-shared.c | 200 +--------------------------------- > arch/x86/pci/mmconfig_32.c | 1 + > arch/x86/pci/mmconfig_64.c | 1 + > drivers/acpi/Makefile | 1 + > drivers/acpi/bus.c | 1 + > drivers/acpi/mmconfig.c | 242 +++++++++++++++++++++++++++++++++++++++++ > include/linux/mmconfig.h | 58 ++++++++++ > include/linux/pci.h | 8 -- > 11 files changed, 308 insertions(+), 235 deletions(-) > create mode 100644 drivers/acpi/mmconfig.c > create mode 100644 include/linux/mmconfig.h > ... Much of the code you're moving to drivers/acpi/mmconfig.c is not actually ACPI-specific and would have to be duplicated for a non-ACPI architecture that supports ECAM. Could that code be moved somewhere like drivers/pci/ecam.c, where it could be shared? Bjorn > diff --git a/drivers/acpi/mmconfig.c b/drivers/acpi/mmconfig.c > new file mode 100644 > index 0000000..d62dccda > --- /dev/null > +++ b/drivers/acpi/mmconfig.c > @@ -0,0 +1,242 @@ > +/* > + * Arch agnostic low-level direct PCI config space access via MMCONFIG > + * > + * 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/mmconfig.h> > + > +#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; > +} > + > +int __init 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; > +} > + > +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; > +} > + > +int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg) > +{ > + return 0; > +} > + > +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; > +} > + > +/* 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; > +} > + > +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, pci_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); > +} > diff --git a/include/linux/mmconfig.h b/include/linux/mmconfig.h > new file mode 100644 > index 0000000..6ccd1ee > --- /dev/null > +++ b/include/linux/mmconfig.h > @@ -0,0 +1,58 @@ > +#ifndef __MMCONFIG_H > +#define __MMCONFIG_H > +#ifdef __KERNEL__ > + > +#include <linux/types.h> > +#include <linux/acpi.h> > + > +#ifdef CONFIG_PCI_MMCONFIG > +/* "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]; > +}; > + > +void pci_mmcfg_early_init(void); > +void pci_mmcfg_late_init(void); > +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); > + > +int pci_parse_mcfg(struct acpi_table_header *header); > +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); > +int acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, > + struct acpi_mcfg_allocation *cfg); > +void free_all_mmcfg(void); > +int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, > + phys_addr_t addr); > +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) > +#else /* CONFIG_PCI_MMCONFIG */ > +static inline void pci_mmcfg_late_init(void) { } > +static inline void pci_mmcfg_early_init(void) { } > +static inline void *pci_mmconfig_lookup(int segment, int bus) > +{ return NULL; } > +#endif /* CONFIG_PCI_MMCONFIG */ > + > +#endif /* __KERNEL__ */ > +#endif /* __MMCONFIG_H */ > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 6afba72..0a8b82e 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -1658,14 +1658,6 @@ void pcibios_release_device(struct pci_dev *dev); > extern struct dev_pm_ops pcibios_pm_ops; > #endif > > -#ifdef CONFIG_PCI_MMCONFIG > -void __init pci_mmcfg_early_init(void); > -void __init pci_mmcfg_late_init(void); > -#else > -static inline void pci_mmcfg_early_init(void) { } > -static inline void pci_mmcfg_late_init(void) { } > -#endif > - > int pci_ext_cfg_avail(void); > > void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); > -- > 1.9.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html