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

Reply via email to