0d24915
From 767b70aa55d013f0c7589955f410d488fed5776a Mon Sep 17 00:00:00 2001
0d24915
From: Peter Robinson <pbrobinson@gmail.com>
0d24915
Date: Tue, 5 Jul 2016 23:49:39 +0100
0d24915
Subject: [PATCH 1/4] Some platforms may not be fully compliant with generic
0d24915
 set of PCI config accessors. For these cases we implement the way to
0d24915
 overwrite accessors set. Algorithm traverses available quirk list, matches
0d24915
 against <oem_id, oem_table_id, domain, bus number> tuple and returns
0d24915
 corresponding PCI config ops. oem_id and oem_table_id come from MCFG table
0d24915
 standard header. All quirks can be defined using DECLARE_ACPI_MCFG_FIXUP()
0d24915
 macro and kept self contained. Example:
0d24915
0d24915
/* Custom PCI config ops */
0d24915
static struct pci_generic_ecam_ops foo_pci_ops = {
0d24915
        .bus_shift      = 24,
0d24915
        .pci_ops = {
0d24915
                .map_bus = pci_ecam_map_bus,
0d24915
                .read = foo_ecam_config_read,
0d24915
                .write = foo_ecam_config_write,
0d24915
        }
0d24915
};
0d24915
0d24915
DECLARE_ACPI_MCFG_FIXUP(&foo_pci_ops, <oem_id_str>, <oem_table_id>, <domain_nr>, <bus_nr>);
0d24915
0d24915
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
0d24915
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
0d24915
---
0d24915
 drivers/acpi/pci_mcfg.c           | 41 ++++++++++++++++++++++++++++++++++++---
0d24915
 include/asm-generic/vmlinux.lds.h |  7 +++++++
0d24915
 include/linux/pci-acpi.h          | 20 +++++++++++++++++++
0d24915
 3 files changed, 65 insertions(+), 3 deletions(-)
0d24915
0d24915
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
0d24915
index d3c3e85..deb0077 100644
0d24915
--- a/drivers/acpi/pci_mcfg.c
0d24915
+++ b/drivers/acpi/pci_mcfg.c
0d24915
@@ -22,6 +22,10 @@
0d24915
 #include <linux/kernel.h>
0d24915
 #include <linux/pci.h>
0d24915
 #include <linux/pci-acpi.h>
0d24915
+#include <linux/pci-ecam.h>
0d24915
+
0d24915
+/* Root pointer to the mapped MCFG table */
0d24915
+static struct acpi_table_mcfg *mcfg_table;
0d24915
 
0d24915
 /* Structure to hold entries from the MCFG table */
0d24915
 struct mcfg_entry {
0d24915
@@ -35,6 +39,38 @@ struct mcfg_entry {
0d24915
 /* List to save mcfg entries */
0d24915
 static LIST_HEAD(pci_mcfg_list);
0d24915
 
0d24915
+extern struct pci_cfg_fixup __start_acpi_mcfg_fixups[];
0d24915
+extern struct pci_cfg_fixup __end_acpi_mcfg_fixups[];
0d24915
+
0d24915
+struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
0d24915
+{
0d24915
+       int bus_num = root->secondary.start;
0d24915
+       int domain = root->segment;
0d24915
+       struct pci_cfg_fixup *f;
0d24915
+
0d24915
+       if (!mcfg_table)
0d24915
+               return &pci_generic_ecam_ops;
0d24915
+
0d24915
+       /*
0d24915
+        * Match against platform specific quirks and return corresponding
0d24915
+        * CAM ops.
0d24915
+        *
0d24915
+        * First match against PCI topology <domain:bus> then use OEM ID and
0d24915
+        * OEM revision from MCFG table standard header.
0d24915
+        */
0d24915
+       for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) {
0d24915
+               if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
0d24915
+                   (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
0d24915
+                   (!strncmp(f->oem_id, mcfg_table->header.oem_id,
0d24915
+                             ACPI_OEM_ID_SIZE)) &&
0d24915
+                   (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id,
0d24915
+                             ACPI_OEM_TABLE_ID_SIZE)))
0d24915
+                       return f->ops;
0d24915
+       }
0d24915
+       /* No quirks, use ECAM */
0d24915
+       return &pci_generic_ecam_ops;
0d24915
+}
0d24915
+
0d24915
 phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
0d24915
 {
0d24915
 	struct mcfg_entry *e;
0d24915
@@ -54,7 +90,6 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
0d24915
 
0d24915
 static __init int pci_mcfg_parse(struct acpi_table_header *header)
0d24915
 {
0d24915
-	struct acpi_table_mcfg *mcfg;
0d24915
 	struct acpi_mcfg_allocation *mptr;
0d24915
 	struct mcfg_entry *e, *arr;
0d24915
 	int i, n;
0d24915
@@ -64,8 +99,8 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header)
0d24915
 
0d24915
 	n = (header->length - sizeof(struct acpi_table_mcfg)) /
0d24915
 					sizeof(struct acpi_mcfg_allocation);
0d24915
-	mcfg = (struct acpi_table_mcfg *)header;
0d24915
-	mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
0d24915
+	mcfg_table = (struct acpi_table_mcfg *)header;
0d24915
+	mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1];
0d24915
 
0d24915
 	arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
0d24915
 	if (!arr)
0d24915
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
0d24915
index 6a67ab9..43604fc 100644
0d24915
--- a/include/asm-generic/vmlinux.lds.h
0d24915
+++ b/include/asm-generic/vmlinux.lds.h
0d24915
@@ -300,6 +300,13 @@
0d24915
 		VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .;	\
0d24915
 	}								\
0d24915
 									\
0d24915
+	/* ACPI MCFG quirks */						\
0d24915
+	.acpi_fixup        : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) {	\
0d24915
+		VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .;		\
0d24915
+		*(.acpi_fixup_mcfg)					\
0d24915
+		VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .;		\
0d24915
+	}								\
0d24915
+									\
0d24915
 	/* Built-in firmware blobs */					\
0d24915
 	.builtin_fw        : AT(ADDR(.builtin_fw) - LOAD_OFFSET) {	\
0d24915
 		VMLINUX_SYMBOL(__start_builtin_fw) = .;			\
0d24915
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
0d24915
index 7d63a66..c8a6559 100644
0d24915
--- a/include/linux/pci-acpi.h
0d24915
+++ b/include/linux/pci-acpi.h
0d24915
@@ -25,6 +25,7 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
0d24915
 extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
0d24915
 
0d24915
 extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res);
0d24915
+extern struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root);
0d24915
 
0d24915
 static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
0d24915
 {
0d24915
@@ -72,6 +73,25 @@ struct acpi_pci_root_ops {
0d24915
 	int (*prepare_resources)(struct acpi_pci_root_info *info);
0d24915
 };
0d24915
 
0d24915
+struct pci_cfg_fixup {
0d24915
+       struct pci_ecam_ops *ops;
0d24915
+       char *oem_id;
0d24915
+       char *oem_table_id;
0d24915
+       int domain;
0d24915
+       int bus_num;
0d24915
+};
0d24915
+
0d24915
+#define PCI_MCFG_DOMAIN_ANY    -1
0d24915
+#define PCI_MCFG_BUS_ANY       -1
0d24915
+
0d24915
+/* Designate a routine to fix up buggy MCFG */
0d24915
+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus)   \
0d24915
+       static const struct pci_cfg_fixup                               \
0d24915
+       __mcfg_fixup_##oem_id##oem_table_id##dom##bus                   \
0d24915
+       __used  __attribute__((__section__(".acpi_fixup_mcfg"),         \
0d24915
+                               aligned((sizeof(void *))))) =           \
0d24915
+       { ops, oem_id, oem_table_id, dom, bus };
0d24915
+
0d24915
 extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info);
0d24915
 extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
0d24915
 					    struct acpi_pci_root_ops *ops,
0d24915
-- 
0d24915
2.7.4
0d24915
0d24915
From 4f86a9b006b25dd7336043dab26058ed6fb2802d Mon Sep 17 00:00:00 2001
0d24915
From: Peter Robinson <pbrobinson@gmail.com>
0d24915
Date: Tue, 5 Jul 2016 23:52:46 +0100
0d24915
Subject: [PATCH 2/4] pci_generic_ecam_ops is used by default. Since there are
0d24915
 platforms which have non-compliant ECAM space we need to overwrite these
0d24915
 accessors prior to PCI buses enumeration. In order to do that we call
0d24915
 pci_mcfg_get_ops to retrieve pci_ecam_ops structure so that we can use proper
0d24915
 PCI config space accessors and bus_shift.
0d24915
0d24915
pci_generic_ecam_ops is still used for platforms free from quirks.
0d24915
0d24915
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
0d24915
---
0d24915
 arch/arm64/kernel/pci.c | 7 ++++---
0d24915
 1 file changed, 4 insertions(+), 3 deletions(-)
0d24915
0d24915
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
0d24915
index 94cd43c..a891bda 100644
0d24915
--- a/arch/arm64/kernel/pci.c
0d24915
+++ b/arch/arm64/kernel/pci.c
0d24915
@@ -139,6 +139,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
0d24915
 	struct pci_config_window *cfg;
0d24915
 	struct resource cfgres;
0d24915
 	unsigned int bsz;
0d24915
+	struct pci_ecam_ops *ops;
0d24915
 
0d24915
 	/* Use address from _CBA if present, otherwise lookup MCFG */
0d24915
 	if (!root->mcfg_addr)
0d24915
@@ -150,12 +151,12 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
0d24915
 		return NULL;
0d24915
 	}
0d24915
 
0d24915
-	bsz = 1 << pci_generic_ecam_ops.bus_shift;
0d24915
+	ops = pci_mcfg_get_ops(root);
0d24915
+	bsz = 1 << ops->bus_shift;
0d24915
 	cfgres.start = root->mcfg_addr + bus_res->start * bsz;
0d24915
 	cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1;
0d24915
 	cfgres.flags = IORESOURCE_MEM;
0d24915
-	cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res,
0d24915
-			      &pci_generic_ecam_ops);
0d24915
+	cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops);
0d24915
 	if (IS_ERR(cfg)) {
0d24915
 		dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n",
0d24915
 			seg, bus_res, PTR_ERR(cfg));
0d24915
-- 
0d24915
2.7.4
0d24915
0d24915
From cbdbd697bd6d716eb9d1705ee55445432e73eabb Mon Sep 17 00:00:00 2001
0d24915
From: Peter Robinson <pbrobinson@gmail.com>
0d24915
Date: Tue, 5 Jul 2016 23:53:59 +0100
0d24915
Subject: [PATCH 3/4] The ECAM quirk matching criteria per the discussion on
0d24915
 https://lkml.org/lkml/2016/6/13/944 includes: OEM ID, OEM Table ID and OEM
0d24915
 Revision. So this patch adds OEM Table ID into the check to match platform
0d24915
 specific ECAM quirks as well.
0d24915
0d24915
This patch also improve strncmp check using strlen and
0d24915
min_t to ignore the padding spaces in OEM ID and OEM
0d24915
Table ID.
0d24915
0d24915
Signed-off-by: Duc Dang <dhdang@apm.com>
0d24915
---
0d24915
 drivers/acpi/pci_mcfg.c  | 7 +++++--
0d24915
 include/linux/pci-acpi.h | 7 ++++---
0d24915
 2 files changed, 9 insertions(+), 5 deletions(-)
0d24915
0d24915
diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
0d24915
index deb0077..307ca9a 100644
0d24915
--- a/drivers/acpi/pci_mcfg.c
0d24915
+++ b/drivers/acpi/pci_mcfg.c
0d24915
@@ -62,9 +62,12 @@ struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
0d24915
                if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
0d24915
                    (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
0d24915
                    (!strncmp(f->oem_id, mcfg_table->header.oem_id,
0d24915
-                             ACPI_OEM_ID_SIZE)) &&
0d24915
+                             min_t(size_t, strlen(f->oem_id),
0d24915
+                                   ACPI_OEM_ID_SIZE))) &&
0d24915
                    (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id,
0d24915
-                             ACPI_OEM_TABLE_ID_SIZE)))
0d24915
+                             min_t(size_t, strlen(f->oem_table_id),
0d24915
+                                   ACPI_OEM_TABLE_ID_SIZE))) &&
0d24915
+                   (f->oem_revision == mcfg_table->header.oem_revision))
0d24915
                        return f->ops;
0d24915
        }
0d24915
        /* No quirks, use ECAM */
0d24915
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
0d24915
index c8a6559..5148c8d 100644
0d24915
--- a/include/linux/pci-acpi.h
0d24915
+++ b/include/linux/pci-acpi.h
0d24915
@@ -77,6 +77,7 @@ struct pci_cfg_fixup {
0d24915
        struct pci_ecam_ops *ops;
0d24915
        char *oem_id;
0d24915
        char *oem_table_id;
0d24915
+       u32 oem_revision;
0d24915
        int domain;
0d24915
        int bus_num;
0d24915
 };
0d24915
@@ -85,12 +86,12 @@ struct pci_cfg_fixup {
0d24915
 #define PCI_MCFG_BUS_ANY       -1
0d24915
 
0d24915
 /* Designate a routine to fix up buggy MCFG */
0d24915
-#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus)   \
0d24915
+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, rev, dom, bus) \
0d24915
        static const struct pci_cfg_fixup                               \
0d24915
-       __mcfg_fixup_##oem_id##oem_table_id##dom##bus                   \
0d24915
+       __mcfg_fixup_##oem_id##oem_table_id##rev##dom##bus              \
0d24915
        __used  __attribute__((__section__(".acpi_fixup_mcfg"),         \
0d24915
                                aligned((sizeof(void *))))) =           \
0d24915
-       { ops, oem_id, oem_table_id, dom, bus };
0d24915
+       { ops, oem_id, oem_table_id, rev, dom, bus };
0d24915
 
0d24915
 extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info);
0d24915
 extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
0d24915
-- 
0d24915
2.7.4
0d24915
0d24915
From 78766cf255bc6aafac2f57372a0446f78322da19 Mon Sep 17 00:00:00 2001
0d24915
From: Peter Robinson <pbrobinson@gmail.com>
0d24915
Date: Tue, 5 Jul 2016 23:55:11 +0100
0d24915
Subject: [PATCH 4/4] X-Gene PCIe controller does not fully support ECAM. This
0d24915
 patch adds required ECAM fixup to allow X-Gene PCIe controller to be
0d24915
 functional in ACPI boot mode.
0d24915
0d24915
Signed-off-by: Duc Dang <dhdang@apm.com>
0d24915
---
0d24915
 drivers/pci/host/Makefile         |   2 +-
0d24915
 drivers/pci/host/pci-xgene-ecam.c | 194 ++++++++++++++++++++++++++++++++++++++
0d24915
 2 files changed, 195 insertions(+), 1 deletion(-)
0d24915
 create mode 100644 drivers/pci/host/pci-xgene-ecam.c
0d24915
0d24915
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
0d24915
index 9c8698e..3480696 100644
0d24915
--- a/drivers/pci/host/Makefile
0d24915
+++ b/drivers/pci/host/Makefile
0d24915
@@ -14,7 +14,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
0d24915
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
0d24915
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
0d24915
 obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
0d24915
-obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
0d24915
+obj-$(CONFIG_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o
0d24915
 obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
0d24915
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
0d24915
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
0d24915
diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c
0d24915
new file mode 100644
0d24915
index 0000000..1bea63f
0d24915
--- /dev/null
0d24915
+++ b/drivers/pci/host/pci-xgene-ecam.c
0d24915
@@ -0,0 +1,194 @@
0d24915
+/*
0d24915
+ * APM X-Gene PCIe ECAM fixup driver
0d24915
+ *
0d24915
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
0d24915
+ * Author:
0d24915
+ *     Duc Dang <dhdang@apm.com>
0d24915
+ *
0d24915
+ * This program is free software; you can redistribute it and/or modify
0d24915
+ * it under the terms of the GNU General Public License version 2 as
0d24915
+ * published by the Free Software Foundation.
0d24915
+ *
0d24915
+ * This program is distributed in the hope that it will be useful,
0d24915
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
0d24915
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0d24915
+ * GNU General Public License for more details.
0d24915
+ *
0d24915
+ * You should have received a copy of the GNU General Public License
0d24915
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0d24915
+ */
0d24915
+
0d24915
+#include <linux/kernel.h>
0d24915
+#include <linux/module.h>
0d24915
+#include <linux/of_address.h>
0d24915
+#include <linux/of_pci.h>
0d24915
+#include <linux/pci-acpi.h>
0d24915
+#include <linux/platform_device.h>
0d24915
+#include <linux/pci-ecam.h>
0d24915
+
0d24915
+#ifdef CONFIG_ACPI
0d24915
+#define RTDID                  0x160
0d24915
+#define ROOT_CAP_AND_CTRL      0x5C
0d24915
+
0d24915
+/* PCIe IP version */
0d24915
+#define XGENE_PCIE_IP_VER_UNKN 0
0d24915
+#define XGENE_PCIE_IP_VER_1    1
0d24915
+
0d24915
+#define APM_OEM_ID             "APM"
0d24915
+#define APM_XGENE_OEM_TABLE_ID "XGENE"
0d24915
+#define APM_XGENE_OEM_REV      0x00000002
0d24915
+
0d24915
+struct xgene_pcie_acpi_root {
0d24915
+       void __iomem *csr_base;
0d24915
+       u32 version;
0d24915
+};
0d24915
+
0d24915
+static acpi_status xgene_pcie_find_csr_base(struct acpi_resource *acpi_res,
0d24915
+                                           void *data)
0d24915
+{
0d24915
+       struct xgene_pcie_acpi_root *root = data;
0d24915
+       struct acpi_resource_fixed_memory32 *fixed32;
0d24915
+
0d24915
+       if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
0d24915
+               fixed32 = &acpi_res->data.fixed_memory32;
0d24915
+               root->csr_base = ioremap(fixed32->address,
0d24915
+                                        fixed32->address_length);
0d24915
+               return AE_CTRL_TERMINATE;
0d24915
+       }
0d24915
+
0d24915
+       return AE_OK;
0d24915
+}
0d24915
+
0d24915
+static int xgene_pcie_ecam_init(struct pci_config_window *cfg)
0d24915
+{
0d24915
+       struct xgene_pcie_acpi_root *xgene_root;
0d24915
+       struct device *dev = cfg->parent;
0d24915
+       struct acpi_device *adev = to_acpi_device(dev);
0d24915
+       acpi_handle handle = acpi_device_handle(adev);
0d24915
+
0d24915
+       xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL);
0d24915
+       if (!xgene_root)
0d24915
+               return -ENOMEM;
0d24915
+
0d24915
+       acpi_walk_resources(handle, METHOD_NAME__CRS,
0d24915
+                           xgene_pcie_find_csr_base, xgene_root);
0d24915
+
0d24915
+       if (!xgene_root->csr_base) {
0d24915
+               kfree(xgene_root);
0d24915
+               return -ENODEV;
0d24915
+       }
0d24915
+
0d24915
+       xgene_root->version = XGENE_PCIE_IP_VER_1;
0d24915
+
0d24915
+       cfg->priv = xgene_root;
0d24915
+
0d24915
+       return 0;
0d24915
+}
0d24915
+
0d24915
+/*
0d24915
+ * For Configuration request, RTDID register is used as Bus Number,
0d24915
+ * Device Number and Function number of the header fields.
0d24915
+ */
0d24915
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
0d24915
+{
0d24915
+       struct pci_config_window *cfg = bus->sysdata;
0d24915
+       struct xgene_pcie_acpi_root *port = cfg->priv;
0d24915
+       unsigned int b, d, f;
0d24915
+       u32 rtdid_val = 0;
0d24915
+
0d24915
+       b = bus->number;
0d24915
+       d = PCI_SLOT(devfn);
0d24915
+       f = PCI_FUNC(devfn);
0d24915
+
0d24915
+       if (!pci_is_root_bus(bus))
0d24915
+               rtdid_val = (b << 8) | (d << 3) | f;
0d24915
+
0d24915
+       writel(rtdid_val, port->csr_base + RTDID);
0d24915
+       /* read the register back to ensure flush */
0d24915
+       readl(port->csr_base + RTDID);
0d24915
+}
0d24915
+
0d24915
+/*
0d24915
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
0d24915
+ * the translation from PCI bus to native BUS.  Entire DDR region
0d24915
+ * is mapped into PCIe space using these registers, so it can be
0d24915
+ * reached by DMA from EP devices.  The BAR0/1 of bridge should be
0d24915
+ * hidden during enumeration to avoid the sizing and resource allocation
0d24915
+ * by PCIe core.
0d24915
+ */
0d24915
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
0d24915
+{
0d24915
+       if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
0d24915
+                                    (offset == PCI_BASE_ADDRESS_1)))
0d24915
+               return true;
0d24915
+
0d24915
+       return false;
0d24915
+}
0d24915
+
0d24915
+void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus,
0d24915
+                                     unsigned int devfn, int where)
0d24915
+{
0d24915
+       struct pci_config_window *cfg = bus->sysdata;
0d24915
+       unsigned int busn = bus->number;
0d24915
+       void __iomem *base;
0d24915
+
0d24915
+       if (busn < cfg->busr.start || busn > cfg->busr.end)
0d24915
+               return NULL;
0d24915
+
0d24915
+       if ((pci_is_root_bus(bus) && devfn != 0) ||
0d24915
+           xgene_pcie_hide_rc_bars(bus, where))
0d24915
+               return NULL;
0d24915
+
0d24915
+       xgene_pcie_set_rtdid_reg(bus, devfn);
0d24915
+
0d24915
+       if (busn > cfg->busr.start)
0d24915
+               base = cfg->win + (1 << cfg->ops->bus_shift);
0d24915
+       else
0d24915
+               base = cfg->win;
0d24915
+
0d24915
+       return base + where;
0d24915
+}
0d24915
+
0d24915
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
0d24915
+                                   int where, int size, u32 *val)
0d24915
+{
0d24915
+       struct pci_config_window *cfg = bus->sysdata;
0d24915
+       struct xgene_pcie_acpi_root *port = cfg->priv;
0d24915
+
0d24915
+       if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
0d24915
+           PCIBIOS_SUCCESSFUL)
0d24915
+               return PCIBIOS_DEVICE_NOT_FOUND;
0d24915
+
0d24915
+       /*
0d24915
+       * The v1 controller has a bug in its Configuration Request
0d24915
+       * Retry Status (CRS) logic: when CRS is enabled and we read the
0d24915
+       * Vendor and Device ID of a non-existent device, the controller
0d24915
+       * fabricates return data of 0xFFFF0001 ("device exists but is not
0d24915
+       * ready") instead of 0xFFFFFFFF ("device does not exist").  This
0d24915
+       * causes the PCI core to retry the read until it times out.
0d24915
+       * Avoid this by not claiming to support CRS.
0d24915
+       */
0d24915
+       if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
0d24915
+           ((where & ~0x3) == ROOT_CAP_AND_CTRL))
0d24915
+               *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
0d24915
+
0d24915
+       if (size <= 2)
0d24915
+               *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
0d24915
+
0d24915
+       return PCIBIOS_SUCCESSFUL;
0d24915
+}
0d24915
+
0d24915
+static struct pci_ecam_ops xgene_pcie_ecam_ops = {
0d24915
+       .bus_shift      = 16,
0d24915
+       .init           = xgene_pcie_ecam_init,
0d24915
+       .pci_ops        = {
0d24915
+               .map_bus        = xgene_pcie_ecam_map_bus,
0d24915
+               .read           = xgene_pcie_config_read32,
0d24915
+               .write          = pci_generic_config_write,
0d24915
+       }
0d24915
+};
0d24915
+
0d24915
+DECLARE_ACPI_MCFG_FIXUP(&xgene_pcie_ecam_ops, APM_OEM_ID,
0d24915
+                       APM_XGENE_OEM_TABLE_ID, APM_XGENE_OEM_REV,
0d24915
+                       PCI_MCFG_DOMAIN_ANY, PCI_MCFG_BUS_ANY);
0d24915
+#endif
0d24915
-- 
0d24915
2.7.4
0d24915