summaryrefslogtreecommitdiff
path: root/drivers/cpu
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2024-10-27 17:14:22 -0600
committerTom Rini <[email protected]>2024-10-27 18:44:13 -0600
commit2800aecce08b47b169d8e9824dd23b1297b2cedc (patch)
tree2f8b61eaee1520b6fd5bd6cedb111aa3ee13f2f7 /drivers/cpu
parent568407fab5336c00cf0265e9de6c507078988504 (diff)
parent25081abf081880930365ff2bc6afc6c0273ca4bf (diff)
Merge patch series "Implement ACPI on aarch64"
Patrick Rudolph <[email protected]> says: Based on the existing work done by Simon Glass this series adds support for booting aarch64 devices using ACPI only. As first target QEMU SBSA support is added, which relies on ACPI only to boot an OS. As secondary target the Raspberry Pi4 was used, which is broadly available and allows easy testing of the proposed solution. The series is split into ACPI cleanups and code movements, adding Arm specific ACPI tables and finally SoC and mainboard related changes to boot a Linux on the QEMU SBSA and RPi4. Currently only the mandatory ACPI tables are supported, allowing to boot into Linux without errors. The QEMU SBSA support is feature complete and provides the same functionality as the EDK2 implementation. The changes were tested on real hardware as well on QEMU v9.0: qemu-system-aarch64 -machine sbsa-ref -nographic -cpu cortex-a57 \ -pflash secure-world.rom \ -pflash unsecure-world.rom qemu-system-aarch64 -machine raspi4b -kernel u-boot.bin -cpu cortex-a72 \ -smp 4 -m 2G -drive file=raspbian.img,format=raw,index=0 \ -dtb bcm2711-rpi-4-b.dtb -nographic Tested against FWTS V24.03.00. Known issues: - The QEMU rpi4 support is currently limited as it doesn't emulate PCI, USB or ethernet devices! - The SMP bringup doesn't work on RPi4, but works in QEMU (Possibly cache related). - PCI on RPI4 isn't working on real hardware since the pcie_brcmstb Linux kernel module doesn't support ACPI yet. Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'drivers/cpu')
-rw-r--r--drivers/cpu/Kconfig7
-rw-r--r--drivers/cpu/Makefile2
-rw-r--r--drivers/cpu/armv8_cpu.c151
-rw-r--r--drivers/cpu/armv8_cpu.h31
-rw-r--r--drivers/cpu/bcm283x_cpu.c214
5 files changed, 405 insertions, 0 deletions
diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig
index 5c06cd9f60e..4cc3679c009 100644
--- a/drivers/cpu/Kconfig
+++ b/drivers/cpu/Kconfig
@@ -26,6 +26,13 @@ config CPU_RISCV
help
Support CPU cores for RISC-V architecture.
+config CPU_ARMV8
+ bool "Enable generic ARMv8 CPU driver"
+ depends on CPU && ARM64
+ select IRQ
+ help
+ Support CPU cores for armv8 architecture.
+
config CPU_MICROBLAZE
bool "Enable Microblaze CPU driver"
depends on CPU && MICROBLAZE
diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile
index bc75d9b974e..eaf494706e2 100644
--- a/drivers/cpu/Makefile
+++ b/drivers/cpu/Makefile
@@ -6,10 +6,12 @@
obj-$(CONFIG_CPU) += cpu-uclass.o
+obj-$(CONFIG_ARCH_BCM283X) += bcm283x_cpu.o
obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o
obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o
obj-$(CONFIG_ARCH_AT91) += at91_cpu.o
obj-$(CONFIG_ARCH_MEDIATEK) += mtk_cpu.o
+obj-$(CONFIG_CPU_ARMV8) += armv8_cpu.o
obj-$(CONFIG_CPU_IMX) += imx8_cpu.o
obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o
diff --git a/drivers/cpu/armv8_cpu.c b/drivers/cpu/armv8_cpu.c
new file mode 100644
index 00000000000..4eedfe5e2c5
--- /dev/null
+++ b/drivers/cpu/armv8_cpu.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 9elements GmbH
+ */
+#include <cpu.h>
+#include <dm.h>
+#include <irq.h>
+#include <acpi/acpigen.h>
+#include <asm/armv8/cpu.h>
+#include <asm/io.h>
+#include <dm/acpi.h>
+#include <linux/bitops.h>
+#include <linux/printk.h>
+#include <linux/sizes.h>
+
+static int armv8_cpu_get_desc(const struct udevice *dev, char *buf, int size)
+{
+ int cpuid;
+
+ cpuid = (read_midr() & MIDR_PARTNUM_MASK) >> MIDR_PARTNUM_SHIFT;
+
+ snprintf(buf, size, "CPU MIDR %04x", cpuid);
+
+ return 0;
+}
+
+static int armv8_cpu_get_info(const struct udevice *dev,
+ struct cpu_info *info)
+{
+ info->cpu_freq = 0;
+ info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
+
+ return 0;
+}
+
+static int armv8_cpu_get_count(const struct udevice *dev)
+{
+ return uclass_id_count(UCLASS_CPU);
+}
+
+#ifdef CONFIG_ACPIGEN
+int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ uint core_id = dev_seq(dev);
+
+ acpigen_write_processor_device(ctx, core_id);
+
+ return 0;
+}
+
+int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ struct acpi_madt_gicc *gicc;
+ struct cpu_plat *cpu_plat;
+ struct udevice *gic;
+ u64 gicc_gicv = 0;
+ u64 gicc_gich = 0;
+ u64 gicc_gicr_base = 0;
+ u64 gicc_phys_base = 0;
+ u32 gicc_perf_gsiv = 0;
+ u64 gicc_mpidr;
+ u32 gicc_vgic_maint_irq = 0;
+ int addr_index;
+ fdt_addr_t addr;
+ int ret;
+ struct irq req_irq;
+
+ cpu_plat = dev_get_parent_plat(dev);
+ if (!cpu_plat)
+ return 0;
+
+ ret = irq_get_interrupt_parent(dev, &gic);
+ if (ret) {
+ log_err("%s: Failed to find interrupt parent for %s\n",
+ __func__, dev->name);
+ return -ENODEV;
+ }
+
+ addr_index = 1;
+
+ if (device_is_compatible(gic, "arm,gic-v3")) {
+ addr = dev_read_addr_index(gic, addr_index++);
+ if (addr != FDT_ADDR_T_NONE)
+ gicc_gicr_base = addr;
+ }
+
+ addr = dev_read_addr_index(gic, addr_index++);
+ if (addr != FDT_ADDR_T_NONE)
+ gicc_phys_base = addr;
+
+ addr = dev_read_addr_index(gic, addr_index++);
+ if (addr != FDT_ADDR_T_NONE)
+ gicc_gich = addr;
+
+ addr = dev_read_addr_index(gic, addr_index++);
+ if (addr != FDT_ADDR_T_NONE)
+ gicc_gicv = addr;
+
+ ret = irq_get_by_index(gic, 0, &req_irq);
+ if (!ret)
+ gicc_vgic_maint_irq = req_irq.id;
+
+ gicc_mpidr = dev_read_u64_default(dev, "reg", 0);
+ if (!gicc_mpidr)
+ gicc_mpidr = dev_read_u32_default(dev, "reg", 0);
+
+ /*
+ * gicc_vgic_maint_irq and gicc_gicv are the same for every CPU
+ */
+ gicc = ctx->current;
+ acpi_write_madt_gicc(gicc,
+ dev_seq(dev),
+ gicc_perf_gsiv, /* FIXME: needs a PMU driver */
+ gicc_phys_base,
+ gicc_gicv,
+ gicc_gich,
+ gicc_vgic_maint_irq,
+ gicc_gicr_base,
+ gicc_mpidr,
+ 0); /* FIXME: Not defined in DT */
+
+ acpi_inc(ctx, gicc->length);
+
+ return 0;
+}
+
+struct acpi_ops armv8_cpu_acpi_ops = {
+ .fill_ssdt = armv8_cpu_fill_ssdt,
+ .fill_madt = armv8_cpu_fill_madt,
+};
+#endif
+
+static const struct cpu_ops cpu_ops = {
+ .get_count = armv8_cpu_get_count,
+ .get_desc = armv8_cpu_get_desc,
+ .get_info = armv8_cpu_get_info,
+};
+
+static const struct udevice_id cpu_ids[] = {
+ { .compatible = "arm,armv8" },
+ {}
+};
+
+U_BOOT_DRIVER(arm_cpu) = {
+ .name = "arm-cpu",
+ .id = UCLASS_CPU,
+ .of_match = cpu_ids,
+ .ops = &cpu_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+ ACPI_OPS_PTR(&armv8_cpu_acpi_ops)
+};
diff --git a/drivers/cpu/armv8_cpu.h b/drivers/cpu/armv8_cpu.h
new file mode 100644
index 00000000000..48c705e98de
--- /dev/null
+++ b/drivers/cpu/armv8_cpu.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 9elements GmbH
+ */
+#include <dm/acpi.h>
+#include <dm/device.h>
+
+#ifndef _ARMV8_CPU_H_
+#define _ARMV8_CPU_H_
+
+/**
+ * armv8_cpu_fill_ssdt() - Fill the SSDT
+ * Parses the FDT and writes the SSDT nodes.
+ *
+ * @dev: cpu device to generate ACPI tables for
+ * @ctx: ACPI context pointer
+ * @return: 0 if OK, or a negative error code.
+ */
+int armv8_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx);
+
+/**
+ * armv8_cpu_fill_madt() - Fill the MADT
+ * Parses the FDT and writes the MADT subtables.
+ *
+ * @dev: cpu device to generate ACPI tables for
+ * @ctx: ACPI context pointer
+ * @return: 0 if OK, or a negative error code.
+ */
+int armv8_cpu_fill_madt(const struct udevice *dev, struct acpi_ctx *ctx);
+
+#endif \ No newline at end of file
diff --git a/drivers/cpu/bcm283x_cpu.c b/drivers/cpu/bcm283x_cpu.c
new file mode 100644
index 00000000000..59a7b142c95
--- /dev/null
+++ b/drivers/cpu/bcm283x_cpu.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 9elements GmbH
+ */
+
+#include <cpu.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <fdt_support.h>
+#include <acpi/acpigen.h>
+#include <asm/armv8/cpu.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <asm/global_data.h>
+#include <asm/system.h>
+#include <asm-generic/sections.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include "armv8_cpu.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct bcm_plat {
+ u64 release_addr;
+};
+
+static int cpu_bcm_get_desc(const struct udevice *dev, char *buf, int size)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+ const char *name;
+
+ if (size < 32)
+ return -ENOSPC;
+
+ if (device_is_compatible(dev, "arm,cortex-a53"))
+ name = "A53";
+ else if (device_is_compatible(dev, "arm,cortex-a72"))
+ name = "A72";
+ else
+ name = "?";
+
+ snprintf(buf, size, "Broadcom Cortex-%s at %u MHz\n",
+ name, plat->timebase_freq);
+
+ return 0;
+}
+
+static int cpu_bcm_get_info(const struct udevice *dev, struct cpu_info *info)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ info->cpu_freq = plat->timebase_freq * 1000;
+ info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
+
+ return 0;
+}
+
+static int cpu_bcm_get_count(const struct udevice *dev)
+{
+ return uclass_id_count(UCLASS_CPU);
+}
+
+static int cpu_bcm_get_vendor(const struct udevice *dev, char *buf, int size)
+{
+ snprintf(buf, size, "Broadcom");
+
+ return 0;
+}
+
+static int cpu_bcm_is_current(struct udevice *dev)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ if (plat->cpu_id == (read_mpidr() & 0xffff))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * bcm_cpu_on - Releases the secondary CPU from it's spintable
+ *
+ * Write the CPU's spintable mailbox and let the CPU enter U-Boot.
+ *
+ * @dev: Device to start
+ * @return: zero on success or error code on failure.
+ */
+static int bcm_cpu_on(struct udevice *dev)
+{
+ struct bcm_plat *plat = dev_get_plat(dev);
+ ulong *start_address;
+
+ if (plat->release_addr == ~0ULL)
+ return -ENODATA;
+
+ start_address = map_physmem(plat->release_addr, sizeof(uintptr_t), MAP_NOCACHE);
+
+ /* Point secondary CPU to U-Boot entry */
+ *start_address = (uintptr_t)_start;
+
+ /* Make sure the other CPUs see the written start address */
+ if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
+ flush_dcache_all();
+
+ /* Send an event to wake up the secondary CPU. */
+ asm("dsb ishst\n"
+ "sev");
+
+ unmap_physmem(start_address, MAP_NOCACHE);
+
+ return 0;
+}
+
+static const struct cpu_ops cpu_bcm_ops = {
+ .get_desc = cpu_bcm_get_desc,
+ .get_info = cpu_bcm_get_info,
+ .get_count = cpu_bcm_get_count,
+ .get_vendor = cpu_bcm_get_vendor,
+ .is_current = cpu_bcm_is_current,
+};
+
+static const struct udevice_id cpu_bcm_ids[] = {
+ { .compatible = "arm,cortex-a53" }, /* RPi 3 */
+ { .compatible = "arm,cortex-a72" }, /* RPi 4 */
+ { }
+};
+
+static int bcm_cpu_bind(struct udevice *dev)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ plat->cpu_id = dev_read_addr(dev);
+
+ return 0;
+}
+
+/**
+ * bcm_cpu_of_to_plat - Gather spin-table release address
+ *
+ * Read the spin-table release address to allow all seconary CPUs to enter
+ * U-Boot when necessary.
+ *
+ * @dev: Device to start
+ */
+static int bcm_cpu_of_to_plat(struct udevice *dev)
+{
+ struct bcm_plat *plat = dev_get_plat(dev);
+ const char *prop;
+
+ if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
+ plat->release_addr = ~0ULL;
+
+ prop = dev_read_string(dev, "enable-method");
+ if (!prop || strcmp(prop, "spin-table"))
+ return -ENODEV;
+
+ plat->release_addr = dev_read_u64_default(dev, "cpu-release-addr", ~0ULL);
+
+ if (plat->release_addr == ~0ULL)
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int bcm_cpu_probe(struct udevice *dev)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+ struct clk clk;
+ int ret;
+
+ /* Get a clock if it exists */
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (!ret) {
+ ret = clk_enable(&clk);
+ if (ret && (ret != -ENOSYS || ret != -EOPNOTSUPP))
+ return ret;
+ ret = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ plat->timebase_freq = ret;
+ }
+
+ /*
+ * The armstub holds the secondary CPUs in a spinloop. When
+ * ARMV8_MULTIENTRY is enabled release the secondary CPUs and
+ * let them enter U-Boot as well.
+ */
+ if (CONFIG_IS_ENABLED(ARMV8_MULTIENTRY)) {
+ ret = bcm_cpu_on(dev);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+struct acpi_ops bcm283x_cpu_acpi_ops = {
+ .fill_ssdt = armv8_cpu_fill_ssdt,
+ .fill_madt = armv8_cpu_fill_madt,
+};
+
+U_BOOT_DRIVER(cpu_bcm_drv) = {
+ .name = "bcm283x_cpu",
+ .id = UCLASS_CPU,
+ .of_match = cpu_bcm_ids,
+ .ops = &cpu_bcm_ops,
+ .probe = bcm_cpu_probe,
+ .bind = bcm_cpu_bind,
+ .of_to_plat = bcm_cpu_of_to_plat,
+ .plat_auto = sizeof(struct bcm_plat),
+ ACPI_OPS_PTR(&bcm283x_cpu_acpi_ops)
+};