diff options
author | Graeme Gregory <graeme.gregory@linaro.org> | 2016-06-07 15:03:32 +0100 |
---|---|---|
committer | Graeme Gregory <graeme.gregory@linaro.org> | 2016-06-07 15:03:32 +0100 |
commit | 224ba1d9a2f024731095b914868e1c4bf5a8933e (patch) | |
tree | d7e9dad6db3042902ac0d03cead7118cf25ad073 | |
parent | 0ca5bc98334a884e958d7c375275983ead19b503 (diff) | |
parent | 9d05dcfdce8beff689fa12e8ccd71d27116bf1ec (diff) |
Merge branch 'topic-pci' into leg-kernel
-rw-r--r-- | arch/arm64/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/pci.c | 144 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 3 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/pci_mcfg.c | 126 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 39 | ||||
-rw-r--r-- | drivers/pci/ecam.c | 6 | ||||
-rw-r--r-- | drivers/pci/host/pci-host-common.c | 3 | ||||
-rw-r--r-- | drivers/pci/host/pci-host-generic.c | 3 | ||||
-rw-r--r-- | drivers/pci/host/pci-thunder-ecam.c | 3 | ||||
-rw-r--r-- | drivers/pci/host/pci-thunder-pem.c | 138 | ||||
-rw-r--r-- | drivers/pci/pci.c | 29 | ||||
-rw-r--r-- | include/asm-generic/vmlinux.lds.h | 7 | ||||
-rw-r--r-- | include/linux/pci-acpi.h | 21 | ||||
-rw-r--r-- | include/linux/pci-ecam.h (renamed from drivers/pci/ecam.h) | 4 | ||||
-rw-r--r-- | include/linux/pci.h | 9 |
16 files changed, 500 insertions, 38 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e167260fcd0c..4413f0660719 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -5,6 +5,7 @@ config ARM64 select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ACPI_SPCR_TABLE if ACPI select HAVE_ACPI_APEI if ACPI + select ACPI_MCFG if ACPI select ARCH_HAS_DEVMEM_IS_ALLOWED select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ELF_RANDOMIZE @@ -98,6 +99,7 @@ config ARM64 select OF_EARLY_FLATTREE select OF_NUMA if NUMA && OF select OF_RESERVED_MEM + select PCI_ECAM if ACPI select PERF_USE_VMALLOC select POWER_RESET select POWER_SUPPLY diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 3c4e308b40a0..3a83c28a790b 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -17,6 +17,9 @@ #include <linux/mm.h> #include <linux/of_pci.h> #include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h> #include <linux/slab.h> /* @@ -50,11 +53,16 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) } /* - * Try to assign the IRQ number from DT when adding a new device + * Try to assign the IRQ number when probing a new device */ -int pcibios_add_device(struct pci_dev *dev) +int pcibios_alloc_irq(struct pci_dev *dev) { - dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); + if (acpi_disabled) + dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); +#ifdef CONFIG_ACPI + else + return acpi_pci_irq_enable(dev); +#endif return 0; } @@ -65,13 +73,21 @@ int pcibios_add_device(struct pci_dev *dev) int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { - return -ENXIO; + struct pci_bus *b = pci_find_bus(domain, bus); + + if (!b) + return PCIBIOS_DEVICE_NOT_FOUND; + return b->ops->read(b, devfn, reg, len, val); } int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val) { - return -ENXIO; + struct pci_bus *b = pci_find_bus(domain, bus); + + if (!b) + return PCIBIOS_DEVICE_NOT_FOUND; + return b->ops->write(b, devfn, reg, len, val); } #ifdef CONFIG_NUMA @@ -85,10 +101,122 @@ EXPORT_SYMBOL(pcibus_to_node); #endif #ifdef CONFIG_ACPI -/* Root bridge scanning */ + +struct acpi_pci_generic_root_info { + struct acpi_pci_root_info common; + struct pci_config_window *cfg; /* config space mapping */ +}; + +int acpi_pci_bus_domain_nr(struct pci_bus *bus) +{ + struct pci_config_window *cfg = bus->sysdata; + struct acpi_device *adev = to_acpi_device(cfg->parent); + struct acpi_pci_root *root = acpi_driver_data(adev); + + return root->segment; +} + +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + if (!acpi_disabled) { + struct pci_config_window *cfg = bridge->bus->sysdata; + struct acpi_device *adev = to_acpi_device(cfg->parent); + ACPI_COMPANION_SET(&bridge->dev, adev); + } + + return 0; +} + +/* + * Lookup the bus range for the domain in MCFG, and set up config space + * mapping. + */ +static int pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root, + struct acpi_pci_generic_root_info *ri) +{ + struct resource *bus_res = &root->secondary; + u16 seg = root->segment; + struct pci_config_window *cfg; + struct resource cfgres; + unsigned int bsz; + int err; + struct pci_ecam_ops *ops; + + err = pci_mcfg_lookup(root); + if (err) { + pr_err("%04x:%pR MCFG region not found\n", seg, bus_res); + return err; + } + + ops = pci_mcfg_get_ops(root); + bsz = 1 << ops->bus_shift; + cfgres.start = root->mcfg_addr + bus_res->start * bsz; + cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1; + cfgres.flags = IORESOURCE_MEM; + cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops); + if (IS_ERR(cfg)) { + pr_err("%04x:%pR error %ld mapping CAM\n", seg, bus_res, + PTR_ERR(cfg)); + return PTR_ERR(cfg); + } + + ri->cfg = cfg; + return 0; +} + +/* release_info: free resrouces allocated by init_info */ +static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci) +{ + struct acpi_pci_generic_root_info *ri; + + ri = container_of(ci, struct acpi_pci_generic_root_info, common); + pci_ecam_free(ri->cfg); + kfree(ri); +} + +static struct acpi_pci_root_ops acpi_pci_root_ops = { + .release_info = pci_acpi_generic_release_info, +}; + +/* Interface called from ACPI code to setup PCI host controller */ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { - /* TODO: Should be revisited when implementing PCI on ACPI */ - return NULL; + int node = acpi_get_node(root->device->handle); + struct acpi_pci_generic_root_info *ri; + struct pci_bus *bus, *child; + int err; + + ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node); + if (!ri) + return NULL; + + err = pci_acpi_setup_ecam_mapping(root, ri); + if (err) + return NULL; + + acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops; + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common, + ri->cfg); + if (!bus) + return NULL; + + pci_bus_size_bridges(bus); + pci_bus_assign_resources(bus); + + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + + return bus; +} + +void pcibios_add_bus(struct pci_bus *bus) +{ + acpi_pci_add_bus(bus); } + +void pcibios_remove_bus(struct pci_bus *bus) +{ + acpi_pci_remove_bus(bus); +} + #endif diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 47ef6f1a1e3c..4752c8c4f7de 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -223,6 +223,9 @@ config ACPI_PROCESSOR_IDLE bool select CPU_IDLE +config ACPI_MCFG + bool + config ACPI_CPPC_LIB bool depends on ACPI_PROCESSOR diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 603563ac2eb2..ad65111687f9 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,6 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o +obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c new file mode 100644 index 000000000000..f3d4570a4f9c --- /dev/null +++ b/drivers/acpi/pci_mcfg.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2016 Broadcom + * Author: Jayachandran C <jchandra@broadcom.com> + * Copyright (C) 2016 Semihalf + * Author: Tomasz Nowicki <tn@semihalf.com> + * + * 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 (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#define pr_fmt(fmt) "ACPI: " fmt + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h> + +/* Root pointer to the mapped MCFG table */ +static struct acpi_table_mcfg *mcfg_table; +static int mcfg_entries; + +extern struct pci_cfg_fixup __start_acpi_mcfg_fixups[]; +extern struct pci_cfg_fixup __end_acpi_mcfg_fixups[]; + +struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) +{ + int bus_num = root->secondary.start; + int domain = root->segment; + struct pci_cfg_fixup *f; + + if (!mcfg_table) + return &pci_generic_ecam_ops; + + /* + * Match against platform specific quirks and return corresponding + * CAM ops. + * + * First match against PCI topology <domain:bus> then use OEM ID and + * OEM revision from MCFG table standard header. + */ + for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) { + if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && + (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && + (!strncmp(f->oem_id, mcfg_table->header.oem_id, + ACPI_OEM_ID_SIZE)) && + (f->oem_revision == mcfg_table->header.oem_revision)) + return f->ops; + } + /* No quirks, use ECAM */ + return &pci_generic_ecam_ops; +} + +int pci_mcfg_lookup(struct acpi_pci_root *root) +{ + struct acpi_mcfg_allocation *mptr, *entry = NULL; + struct resource *bus_res = &root->secondary; + int i; + + if (mcfg_table) { + mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1]; + for (i = 0; i < mcfg_entries && !entry; i++, mptr++) + if (mptr->pci_segment == root->segment && + mptr->start_bus_number == bus_res->start) + entry = mptr; + } + + /* not found, use _CBA if available, else error */ + if (!entry) { + if (root->mcfg_addr) + return root->mcfg_addr; + pr_err("%04x:%pR MCFG lookup failed\n", root->segment, bus_res); + return -ENOENT; + } else if (root->mcfg_addr && entry->address != root->mcfg_addr) { + pr_warn("%04x:%pR CBA %pa != MCFG %lx, using CBA\n", + root->segment, bus_res, &root->mcfg_addr, + (unsigned long)entry->address); + return 0; + } + + /* found matching entry, bus range check */ + if (entry->end_bus_number != bus_res->end) { + resource_size_t bus_end = min_t(resource_size_t, + entry->end_bus_number, bus_res->end); + pr_warn("%04x:%pR bus end mismatch, using %02lx\n", + root->segment, bus_res, (unsigned long)bus_end); + bus_res->end = bus_end; + } + + if (!root->mcfg_addr) + root->mcfg_addr = entry->address; + return 0; +} + +static __init int pci_mcfg_parse(struct acpi_table_header *header) +{ + if (header->length < sizeof(struct acpi_table_mcfg)) + return -EINVAL; + + mcfg_entries = (header->length - sizeof(struct acpi_table_mcfg)) / + sizeof(struct acpi_mcfg_allocation); + if (mcfg_entries == 0) { + pr_err("MCFG has no entries\n"); + return -EINVAL; + } + + mcfg_table = (struct acpi_table_mcfg *)header; + pr_info("MCFG table detected, %d entries\n", mcfg_entries); + return 0; +} + +/* Interface called by ACPI - parse and save MCFG table */ +void __init pci_mmcfg_late_init(void) +{ + int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); + if (err) + pr_err("Failed to parse MCFG (%d)\n", err); +} diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index a35dd237ded9..31323b19d305 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -719,6 +719,40 @@ next: } } +#ifdef PCI_IOBASE +static void acpi_pci_root_remap_iospace(struct resource_entry *entry) +{ + struct resource *res = entry->res; + resource_size_t cpu_addr = res->start; + resource_size_t pci_addr = cpu_addr - entry->offset; + resource_size_t length = resource_size(res); + unsigned long port; + + if (pci_register_io_range(cpu_addr, length)) + goto err; + + port = pci_address_to_pio(cpu_addr); + if (port == (unsigned long)-1) + goto err; + + res->start = port; + res->end = port + length - 1; + entry->offset = port - pci_addr; + + if (pci_remap_iospace(res, cpu_addr) < 0) + goto err; + + pr_info("Remapped I/O %pa to %pR\n", &cpu_addr, res); + return; +err: + res->flags |= IORESOURCE_DISABLED; +} +#else +static void acpi_pci_root_remap_iospace(struct resource_entry *entry) +{ +} +#endif + int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) { int ret; @@ -739,6 +773,9 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) "no IO and memory resources present in _CRS\n"); else { resource_list_for_each_entry_safe(entry, tmp, list) { + if (entry->res->flags & IORESOURCE_IO) + acpi_pci_root_remap_iospace(entry); + if (entry->res->flags & IORESOURCE_DISABLED) resource_list_destroy_entry(entry); else @@ -810,6 +847,8 @@ static void acpi_pci_root_release_info(struct pci_host_bridge *bridge) resource_list_for_each_entry(entry, &bridge->windows) { res = entry->res; + if (res->flags & IORESOURCE_IO) + pci_unmap_iospace(res); if (res->parent && (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) release_resource(res); diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index f9832ad8efe2..66e0d718472f 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -19,10 +19,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pci-ecam.h> #include <linux/slab.h> -#include "ecam.h" - /* * On 64-bit systems, we do a single ioremap for the whole config space * since we have enough virtual address range available. On 32-bit, we @@ -52,6 +51,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev, if (!cfg) return ERR_PTR(-ENOMEM); + cfg->parent = dev; cfg->ops = ops; cfg->busr.start = busr->start; cfg->busr.end = busr->end; @@ -95,7 +95,7 @@ struct pci_config_window *pci_ecam_create(struct device *dev, } if (ops->init) { - err = ops->init(dev, cfg); + err = ops->init(cfg); if (err) goto err_exit; } diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c index 8cba7ab73df9..c18b9e3bb8bd 100644 --- a/drivers/pci/host/pci-host-common.c +++ b/drivers/pci/host/pci-host-common.c @@ -20,10 +20,9 @@ #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_pci.h> +#include <linux/pci-ecam.h> #include <linux/platform_device.h> -#include "../ecam.h" - static int gen_pci_parse_request_of_pci_ranges(struct device *dev, struct list_head *resources, struct resource **bus_range) { diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 6eaceab1bf04..f0ca6de0d87e 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -23,10 +23,9 @@ #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_pci.h> +#include <linux/pci-ecam.h> #include <linux/platform_device.h> -#include "../ecam.h" - static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { .bus_shift = 16, .pci_ops = { diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c index 540d030613eb..a9fc1c9105fd 100644 --- a/drivers/pci/host/pci-thunder-ecam.c +++ b/drivers/pci/host/pci-thunder-ecam.c @@ -11,10 +11,9 @@ #include <linux/ioport.h> #include <linux/of_pci.h> #include <linux/of.h> +#include <linux/pci-ecam.h> #include <linux/platform_device.h> -#include "../ecam.h" - static void set_val(u32 v, int where, int size, u32 *val) { int shift = (where & 3) * 8; diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c index 9b8ab94f3c8c..8f002ef5ea2a 100644 --- a/drivers/pci/host/pci-thunder-pem.c +++ b/drivers/pci/host/pci-thunder-pem.c @@ -18,10 +18,10 @@ #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_pci.h> +#include <linux/pci-acpi.h> +#include <linux/pci-ecam.h> #include <linux/platform_device.h> -#include "../ecam.h" - #define PEM_CFG_WR 0x28 #define PEM_CFG_RD 0x30 @@ -285,31 +285,109 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, return pci_generic_config_write(bus, devfn, where, size, val); } -static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg) +#ifdef CONFIG_ACPI + +struct pem_acpi_res { + struct resource resource; + int found; +}; + +static acpi_status +thunder_pem_cfg(struct acpi_resource *resource, void *ctx) +{ + struct pem_acpi_res *pem_ctx = ctx; + struct resource *res = &pem_ctx->resource; + + if ((resource->type != ACPI_RESOURCE_TYPE_ADDRESS64) || + (resource->data.address32.resource_type != ACPI_MEMORY_RANGE)) + return AE_OK; + + res->start = resource->data.address64.address.minimum; + res->end = resource->data.address64.address.maximum; + res->flags = IORESOURCE_MEM; + + pem_ctx->found++; + return AE_OK; +} + +static acpi_status +thunder_pem_find_dev(acpi_handle handle, u32 level, void *ctx, void **ret) +{ + struct pem_acpi_res *pem_ctx = ctx; + struct acpi_device_info *info; + acpi_status status = AE_OK; + + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) + return AE_OK; + + if (strncmp(info->hardware_id.string, "THRX0001", 8) != 0) + goto out; + + pem_ctx->found = 0; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, thunder_pem_cfg, + pem_ctx); + if (ACPI_FAILURE(status)) + goto out; + + if (pem_ctx->found) + status = AE_CTRL_TERMINATE; +out: + kfree(info); + return status; +} + +static struct resource *thunder_pem_get_acpi_res(struct device *dev) +{ + struct acpi_device *adev = to_acpi_device(dev); + acpi_handle handle = acpi_device_handle(adev); + struct pem_acpi_res *pem_ctx; + acpi_status status; + + pem_ctx = devm_kzalloc(dev, sizeof(*pem_ctx), GFP_KERNEL); + if (!pem_ctx) + return NULL; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + thunder_pem_find_dev, NULL, pem_ctx, NULL); + if (ACPI_FAILURE(status) || !pem_ctx->found) + return NULL; + + return &pem_ctx->resource; +} +#else +static struct resource *thunder_pem_get_acpi_res(struct device *dev) +{ + return NULL; +} +#endif + +static int thunder_pem_init(struct pci_config_window *cfg) { + struct device *dev = cfg->parent; resource_size_t bar4_start; struct resource *res_pem; struct thunder_pem_pci *pem_pci; struct platform_device *pdev; - /* Only OF support for now */ - if (!dev->of_node) - return -EINVAL; - pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); if (!pem_pci) return -ENOMEM; - pdev = to_platform_device(dev); - - /* - * The second register range is the PEM bridge to the PCIe - * bus. It has a different config access method than those - * devices behind the bridge. - */ - res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (acpi_disabled) { + pdev = to_platform_device(dev); + + /* + * The second register range is the PEM bridge to the PCIe + * bus. It has a different config access method than those + * devices behind the bridge. + */ + res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + } else { + res_pem = thunder_pem_get_acpi_res(dev); + } if (!res_pem) { - dev_err(dev, "missing \"reg[1]\"property\n"); + dev_err(dev, "missing configuration region\n"); return -EINVAL; } @@ -362,5 +440,33 @@ static struct platform_driver thunder_pem_driver = { }; module_platform_driver(thunder_pem_driver); +#ifdef CONFIG_ACPI + +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 4, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 5, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 6, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 7, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 8, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 9, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 14, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 15, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 16, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 17, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 18, PCI_MCFG_BUS_ANY); +DECLARE_ACPI_MCFG_FIXUP(&pci_thunder_pem_ops, "CAVIUM", 1, + 19, PCI_MCFG_BUS_ANY); +#endif + MODULE_DESCRIPTION("Thunder PEM PCIe host driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c8b4dbdd1bdd..2b52178a11c9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -7,6 +7,7 @@ * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz> */ +#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/init.h> @@ -25,6 +26,7 @@ #include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/pci_hotplug.h> +#include <linux/vmalloc.h> #include <asm/setup.h> #include <linux/aer.h> #include "pci.h" @@ -3165,6 +3167,23 @@ int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) #endif } +/** + * pci_unmap_iospace - Unmap the memory mapped I/O space + * @res: resource to be unmapped + * + * Unmap the CPU virtual address @res from virtual address space. + * Only architectures that have memory mapped IO functions defined + * (and the PCI_IOBASE value defined) should call this function. + */ +void pci_unmap_iospace(struct resource *res) +{ +#if defined(PCI_IOBASE) && defined(CONFIG_MMU) + unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start; + + unmap_kernel_range(vaddr, resource_size(res)); +#endif +} + static void __pci_set_master(struct pci_dev *dev, bool enable) { u16 old_cmd, cmd; @@ -4923,7 +4942,7 @@ int pci_get_new_domain_nr(void) } #ifdef CONFIG_PCI_DOMAINS_GENERIC -void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +static int of_pci_bus_domain_nr(struct device *parent) { static int use_dt_domains = -1; int domain = -1; @@ -4967,7 +4986,13 @@ void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) domain = -1; } - bus->domain_nr = domain; + return domain; +} + +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +{ + bus->domain_nr = acpi_disabled ? of_pci_bus_domain_nr(parent) : + acpi_pci_bus_domain_nr(bus); } #endif #endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 6a67ab94b553..43604fc68507 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -300,6 +300,13 @@ VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ } \ \ + /* ACPI MCFG quirks */ \ + .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \ + *(.acpi_fixup_mcfg) \ + VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \ + } \ + \ /* Built-in firmware blobs */ \ .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_builtin_fw) = .; \ diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 89ab0572dbc6..b4e810641301 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -24,6 +24,9 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) } extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); +extern int pci_mcfg_lookup(struct acpi_pci_root *root); +extern struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { struct pci_bus *pbus = pdev->bus; @@ -70,6 +73,24 @@ struct acpi_pci_root_ops { int (*prepare_resources)(struct acpi_pci_root_info *info); }; +struct pci_cfg_fixup { + struct pci_ecam_ops *ops; + char *oem_id; + u32 oem_revision; + int domain; + int bus_num; +}; + +#define PCI_MCFG_DOMAIN_ANY -1 +#define PCI_MCFG_BUS_ANY -1 + +/* Designate a routine to fix up buggy MCFG */ +#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, rev, dom, bus) \ + static const struct pci_cfg_fixup __mcfg_fixup_##system##dom##bus\ + __used __attribute__((__section__(".acpi_fixup_mcfg"), \ + aligned((sizeof(void *))))) = \ + { ops, oem_id, rev, dom, bus }; + extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, struct acpi_pci_root_ops *ops, diff --git a/drivers/pci/ecam.h b/include/linux/pci-ecam.h index 9878bebd45bb..7adad206b1f4 100644 --- a/drivers/pci/ecam.h +++ b/include/linux/pci-ecam.h @@ -27,8 +27,7 @@ struct pci_config_window; struct pci_ecam_ops { unsigned int bus_shift; struct pci_ops pci_ops; - int (*init)(struct device *, - struct pci_config_window *); + int (*init)(struct pci_config_window *); }; /* @@ -45,6 +44,7 @@ struct pci_config_window { void __iomem *win; /* 64-bit single mapping */ void __iomem **winp; /* 32-bit per-bus mapping */ }; + struct device *parent;/* ECAM res was from this dev */ }; /* create and free pci_config_window */ diff --git a/include/linux/pci.h b/include/linux/pci.h index b67e4df20801..f66d1881c2d9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1167,6 +1167,7 @@ int pci_register_io_range(phys_addr_t addr, resource_size_t size); unsigned long pci_address_to_pio(phys_addr_t addr); phys_addr_t pci_pio_to_address(unsigned long pio); int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); +void pci_unmap_iospace(struct resource *res); static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar) { @@ -1389,6 +1390,12 @@ static inline int pci_domain_nr(struct pci_bus *bus) { return bus->domain_nr; } +/* Arch specific ACPI hook to set-up domain number */ +#ifdef CONFIG_ACPI +int acpi_pci_bus_domain_nr(struct pci_bus *bus); +#else +static inline int acpi_pci_bus_domain_nr(struct pci_bus *bus) { return -1; } +#endif void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent); #else static inline void pci_bus_assign_domain_nr(struct pci_bus *bus, @@ -1722,7 +1729,7 @@ void pcibios_free_irq(struct pci_dev *dev); extern struct dev_pm_ops pcibios_pm_ops; #endif -#ifdef CONFIG_PCI_MMCONFIG +#if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) void __init pci_mmcfg_early_init(void); void __init pci_mmcfg_late_init(void); #else |