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 <[email protected]>
> Tested-by: Hanjun Guo <[email protected]>
> ---
>  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-kernel" in
the body of a message to [email protected]
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