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.nowi...@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-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to