509 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			509 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 965e95a91066290f6555546f066a6e2aaba1199e Mon Sep 17 00:00:00 2001
 | 
						|
From: Peter Robinson <pbrobinson@gmail.com>
 | 
						|
Date: Tue, 5 Jul 2016 23:49:39 +0100
 | 
						|
Subject: [PATCH] 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
 | 
						|
 <oem_id, oem_table_id, domain, bus number> 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, <oem_id_str>, <oem_table_id>, <domain_nr>, <bus_nr>);
 | 
						|
 | 
						|
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
 | 
						|
Signed-off-by: Dongdong Liu <liudongdong3@huawei.com>
 | 
						|
---
 | 
						|
 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 b5b376e..a5c9067 100644
 | 
						|
--- a/drivers/acpi/pci_mcfg.c
 | 
						|
+++ b/drivers/acpi/pci_mcfg.c
 | 
						|
@@ -22,6 +22,10 @@
 | 
						|
 #include <linux/kernel.h>
 | 
						|
 #include <linux/pci.h>
 | 
						|
 #include <linux/pci-acpi.h>
 | 
						|
+#include <linux/pci-ecam.h>
 | 
						|
+
 | 
						|
+/* Root pointer to the mapped MCFG table */
 | 
						|
+static struct acpi_table_mcfg *mcfg_table;
 | 
						|
 
 | 
						|
 /* 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 <domain:bus> then use OEM ID and
 | 
						|
+        * OEM revision from MCFG table standard header.
 | 
						|
+        */
 | 
						|
+       for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) {
 | 
						|
+               if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
 | 
						|
+                   (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
 | 
						|
+                   (!strncmp(f->oem_id, mcfg_table->header.oem_id,
 | 
						|
+                             ACPI_OEM_ID_SIZE)) &&
 | 
						|
+                   (!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 2456397..c49bd36 100644
 | 
						|
--- a/include/asm-generic/vmlinux.lds.h
 | 
						|
+++ b/include/asm-generic/vmlinux.lds.h
 | 
						|
@@ -308,6 +308,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.9.2
 | 
						|
 | 
						|
From 817d09d7650319a827f00bd3b4c9b407d3977ba0 Mon Sep 17 00:00:00 2001
 | 
						|
From: Peter Robinson <pbrobinson@gmail.com>
 | 
						|
Date: Tue, 5 Jul 2016 23:52:46 +0100
 | 
						|
Subject: [PATCH] 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 <tn@semihalf.com>
 | 
						|
---
 | 
						|
 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 acf3872..ec513f1 100644
 | 
						|
--- a/arch/arm64/kernel/pci.c
 | 
						|
+++ b/arch/arm64/kernel/pci.c
 | 
						|
@@ -126,6 +126,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)
 | 
						|
@@ -137,12 +138,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.9.2
 | 
						|
 | 
						|
From ac5cff2e2304a1969e39e967567aa41cade1839f Mon Sep 17 00:00:00 2001
 | 
						|
From: Peter Robinson <pbrobinson@gmail.com>
 | 
						|
Date: Tue, 5 Jul 2016 23:53:59 +0100
 | 
						|
Subject: [PATCH] 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 <dhdang@apm.com>
 | 
						|
---
 | 
						|
 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 a5c9067..5137d16 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.9.2
 | 
						|
 | 
						|
From b9c1592c6b615da0c26168c5c3e0f8fc256a23ca Mon Sep 17 00:00:00 2001
 | 
						|
From: Peter Robinson <pbrobinson@gmail.com>
 | 
						|
Date: Tue, 5 Jul 2016 23:55:11 +0100
 | 
						|
Subject: [PATCH] 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 <dhdang@apm.com>
 | 
						|
---
 | 
						|
 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 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..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 <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 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.9.2
 | 
						|
 |