From b59e0d6c6035db80fc9044df0333f96ede53ad7a 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>
Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com>
Signed-off-by: Adam Johnston <adam.johnston@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 2d4c1c699ffe..27f1e9a45c17 100644
--- a/drivers/acpi/pci_mcfg.c
+++ b/drivers/acpi/pci_mcfg.c
@@ -178,6 +178,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),
 #endif /* ARM64 */
 
 #ifdef CONFIG_LOONGARCH
diff --git a/drivers/pci/controller/pcie-n1sdp.c b/drivers/pci/controller/pcie-n1sdp.c
index 408699b9dcb1..b3b02417fd7d 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 b3cf3adeab28..d4316795c00d 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -90,6 +90,7 @@ extern const struct pci_ecam_ops tegra194_pcie_ops; /* Tegra194 PCIe */
 extern const struct pci_ecam_ops loongson_pci_ecam_ops; /* Loongson PCIe */
 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 */
+extern const struct pci_ecam_ops pci_n1sdp_remote_pcie_ecam_ops; /* Arm N1SDP PCIe */
 #endif
 
 #if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
