Adds functionality for RISC-V CPUs that have an M-mode IMISC to use that for IPIs in U-boot SPL and M-mode U-boot.
This driver supports SoCs which have monotonically increasing hart IDs starting from 0 and a single IMSIC address range. Signed-off-by: Anirudh Srinivasan <[email protected]> --- arch/riscv/Kconfig | 16 ++++++ arch/riscv/cpu/start.S | 32 +++++++++++ arch/riscv/include/asm/csr.h | 11 ++++ arch/riscv/include/asm/global_data.h | 5 ++ arch/riscv/include/asm/syscon.h | 1 + arch/riscv/lib/Makefile | 1 + arch/riscv/lib/mimsic_ipi.c | 103 +++++++++++++++++++++++++++++++++++ 7 files changed, 169 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 17194f9f6ac..0aad105b972 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -416,6 +416,22 @@ config SPL_RISCV_ACLINT_IPI help Use the MSWI block in ACLINT for IPI in M-Mode U-boot SPL. +config RISCV_MIMSIC_IPI + bool + depends on RISCV_MMODE + select REGMAP + select SYSCON + help + Use M-Mode IMSIC for IPI in M-Mode U-boot. + +config SPL_RISCV_MIMSIC_IPI + bool + depends on SPL_RISCV_MMODE + select SPL_REGMAP + select SPL_SYSCON + help + Use M-Mode IMSIC for IPI in M-Mode U-boot SPL. + config SIFIVE_CACHE bool help diff --git a/arch/riscv/cpu/start.S b/arch/riscv/cpu/start.S index 7bafdfd390a..1a16636d362 100644 --- a/arch/riscv/cpu/start.S +++ b/arch/riscv/cpu/start.S @@ -79,11 +79,38 @@ _start: /* set xSIE bit to receive IPIs */ #if CONFIG_IS_ENABLED(RISCV_MMODE) +#if CONFIG_IS_ENABLED(RISCV_MIMSIC_IPI) + li t0, MIE_MEIE +#else li t0, MIE_MSIE +#endif #else li t0, SIE_SSIE #endif csrs MODE_PREFIX(ie), t0 + +#if CONFIG_IS_ENABLED(RISCV_MMODE) && CONFIG_IS_ENABLED(RISCV_MIMSIC_IPI) + /* Disable trapping on interrupts */ + li t0, MSTATUS_MIE + csrc CSR_MSTATUS, t0 + + /* Enable IMSIC interrupt delivery to mip.MEIP on this hart */ + li t0, IMSIC_EIDELIVERY + csrw CSR_MISELECT, t0 + li t0, 1 + csrw CSR_MIREG, t0 + + /* Set threshold to 0 so all priorities are delivered */ + li t0, IMSIC_EITHRESHOLD + csrw CSR_MISELECT, t0 + csrw CSR_MIREG, zero + + /* Enable interrupt identity 1 (IPI) in eie0 */ + li t0, IMSIC_EIE0 + csrw CSR_MISELECT, t0 + li t0, (1 << IMSIC_IPI_ID) + csrw CSR_MIREG, t0 +#endif #endif /* @@ -452,7 +479,12 @@ secondary_hart_loop: #if CONFIG_IS_ENABLED(SMP) csrr t0, MODE_PREFIX(ip) #if CONFIG_IS_ENABLED(RISCV_MMODE) +#if CONFIG_IS_ENABLED(RISCV_MIMSIC_IPI) + li t1, MIE_MEIE + and t0, t0, t1 +#else andi t0, t0, MIE_MSIE +#endif #else andi t0, t0, SIE_SSIE #endif diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 986f951c31a..0f9f803b212 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -93,10 +93,18 @@ /* SIE (Interrupt Enable) and SIP (Interrupt Pending) flags */ #define MIE_MSIE (_AC(0x1, UL) << IRQ_M_SOFT) +#define MIE_MEIE (_AC(0x1, UL) << IRQ_M_EXT) #define SIE_SSIE (_AC(0x1, UL) << IRQ_S_SOFT) #define SIE_STIE (_AC(0x1, UL) << IRQ_S_TIMER) #define SIE_SEIE (_AC(0x1, UL) << IRQ_S_EXT) +/* IMSIC indirect register addresses (indexed via CSR_MISELECT) */ +#define IMSIC_EIDELIVERY 0x70 +#define IMSIC_EITHRESHOLD 0x72 +#define IMSIC_EIE0 0xC0 + +#define IMSIC_IPI_ID 1 + #define CSR_FCSR 0x003 #define CSR_CYCLE 0xc00 #define CSR_TIME 0xc01 @@ -131,6 +139,9 @@ #define CSR_MCAUSE 0x342 #define CSR_MTVAL 0x343 #define CSR_MIP 0x344 +#define CSR_MISELECT 0x350 +#define CSR_MIREG 0x351 +#define CSR_MTOPEI 0x35C #ifdef CONFIG_RISCV_PRIV_1_9 #define CSR_MBASE 0x380 #define CSR_MBOUND 0x381 diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h index 33f2b5ec5c8..95fdc6f121d 100644 --- a/arch/riscv/include/asm/global_data.h +++ b/arch/riscv/include/asm/global_data.h @@ -26,6 +26,11 @@ struct arch_global_data { #ifdef CONFIG_ANDES_PLICSW void __iomem *plicsw; /* andes plicsw base address */ #endif +#if CONFIG_IS_ENABLED(RISCV_MIMSIC_IPI) + void __iomem *mimsic; + u32 mimsic_guest_index_bits; + unsigned int mimsic_ipi_pending[CONFIG_NR_CPUS]; +#endif #if CONFIG_IS_ENABLED(SMP) struct ipi_data ipi[CONFIG_NR_CPUS]; #endif diff --git a/arch/riscv/include/asm/syscon.h b/arch/riscv/include/asm/syscon.h index 5787702e746..f79ff7145eb 100644 --- a/arch/riscv/include/asm/syscon.h +++ b/arch/riscv/include/asm/syscon.h @@ -14,6 +14,7 @@ enum { RISCV_NONE, RISCV_SYSCON_ACLINT, /* Advanced Core Local Interruptor (ACLINT) */ RISCV_SYSCON_PLICSW, /* Andes PLICSW */ + RISCV_SYSCON_MIMSIC, /* M-mode IMSIC on AIA Compatible Machine */ }; #endif /* _ASM_SYSCON_H */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index a4f2968f05f..54fa18a955a 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SIFIVE_CACHE) += sifive_cache.o obj-$(CONFIG_SYS_CACHE_THEAD_CMO) += thead_cmo.o ifeq ($(CONFIG_$(PHASE_)RISCV_MMODE),y) obj-$(CONFIG_$(PHASE_)RISCV_ACLINT_IPI) += aclint_ipi.o +obj-$(CONFIG_$(PHASE_)RISCV_MIMSIC_IPI) += mimsic_ipi.o obj-$(CONFIG_ANDES_PLICSW) += andes_plicsw.o else obj-$(CONFIG_SBI) += sbi.o diff --git a/arch/riscv/lib/mimsic_ipi.c b/arch/riscv/lib/mimsic_ipi.c new file mode 100644 index 00000000000..ca974b49bd6 --- /dev/null +++ b/arch/riscv/lib/mimsic_ipi.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 Tenstorrent + */ + +#include <dm.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/syscon.h> +#include <asm/csr.h> +#include <dm/device-internal.h> + +#define IMSIC_MMIO_PAGE_SHIFT 12 + +DECLARE_GLOBAL_DATA_PTR; + +int riscv_init_ipi(void) +{ + struct udevice *dev; + struct uclass *uc; + const fdt32_t *prop; + bool mimsic_found = false; + int size; + int ret; + + uclass_id_foreach_dev(UCLASS_SYSCON, dev, uc) { + if (dev_get_driver_data(dev) == RISCV_SYSCON_MIMSIC) { + /* + * Look at interrupts-extended property for each IMSIC node + * to check the 2nd entry + * M-Mode IMSICs would have IRQ_M_EXT (11), which we want + * S-Mode IMSICs would have IRQ_S_EXT (9), which we don't want + */ + prop = dev_read_prop(dev, "interrupts-extended", &size); + if (!prop || size < 2 * sizeof(fdt32_t)) + continue; + + if (fdt32_to_cpu(prop[1]) == IRQ_M_EXT) { + mimsic_found = true; + break; + } + } + } + + if (!mimsic_found) + return -ENODEV; + + ret = device_probe(dev); + if (ret) + return ret; + + gd->arch.mimsic = regmap_get_range(syscon_get_regmap(dev), 0); + if (!gd->arch.mimsic) + return -ENOENT; + + gd->arch.mimsic_guest_index_bits = + dev_read_u32_default(dev, "riscv,guest-index-bits", 0); + + return 0; +} + +int riscv_send_ipi(int hart) +{ + __smp_store_release(&gd->arch.mimsic_ipi_pending[hart], 1); + writel(IMSIC_IPI_ID, + (void __iomem *)(gd->arch.mimsic + + (hart << (IMSIC_MMIO_PAGE_SHIFT + + gd->arch.mimsic_guest_index_bits)))); + + return 0; +} + +int riscv_clear_ipi(int hart) +{ + /* + * Read and write to mtopei in one instruction to claim the interrupt + * Value written to mtopei is ignored. + */ + csr_swap(CSR_MTOPEI, 0); + __smp_store_release(&gd->arch.mimsic_ipi_pending[hart], 0); + return 0; +} + +int riscv_get_ipi(int hart, int *pending) +{ + *pending = __smp_load_acquire(&gd->arch.mimsic_ipi_pending[hart]); + return 0; +} + +static const struct udevice_id riscv_mimsic_ipi_ids[] = { + { .compatible = "riscv,imsics", .data = RISCV_SYSCON_MIMSIC }, + {} +}; + +U_BOOT_DRIVER(riscv_mimsic_ipi) = { + .name = "riscv_mimsic_ipi", + .id = UCLASS_SYSCON, + .of_match = riscv_mimsic_ipi_ids, + .flags = DM_FLAG_PRE_RELOC, +}; -- 2.43.0
