[PATCH v3 20/30] drivers/arm: Implement acpi_fill_madt

From: Patrick Rudolph
Date: Wed Sep 11 2024 - 02:26:29 EST


Fill the MADT table in the GIC driver and armv8 CPU driver to
drop SoC specific code. While the GIC only needs devicetree
data, the CPU driver needs additional information stored in
the cpu_plat struct.

While on it update the only board making use of the existing
drivers and writing ACPI MADT in mainboard code.

TEST: Booted on QEMU sbsa using driver model generated MADT.
Signed-off-by: Patrick Rudolph <patrick.rudolph@xxxxxxxxxxxxx>
Cc: Simon Glass <sjg@xxxxxxxxxxxx>
---
arch/arm/lib/acpi_table.c | 1 +
arch/arm/lib/gic-v3-its.c | 89 ++++++++++++++++++++++++++++++++++++++-
drivers/cpu/armv8_cpu.c | 27 ++++++++++++
include/cpu.h | 27 +++++++++++-
4 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/arch/arm/lib/acpi_table.c b/arch/arm/lib/acpi_table.c
index 8c17e9eb36..75a0bc5b6e 100644
--- a/arch/arm/lib/acpi_table.c
+++ b/arch/arm/lib/acpi_table.c
@@ -10,6 +10,7 @@
#include <acpi/acpigen.h>
#include <acpi/acpi_device.h>
#include <acpi/acpi_table.h>
+#include <dm/acpi.h>
#include <string.h>

void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num,
diff --git a/arch/arm/lib/gic-v3-its.c b/arch/arm/lib/gic-v3-its.c
index 22fa46a341..23769c46b5 100644
--- a/arch/arm/lib/gic-v3-its.c
+++ b/arch/arm/lib/gic-v3-its.c
@@ -7,6 +7,8 @@
#include <asm/gic.h>
#include <asm/gic-v3.h>
#include <asm/io.h>
+#include <asm/acpi_table.h>
+#include <dm/acpi.h>
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/sizes.h>
@@ -26,12 +28,14 @@ static u32 lpi_id_bits;
struct gic_v3_its_priv {
ulong gicd_base;
ulong gicr_base;
+ ulong gicr_length;
};

static int gic_v3_its_get_gic_addr(struct gic_v3_its_priv *priv)
{
struct udevice *dev;
fdt_addr_t addr;
+ fdt_size_t size;
int ret;

ret = uclass_get_device_by_driver(UCLASS_IRQ,
@@ -49,12 +53,13 @@ static int gic_v3_its_get_gic_addr(struct gic_v3_its_priv *priv)
}
priv->gicd_base = addr;

- addr = dev_read_addr_index(dev, 1);
+ addr = dev_read_addr_size_index(dev, 1, &size);
if (addr == FDT_ADDR_T_NONE) {
pr_err("%s: failed to get GICR address\n", __func__);
return -EINVAL;
}
priv->gicr_base = addr;
+ priv->gicr_length = size;

return 0;
}
@@ -158,6 +163,42 @@ int gic_lpi_tables_init(u64 base, u32 num_redist)
return 0;
}

+#ifdef CONFIG_ACPIGEN
+/**
+ * acpi_gicv3_fill_madt() - Fill out the body of the MADT
+ *
+ * Write GICD and GICR tables based on collected devicetree data.
+ *
+ * @dev: Device to write ACPI tables for
+ * @ctx: ACPI context to write MADT sub-tables to
+ * Return: 0 if OK
+ */
+static int acpi_gicv3_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ struct acpi_madt_gicd *gicd;
+ struct acpi_madt_gicr *gicr;
+
+ struct gic_v3_its_priv priv;
+
+ if (gic_v3_its_get_gic_addr(&priv))
+ return -EINVAL;
+
+ gicd = ctx->current;
+ acpi_write_madt_gicd(gicd, dev_seq(dev), priv.gicd_base, 3);
+ acpi_inc(ctx, gicd->length);
+
+ gicr = ctx->current;
+ acpi_write_madt_gicr(gicr, priv.gicr_base, priv.gicr_length);
+ acpi_inc(ctx, gicr->length);
+
+ return 0;
+}
+
+struct acpi_ops gic_v3_acpi_ops = {
+ .fill_madt = acpi_gicv3_fill_madt,
+};
+#endif
+
static const struct udevice_id gic_v3_ids[] = {
{ .compatible = "arm,gic-v3" },
{}
@@ -167,4 +208,50 @@ U_BOOT_DRIVER(arm_gic_v3) = {
.name = "gic-v3",
.id = UCLASS_IRQ,
.of_match = gic_v3_ids,
+ ACPI_OPS_PTR(&gic_v3_acpi_ops)
+};
+
+#ifdef CONFIG_ACPIGEN
+/**
+ * acpi_gic_its_fill_madt() - Fill out the body of the MADT
+ *
+ * Write ITS tables based on collected devicetree data.
+ *
+ * @dev: Device to write ACPI tables for
+ * @ctx: ACPI context to write MADT sub-tables to
+ * Return: 0 if OK
+ */
+static int acpi_gic_its_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ struct acpi_madt_its *its;
+ fdt_addr_t addr;
+
+ addr = dev_read_addr_index(dev, 0);
+ if (addr == FDT_ADDR_T_NONE) {
+ pr_err("%s: failed to get GIC ITS address\n", __func__);
+ return -EINVAL;
+ }
+
+ its = ctx->current;
+ acpi_write_madt_its(its, dev_seq(dev), addr);
+ acpi_inc(ctx, its->length);
+
+ return 0;
+}
+
+struct acpi_ops gic_v3_its_acpi_ops = {
+ .fill_madt = acpi_gic_its_fill_madt,
+};
+#endif
+
+static const struct udevice_id gic_v3_its_ids[] = {
+ { .compatible = "arm,gic-v3-its" },
+ {}
+};
+
+U_BOOT_DRIVER(arm_gic_v3_its) = {
+ .name = "gic-v3-its",
+ .id = UCLASS_IRQ,
+ .of_match = gic_v3_its_ids,
+ ACPI_OPS_PTR(&gic_v3_its_acpi_ops)
};
diff --git a/drivers/cpu/armv8_cpu.c b/drivers/cpu/armv8_cpu.c
index 08b8d45f6f..6dcfc14751 100644
--- a/drivers/cpu/armv8_cpu.c
+++ b/drivers/cpu/armv8_cpu.c
@@ -64,8 +64,35 @@ static int acpi_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
return 0;
}

+static int acpi_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ struct acpi_madt_gicc *gicc;
+ struct cpu_plat *cpu_plat;
+
+ cpu_plat = dev_get_parent_plat(dev);
+ if (!cpu_plat)
+ return 0;
+
+ gicc = ctx->current;
+ acpi_write_madt_gicc(gicc,
+ cpu_plat->gicc_cpu_if_num,
+ cpu_plat->gicc_perf_gsiv,
+ cpu_plat->gicc_phys_base,
+ cpu_plat->gicc_gicv,
+ cpu_plat->gicc_gich,
+ cpu_plat->gicc_vgic_maint_irq,
+ cpu_plat->gicc_gicr_base,
+ cpu_plat->gicc_mpidr,
+ cpu_plat->gicc_efficiency);
+
+ acpi_inc(ctx, gicc->length);
+
+ return 0;
+}
+
struct acpi_ops armv8_cpu_acpi_ops = {
.fill_ssdt = acpi_cpu_fill_ssdt,
+ .fill_madt = acpi_cpu_fill_madt,
};
#endif

diff --git a/include/cpu.h b/include/cpu.h
index 0018910d61..c2a4ca08fe 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -13,17 +13,29 @@ struct udevice;

/**
* struct cpu_plat - platform data for a CPU
- * @cpu_id: Platform-specific way of identifying the CPU.
+ * @cpu_id: Platform-specific way of identifying the CPU.
* @ucode_version: Microcode version, if CPU_FEAT_UCODE is set
* @device_id: Driver-defined device identifier
* @family: DMTF CPU Family identifier
* @id: DMTF CPU Processor identifier
* @timebase_freq: the current frequency at which the cpu timer timebase
- * registers are updated (in Hz)
+ * registers are updated (in Hz)
+ * @gicc_cpu_if_num: GIC's CPU Interface Number
+ * @gicc_perf_gsiv: The GSIV used for Performance Monitoring Interrupts
+ * @gicc_phys_base: Address at which the processor can access this
+ * GIC CPU Interface
+ * @gicc_gicv: Address of the GIC virtual CPU interface registers
+ * @gicc_gich: Address of the GIC virtual interface control block
+ * registers
+ * @gicc_vgic_maint_irq: GSIV for Virtual GIC maintenance interrupt
+ * @gicc_gicr_base: Physical address of the associated Redistributor
+ * @gicc_mpidr: MPIDR as defined by ARM architecture
+ * @gicc_efficiency: Describes the relative power efficiency
*
* This can be accessed with dev_get_parent_plat() for any UCLASS_CPU
* device.
*/
+
struct cpu_plat {
int cpu_id;
int ucode_version;
@@ -31,6 +43,17 @@ struct cpu_plat {
u16 family;
u32 id[2];
u32 timebase_freq;
+#if defined(CONFIG_GENERATE_ACPI_TABLE)
+ u32 gicc_cpu_if_num;
+ u32 gicc_perf_gsiv;
+ u64 gicc_phys_base;
+ u64 gicc_gicv;
+ u64 gicc_gich;
+ u32 gicc_vgic_maint_irq;
+ u64 gicc_gicr_base;
+ u64 gicc_mpidr;
+ u8 gicc_efficiency;
+#endif
};

/* CPU features - mostly just a placeholder for now */
--
2.46.0