From 7a5147f51f2eb80ecb6f62a28b8bd86aa5ceebf7 Mon Sep 17 00:00:00 2001
From: Sayanta Pattanayak <sayanta.pattanayak@arm.com>
Date: Wed, 9 Feb 2022 20:37:43 +0530
Subject: [PATCH] n1sdp: pcie: add quirk support enabling remote chip PCIe

Base address mapping for remote chip Root PCIe ECAM space.

When two N1SDP boards are coupled via the CCIX connection, the PCI host
complex of the remote board appears as PCIe segment 2 on the primary board.
The resources of the secondary board, including the host complex, are
mapped at offset 0x40000000000 into the address space of the primary
board, so take that into account when accessing the remote PCIe segment.

Change-Id: I0e8d1eb119aef6444b9df854a39b24441c12195a
Signed-off-by: Sayanta Pattanayak <sayanta.pattanayak@arm.com>
Signed-off-by: Khasim Syed Mohammed <khasim.mohammed@arm.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: sahil <sahil@arm.com>

Upstream-Status: Inappropriate [will not be submitted as its an hack required to fix the hardware issue]
Signed-off-by: Sayanta Pattanayak <sayanta.pattanayak@arm.com>
---
 drivers/acpi/pci_mcfg.c             |  1 +
 drivers/pci/controller/pcie-n1sdp.c | 32 +++++++++++++++++++++++++----
 include/linux/pci-ecam.h            |  1 +
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
index 67c34e6c24a7..4584a5a2ca20 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -158,6 +158,7 @@ static struct mcfg_fixup mcfg_quirks[] = {
 	/* N1SDP SoC with v1 PCIe controller */
 	N1SDP_ECAM_MCFG(0x20181101, 0, &pci_n1sdp_pcie_ecam_ops),
 	N1SDP_ECAM_MCFG(0x20181101, 1, &pci_n1sdp_ccix_ecam_ops),
+	N1SDP_ECAM_MCFG(0x20181101, 2, &pci_n1sdp_remote_pcie_ecam_ops),
 
 #define ALTRA_ECAM_QUIRK(rev, seg) \
 	{ "Ampere", "Altra   ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops }
diff --git a/drivers/pci/controller/pcie-n1sdp.c b/drivers/pci/controller/pcie-n1sdp.c
index 408699b9dcb1..a03665dd056a 100644
--- a/drivers/pci/controller/pcie-n1sdp.c
+++ b/drivers/pci/controller/pcie-n1sdp.c
@@ -30,8 +30,10 @@
 
 /* Platform specific values as hardcoded in the firmware. */
 #define AP_NS_SHARED_MEM_BASE	0x06000000
-#define MAX_SEGMENTS		2		/* Two PCIe root complexes. */
+/* Two PCIe root complexes in One Chip + One PCIe RC in Remote Chip */
+#define MAX_SEGMENTS		3
 #define BDF_TABLE_SIZE		SZ_16K
+#define REMOTE_CHIP_ADDR_OFFSET	0x40000000000
 
 /*
  * Shared memory layout as written by the SCP upon boot time:
@@ -97,12 +99,17 @@ static int pci_n1sdp_init(struct pci_config_window *cfg, unsigned int segment)
 	phys_addr_t table_base;
 	struct device *dev = cfg->parent;
 	struct pcie_discovery_data *shared_data;
-	size_t bdfs_size;
+	size_t bdfs_size, rc_base_addr = 0;
 
 	if (segment >= MAX_SEGMENTS)
 		return -ENODEV;
 
-	table_base = AP_NS_SHARED_MEM_BASE + segment * BDF_TABLE_SIZE;
+	if (segment > 1) {
+	    rc_base_addr = REMOTE_CHIP_ADDR_OFFSET;
+	    table_base = AP_NS_SHARED_MEM_BASE + REMOTE_CHIP_ADDR_OFFSET;
+        } else {
+            table_base = AP_NS_SHARED_MEM_BASE + segment * BDF_TABLE_SIZE;
+	}
 
 	if (!request_mem_region(table_base, BDF_TABLE_SIZE,
 				"PCIe valid BDFs")) {
@@ -114,6 +121,7 @@ static int pci_n1sdp_init(struct pci_config_window *cfg, unsigned int segment)
 				   table_base, BDF_TABLE_SIZE);
 	if (!shared_data)
 		return -ENOMEM;
+	rc_base_addr += shared_data->rc_base_addr;
 
 	/* Copy the valid BDFs structure to allocated normal memory. */
 	bdfs_size = sizeof(struct pcie_discovery_data) +
@@ -125,7 +133,7 @@ static int pci_n1sdp_init(struct pci_config_window *cfg, unsigned int segment)
 	memcpy_fromio(pcie_discovery_data[segment], shared_data, bdfs_size);
 
 	rc_remapped_addr[segment] = devm_ioremap(dev,
-						 shared_data->rc_base_addr,
+						 rc_base_addr,
 						 PCI_CFG_SPACE_EXP_SIZE);
 	if (!rc_remapped_addr[segment]) {
 		dev_err(dev, "Cannot remap root port base\n");
@@ -161,6 +169,12 @@ static int pci_n1sdp_ccix_init(struct pci_config_window *cfg)
 	return pci_n1sdp_init(cfg, 1);
 }
 
+/* Called for ACPI segment 2. */
+static int pci_n1sdp_remote_pcie_init(struct pci_config_window *cfg)
+{
+        return pci_n1sdp_init(cfg, 2);
+}
+
 const struct pci_ecam_ops pci_n1sdp_pcie_ecam_ops = {
 	.bus_shift	= 20,
 	.init		= pci_n1sdp_pcie_init,
@@ -181,6 +195,16 @@ const struct pci_ecam_ops pci_n1sdp_ccix_ecam_ops = {
 	}
 };
 
+const struct pci_ecam_ops pci_n1sdp_remote_pcie_ecam_ops = {
+	.bus_shift	= 20,
+	.init		= pci_n1sdp_remote_pcie_init,
+	.pci_ops	= {
+		.map_bus        = pci_n1sdp_map_bus,
+		.read           = pci_generic_config_read32,
+		.write          = pci_generic_config_write32,
+	}
+};
+
 static const struct of_device_id n1sdp_pcie_of_match[] = {
 	{ .compatible = "arm,n1sdp-pcie", .data = &pci_n1sdp_pcie_ecam_ops },
 	{ },
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
index e6bbc037cef8..d936d3f14bce 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -91,6 +91,7 @@ extern const struct pci_ecam_ops pci_n1sdp_pcie_ecam_ops; /* Arm N1SDP PCIe */
 extern const struct pci_ecam_ops pci_n1sdp_ccix_ecam_ops; /* Arm N1SDP PCIe */
 #endif
 
+extern const struct pci_ecam_ops pci_n1sdp_remote_pcie_ecam_ops; /* Arm N1SDP PCIe */
 #if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
 /* for DT-based PCI controllers that support ECAM */
 int pci_host_common_probe(struct platform_device *pdev);
