From 08a2ca5b9bad5a8ed5d7ab708b645e874da5bd06 Mon Sep 17 00:00:00 2001 From: Greter Raffael Date: Thu, 21 Dec 2023 12:41:59 +0000 Subject: [PATCH] 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 --- arch/riscv/Kconfig | 7 ++ arch/riscv/core/isr.S | 10 +++ drivers/interrupt_controller/CMakeLists.txt | 1 + drivers/interrupt_controller/Kconfig.clic | 1 + .../interrupt_controller/intc_nuclei_eclic.S | 79 +++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 drivers/interrupt_controller/intc_nuclei_eclic.S diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 1c5defbacaf..357cce3d118 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -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 diff --git a/arch/riscv/core/isr.S b/arch/riscv/core/isr.S index 558d2a41fbe..04e829a726e 100644 --- a/arch/riscv/core/isr.S +++ b/arch/riscv/core/isr.S @@ -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) diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index 989a75ab6dc..1d4175e5408 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -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) diff --git a/drivers/interrupt_controller/Kconfig.clic b/drivers/interrupt_controller/Kconfig.clic index 95c7004ce19..a047a832331 100644 --- a/drivers/interrupt_controller/Kconfig.clic +++ b/drivers/interrupt_controller/Kconfig.clic @@ -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. diff --git a/drivers/interrupt_controller/intc_nuclei_eclic.S b/drivers/interrupt_controller/intc_nuclei_eclic.S new file mode 100644 index 00000000000..b077d281751 --- /dev/null +++ b/drivers/interrupt_controller/intc_nuclei_eclic.S @@ -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 + + +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