feat(gic): add support for local chip addressing

This patch adds support for Local Chip Addressing (LCA). In a multi-chip
system, enablig LCA allows each GIC Distributor to maintain its own
version of routing table. This feature is activated when the
GICD_CFGID.LCA bit is set to 1.

The existing `gic600_multichip_data` data structure did not account for
the LCA feature. To support LCA:
- `rt_owner_base` is replaced by `base_addrs[]`. This is required
  because each GICD in the system needs to be configured independently,
  and their base addresses must be passed to the driver.
- `chip_addrs` is changed from 1D to 2D array to store the routing table
  for each chip's GICD. The entries in `chip_addrs` are configuration
  dependent, as the GIC specification does not enforce this.

On a multi-chip platform with chip count N where LCA is enabled by
default, the `gic600_multichip_data` structure should contain all copies
of the routing table (N*N entries). On platforms where LCA is not
supported, only the first sub-array with N entries is required. The
function signature of `gic600_multichip_init` remains unchanged, but if
the LCA feature is enabled, the driver will expect the routing table
configuration in the described format.

Change-Id: I8830c2cf90db6a0cae78e99914cd32c637284a2b
Signed-off-by: Jerry Wang <Jerry.Wang4@arm.com>
diff --git a/drivers/arm/gic/v3/gic600_multichip.c b/drivers/arm/gic/v3/gic600_multichip.c
index 5e44aa9..6e9567e 100644
--- a/drivers/arm/gic/v3/gic600_multichip.c
+++ b/drivers/arm/gic/v3/gic600_multichip.c
@@ -322,12 +322,40 @@
 }
 
 /*******************************************************************************
+ * Initialize GIC-600 and GIC-700 Multichip operation in LCA mode by setting up
+ * the routing table first.
+ ******************************************************************************/
+static void gic600_multichip_lca_init(
+		struct gic600_multichip_data *multichip_data)
+{
+	unsigned int i, j;
+	unsigned int rt_owner = multichip_data->rt_owner;
+
+	for (i = 0; i < multichip_data->chip_count; i++) {
+		for (j = 0; j < multichip_data->chip_count; j++) {
+			INFO("RT(LCA): CHIP%u -> CHIP%u 0x%lx\n", i, j,
+					multichip_data->chip_addrs[i][j]);
+			set_gicd_chipr_n(multichip_data->base_addrs[i], j,
+				multichip_data->chip_addrs[i][j],
+				multichip_data->spi_ids[j].spi_id_min,
+				multichip_data->spi_ids[j].spi_id_max);
+		}
+	}
+
+	/* Initialize the GICD which is marked as routing table owner last */
+	set_gicd_dchipr_rt_owner(multichip_data->base_addrs[rt_owner],
+			rt_owner);
+}
+
+/*******************************************************************************
  * Initialize GIC-600 and GIC-700 Multichip operation.
  ******************************************************************************/
 void gic600_multichip_init(struct gic600_multichip_data *multichip_data)
 {
 	unsigned int i;
-	uint32_t gicd_iidr_val = gicd_read_iidr(multichip_data->rt_owner_base);
+	unsigned int rt_owner = multichip_data->rt_owner;
+	uint32_t gicd_iidr_val =
+			gicd_read_iidr(multichip_data->base_addrs[rt_owner]);
 
 	if ((gicd_iidr_val & IIDR_MODEL_MASK) == IIDR_MODEL_ARM_GIC_600) {
 		gic600_multichip_validate_data(multichip_data);
@@ -341,16 +369,16 @@
 	 * Ensure that G0/G1S/G1NS interrupts are disabled. This also ensures
 	 * that GIC-600 Multichip configuration is done first.
 	 */
-	if ((gicd_read_ctlr(multichip_data->rt_owner_base) &
-			(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT |
-			 CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) {
+	if ((gicd_read_ctlr(multichip_data->base_addrs[rt_owner]) &
+		 (CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1S_BIT |
+		  CTLR_ENABLE_G1NS_BIT | GICD_CTLR_RWP_BIT)) != 0) {
 		ERROR("GICD_CTLR group interrupts are either enabled or have "
 				"pending writes.\n");
 		panic();
 	}
 
 	/* Ensure that the routing table owner is in disconnected state */
-	if (((read_gicd_chipsr(multichip_data->rt_owner_base) &
+	if (((read_gicd_chipsr(multichip_data->base_addrs[rt_owner]) &
 		GICD_CHIPSR_RTS_MASK) >> GICD_CHIPSR_RTS_SHIFT) !=
 			GICD_CHIPSR_RTS_STATE_DISCONNECTED) {
 		ERROR("GIC-600 routing table owner is not in disconnected "
@@ -358,25 +386,34 @@
 		panic();
 	}
 
-	/* Initialize the GICD which is marked as routing table owner first */
-	set_gicd_dchipr_rt_owner(multichip_data->rt_owner_base,
-			multichip_data->rt_owner);
+	/* If LCA is not enabled */
+	if ((read_gicd_cfgid(multichip_data->base_addrs[rt_owner]) &
+			GICD_CFGID_LCA_BIT) == 0) {
+		/*
+		 * Initialize the GICD which is marked as routing table
+		 * owner first.
+		 */
+		set_gicd_dchipr_rt_owner(multichip_data->base_addrs[rt_owner],
+			rt_owner);
 
-	set_gicd_chipr_n(multichip_data->rt_owner_base, multichip_data->rt_owner,
-			multichip_data->chip_addrs[multichip_data->rt_owner],
-			multichip_data->
-			spi_ids[multichip_data->rt_owner].spi_id_min,
-			multichip_data->
-			spi_ids[multichip_data->rt_owner].spi_id_max);
+		set_gicd_chipr_n(multichip_data->base_addrs[rt_owner], rt_owner,
+			multichip_data->chip_addrs[rt_owner][rt_owner],
+			multichip_data->spi_ids[rt_owner].spi_id_min,
+			multichip_data->spi_ids[rt_owner].spi_id_max);
 
-	for (i = 0; i < multichip_data->chip_count; i++) {
-		if (i == multichip_data->rt_owner)
-			continue;
-
-		set_gicd_chipr_n(multichip_data->rt_owner_base, i,
-				multichip_data->chip_addrs[i],
+		for (i = 0; i < multichip_data->chip_count; i++) {
+			if (i == rt_owner)
+				continue;
+			set_gicd_chipr_n(
+				multichip_data->base_addrs[rt_owner], i,
+				multichip_data->chip_addrs[rt_owner][i],
 				multichip_data->spi_ids[i].spi_id_min,
 				multichip_data->spi_ids[i].spi_id_max);
+		}
+	} else {
+		/* If LCA is enabled */
+		INFO("GIC Local chip addressing is enabled\n");
+		gic600_multichip_lca_init(multichip_data);
 	}
 
 	plat_gic_multichip_data = multichip_data;
diff --git a/drivers/arm/gic/v3/gic600_multichip_private.h b/drivers/arm/gic/v3/gic600_multichip_private.h
index fd1cb57..33030b3 100644
--- a/drivers/arm/gic/v3/gic600_multichip_private.h
+++ b/drivers/arm/gic/v3/gic600_multichip_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2023, ARM Limited. All rights reserved.
+ * Copyright (c) 2019-2024, ARM Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -15,12 +15,14 @@
 #define GICD_CHIPSR			U(0xC000)
 #define GICD_DCHIPR			U(0xC004)
 #define GICD_CHIPR			U(0xC008)
+#define GICD_CFGID			U(0xF000)
 
 /* GIC600 GICD multichip related masks */
 #define GICD_CHIPRx_PUP_BIT		BIT_64(1)
 #define GICD_CHIPRx_SOCKET_STATE	BIT_64(0)
 #define GICD_DCHIPR_PUP_BIT		BIT_32(0)
 #define GICD_CHIPSR_RTS_MASK		(BIT_32(4) | BIT_32(5))
+#define GICD_CFGID_LCA_BIT		BIT_64(21)
 
 /* GIC600 GICD multichip related shifts */
 #define GICD_CHIPRx_ADDR_SHIFT		16
@@ -98,6 +100,11 @@
 	return mmio_read_32(base + GICD_CHIPSR);
 }
 
+static inline uint64_t read_gicd_cfgid(uintptr_t base)
+{
+	return mmio_read_64(base + GICD_CFGID);
+}
+
 static inline void write_gicd_dchipr(uintptr_t base, uint32_t val)
 {
 	mmio_write_32(base + GICD_DCHIPR, val);
diff --git a/include/drivers/arm/gic600_multichip.h b/include/drivers/arm/gic600_multichip.h
index 978d735..d0f9027 100644
--- a/include/drivers/arm/gic600_multichip.h
+++ b/include/drivers/arm/gic600_multichip.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019, ARM Limited. All rights reserved.
+ * Copyright (c) 2019-2024, ARM Limited. All rights reserved.
  * Copyright (c) 2023, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -49,10 +49,10 @@
  * value of {0, 0, 0} should be passed.
  ******************************************************************************/
 struct gic600_multichip_data {
-	uintptr_t rt_owner_base;
+	uintptr_t base_addrs[GIC600_MAX_MULTICHIP];
 	unsigned int rt_owner;
 	unsigned int chip_count;
-	uint64_t chip_addrs[GIC600_MAX_MULTICHIP];
+	uint64_t chip_addrs[GIC600_MAX_MULTICHIP][GIC600_MAX_MULTICHIP];
 	multichip_spi_ids_desc_t spi_ids[GIC600_MAX_MULTICHIP];
 };
 
diff --git a/plat/arm/board/n1sdp/n1sdp_bl31_setup.c b/plat/arm/board/n1sdp/n1sdp_bl31_setup.c
index 27ea7f7..d8d60ef 100644
--- a/plat/arm/board/n1sdp/n1sdp_bl31_setup.c
+++ b/plat/arm/board/n1sdp/n1sdp_bl31_setup.c
@@ -17,6 +17,8 @@
 #include "n1sdp_private.h"
 #include <platform_def.h>
 
+#define RT_OWNER 0
+
 /*
  * Platform information structure stored in SDS.
  * This structure holds information about platform's DDR
@@ -44,12 +46,16 @@
 };
 
 static struct gic600_multichip_data n1sdp_multichip_data __init = {
-	.rt_owner_base = PLAT_ARM_GICD_BASE,
-	.rt_owner = 0,
+	.base_addrs = {
+		PLAT_ARM_GICD_BASE
+	},
+	.rt_owner = RT_OWNER,
 	.chip_count = 1,
 	.chip_addrs = {
-		PLAT_ARM_GICD_BASE >> 16,
-		PLAT_ARM_GICD_BASE >> 16
+		[RT_OWNER] = {
+			PLAT_ARM_GICD_BASE >> 16,
+			PLAT_ARM_GICD_BASE >> 16
+		}
 	},
 	.spi_ids = {
 		{PLAT_ARM_GICD_BASE, 32, 511},
diff --git a/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c
index ccabe22..5cbdd5f 100644
--- a/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c
+++ b/plat/arm/board/neoverse_rd/platform/rdn1edge/rdn1edge_plat.c
@@ -11,6 +11,8 @@
 
 #include <nrd_plat.h>
 
+#define RT_OWNER 0
+
 #if defined(IMAGE_BL31)
 static const mmap_region_t rdn1edge_dynamic_mmap[] = {
 	NRD_CSS_SHARED_RAM_MMAP(1),
@@ -19,12 +21,17 @@
 };
 
 static struct gic600_multichip_data rdn1e1_multichip_data __init = {
-	.rt_owner_base = PLAT_ARM_GICD_BASE,
-	.rt_owner = 0,
+	.base_addrs = {
+		PLAT_ARM_GICD_BASE
+	},
+	.rt_owner = RT_OWNER,
 	.chip_count = NRD_CHIP_COUNT,
 	.chip_addrs = {
-		PLAT_ARM_GICD_BASE >> 16,
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16
+		[RT_OWNER] = {
+			PLAT_ARM_GICD_BASE >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16
+		}
 	},
 	.spi_ids = {
 		{PLAT_ARM_GICD_BASE,
diff --git a/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c
index b1046d6..84be243 100644
--- a/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c
+++ b/plat/arm/board/neoverse_rd/platform/rdn2/rdn2_plat.c
@@ -13,6 +13,8 @@
 #include <nrd_plat.h>
 #include <rdn2_ras.h>
 
+#define RT_OWNER 0
+
 #if defined(IMAGE_BL31)
 #if (NRD_PLATFORM_VARIANT == 2)
 static const mmap_region_t rdn2mc_dynamic_mmap[] = {
@@ -33,20 +35,27 @@
 
 #if (NRD_PLATFORM_VARIANT == 2)
 static struct gic600_multichip_data rdn2mc_multichip_data __init = {
-	.rt_owner_base = PLAT_ARM_GICD_BASE,
-	.rt_owner = 0,
+	.base_addrs = {
+		PLAT_ARM_GICD_BASE
+	},
+	.rt_owner = RT_OWNER,
 	.chip_count = NRD_CHIP_COUNT,
 	.chip_addrs = {
-		PLAT_ARM_GICD_BASE >> 16,
+		[RT_OWNER] = {
+			PLAT_ARM_GICD_BASE >> 16,
 #if NRD_CHIP_COUNT > 1
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
 #endif
 #if NRD_CHIP_COUNT > 2
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
 #endif
 #if NRD_CHIP_COUNT > 3
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
 #endif
+		}
 	},
 	.spi_ids = {
 		{PLAT_ARM_GICD_BASE,
diff --git a/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c
index 5713cb9..0a40762 100644
--- a/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c
+++ b/plat/arm/board/neoverse_rd/platform/rdv1mc/rdv1mc_plat.c
@@ -11,6 +11,8 @@
 
 #include <nrd_plat.h>
 
+#define RT_OWNER 0
+
 #if defined(IMAGE_BL31)
 static const mmap_region_t rdv1mc_dynamic_mmap[] = {
 	NRD_CSS_SHARED_RAM_MMAP(1),
@@ -29,18 +31,25 @@
 };
 
 static struct gic600_multichip_data rdv1mc_multichip_data __init = {
-	.rt_owner_base = PLAT_ARM_GICD_BASE,
-	.rt_owner = 0,
+	.base_addrs = {
+		PLAT_ARM_GICD_BASE
+	},
+	.rt_owner = RT_OWNER,
 	.chip_count = NRD_CHIP_COUNT,
 	.chip_addrs = {
-		PLAT_ARM_GICD_BASE >> 16,
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
+		[RT_OWNER] = {
+			PLAT_ARM_GICD_BASE >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
 #if (NRD_CHIP_COUNT > 2)
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
 #endif
 #if (NRD_CHIP_COUNT > 3)
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
 #endif
+		}
 	},
 	.spi_ids = {
 		{PLAT_ARM_GICD_BASE,
diff --git a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c
index a5d687e..3b32ecf 100644
--- a/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c
+++ b/plat/arm/board/neoverse_rd/platform/rdv3/rdv3_bl31_setup.c
@@ -15,6 +15,8 @@
 #include <nrd_variant.h>
 #include <rdv3_rse_comms.h>
 
+#define RT_OWNER 0
+
 #if (NRD_PLATFORM_VARIANT == 2)
 static const mmap_region_t rdv3mc_dynamic_mmap[] = {
 #if NRD_CHIP_COUNT > 1
@@ -32,20 +34,27 @@
 };
 
 static struct gic600_multichip_data rdv3mc_multichip_data __init = {
-	.rt_owner_base = PLAT_ARM_GICD_BASE,
-	.rt_owner = 0,
+	.base_addrs = {
+		PLAT_ARM_GICD_BASE
+	},
+	.rt_owner = RT_OWNER,
 	.chip_count = NRD_CHIP_COUNT,
 	.chip_addrs = {
-		PLAT_ARM_GICD_BASE >> 16,
+		[RT_OWNER] = {
+			PLAT_ARM_GICD_BASE >> 16,
 #if NRD_CHIP_COUNT > 1
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(1)) >> 16,
 #endif
 #if NRD_CHIP_COUNT > 2
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(2)) >> 16,
 #endif
 #if NRD_CHIP_COUNT > 3
-		(PLAT_ARM_GICD_BASE + NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
+			(PLAT_ARM_GICD_BASE
+				+ NRD_REMOTE_CHIP_MEM_OFFSET(3)) >> 16,
 #endif
+		}
 	},
 	.spi_ids = {
 		{PLAT_ARM_GICD_BASE, 32, 511},