diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 83e3ef86d07..72907a41fee 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -220,9 +220,20 @@ config RISCV_HAS_PLIC config RISCV_HAS_CLIC bool depends on RISCV_PRIVILEGED + select RISCV_ALWAYS_SWITCH_THROUGH_ECALL if MULTITHREADING + select CLIC_SUPPORT_INTERRUPT_LEVEL if !NRFX_CLIC help Does the SOC provide support for a Core-Local Interrupt Controller (CLIC). +config CLIC_SUPPORT_INTERRUPT_LEVEL + bool + depends on RISCV_HAS_CLIC + help + For CLIC implementations with extended interrupt level, where + higher-numbered interrupt levels can preempt lower-numbered interrupt + levels. This option handles interrupt level in ISR to ensure proper + nested ISR exits. + config RISCV_SOC_EXCEPTION_FROM_IRQ bool help diff --git a/arch/riscv/core/isr.S b/arch/riscv/core/isr.S index 8cb3da667c6..dad96974dcc 100644 --- a/arch/riscv/core/isr.S +++ b/arch/riscv/core/isr.S @@ -198,6 +198,12 @@ SECTION_FUNC(exception.entry, _isr_wrapper) sr s0, __struct_arch_esf_s0_OFFSET(sp) get_current_cpu s0 +#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL + /* Save mcause register */ + csrr t0, mcause + sr t0, __struct_arch_esf_mcause_OFFSET(sp) +#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */ + /* Save MEPC register */ csrr t0, mepc sr t0, __struct_arch_esf_mepc_OFFSET(sp) @@ -737,6 +743,13 @@ fp_trap_exit: /* Restore MEPC and MSTATUS registers */ lr t0, __struct_arch_esf_mepc_OFFSET(sp) lr t2, __struct_arch_esf_mstatus_OFFSET(sp) + +#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL + /* Restore MCAUSE register for previous interrupt level. */ + lr t1, __struct_arch_esf_mcause_OFFSET(sp) + csrw mcause, t1 +#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */ + csrw mepc, t0 csrw mstatus, t2 diff --git a/arch/riscv/core/offsets/offsets.c b/arch/riscv/core/offsets/offsets.c index 99eba096824..c526ffbaed0 100644 --- a/arch/riscv/core/offsets/offsets.c +++ b/arch/riscv/core/offsets/offsets.c @@ -112,6 +112,10 @@ GEN_OFFSET_STRUCT(arch_esf, a7); GEN_OFFSET_STRUCT(arch_esf, mepc); GEN_OFFSET_STRUCT(arch_esf, mstatus); +#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL +GEN_OFFSET_STRUCT(arch_esf, mcause); +#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */ + GEN_OFFSET_STRUCT(arch_esf, s0); #ifdef CONFIG_USERSPACE diff --git a/arch/riscv/core/thread.c b/arch/riscv/core/thread.c index 896f8e5199d..ab2cbfe0036 100644 --- a/arch/riscv/core/thread.c +++ b/arch/riscv/core/thread.c @@ -110,6 +110,11 @@ void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, SOC_ISR_STACKING_ESR_INIT; #endif +#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL + /* Clear the previous interrupt level. */ + stack_init->mcause = 0; +#endif + thread->callee_saved.sp = (unsigned long)stack_init; /* where to go when returning from z_riscv_switch() */ diff --git a/include/zephyr/arch/riscv/exception.h b/include/zephyr/arch/riscv/exception.h index 4e46417048c..eb3cabebd41 100644 --- a/include/zephyr/arch/riscv/exception.h +++ b/include/zephyr/arch/riscv/exception.h @@ -78,6 +78,10 @@ struct arch_esf { unsigned long a7; /* function argument */ #endif /* !CONFIG_RISCV_ISA_RV32E */ +#ifdef CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL + unsigned long mcause; /* machine cause register */ +#endif /* CONFIG_CLIC_SUPPORT_INTERRUPT_LEVEL */ + unsigned long mepc; /* machine exception program counter */ unsigned long mstatus; /* machine status register */