acpi: Add functions to declare ARM GIC V3 hardware

For GICD and GICR a SOC needs to implement 2 callbacks to get the base
of those interrupt controllers.

For all the cpu GIC the code loops over all the DEVICE_PATH_GICC_V3
devices in a similar fashion to how x86 lapics are added. It's up to the
SOC to add those devices to the tree.

Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Change-Id: I5074d0a76316e854b7801e14b3241f88e805b02f
Reviewed-on: https://review.coreboot.org/c/coreboot/+/76132
Reviewed-by: Lean Sheng Tan <sheng.tan@9elements.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/acpi/Kconfig b/src/acpi/Kconfig
index dc57fef..cb2befb 100644
--- a/src/acpi/Kconfig
+++ b/src/acpi/Kconfig
@@ -53,6 +53,10 @@
 config ACPI_COMMON_MADT_IOAPIC
 	bool
 
+config ACPI_COMMON_MADT_GICC_V3
+	bool
+	depends on ARCH_ARM64
+
 config ACPI_NO_PCAT_8259
 	bool
 	help
diff --git a/src/acpi/Makefile.inc b/src/acpi/Makefile.inc
index e4a832f..cb0bbb4 100644
--- a/src/acpi/Makefile.inc
+++ b/src/acpi/Makefile.inc
@@ -20,6 +20,7 @@
 ramstage-y += sata.c
 ramstage-y += soundwire.c
 ramstage-y += fadt_filler.c
+ramstage-$(CONFIG_ACPI_COMMON_MADT_GICC_V3) += acpi_gic.c
 
 all-y += acpi_pm.c
 smm-y += acpi_pm.c
diff --git a/src/acpi/acpi_gic.c b/src/acpi/acpi_gic.c
new file mode 100644
index 0000000..74e26fd
--- /dev/null
+++ b/src/acpi/acpi_gic.c
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+
+static int acpi_create_madt_one_gicc_v3(acpi_madt_gicc_t *gicc, u32 acpi_uid, u32 pi_gsiv,
+					uint32_t vgic_mi, uint64_t mpidr)
+{
+	memset(gicc, 0, sizeof(acpi_madt_gicc_t));
+	gicc->type = GICC;
+	gicc->length = sizeof(acpi_madt_gicc_t);
+	gicc->reserved = 0;
+	gicc->cpu_interface_number = 0; /* V3, no compat mode */
+	gicc->acpi_processor_uid = acpi_uid;
+	gicc->flags.enabled = 1;
+	gicc->parking_protocol_persion = 0; /* Assume PSCI exclusively */
+	gicc->performance_interrupt_gsiv = pi_gsiv;
+	gicc->parked_address = 0;
+	gicc->physical_base_address = 0; /* V3, no compat mode */
+	gicc->vgic_maintenance_interrupt = vgic_mi;
+	gicc->gicr_base_address = 0; /* ignored by OSPM if GICR is present */
+	gicc->processor_power_efficiency_class = 0; /* Ignore for now */
+	/* For platforms implementing GIC V3 the format must be:
+	 * Bits [63:40] Must be zero
+	 * Bits [39:32] Aff3 : Match Aff3 of target processor MPIDR
+	 * Bits [31:24] Must be zero
+	 * Bits [23:16] Aff2 : Match Aff2 of target processor MPIDR
+	 * Bits [15:8] Aff1 : Match Aff1 of target processor MPIDR
+	 * Bits [7:0] Aff0 : Match Aff0 of target processor MPIDR
+	*/
+	gicc->mpidr = mpidr & 0xff00fffffful;
+
+	return gicc->length;
+}
+
+static unsigned long acpi_create_madt_giccs_v3(unsigned long current)
+{
+	// Loop over CPUs GIC
+	uint32_t acpi_id = 0;
+	for (struct device *dev = dev_find_path(NULL, DEVICE_PATH_GICC_V3); dev;
+	     dev = dev_find_path(dev, DEVICE_PATH_GICC_V3)) {
+		acpi_madt_gicc_t *gicc = (acpi_madt_gicc_t *)current;
+		current += acpi_create_madt_one_gicc_v3(gicc, acpi_id++,
+							dev->path.gicc_v3.pi_gsiv,
+							dev->path.gicc_v3.vgic_mi,
+							dev->path.gicc_v3.mpidr);
+	}
+
+	return current;
+}
+
+static unsigned long acpi_create_madt_gicd_v3(unsigned long current)
+{
+	acpi_madt_gicd_t *gicd = (acpi_madt_gicd_t *)current;
+	memset(gicd, 0, sizeof(acpi_madt_gicd_t));
+	gicd->type = GICD;
+	gicd->length = sizeof(acpi_madt_gicd_t);
+	gicd->physical_base_address = platform_get_gicd_base();
+	gicd->system_vector_base = 0;
+	gicd->gic_version = 3;
+
+	return current + gicd->length;
+}
+
+/*
+ * The redistributor in GICv3 has two 64KB frames per CPU; in
+ * GICv4 it has four 64KB frames per CPU.
+ */
+#define GICV3_REDIST_SIZE 0x20000
+#define GICV4_REDIST_SIZE 0x40000
+static unsigned long acpi_create_madt_gicr_v3(unsigned long current)
+{
+	acpi_madt_gicr_t *gicr = (acpi_madt_gicr_t *)current;
+	memset(gicr, 0, sizeof(acpi_madt_gicr_t));
+	gicr->type = GICR;
+	gicr->length = sizeof(acpi_madt_gicr_t);
+	gicr->discovery_range_base_address = platform_get_gicr_base();
+	gicr->discovery_range_length = GICV3_REDIST_SIZE * CONFIG_MAX_CPUS;
+
+	return current + gicr->length;
+}
+
+unsigned long acpi_arch_fill_madt(acpi_madt_t *madt, unsigned long current)
+{
+	current = acpi_create_madt_giccs_v3(current);
+	current = acpi_create_madt_gicd_v3(current);
+	current = acpi_create_madt_gicr_v3(current);
+
+	return current;
+}
diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h
index 16fe912..201d851 100644
--- a/src/include/acpi/acpi.h
+++ b/src/include/acpi/acpi.h
@@ -1566,6 +1566,9 @@
 
 unsigned long acpi_create_madt_lapic_nmis(unsigned long current);
 
+uintptr_t platform_get_gicd_base(void);
+uintptr_t platform_get_gicr_base(void);
+
 int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic);
 int acpi_create_srat_x2apic(acpi_srat_x2apic_t *x2apic, u32 node, u32 apic);
 int acpi_create_srat_mem(acpi_srat_mem_t *mem, u8 node, u32 basek, u32 sizek,
diff --git a/src/include/device/path.h b/src/include/device/path.h
index c0df66b..f8c4390 100644
--- a/src/include/device/path.h
+++ b/src/include/device/path.h
@@ -23,6 +23,7 @@
 	DEVICE_PATH_MMIO,
 	DEVICE_PATH_GPIO,
 	DEVICE_PATH_MDIO,
+	DEVICE_PATH_GICC_V3,
 
 	/*
 	 * When adding path types to this table, please also update the
@@ -48,6 +49,7 @@
 		"DEVICE_PATH_MMIO",		\
 		"DEVICE_PATH_GPIO",		\
 		"DEVICE_PATH_MDIO",		\
+		"DEVICE_PATH_GICC_V3",		\
 }
 
 struct domain_path {
@@ -120,6 +122,12 @@
 	unsigned int addr;
 };
 
+struct gicc_v3_path {
+	unsigned long long mpidr;
+	unsigned int vgic_mi;
+	unsigned int pi_gsiv;
+};
+
 struct device_path {
 	enum device_path_type type;
 	union {
@@ -138,6 +146,7 @@
 		struct mmio_path	mmio;
 		struct gpio_path	gpio;
 		struct mdio_path	mdio;
+		struct gicc_v3_path	gicc_v3;
 	};
 };