riscv: irq: Correct interrupt handling in clic non-vectored mode
According to the clic specification (https://github.com/riscv/riscv-fast-interrupt), the mnxti register has be written, in order to clear the pending bit for non-vectored interrupts. For vectored interrupts, this is automatically done. From the spec: "If the pending interrupt is edge-triggered, hardware will automatically clear the corresponding pending bit when the CSR instruction that accesses xnxti includes a write." I added a kconfig `RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING` to allow custom irq handling. If enabled, `__soc_handle_all_irqs` has to be implemented. For clic, non-vectored mode, I added a `__soc_handle_all_irqs`, that handles the pending interrupts according to the pseudo code in the spec. Signed-off-by: Greter Raffael <rgreter@baumer.com>
This commit is contained in:
parent
9c955ce869
commit
08a2ca5b9b
|
@ -84,6 +84,13 @@ config RISCV_SOC_HAS_ISR_STACKING
|
|||
saved on the stack by the hardware, and the registers saved by the
|
||||
software macros. The structure must be called '__esf'.
|
||||
|
||||
config RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
|
||||
bool
|
||||
help
|
||||
This allows the SoC to overwrite the irq handling. If enabled, the
|
||||
function __soc_handle_all_irqs has to be implemented. It shall service
|
||||
and clear all pending interrupts.
|
||||
|
||||
config RISCV_SOC_HAS_CUSTOM_IRQ_LOCK_OPS
|
||||
bool
|
||||
help
|
||||
|
|
|
@ -73,6 +73,10 @@ GTEXT(sys_trace_isr_exit)
|
|||
GDATA(_k_syscall_table)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
|
||||
GTEXT(__soc_handle_all_irqs)
|
||||
#endif
|
||||
|
||||
/* exports */
|
||||
GTEXT(_isr_wrapper)
|
||||
|
||||
|
@ -522,6 +526,10 @@ is_interrupt:
|
|||
|
||||
on_irq_stack:
|
||||
|
||||
#ifdef CONFIG_RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
|
||||
call __soc_handle_all_irqs
|
||||
#else
|
||||
|
||||
#ifdef CONFIG_TRACING_ISR
|
||||
call sys_trace_isr_enter
|
||||
#endif
|
||||
|
@ -558,6 +566,8 @@ on_irq_stack:
|
|||
call sys_trace_isr_exit
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
irq_done:
|
||||
/* Decrement _current_cpu->nested */
|
||||
lw t2, ___cpu_t_nested_OFFSET(s0)
|
||||
|
|
|
@ -32,6 +32,7 @@ zephyr_library_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_VIM intc_vim.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_nuclei_eclic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_nuclei_eclic.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_NXP_S32_EIRQ intc_eirq_nxp_s32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NXP_S32_WKPU intc_wkpu_nxp_s32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_XMC4XXX_INTC intc_xmc4xxx.c)
|
||||
|
|
|
@ -6,6 +6,7 @@ config NUCLEI_ECLIC
|
|||
default y
|
||||
depends on DT_HAS_NUCLEI_ECLIC_ENABLED
|
||||
select MULTI_LEVEL_INTERRUPTS
|
||||
select RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING if !RISCV_VECTORED_MODE
|
||||
help
|
||||
Interrupt controller for Nuclei SoC core.
|
||||
|
||||
|
|
79
drivers/interrupt_controller/intc_nuclei_eclic.S
Normal file
79
drivers/interrupt_controller/intc_nuclei_eclic.S
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Baumer Electric AG
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Assembler-hooks specific to Nuclei's Extended Core Interrupt Controller
|
||||
*/
|
||||
|
||||
#include <zephyr/arch/cpu.h>
|
||||
|
||||
|
||||
GTEXT(__soc_handle_irq)
|
||||
/*
|
||||
* In an ECLIC, pending interrupts don't have to be cleared by hand.
|
||||
* In vectored mode, interrupts are cleared automatically.
|
||||
* In non-vectored mode, interrupts are cleared when writing the mnxti register (done in
|
||||
* __soc_handle_all_irqs).
|
||||
* Thus this function can directly return.
|
||||
*/
|
||||
SECTION_FUNC(exception.other, __soc_handle_irq)
|
||||
ret
|
||||
|
||||
#if !defined(CONFIG_RISCV_VECTORED_MODE)
|
||||
|
||||
GTEXT(__soc_handle_all_irqs)
|
||||
|
||||
#ifdef CONFIG_TRACING
|
||||
/* imports */
|
||||
GTEXT(sys_trace_isr_enter)
|
||||
GTEXT(sys_trace_isr_exit)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function services and clears all pending interrupts for an ECLIC in non-vectored mode.
|
||||
*/
|
||||
SECTION_FUNC(exception.other, __soc_handle_all_irqs)
|
||||
mv t2, ra
|
||||
|
||||
/* Read and clear mnxti to get highest current interrupt and enable interrupts. Will return
|
||||
* original interrupt if no others appear. */
|
||||
csrrci a0, 0x345, MSTATUS_IEN
|
||||
beqz a0, irq_done /* Check if original interrupt vanished. */
|
||||
|
||||
irq_loop:
|
||||
|
||||
#ifdef CONFIG_TRACING_ISR
|
||||
call sys_trace_isr_enter
|
||||
#endif
|
||||
|
||||
/* Call corresponding registered function in _sw_isr_table. a0 is offset in words, table is
|
||||
* 2-word wide -> shift by one */
|
||||
la t0, _sw_isr_table
|
||||
slli a0, a0, (1)
|
||||
add t0, t0, a0
|
||||
|
||||
/* Load argument in a0 register */
|
||||
lw a0, 0(t0)
|
||||
|
||||
/* Load ISR function address in register t1 */
|
||||
lw t1, RV_REGSIZE(t0)
|
||||
|
||||
/* Call ISR function */
|
||||
jalr ra, t1, 0
|
||||
|
||||
/* Read and clear mnxti to get highest current interrupt and enable interrupts. */
|
||||
csrrci a0, 0x345, MSTATUS_IEN
|
||||
|
||||
#ifdef CONFIG_TRACING_ISR
|
||||
call sys_trace_isr_exit
|
||||
#endif
|
||||
|
||||
bnez a0, irq_loop
|
||||
|
||||
irq_done:
|
||||
mv ra, t2
|
||||
ret
|
||||
#endif
|
Loading…
Reference in a new issue