diff options
Diffstat (limited to 'freed-ora/current/master/arm64-pcie-quirks.patch')
-rw-r--r-- | freed-ora/current/master/arm64-pcie-quirks.patch | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/freed-ora/current/master/arm64-pcie-quirks.patch b/freed-ora/current/master/arm64-pcie-quirks.patch new file mode 100644 index 000000000..36e8d3724 --- /dev/null +++ b/freed-ora/current/master/arm64-pcie-quirks.patch @@ -0,0 +1,977 @@ +From 5c4f8b5b68451e5d208a5aefb195fdd108629da4 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki <tn@semihalf.com> +Date: Fri, 9 Sep 2016 21:24:03 +0200 +Subject: [PATCH 1/6] PCI/ACPI: Extend pci_mcfg_lookup() responsibilities + +In preparation for adding MCFG platform specific quirk handling move +CFG resource calculation and ECAM ops assignment to pci_mcfg_lookup(). +It becomes the gate for further ops and CFG resource manipulation +in arch-agnostic code (drivers/acpi/pci_mcfg.c). + +No functionality changes in this patch. + +Signed-off-by: Tomasz Nowicki <tn@semihalf.com> +--- + arch/arm64/kernel/pci.c | 17 +++++------------ + drivers/acpi/pci_mcfg.c | 28 +++++++++++++++++++++++++--- + include/linux/pci-acpi.h | 4 +++- + 3 files changed, 33 insertions(+), 16 deletions(-) + +diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c +index acf3872..fb439c7 100644 +--- a/arch/arm64/kernel/pci.c ++++ b/arch/arm64/kernel/pci.c +@@ -125,24 +125,17 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) + u16 seg = root->segment; + struct pci_config_window *cfg; + struct resource cfgres; +- unsigned int bsz; ++ struct pci_ecam_ops *ecam_ops; ++ int ret; + +- /* 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) { ++ ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); ++ if (ret) { + 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); ++ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ecam_ops); + if (IS_ERR(cfg)) { + dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", + seg, bus_res, PTR_ERR(cfg)); +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index b5b376e..ffcc651 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -22,6 +22,7 @@ + #include <linux/kernel.h> + #include <linux/pci.h> + #include <linux/pci-acpi.h> ++#include <linux/pci-ecam.h> + + /* Structure to hold entries from the MCFG table */ + struct mcfg_entry { +@@ -35,9 +36,18 @@ struct mcfg_entry { + /* List to save MCFG entries */ + static LIST_HEAD(pci_mcfg_list); + +-phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) ++int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, ++ struct pci_ecam_ops **ecam_ops) + { ++ struct pci_ecam_ops *ops = &pci_generic_ecam_ops; ++ struct resource *bus_res = &root->secondary; ++ u16 seg = root->segment; + struct mcfg_entry *e; ++ struct resource res; ++ ++ /* Use address from _CBA if present, otherwise lookup MCFG */ ++ if (root->mcfg_addr) ++ goto skip_lookup; + + /* + * We expect exact match, unless MCFG entry end bus covers more than +@@ -45,10 +55,22 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) + */ + 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; ++ e->bus_end >= bus_res->end) { ++ root->mcfg_addr = e->addr; ++ } ++ + } + ++ if (!root->mcfg_addr) ++ return -ENXIO; ++ ++skip_lookup: ++ memset(&res, 0, sizeof(res)); ++ res.start = root->mcfg_addr + (bus_res->start << 20); ++ res.end = res.start + (resource_size(bus_res) << 20) - 1; ++ res.flags = IORESOURCE_MEM; ++ *cfgres = res; ++ *ecam_ops = ops; + return 0; + } + +diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h +index 7d63a66..7a4e83a 100644 +--- a/include/linux/pci-acpi.h ++++ b/include/linux/pci-acpi.h +@@ -24,7 +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 phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); ++struct pci_ecam_ops; ++extern int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, ++ struct pci_ecam_ops **ecam_ops); + + static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) + { +-- +2.9.3 + +From 16c02d9cc0e67b48c343aecc4b5566e729a97683 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki <tn@semihalf.com> +Date: Fri, 9 Sep 2016 21:24:04 +0200 +Subject: [PATCH 2/6] PCI/ACPI: Check platform specific ECAM quirks + +Some platforms may not be fully compliant with generic set of PCI config +accessors. For these cases we implement the way to overwrite CFG accessors +set and configuration space range. + +In first place pci_mcfg_parse() saves machine's IDs and revision number +(these come from MCFG header) in order to match against known quirk entries. +Then the algorithm traverses available quirk list (static array), +matches against <oem_id, oem_table_id, rev, domain, bus number range> and +returns custom PCI config ops and/or CFG resource structure. + +When adding new quirk there are two possibilities: +1. Override default pci_generic_ecam_ops ops but CFG resource comes from MCFG +{ "OEM_ID", "OEM_TABLE_ID", <REV>, <DOMAIN>, <BUS_NR>, &foo_ops, MCFG_RES_EMPTY }, +2. Override default pci_generic_ecam_ops ops and CFG resource. For this case +it is also allowed get CFG resource from quirk entry w/o having it in MCFG. +{ "OEM_ID", "OEM_TABLE_ID", <REV>, <DOMAIN>, <BUS_NR>, &boo_ops, + DEFINE_RES_MEM(START, SIZE) }, + +pci_generic_ecam_ops and MCFG entries will be used for platforms +free from quirks. + +Signed-off-by: Tomasz Nowicki <tn@semihalf.com> +Signed-off-by: Dongdong Liu <liudongdong3@huawei.com> +Signed-off-by: Christopher Covington <cov@codeaurora.org> +--- + drivers/acpi/pci_mcfg.c | 80 +++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 74 insertions(+), 6 deletions(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index ffcc651..2b8acc7 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -32,6 +32,59 @@ struct mcfg_entry { + u8 bus_start; + u8 bus_end; + }; ++struct mcfg_fixup { ++ char oem_id[ACPI_OEM_ID_SIZE + 1]; ++ char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; ++ u32 oem_revision; ++ u16 seg; ++ struct resource bus_range; ++ struct pci_ecam_ops *ops; ++ struct resource cfgres; ++}; ++ ++#define MCFG_DOM_ANY (-1) ++#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \ ++ ((end) - (start) + 1), \ ++ NULL, IORESOURCE_BUS) ++#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff) ++#define MCFG_RES_EMPTY DEFINE_RES_NAMED(0, 0, NULL, 0) ++ ++static struct mcfg_fixup mcfg_quirks[] = { ++/* { OEM_ID, OEM_TABLE_ID, REV, DOMAIN, BUS_RANGE, cfgres, ops }, */ ++}; ++ ++static char mcfg_oem_id[ACPI_OEM_ID_SIZE]; ++static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; ++static u32 mcfg_oem_revision; ++ ++static void pci_mcfg_match_quirks(struct acpi_pci_root *root, ++ struct resource *cfgres, ++ struct pci_ecam_ops **ecam_ops) ++{ ++ struct mcfg_fixup *f; ++ int i; ++ ++ /* ++ * First match against PCI topology <domain:bus> then use OEM ID, OEM ++ * table ID, and OEM revision from MCFG table standard header. ++ */ ++ for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) { ++ if (f->seg == root->segment && ++ resource_contains(&f->bus_range, &root->secondary) && ++ !memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) && ++ !memcmp(f->oem_table_id, mcfg_oem_table_id, ++ ACPI_OEM_TABLE_ID_SIZE) && ++ f->oem_revision == mcfg_oem_revision) { ++ if (f->cfgres.start) ++ *cfgres = f->cfgres; ++ if (f->ops) ++ *ecam_ops = f->ops; ++ dev_info(&root->device->dev, "Applying PCI MCFG quirks for %s %s rev: %d\n", ++ f->oem_id, f->oem_table_id, f->oem_revision); ++ return; ++ } ++ } ++} + + /* List to save MCFG entries */ + static LIST_HEAD(pci_mcfg_list); +@@ -61,14 +114,24 @@ int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, + + } + +- if (!root->mcfg_addr) +- return -ENXIO; +- + skip_lookup: + memset(&res, 0, sizeof(res)); +- res.start = root->mcfg_addr + (bus_res->start << 20); +- res.end = res.start + (resource_size(bus_res) << 20) - 1; +- res.flags = IORESOURCE_MEM; ++ if (root->mcfg_addr) { ++ res.start = root->mcfg_addr + (bus_res->start << 20); ++ res.end = res.start + (resource_size(bus_res) << 20) - 1; ++ res.flags = IORESOURCE_MEM; ++ } ++ ++ /* ++ * Let to override default ECAM ops and CFG resource range. ++ * Also, this might even retrieve CFG resource range in case MCFG ++ * does not have it. Invalid CFG start address means MCFG firmware bug ++ * or we need another quirk in array. ++ */ ++ pci_mcfg_match_quirks(root, &res, &ops); ++ if (!res.start) ++ return -ENXIO; ++ + *cfgres = res; + *ecam_ops = ops; + return 0; +@@ -101,6 +164,11 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header) + list_add(&e->list, &pci_mcfg_list); + } + ++ /* Save MCFG IDs and revision for quirks matching */ ++ memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE); ++ memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); ++ mcfg_oem_revision = header->revision; ++ + pr_info("MCFG table detected, %d entries\n", n); + return 0; + } +-- +2.9.3 + +From 2243ab64c12a873e47b72c8e636b40ed09c5f0d4 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki <tn@semihalf.com> +Date: Fri, 9 Sep 2016 21:24:05 +0200 +Subject: [PATCH 3/6] PCI: thunder-pem: Allow to probe PEM-specific register + range for ACPI case + +thunder-pem driver stands for being ACPI based PCI host controller. +However, there is no standard way to describe its PEM-specific register +ranges in ACPI tables. Thus we add thunder_pem_init() ACPI extension +to obtain hardcoded addresses from static resource array. +Although it is not pretty, it prevents from creating standard mechanism to +handle similar cases in future. + +Signed-off-by: Tomasz Nowicki <tn@semihalf.com> +--- + drivers/pci/host/pci-thunder-pem.c | 61 ++++++++++++++++++++++++++++++-------- + 1 file changed, 48 insertions(+), 13 deletions(-) + +diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c +index 6abaf80..b048761 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -18,6 +18,7 @@ + #include <linux/init.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> + +@@ -284,6 +285,40 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, + return pci_generic_config_write(bus, devfn, where, size, val); + } + ++#ifdef CONFIG_ACPI ++static struct resource thunder_pem_reg_res[] = { ++ [4] = DEFINE_RES_MEM(0x87e0c0000000UL, SZ_16M), ++ [5] = DEFINE_RES_MEM(0x87e0c1000000UL, SZ_16M), ++ [6] = DEFINE_RES_MEM(0x87e0c2000000UL, SZ_16M), ++ [7] = DEFINE_RES_MEM(0x87e0c3000000UL, SZ_16M), ++ [8] = DEFINE_RES_MEM(0x87e0c4000000UL, SZ_16M), ++ [9] = DEFINE_RES_MEM(0x87e0c5000000UL, SZ_16M), ++ [14] = DEFINE_RES_MEM(0x97e0c0000000UL, SZ_16M), ++ [15] = DEFINE_RES_MEM(0x97e0c1000000UL, SZ_16M), ++ [16] = DEFINE_RES_MEM(0x97e0c2000000UL, SZ_16M), ++ [17] = DEFINE_RES_MEM(0x97e0c3000000UL, SZ_16M), ++ [18] = DEFINE_RES_MEM(0x97e0c4000000UL, SZ_16M), ++ [19] = DEFINE_RES_MEM(0x97e0c5000000UL, SZ_16M), ++}; ++ ++static struct resource *thunder_pem_acpi_res(struct pci_config_window *cfg) ++{ ++ struct acpi_device *adev = to_acpi_device(cfg->parent); ++ struct acpi_pci_root *root = acpi_driver_data(adev); ++ ++ if ((root->segment >= 4 && root->segment <= 9) || ++ (root->segment >= 14 && root->segment <= 19)) ++ return &thunder_pem_reg_res[root->segment]; ++ ++ return NULL; ++} ++#else ++static struct resource *thunder_pem_acpi_res(struct pci_config_window *cfg) ++{ ++ return NULL; ++} ++#endif ++ + static int thunder_pem_init(struct pci_config_window *cfg) + { + struct device *dev = cfg->parent; +@@ -292,24 +327,24 @@ static int thunder_pem_init(struct pci_config_window *cfg) + 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_acpi_res(cfg); ++ } + if (!res_pem) { +- dev_err(dev, "missing \"reg[1]\"property\n"); ++ dev_err(dev, "missing configuration region\n"); + return -EINVAL; + } + +-- +2.9.3 + +From 443d85d47ee00b3f0b6f39d470a11e7eb116817d Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki <tn@semihalf.com> +Date: Fri, 9 Sep 2016 21:24:06 +0200 +Subject: [PATCH 4/6] PCI: thunder: Enable ACPI PCI controller for ThunderX + pass2.x silicon version + +ThunderX PCIe controller to off-chip devices (so-called PEM) is not fully +compliant with ECAM standard. It uses non-standard configuration space +accessors (see pci_thunder_pem_ops) and custom configuration space granulation +(see bus_shift = 24). In order to access configuration space and +probe PEM as ACPI based PCI host controller we need to add MCFG quirk +infrastructure. This involves: +1. Export PEM pci_thunder_pem_ops structure so it is visible to MCFG quirk + code. +2. New quirk entries for each PEM segment. Each contains platform IDs, + mentioned pci_thunder_pem_ops and CFG resources. + +Quirk is considered for ThunderX silicon pass2.x only which is identified +via MCFG revision 1. + +Signed-off-by: Tomasz Nowicki <tn@semihalf.com> +--- + drivers/acpi/pci_mcfg.c | 27 +++++++++++++++++++++++++++ + drivers/pci/host/pci-thunder-pem.c | 2 +- + include/linux/pci-ecam.h | 4 ++++ + 3 files changed, 32 insertions(+), 1 deletion(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index 2b8acc7..1f73d7b 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -51,6 +51,33 @@ struct mcfg_fixup { + + static struct mcfg_fixup mcfg_quirks[] = { + /* { OEM_ID, OEM_TABLE_ID, REV, DOMAIN, BUS_RANGE, cfgres, ops }, */ ++#ifdef CONFIG_PCI_HOST_THUNDER_PEM ++ /* SoC pass2.x */ ++ { "CAVIUM", "THUNDERX", 1, 4, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x88001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 5, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x884057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 6, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x88808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 7, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x89001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 8, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x894057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 9, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x89808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 14, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x98001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 15, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x984057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 16, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x98808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 17, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x99001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 18, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x994057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 1, 19, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x99808f000000UL, 0x39 * SZ_16M) }, ++#endif + }; + + static char mcfg_oem_id[ACPI_OEM_ID_SIZE]; +diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c +index b048761..d7c10cc 100644 +--- a/drivers/pci/host/pci-thunder-pem.c ++++ b/drivers/pci/host/pci-thunder-pem.c +@@ -367,7 +367,7 @@ static int thunder_pem_init(struct pci_config_window *cfg) + return 0; + } + +-static struct pci_ecam_ops pci_thunder_pem_ops = { ++struct pci_ecam_ops pci_thunder_pem_ops = { + .bus_shift = 24, + .init = thunder_pem_init, + .pci_ops = { +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +index 7adad20..65505ea 100644 +--- a/include/linux/pci-ecam.h ++++ b/include/linux/pci-ecam.h +@@ -58,6 +58,10 @@ 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; ++/* ECAM ops for known quirks */ ++#ifdef CONFIG_PCI_HOST_THUNDER_PEM ++extern struct pci_ecam_ops pci_thunder_pem_ops; ++#endif + + #ifdef CONFIG_PCI_HOST_GENERIC + /* for DT-based PCI controllers that support ECAM */ +-- +2.9.3 + +From 6eca99cc392a11bb07b9ef88bca71a85f8bbe273 Mon Sep 17 00:00:00 2001 +From: Tomasz Nowicki <tn@semihalf.com> +Date: Fri, 9 Sep 2016 21:24:07 +0200 +Subject: [PATCH 5/6] PCI: thunder: Enable ACPI PCI controller for ThunderX + pass1.x silicon version + +ThunderX pass1.x requires to emulate the EA headers for on-chip devices +hence it has to use custom pci_thunder_ecam_ops for accessing PCI config +space (pci-thuner-ecam.c). Add new entries to MCFG quirk array where they +can be applied while probing ACPI based PCI host controller. + +ThunderX pass1.x is using the same way for accessing off-chip devices +(so-called PEM) as silicon pass-2.x so we need to add PEM quirk +entries too. + +Quirk is considered for ThunderX silicon pass1.x only which is identified +via MCFG revision 2. + +Signed-off-by: Tomasz Nowicki <tn@semihalf.com> +--- + drivers/acpi/pci_mcfg.c | 45 +++++++++++++++++++++++++++++++++++++ + drivers/pci/host/pci-thunder-ecam.c | 2 +- + include/linux/pci-ecam.h | 3 +++ + 3 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index 1f73d7b..eb14f74 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -77,6 +77,51 @@ static struct mcfg_fixup mcfg_quirks[] = { + DEFINE_RES_MEM(0x994057000000UL, 0x39 * SZ_16M) }, + { "CAVIUM", "THUNDERX", 1, 19, MCFG_BUS_ANY, &pci_thunder_pem_ops, + DEFINE_RES_MEM(0x99808f000000UL, 0x39 * SZ_16M) }, ++ ++ /* SoC pass1.x */ ++ { "CAVIUM", "THUNDERX", 2, 4, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x88001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 5, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x884057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 6, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x88808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 7, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x89001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 8, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x894057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 9, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x89808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 14, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x98001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 15, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x984057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 16, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x98808f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 17, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x99001f000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 18, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x994057000000UL, 0x39 * SZ_16M) }, ++ { "CAVIUM", "THUNDERX", 2, 19, MCFG_BUS_ANY, &pci_thunder_pem_ops, ++ DEFINE_RES_MEM(0x99808f000000UL, 0x39 * SZ_16M) }, ++#endif ++#ifdef CONFIG_PCI_HOST_THUNDER_ECAM ++ /* SoC pass1.x */ ++ { "CAVIUM", "THUNDERX", 2, 0, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 1, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 2, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 3, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 10, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 11, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 12, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, ++ { "CAVIUM", "THUNDERX", 2, 13, MCFG_BUS_ANY, &pci_thunder_ecam_ops, ++ MCFG_RES_EMPTY}, + #endif + }; + +diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c +index d50a3dc..b6c17e2 100644 +--- a/drivers/pci/host/pci-thunder-ecam.c ++++ b/drivers/pci/host/pci-thunder-ecam.c +@@ -346,7 +346,7 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, + return pci_generic_config_write(bus, devfn, where, size, val); + } + +-static struct pci_ecam_ops pci_thunder_ecam_ops = { ++struct pci_ecam_ops pci_thunder_ecam_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_ecam_map_bus, +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +index 65505ea..35f0e81 100644 +--- a/include/linux/pci-ecam.h ++++ b/include/linux/pci-ecam.h +@@ -62,6 +62,9 @@ extern struct pci_ecam_ops pci_generic_ecam_ops; + #ifdef CONFIG_PCI_HOST_THUNDER_PEM + extern struct pci_ecam_ops pci_thunder_pem_ops; + #endif ++#ifdef CONFIG_PCI_HOST_THUNDER_ECAM ++extern struct pci_ecam_ops pci_thunder_ecam_ops; ++#endif + + #ifdef CONFIG_PCI_HOST_GENERIC + /* for DT-based PCI controllers that support ECAM */ +-- +2.9.3 + +From 3080ac5bb527155ccdf8490ce221b1c6ad01f502 Mon Sep 17 00:00:00 2001 +From: Duc Dang <dhdang@apm.com> +Date: Sat, 17 Sep 2016 07:24:38 -0700 +Subject: [PATCH 6/6] PCI/ACPI: xgene: Add ECAM quirk for X-Gene PCIe + controller + +PCIe controller in X-Gene SoCs is not ECAM compliant: software +needs to configure additional concontroller register to address +device at bus:dev:function. + +This patch depends on "ECAM quirks handling for ARM64 platforms" +series (http://www.spinics.net/lists/arm-kernel/msg530692.html) +to address the limitation above for X-Gene PCIe controller. + +The quirk will only be applied for X-Gene PCIe MCFG table with +OEM revison 1, 2, 3 or 4 (PCIe controller v1 and v2 on X-Gene SoCs). + +Signed-off-by: Duc Dang <dhdang@apm.com> +--- + drivers/acpi/pci_mcfg.c | 32 +++++ + drivers/pci/host/Makefile | 2 +- + drivers/pci/host/pci-xgene-ecam.c | 280 ++++++++++++++++++++++++++++++++++++++ + include/linux/pci-ecam.h | 5 + + 4 files changed, 318 insertions(+), 1 deletion(-) + create mode 100644 drivers/pci/host/pci-xgene-ecam.c + +diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c +index eb14f74..635ab24 100644 +--- a/drivers/acpi/pci_mcfg.c ++++ b/drivers/acpi/pci_mcfg.c +@@ -123,6 +123,38 @@ static struct mcfg_fixup mcfg_quirks[] = { + { "CAVIUM", "THUNDERX", 2, 13, MCFG_BUS_ANY, &pci_thunder_ecam_ops, + MCFG_RES_EMPTY}, + #endif ++#ifdef CONFIG_PCI_XGENE ++ {"APM ", "XGENE ", 1, 0, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 1, 1, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 1, 2, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 1, 3, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 1, 4, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 2, 0, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 2, 1, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 2, 2, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 2, 3, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 2, 4, MCFG_BUS_ANY, ++ &xgene_v1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 3, 0, MCFG_BUS_ANY, ++ &xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 3, 1, MCFG_BUS_ANY, ++ &xgene_v2_1_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 4, 0, MCFG_BUS_ANY, ++ &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 4, 1, MCFG_BUS_ANY, ++ &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY}, ++ {"APM ", "XGENE ", 4, 2, MCFG_BUS_ANY, ++ &xgene_v2_2_pcie_ecam_ops, MCFG_RES_EMPTY}, ++#endif + }; + + static char mcfg_oem_id[ACPI_OEM_ID_SIZE]; +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 8843410..af4f505 100644 +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -15,7 +15,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..b66a04f +--- /dev/null ++++ b/drivers/pci/host/pci-xgene-ecam.c +@@ -0,0 +1,280 @@ ++/* ++ * APM X-Gene PCIe ECAM fixup driver ++ * ++ * Copyright (c) 2016, Applied Micro Circuits Corporation ++ * Author: ++ * Duc Dang <dhdang@apm.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. ++ * ++ * 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 <http://www.gnu.org/licenses/>. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_address.h> ++#include <linux/of_pci.h> ++#include <linux/pci-acpi.h> ++#include <linux/platform_device.h> ++#include <linux/pci-ecam.h> ++ ++#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 XGENE_PCIE_IP_VER_2 2 ++ ++#define XGENE_CSR_LENGTH 0x10000 ++ ++struct xgene_pcie_acpi_root { ++ void __iomem *csr_base; ++ u32 version; ++}; ++ ++static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg) ++{ ++ struct xgene_pcie_acpi_root *xgene_root; ++ struct device *dev = cfg->parent; ++ u32 csr_base; ++ ++ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); ++ if (!xgene_root) ++ return -ENOMEM; ++ ++ switch (cfg->res.start) { ++ case 0xE0D0000000ULL: ++ csr_base = 0x1F2B0000; ++ break; ++ case 0xD0D0000000ULL: ++ csr_base = 0x1F2C0000; ++ break; ++ case 0x90D0000000ULL: ++ csr_base = 0x1F2D0000; ++ break; ++ case 0xA0D0000000ULL: ++ csr_base = 0x1F500000; ++ break; ++ case 0xC0D0000000ULL: ++ csr_base = 0x1F510000; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH); ++ if (!xgene_root->csr_base) { ++ kfree(xgene_root); ++ return -ENODEV; ++ } ++ ++ xgene_root->version = XGENE_PCIE_IP_VER_1; ++ ++ cfg->priv = xgene_root; ++ ++ return 0; ++} ++ ++static int xgene_v2_1_pcie_ecam_init(struct pci_config_window *cfg) ++{ ++ struct xgene_pcie_acpi_root *xgene_root; ++ struct device *dev = cfg->parent; ++ resource_size_t csr_base; ++ ++ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); ++ if (!xgene_root) ++ return -ENOMEM; ++ ++ switch (cfg->res.start) { ++ case 0xC0D0000000ULL: ++ csr_base = 0x1F2B0000; ++ break; ++ case 0xA0D0000000ULL: ++ csr_base = 0x1F2C0000; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH); ++ if (!xgene_root->csr_base) { ++ kfree(xgene_root); ++ return -ENODEV; ++ } ++ ++ xgene_root->version = XGENE_PCIE_IP_VER_2; ++ ++ cfg->priv = xgene_root; ++ ++ return 0; ++} ++ ++static int xgene_v2_2_pcie_ecam_init(struct pci_config_window *cfg) ++{ ++ struct xgene_pcie_acpi_root *xgene_root; ++ struct device *dev = cfg->parent; ++ resource_size_t csr_base; ++ ++ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); ++ if (!xgene_root) ++ return -ENOMEM; ++ ++ switch (cfg->res.start) { ++ case 0xE0D0000000ULL: ++ csr_base = 0x1F2B0000; ++ break; ++ case 0xA0D0000000ULL: ++ csr_base = 0x1F500000; ++ break; ++ case 0x90D0000000ULL: ++ csr_base = 0x1F2D0000; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ xgene_root->csr_base = ioremap(csr_base, XGENE_CSR_LENGTH); ++ if (!xgene_root->csr_base) { ++ kfree(xgene_root); ++ return -ENODEV; ++ } ++ ++ xgene_root->version = XGENE_PCIE_IP_VER_2; ++ ++ 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; ++} ++ ++struct pci_ecam_ops xgene_v1_pcie_ecam_ops = { ++ .bus_shift = 16, ++ .init = xgene_v1_pcie_ecam_init, ++ .pci_ops = { ++ .map_bus = xgene_pcie_ecam_map_bus, ++ .read = xgene_pcie_config_read32, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops = { ++ .bus_shift = 16, ++ .init = xgene_v2_1_pcie_ecam_init, ++ .pci_ops = { ++ .map_bus = xgene_pcie_ecam_map_bus, ++ .read = xgene_pcie_config_read32, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops = { ++ .bus_shift = 16, ++ .init = xgene_v2_2_pcie_ecam_init, ++ .pci_ops = { ++ .map_bus = xgene_pcie_ecam_map_bus, ++ .read = xgene_pcie_config_read32, ++ .write = pci_generic_config_write, ++ } ++}; ++#endif +diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h +index 35f0e81..40da3e7 100644 +--- a/include/linux/pci-ecam.h ++++ b/include/linux/pci-ecam.h +@@ -65,6 +65,11 @@ extern struct pci_ecam_ops pci_thunder_pem_ops; + #ifdef CONFIG_PCI_HOST_THUNDER_ECAM + extern struct pci_ecam_ops pci_thunder_ecam_ops; + #endif ++#ifdef CONFIG_PCI_XGENE ++extern struct pci_ecam_ops xgene_v1_pcie_ecam_ops; ++extern struct pci_ecam_ops xgene_v2_1_pcie_ecam_ops; ++extern struct pci_ecam_ops xgene_v2_2_pcie_ecam_ops; ++#endif + + #ifdef CONFIG_PCI_HOST_GENERIC + /* for DT-based PCI controllers that support ECAM */ +-- +2.9.3 + |