diff options
Diffstat (limited to 'drivers/cpu')
-rw-r--r-- | drivers/cpu/Kconfig | 7 | ||||
-rw-r--r-- | drivers/cpu/Makefile | 2 | ||||
-rw-r--r-- | drivers/cpu/armv8_cpu.c | 151 | ||||
-rw-r--r-- | drivers/cpu/armv8_cpu.h | 31 | ||||
-rw-r--r-- | drivers/cpu/bcm283x_cpu.c | 214 |
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) +}; |