diff --git a/arm64-pcie-acpi.patch b/arm64-pcie-acpi.patch new file mode 100644 index 0000000..e9a359d --- /dev/null +++ b/arm64-pcie-acpi.patch @@ -0,0 +1,1247 @@ +From 1fc02559de87cd88339a83ad05baa9c2b5bd1ac0 Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:09 +0200 +Subject: [PATCH 01/11] PCI/ECAM: Move ecam.h to linux/include/pci-ecam.h + +This header will be used from arch/arm64 for ACPI PCI implementation +so it needs to be moved out of drivers/pci. + +Update users of the header file to use the new name. No functional +changes. + +Signed-off-by: Jayachandran C +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/ecam.c | 3 +- + drivers/pci/ecam.h | 67 ------------------------------------- + drivers/pci/host/pci-host-common.c | 3 +- + drivers/pci/host/pci-host-generic.c | 3 +- + drivers/pci/host/pci-thunder-ecam.c | 3 +- + drivers/pci/host/pci-thunder-pem.c | 3 +- + include/linux/pci-ecam.h | 67 +++++++++++++++++++++++++++++++++++++ + 7 files changed, 72 insertions(+), 77 deletions(-) + delete mode 100644 drivers/pci/ecam.h + create mode 100644 include/linux/pci-ecam.h + +diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c +index f9832ad..820e26b 100644 +--- a/drivers/pci/ecam.c ++++ b/drivers/pci/ecam.c +@@ -19,10 +19,9 @@ + #include + #include + #include ++#include + #include + +-#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 +diff --git a/drivers/pci/ecam.h b/drivers/pci/ecam.h +deleted file mode 100644 +index 9878beb..0000000 +--- a/drivers/pci/ecam.h ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Copyright 2016 Broadcom +- * +- * 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. +- */ +-#ifndef DRIVERS_PCI_ECAM_H +-#define DRIVERS_PCI_ECAM_H +- +-#include +-#include +- +-/* +- * struct to hold pci ops and bus shift of the config window +- * for a PCI controller. +- */ +-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 *); +-}; +- +-/* +- * struct to hold the mappings of a config space window. This +- * is expected to be used as sysdata for PCI controllers that +- * use ECAM. +- */ +-struct pci_config_window { +- struct resource res; +- struct resource busr; +- void *priv; +- struct pci_ecam_ops *ops; +- union { +- void __iomem *win; /* 64-bit single mapping */ +- void __iomem **winp; /* 32-bit per-bus mapping */ +- }; +-}; +- +-/* create and free pci_config_window */ +-struct pci_config_window *pci_ecam_create(struct device *dev, +- struct resource *cfgres, struct resource *busr, +- struct pci_ecam_ops *ops); +-void pci_ecam_free(struct pci_config_window *cfg); +- +-/* map_bus when ->sysdata is an instance of pci_config_window */ +-void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, +- int where); +-/* default ECAM ops */ +-extern struct pci_ecam_ops pci_generic_ecam_ops; +- +-#ifdef CONFIG_PCI_HOST_GENERIC +-/* for DT-based PCI controllers that support ECAM */ +-int pci_host_common_probe(struct platform_device *pdev, +- struct pci_ecam_ops *ops); +-#endif +-#endif +diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c +index 8cba7ab..c18b9e3 100644 +--- a/drivers/pci/host/pci-host-common.c ++++ b/drivers/pci/host/pci-host-common.c +@@ -20,10 +20,9 @@ + #include + #include + #include ++#include + #include + +-#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 6eaceab..f0ca6de 100644 +--- a/drivers/pci/host/pci-host-generic.c ++++ b/drivers/pci/host/pci-host-generic.c +@@ -23,10 +23,9 @@ + #include + #include + #include ++#include + #include + +-#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 540d030..a9fc1c9 100644 +--- a/drivers/pci/host/pci-thunder-ecam.c ++++ b/drivers/pci/host/pci-thunder-ecam.c +@@ -11,10 +11,9 @@ + #include + #include + #include ++#include + #include + +-#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 9b8ab94..5020d3d 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -18,10 +18,9 @@ + #include + #include + #include ++#include + #include + +-#include "../ecam.h" +- + #define PEM_CFG_WR 0x28 + #define PEM_CFG_RD 0x30 + +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +new file mode 100644 +index 0000000..9878beb +--- /dev/null ++++ b/include/linux/pci-ecam.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2016 Broadcom ++ * ++ * 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. ++ */ ++#ifndef DRIVERS_PCI_ECAM_H ++#define DRIVERS_PCI_ECAM_H ++ ++#include ++#include ++ ++/* ++ * struct to hold pci ops and bus shift of the config window ++ * for a PCI controller. ++ */ ++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 *); ++}; ++ ++/* ++ * struct to hold the mappings of a config space window. This ++ * is expected to be used as sysdata for PCI controllers that ++ * use ECAM. ++ */ ++struct pci_config_window { ++ struct resource res; ++ struct resource busr; ++ void *priv; ++ struct pci_ecam_ops *ops; ++ union { ++ void __iomem *win; /* 64-bit single mapping */ ++ void __iomem **winp; /* 32-bit per-bus mapping */ ++ }; ++}; ++ ++/* create and free pci_config_window */ ++struct pci_config_window *pci_ecam_create(struct device *dev, ++ struct resource *cfgres, struct resource *busr, ++ struct pci_ecam_ops *ops); ++void pci_ecam_free(struct pci_config_window *cfg); ++ ++/* map_bus when ->sysdata is an instance of pci_config_window */ ++void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where); ++/* default ECAM ops */ ++extern struct pci_ecam_ops pci_generic_ecam_ops; ++ ++#ifdef CONFIG_PCI_HOST_GENERIC ++/* for DT-based PCI controllers that support ECAM */ ++int pci_host_common_probe(struct platform_device *pdev, ++ struct pci_ecam_ops *ops); ++#endif ++#endif +-- +2.7.4 + +From 5eb9996fc097629854f359f9ad3d959fdacb7f8f Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:10 +0200 +Subject: [PATCH 02/11] PCI/ECAM: Add parent device field to pci_config_window + +Add a parent device field to struct pci_config_window. The parent +is not saved now, but will be useful to save it in some cases. +Specifically in case of ACPI for ARM64, it can be used to setup +ACPI companion and domain. + +Since the parent dev is in struct pci_config_window now, we need +not pass it to the init function as a separate argument. + +Signed-off-by: Jayachandran C +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/ecam.c | 3 ++- + drivers/pci/host/pci-thunder-pem.c | 3 ++- + include/linux/pci-ecam.h | 4 ++-- + 3 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c +index 820e26b..66e0d71 100644 +--- a/drivers/pci/ecam.c ++++ b/drivers/pci/ecam.c +@@ -51,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; +@@ -94,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-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c +index 5020d3d..91f6fc6 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -284,8 +284,9 @@ 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) ++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; +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +index 9878beb..7adad20 100644 +--- a/include/linux/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 */ +-- +2.7.4 + +From 6ed6c1365df5c9201e9b275e8ed4eaa64ef2ec0d Mon Sep 17 00:00:00 2001 +From: Sinan Kaya +Date: Fri, 10 Jun 2016 21:55:11 +0200 +Subject: [PATCH 03/11] PCI: Add new function to unmap IO resources + +We need to release I/O resources so that the same I/O resources +can be allocated again in pci_remap_iospace(), like in PCI hotplug removal +scenario. Therefore implement new pci_unmap_iospace() call which +unmaps I/O space as the symmetry to pci_remap_iospace(). + +Signed-off-by: Sinan Kaya +Signed-off-by: Tomasz Nowicki +Acked-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 18 ++++++++++++++++++ + include/linux/pci.h | 1 + + 2 files changed, 19 insertions(+) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index c8b4dbd..eb431b5 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include "pci.h" +@@ -3165,6 +3166,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; +diff --git a/include/linux/pci.h b/include/linux/pci.h +index b67e4df..12349de 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) + { +-- +2.7.4 + +From 62f4d54fc2d2882b9ebaa920189574f422e12207 Mon Sep 17 00:00:00 2001 +From: Jayachandran C +Date: Fri, 10 Jun 2016 21:55:12 +0200 +Subject: [PATCH 04/11] ACPI/PCI: Support IO resources when parsing PCI host + bridge resources + +Platforms that have memory mapped IO port (such as ARM64) need special +handling for PCI I/O resources. For host bridge's resource probing case +these resources need to be fixed up with +pci_register_io_range()/pci_remap_iospace() etc. + +The same I/O resources need to be released after hotplug +removal so that it can be re-added back by the pci_remap_iospace() +function during insertion. As a consequence unmap I/O resources +with pci_unmap_iospace() when we release host bridge resources. + +Signed-off-by: Jayachandran C +Signed-off-by: Sinan Kaya +[ Tomasz: merged in Sinan's patch to unmap IO resources properly, updated changelog] +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/acpi/pci_root.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c +index ae3fe4e..9a26dd1 100644 +--- a/drivers/acpi/pci_root.c ++++ b/drivers/acpi/pci_root.c +@@ -720,6 +720,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 inline 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; +@@ -740,6 +774,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 +@@ -811,6 +848,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); +-- +2.7.4 + +From dd4f7822d702cca83baa1de7a5f69344ffb82af9 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:13 +0200 +Subject: [PATCH 05/11] ACPI/PCI: Add generic MCFG table handling + +According to PCI firmware specifications, on systems booting with ACPI, +PCI configuration for a host bridge must be set-up through the MCFG table +regions for non-hotpluggable bridges and _CBA method for hotpluggable ones. + +Current MCFG table handling code, as implemented for x86, cannot be +easily generalized owing to x86 specific quirks handling and related +code, which makes it hard to reuse on other architectures. + +In order to implement MCFG PCI configuration handling for new platforms +booting with ACPI (eg ARM64) this patch re-implements MCFG handling from +scratch in a streamlined fashion and provides (through a generic +interface available to all arches): + +- Simplified MCFG table parsing (executed through the pci_mmcfg_late_init() + hook as in current x86) +- MCFG regions look-up interface through domain:bus_start:bus_end tuple + +The new MCFG regions handling interface is added to generic ACPI code +so that existing architectures (eg x86) can be moved over to it and +architectures relying on MCFG for ACPI PCI config space can rely on it +without having to resort to arch specific implementations. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + drivers/acpi/Kconfig | 3 ++ + drivers/acpi/Makefile | 1 + + drivers/acpi/pci_mcfg.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/pci-acpi.h | 2 ++ + include/linux/pci.h | 2 +- + 5 files changed, 99 insertions(+), 1 deletion(-) + create mode 100644 drivers/acpi/pci_mcfg.c + +diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig +index b7e2e77..f98c328 100644 +--- a/drivers/acpi/Kconfig ++++ b/drivers/acpi/Kconfig +@@ -217,6 +217,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 251ce85..632e81f 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 0000000..d3c3e85 +--- /dev/null ++++ b/drivers/acpi/pci_mcfg.c +@@ -0,0 +1,92 @@ ++/* ++ * Copyright (C) 2016 Broadcom ++ * Author: Jayachandran C ++ * Copyright (C) 2016 Semihalf ++ * Author: Tomasz Nowicki ++ * ++ * 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 ++#include ++#include ++ ++/* Structure to hold entries from the MCFG table */ ++struct mcfg_entry { ++ struct list_head list; ++ phys_addr_t addr; ++ u16 segment; ++ u8 bus_start; ++ u8 bus_end; ++}; ++ ++/* List to save mcfg entries */ ++static LIST_HEAD(pci_mcfg_list); ++ ++phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) ++{ ++ struct mcfg_entry *e; ++ ++ /* ++ * We expect exact match, unless MCFG entry end bus covers more than ++ * specified by caller. ++ */ ++ list_for_each_entry(e, &pci_mcfg_list, list) { ++ if (e->segment == seg && e->bus_start == bus_res->start && ++ e->bus_end >= bus_res->end) ++ return e->addr; ++ } ++ ++ return 0; ++} ++ ++static __init int pci_mcfg_parse(struct acpi_table_header *header) ++{ ++ struct acpi_table_mcfg *mcfg; ++ struct acpi_mcfg_allocation *mptr; ++ struct mcfg_entry *e, *arr; ++ int i, n; ++ ++ if (header->length < sizeof(struct acpi_table_mcfg)) ++ return -EINVAL; ++ ++ n = (header->length - sizeof(struct acpi_table_mcfg)) / ++ sizeof(struct acpi_mcfg_allocation); ++ mcfg = (struct acpi_table_mcfg *)header; ++ mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; ++ ++ arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); ++ if (!arr) ++ return -ENOMEM; ++ ++ for (i = 0, e = arr; i < n; i++, mptr++, e++) { ++ e->segment = mptr->pci_segment; ++ e->addr = mptr->address; ++ e->bus_start = mptr->start_bus_number; ++ e->bus_end = mptr->end_bus_number; ++ list_add(&e->list, &pci_mcfg_list); ++ } ++ ++ pr_info("MCFG table detected, %d entries\n", n); ++ 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/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index 89ab057..7d63a66 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -24,6 +24,8 @@ 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 phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); ++ + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) + { + struct pci_bus *pbus = pdev->bus; +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 12349de..ce03d65 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1723,7 +1723,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 +-- +2.7.4 + +From fc1907f9c79f9a0a0c2f4d579896e678915fec48 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:14 +0200 +Subject: [PATCH 06/11] PCI: Refactor generic bus domain assignment + +Change the way PCI bus domain number is assigned and improve function +name to reflect what function does. No functional changes. + +Instead of assigning bus domain number inside of pci_bus_assign_domain_nr() +simply return domain number and let pci_create_root_bus() do assignment. +This way pci_create_root_bus() setups bus structure data in the consistent +way. Since pci_bus_assign_domain_nr() now does not assign but retrieves +domain number instead, rename it to pci_bus_find_domain_nr(). + +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 4 ++-- + drivers/pci/probe.c | 4 +++- + include/linux/pci.h | 7 +------ + 3 files changed, 6 insertions(+), 9 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index eb431b5..b9a7833 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -4941,7 +4941,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) ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + { + static int use_dt_domains = -1; + int domain = -1; +@@ -4985,7 +4985,7 @@ void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) + domain = -1; + } + +- bus->domain_nr = domain; ++ return domain; + } + #endif + #endif +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index 8e3ef72..380d46d 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -2127,7 +2127,9 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, + b->sysdata = sysdata; + b->ops = ops; + b->number = b->busn_res.start = bus; +- pci_bus_assign_domain_nr(b, parent); ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ b->domain_nr = pci_bus_find_domain_nr(b, parent); ++#endif + b2 = pci_find_bus(pci_domain_nr(b), bus); + if (b2) { + /* If we already got to this bus through a different bridge, ignore it */ +diff --git a/include/linux/pci.h b/include/linux/pci.h +index ce03d65..48839e8 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1390,12 +1390,7 @@ static inline int pci_domain_nr(struct pci_bus *bus) + { + return bus->domain_nr; + } +-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, +- struct device *parent) +-{ +-} ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + + /* some architectures require additional setup to direct VGA traffic */ +-- +2.7.4 + +From d6d45ae1d58658111d5e838c41b9ed4729bbb81e Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:15 +0200 +Subject: [PATCH 07/11] PCI: Factor DT specific pci_bus_find_domain_nr() code + out + +pci_bus_find_domain_nr() retrieves the host bridge domain number in a DT +specific way. Factor our pci_bus_find_domain_nr() in a separate DT +function (ie of_pci_bus_find_domain_nr()) so that DT code is self +contained, paving the way for retrieving domain number in +pci_bus_find_domain_nr() with additional firmware methods (ie ACPI). + +Signed-off-by: Tomasz Nowicki +Reviewed-by: Lorenzo Pieralisi +--- + drivers/pci/pci.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index b9a7833..97f7cd4 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -4941,7 +4941,7 @@ int pci_get_new_domain_nr(void) + } + + #ifdef CONFIG_PCI_DOMAINS_GENERIC +-int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) ++static int of_pci_bus_find_domain_nr(struct device *parent) + { + static int use_dt_domains = -1; + int domain = -1; +@@ -4987,6 +4987,11 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + + return domain; + } ++ ++int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ return of_pci_bus_find_domain_nr(parent); ++} + #endif + #endif + +-- +2.7.4 + +From 92d59511fd365d3c0c31d074b5e96cf48c58f68b Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Thu, 30 Jun 2016 16:56:24 +0100 +Subject: [PATCH 08/11] ARM64/PCI: Add ACPI hook to assign domain number + +PCI core code provides a config option (CONFIG_PCI_DOMAINS_GENERIC) +that allows assigning the PCI bus domain number generically by +relying on device tree bindings, and falling back to a simple counter +when the respective DT properties (ie "linux,pci-domain") are not +specified in the host bridge device tree node. + +In a similar way, when a system is booted through ACPI, architectures +that are selecting CONFIG_PCI_DOMAINS_GENERIC (ie ARM64) require kernel +hooks to retrieve the domain number so that the PCI bus domain number +set-up can be handled seamlessly with DT and ACPI in generic core code +when CONFIG_PCI_DOMAINS_GENERIC is selected. + +Since currently it is not possible to retrieve a pointer to the PCI +host bridge ACPI device backing the host bridge from core PCI code +(which would allow retrieving the domain number in an arch agnostic +way through the ACPI _SEG method), an arch specific ACPI hook has to +be declared and implemented by all arches that rely on +CONFIG_PCI_DOMAINS_GENERIC to retrieve the domain number and set it +up in core PCI code. + +For the aforementioned reasons, introduce acpi_pci_bus_find_domain_nr() +hook to retrieve the domain number on a per-arch basis when the system +boots through ACPI. ARM64 dummy implementation of the same is provided +in first place in preparation for ARM64 ACPI based PCI host controller +driver. + +acpi_pci_bus_find_domain_nr() is called from generic +pci_bus_find_domain_nr() as an ACPI option to DT domain assignment. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 7 +++++++ + drivers/pci/pci.c | 4 +++- + include/linux/pci.h | 7 +++++++ + 3 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 3c4e308..d5d3d26 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + /* +@@ -85,6 +86,12 @@ EXPORT_SYMBOL(pcibus_to_node); + #endif + + #ifdef CONFIG_ACPI ++ ++int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) ++{ ++ return 0; ++} ++ + /* Root bridge scanning */ + struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) + { +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 97f7cd4..4834cee 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -7,6 +7,7 @@ + * Copyright 1997 -- 2000 Martin Mares + */ + ++#include + #include + #include + #include +@@ -4990,7 +4991,8 @@ static int of_pci_bus_find_domain_nr(struct device *parent) + + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + { +- return of_pci_bus_find_domain_nr(parent); ++ return acpi_disabled ? of_pci_bus_find_domain_nr(parent) : ++ acpi_pci_bus_find_domain_nr(bus); + } + #endif + #endif +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 48839e8..49ba8af 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1390,6 +1390,13 @@ 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_find_domain_nr(struct pci_bus *bus); ++#else ++static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) ++{ return 0; } ++#endif + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + +-- +2.7.4 + +From bb06753d2e163d8100a017f06a6d9dd195d68a76 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:17 +0200 +Subject: [PATCH 09/11] ARM64/PCI: ACPI support for legacy IRQs parsing and + consolidation with DT code + +To enable PCI legacy IRQs on platforms booting with ACPI, arch code +should include ACPI specific callbacks that parse and set-up the +device IRQ number, equivalent to the DT boot path. Owing to the current +ACPI core scan handlers implementation, ACPI PCI legacy IRQs bindings +cannot be parsed at device add time, since that would trigger ACPI scan +handlers ordering issues depending on how the ACPI tables are defined. + +To solve this problem and consolidate FW PCI legacy IRQs parsing in +one single pcibios callback (pending final removal), this patch moves +DT PCI IRQ parsing to the pcibios_alloc_irq() callback (called by +PCI core code at device probe time) and adds ACPI PCI legacy IRQs +parsing to the same callback too, so that FW PCI legacy IRQs parsing +is confined in one single arch callback that can be easily removed +when code parsing PCI legacy IRQs is consolidated and moved to core +PCI code. + +Signed-off-by: Tomasz Nowicki +Suggested-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index d5d3d26..b3b8a2c 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -51,11 +51,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; + } +-- +2.7.4 + +From b6e298840f532192a589f8ade128dec3fef3a4c6 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:18 +0200 +Subject: [PATCH 10/11] ARM64/PCI: Implement ACPI low-level calls to access + PCI_Config region from AML + +ACPI spec6.1 - chapter: 5.5.2.4 defines OperationRegion (Declare Operation +Region). Following the spec: " [...] An Operation Region is a specific +region of operation within an address space that is declared as a subset +of the entire address space using a starting address (offset) and a length. +Control methods must have exclusive access to any address accessed via +fields declared in Operation Regions. [...]". + +OperationRegion allows to declare various of operation region address space +identifiers including PCI_Config. PCI_Config is meant to access PCI +configuration space from the ASL. So every time ASL opcode operates +on PCI_Config space region, ASL interpreter dispatches accesses to OS +low-level calls - raw_pci_write() and raw_pci_read() for Linux - so-called +ACPI RAW accessors. + +In order to support PCI_Config operation region, implement mentioned +raw_pci_write() and raw_pci_read() calls so they find associated bus +and call read/write ops. + +Waiting for clarification in the ACPI specifications in relation +to PCI_Config space handling before PCI bus enumeration is completed, +current code does not support PCI_Config region accesses before PCI bus +enumeration whilst providing full AML PCI_Config access availability +when the PCI bus enumeration is completed by the kernel so that +RAW accessors can look-up PCI operations through the struct pci_bus +associated with a PCI bus. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + arch/arm64/kernel/pci.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index b3b8a2c..328f857 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -71,13 +71,21 @@ int pcibios_alloc_irq(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 +-- +2.7.4 + +From 3b8cff3fa89ba3ef6f1cf09a0667aa470b7fad0b Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki +Date: Fri, 10 Jun 2016 21:55:19 +0200 +Subject: [PATCH 11/11] ARM64/PCI: Support for ACPI based PCI host controller + +Implement pci_acpi_scan_root and other arch-specific call so that ARM64 +can start using ACPI to setup and enumerate PCI buses. + +Prior to buses enumeration the pci_acpi_scan_root() implementation looks +for configuration space start address (obtained through ACPI _CBA method or +MCFG interface). If succeed, it uses ECAM library to create new mapping. +Then it attaches generic ECAM ops (pci_generic_ecam_ops) which are used +for accessing configuration space later on. + +On ARM64, we need to use generic domains (CONFIG_PCI_DOMAINS_GENERIC). +In order to achieve that for ACPI case implement +acpi_pci_bus_find_domain_nr() body so that it retrieves pci_config_window +structure from bus sysdata and eventually gets domain number from +acpi_pci_root structure. + +ACPI requires to run acpi_pci_{add|remove}_bus while new PCI bus is created. +This allows to do some ACPI-specific additional configuration, like +PCI hotplug slot enumeration. In order to fulfill these requirements, +we implement arch-specific pcibios_{add|remove}_bus calls +and call acpi_pci_{add|remove}_bus from there. + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Jayachandran C +Reviewed-by: Lorenzo Pieralisi +--- + arch/arm64/Kconfig | 2 + + arch/arm64/kernel/pci.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 115 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 5a0a691..4806cde 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -3,6 +3,7 @@ config ARM64 + select ACPI_CCA_REQUIRED if ACPI + select ACPI_GENERIC_GSI if ACPI + select ACPI_REDUCED_HARDWARE_ONLY 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 +@@ -96,6 +97,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 328f857..94cd43c 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + #include + + /* +@@ -100,15 +102,123 @@ EXPORT_SYMBOL(pcibus_to_node); + + #ifdef CONFIG_ACPI + ++struct acpi_pci_generic_root_info { ++ struct acpi_pci_root_info common; ++ struct pci_config_window *cfg; /* config space mapping */ ++}; ++ + int acpi_pci_bus_find_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; + } + +-/* Root bridge scanning */ ++/* ++ * Lookup the bus range for the domain in MCFG, and set up config space ++ * mapping. ++ */ ++static struct pci_config_window * ++pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) ++{ ++ struct resource *bus_res = &root->secondary; ++ u16 seg = root->segment; ++ struct pci_config_window *cfg; ++ struct resource cfgres; ++ unsigned int bsz; ++ ++ /* Use address from _CBA if present, otherwise lookup MCFG */ ++ if (!root->mcfg_addr) ++ root->mcfg_addr = pci_mcfg_lookup(seg, bus_res); ++ ++ if (!root->mcfg_addr) { ++ dev_err(&root->device->dev, "%04x:%pR ECAM region not found\n", ++ seg, bus_res); ++ return NULL; ++ } ++ ++ bsz = 1 << pci_generic_ecam_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, ++ &pci_generic_ecam_ops); ++ if (IS_ERR(cfg)) { ++ dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", ++ seg, bus_res, PTR_ERR(cfg)); ++ return NULL; ++ } ++ ++ return cfg; ++} ++ ++/* release_info: free resources 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; ++ ++ ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node); ++ if (!ri) ++ return NULL; ++ ++ ri->cfg = pci_acpi_setup_ecam_mapping(root); ++ if (!ri->cfg) { ++ kfree(ri); ++ 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 +-- +2.7.4 + diff --git a/arm64-pcie-quirks-xgene.patch b/arm64-pcie-quirks-xgene.patch new file mode 100644 index 0000000..8e6805d --- /dev/null +++ b/arm64-pcie-quirks-xgene.patch @@ -0,0 +1,508 @@ +From 767b70aa55d013f0c7589955f410d488fed5776a Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:49:39 +0100 +Subject: [PATCH 1/4] Some platforms may not be fully compliant with generic + set of PCI config accessors. For these cases we implement the way to + overwrite accessors set. Algorithm traverses available quirk list, matches + against tuple and returns + corresponding PCI config ops. oem_id and oem_table_id come from MCFG table + standard header. All quirks can be defined using DECLARE_ACPI_MCFG_FIXUP() + macro and kept self contained. Example: + +/* Custom PCI config ops */ +static struct pci_generic_ecam_ops foo_pci_ops = { + .bus_shift = 24, + .pci_ops = { + .map_bus = pci_ecam_map_bus, + .read = foo_ecam_config_read, + .write = foo_ecam_config_write, + } +}; + +DECLARE_ACPI_MCFG_FIXUP(&foo_pci_ops, , , , ); + +Signed-off-by: Tomasz Nowicki +Signed-off-by: Dongdong Liu +--- + drivers/acpi/pci_mcfg.c | 41 ++++++++++++++++++++++++++++++++++++--- + include/asm-generic/vmlinux.lds.h | 7 +++++++ + include/linux/pci-acpi.h | 20 +++++++++++++++++++ + 3 files changed, 65 insertions(+), 3 deletions(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index d3c3e85..deb0077 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -22,6 +22,10 @@ + #include + #include + #include ++#include ++ ++/* Root pointer to the mapped MCFG table */ ++static struct acpi_table_mcfg *mcfg_table; + + /* Structure to hold entries from the MCFG table */ + struct mcfg_entry { +@@ -35,6 +39,38 @@ struct mcfg_entry { + /* List to save mcfg entries */ + static LIST_HEAD(pci_mcfg_list); + ++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 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)) && ++ (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, ++ ACPI_OEM_TABLE_ID_SIZE))) ++ return f->ops; ++ } ++ /* No quirks, use ECAM */ ++ return &pci_generic_ecam_ops; ++} ++ + phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) + { + struct mcfg_entry *e; +@@ -54,7 +90,6 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) + + static __init int pci_mcfg_parse(struct acpi_table_header *header) + { +- struct acpi_table_mcfg *mcfg; + struct acpi_mcfg_allocation *mptr; + struct mcfg_entry *e, *arr; + int i, n; +@@ -64,8 +99,8 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header) + + n = (header->length - sizeof(struct acpi_table_mcfg)) / + sizeof(struct acpi_mcfg_allocation); +- mcfg = (struct acpi_table_mcfg *)header; +- mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; ++ mcfg_table = (struct acpi_table_mcfg *)header; ++ mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1]; + + arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); + if (!arr) +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index 6a67ab9..43604fc 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 7d63a66..c8a6559 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -25,6 +25,7 @@ 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 phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); ++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) + { +@@ -72,6 +73,25 @@ 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; ++ char *oem_table_id; ++ 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, oem_table_id, dom, bus) \ ++ static const struct pci_cfg_fixup \ ++ __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ ++ __used __attribute__((__section__(".acpi_fixup_mcfg"), \ ++ aligned((sizeof(void *))))) = \ ++ { ops, oem_id, oem_table_id, 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, +-- +2.7.4 + +From 4f86a9b006b25dd7336043dab26058ed6fb2802d Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:52:46 +0100 +Subject: [PATCH 2/4] pci_generic_ecam_ops is used by default. Since there are + platforms which have non-compliant ECAM space we need to overwrite these + accessors prior to PCI buses enumeration. In order to do that we call + pci_mcfg_get_ops to retrieve pci_ecam_ops structure so that we can use proper + PCI config space accessors and bus_shift. + +pci_generic_ecam_ops is still used for platforms free from quirks. + +Signed-off-by: Tomasz Nowicki +--- + arch/arm64/kernel/pci.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index 94cd43c..a891bda 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -139,6 +139,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) + struct pci_config_window *cfg; + struct resource cfgres; + unsigned int bsz; ++ struct pci_ecam_ops *ops; + + /* Use address from _CBA if present, otherwise lookup MCFG */ + if (!root->mcfg_addr) +@@ -150,12 +151,12 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) + return NULL; + } + +- bsz = 1 << pci_generic_ecam_ops.bus_shift; ++ 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, +- &pci_generic_ecam_ops); ++ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops); + if (IS_ERR(cfg)) { + dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", + seg, bus_res, PTR_ERR(cfg)); +-- +2.7.4 + +From cbdbd697bd6d716eb9d1705ee55445432e73eabb Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:53:59 +0100 +Subject: [PATCH 3/4] The ECAM quirk matching criteria per the discussion on + https://lkml.org/lkml/2016/6/13/944 includes: OEM ID, OEM Table ID and OEM + Revision. So this patch adds OEM Table ID into the check to match platform + specific ECAM quirks as well. + +This patch also improve strncmp check using strlen and +min_t to ignore the padding spaces in OEM ID and OEM +Table ID. + +Signed-off-by: Duc Dang +--- + drivers/acpi/pci_mcfg.c | 7 +++++-- + include/linux/pci-acpi.h | 7 ++++--- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index deb0077..307ca9a 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -62,9 +62,12 @@ struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) + 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)) && ++ min_t(size_t, strlen(f->oem_id), ++ ACPI_OEM_ID_SIZE))) && + (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, +- ACPI_OEM_TABLE_ID_SIZE))) ++ min_t(size_t, strlen(f->oem_table_id), ++ ACPI_OEM_TABLE_ID_SIZE))) && ++ (f->oem_revision == mcfg_table->header.oem_revision)) + return f->ops; + } + /* No quirks, use ECAM */ +diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index c8a6559..5148c8d 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -77,6 +77,7 @@ struct pci_cfg_fixup { + struct pci_ecam_ops *ops; + char *oem_id; + char *oem_table_id; ++ u32 oem_revision; + int domain; + int bus_num; + }; +@@ -85,12 +86,12 @@ struct pci_cfg_fixup { + #define PCI_MCFG_BUS_ANY -1 + + /* Designate a routine to fix up buggy MCFG */ +-#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \ ++#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, rev, dom, bus) \ + static const struct pci_cfg_fixup \ +- __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ ++ __mcfg_fixup_##oem_id##oem_table_id##rev##dom##bus \ + __used __attribute__((__section__(".acpi_fixup_mcfg"), \ + aligned((sizeof(void *))))) = \ +- { ops, oem_id, oem_table_id, dom, bus }; ++ { ops, oem_id, oem_table_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, +-- +2.7.4 + +From 78766cf255bc6aafac2f57372a0446f78322da19 Mon Sep 17 00:00:00 2001 +From: Peter Robinson +Date: Tue, 5 Jul 2016 23:55:11 +0100 +Subject: [PATCH 4/4] X-Gene PCIe controller does not fully support ECAM. This + patch adds required ECAM fixup to allow X-Gene PCIe controller to be + functional in ACPI boot mode. + +Signed-off-by: Duc Dang +--- + drivers/pci/host/Makefile | 2 +- + drivers/pci/host/pci-xgene-ecam.c | 194 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 195 insertions(+), 1 deletion(-) + create mode 100644 drivers/pci/host/pci-xgene-ecam.c + +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 9c8698e..3480696 100644 +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -14,7 +14,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o + obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o + obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o + obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o +-obj-$(CONFIG_PCI_XGENE) += pci-xgene.o ++obj-$(CONFIG_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o + obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o + obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o + obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o +diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c +new file mode 100644 +index 0000000..1bea63f +--- /dev/null ++++ b/drivers/pci/host/pci-xgene-ecam.c +@@ -0,0 +1,194 @@ ++/* ++ * APM X-Gene PCIe ECAM fixup driver ++ * ++ * Copyright (c) 2016, Applied Micro Circuits Corporation ++ * Author: ++ * Duc Dang ++ * ++ * 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. ++ * ++ * 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 for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_ACPI ++#define RTDID 0x160 ++#define ROOT_CAP_AND_CTRL 0x5C ++ ++/* PCIe IP version */ ++#define XGENE_PCIE_IP_VER_UNKN 0 ++#define XGENE_PCIE_IP_VER_1 1 ++ ++#define APM_OEM_ID "APM" ++#define APM_XGENE_OEM_TABLE_ID "XGENE" ++#define APM_XGENE_OEM_REV 0x00000002 ++ ++struct xgene_pcie_acpi_root { ++ void __iomem *csr_base; ++ u32 version; ++}; ++ ++static acpi_status xgene_pcie_find_csr_base(struct acpi_resource *acpi_res, ++ void *data) ++{ ++ struct xgene_pcie_acpi_root *root = data; ++ struct acpi_resource_fixed_memory32 *fixed32; ++ ++ if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { ++ fixed32 = &acpi_res->data.fixed_memory32; ++ root->csr_base = ioremap(fixed32->address, ++ fixed32->address_length); ++ return AE_CTRL_TERMINATE; ++ } ++ ++ return AE_OK; ++} ++ ++static int xgene_pcie_ecam_init(struct pci_config_window *cfg) ++{ ++ struct xgene_pcie_acpi_root *xgene_root; ++ struct device *dev = cfg->parent; ++ struct acpi_device *adev = to_acpi_device(dev); ++ acpi_handle handle = acpi_device_handle(adev); ++ ++ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); ++ if (!xgene_root) ++ return -ENOMEM; ++ ++ acpi_walk_resources(handle, METHOD_NAME__CRS, ++ xgene_pcie_find_csr_base, xgene_root); ++ ++ if (!xgene_root->csr_base) { ++ kfree(xgene_root); ++ return -ENODEV; ++ } ++ ++ xgene_root->version = XGENE_PCIE_IP_VER_1; ++ ++ cfg->priv = xgene_root; ++ ++ return 0; ++} ++ ++/* ++ * For Configuration request, RTDID register is used as Bus Number, ++ * Device Number and Function number of the header fields. ++ */ ++static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ struct xgene_pcie_acpi_root *port = cfg->priv; ++ unsigned int b, d, f; ++ u32 rtdid_val = 0; ++ ++ b = bus->number; ++ d = PCI_SLOT(devfn); ++ f = PCI_FUNC(devfn); ++ ++ if (!pci_is_root_bus(bus)) ++ rtdid_val = (b << 8) | (d << 3) | f; ++ ++ writel(rtdid_val, port->csr_base + RTDID); ++ /* read the register back to ensure flush */ ++ readl(port->csr_base + RTDID); ++} ++ ++/* ++ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as ++ * the translation from PCI bus to native BUS. Entire DDR region ++ * is mapped into PCIe space using these registers, so it can be ++ * reached by DMA from EP devices. The BAR0/1 of bridge should be ++ * hidden during enumeration to avoid the sizing and resource allocation ++ * by PCIe core. ++ */ ++static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) ++{ ++ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || ++ (offset == PCI_BASE_ADDRESS_1))) ++ return true; ++ ++ return false; ++} ++ ++void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus, ++ unsigned int devfn, int where) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ unsigned int busn = bus->number; ++ void __iomem *base; ++ ++ if (busn < cfg->busr.start || busn > cfg->busr.end) ++ return NULL; ++ ++ if ((pci_is_root_bus(bus) && devfn != 0) || ++ xgene_pcie_hide_rc_bars(bus, where)) ++ return NULL; ++ ++ xgene_pcie_set_rtdid_reg(bus, devfn); ++ ++ if (busn > cfg->busr.start) ++ base = cfg->win + (1 << cfg->ops->bus_shift); ++ else ++ base = cfg->win; ++ ++ return base + where; ++} ++ ++static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ struct pci_config_window *cfg = bus->sysdata; ++ struct xgene_pcie_acpi_root *port = cfg->priv; ++ ++ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) != ++ PCIBIOS_SUCCESSFUL) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ /* ++ * The v1 controller has a bug in its Configuration Request ++ * Retry Status (CRS) logic: when CRS is enabled and we read the ++ * Vendor and Device ID of a non-existent device, the controller ++ * fabricates return data of 0xFFFF0001 ("device exists but is not ++ * ready") instead of 0xFFFFFFFF ("device does not exist"). This ++ * causes the PCI core to retry the read until it times out. ++ * Avoid this by not claiming to support CRS. ++ */ ++ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) && ++ ((where & ~0x3) == ROOT_CAP_AND_CTRL)) ++ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); ++ ++ if (size <= 2) ++ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ecam_ops xgene_pcie_ecam_ops = { ++ .bus_shift = 16, ++ .init = xgene_pcie_ecam_init, ++ .pci_ops = { ++ .map_bus = xgene_pcie_ecam_map_bus, ++ .read = xgene_pcie_config_read32, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++DECLARE_ACPI_MCFG_FIXUP(&xgene_pcie_ecam_ops, APM_OEM_ID, ++ APM_XGENE_OEM_TABLE_ID, APM_XGENE_OEM_REV, ++ PCI_MCFG_DOMAIN_ANY, PCI_MCFG_BUS_ANY); ++#endif +-- +2.7.4 + diff --git a/kernel.spec b/kernel.spec index d2eb206..dfd0665 100644 --- a/kernel.spec +++ b/kernel.spec @@ -506,6 +506,9 @@ Patch422: geekbox-v4-device-tree-support.patch # This has major conflicts and needs to be rebased # Patch423: Initial-AllWinner-A64-and-PINE64-support.patch +Patch424: arm64-pcie-acpi.patch +Patch425: arm64-pcie-quirks-xgene.patch + # http://www.spinics.net/lists/linux-tegra/msg26029.html Patch426: usb-phy-tegra-Add-38.4MHz-clock-table-entry.patch @@ -2159,6 +2162,9 @@ fi # # %changelog +* Tue Jul 19 2016 Peter Robinson +- Add aarch64 ACPI pci-e patches headed for 4.8 + * Mon Jul 18 2016 Laura Abbott - 4.7.0-0.rc7.git4.1 - Linux v4.7-rc7-92-g47ef4ad