From 5c7a0ef8884ac05ab25a160e9945d91483622013 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Mon, 15 Nov 2021 08:34:11 +0900 Subject: [PATCH] drivers: interrupt-controller: add Nuclei ECLIC driver Add support for the ECLIC interrupt controller which is used with the Nuclei processor core. Signed-off-by: TOKITA Hiroshi --- CODEOWNERS | 1 + drivers/interrupt_controller/CMakeLists.txt | 1 + drivers/interrupt_controller/Kconfig | 2 + drivers/interrupt_controller/Kconfig.eclic | 18 ++ .../interrupt_controller/intc_nuclei_eclic.c | 184 ++++++++++++++++++ .../interrupt-controller/nuclei,eclic.yaml | 19 ++ dts/riscv/gigadevice/gd32vf103.dtsi | 11 ++ include/arch/riscv/arch.h | 7 + modules/hal_gigadevice/Kconfig | 6 - soc/riscv/riscv-privilege/common/soc_common.h | 7 + .../riscv-privilege/common/soc_common_irq.c | 14 ++ .../gd32vf103/Kconfig.defconfig.gd32vf103 | 6 +- .../riscv-privilege/gd32vf103/Kconfig.series | 1 + 13 files changed, 270 insertions(+), 7 deletions(-) create mode 100644 drivers/interrupt_controller/Kconfig.eclic create mode 100644 drivers/interrupt_controller/intc_nuclei_eclic.c create mode 100644 dts/bindings/interrupt-controller/nuclei,eclic.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 3473a454a6a..ed5d9f347c3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -277,6 +277,7 @@ /drivers/interrupt_controller/ @dcpleung @nashif /drivers/interrupt_controller/intc_gic.c @stephanosio /drivers/interrupt_controller/*esp32* @glaubermaroto +/drivers/interrupt_controller/intc_nuclei_eclic.c @soburi /drivers/ipm/ipm_mhu* @karl-zh /drivers/ipm/Kconfig.nrfx @masz-nordic /drivers/ipm/Kconfig.nrfx_ipc_channel @masz-nordic diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt index c6966334ca7..ca292c203cc 100644 --- a/drivers/interrupt_controller/CMakeLists.txt +++ b/drivers/interrupt_controller/CMakeLists.txt @@ -26,3 +26,4 @@ zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.c) zephyr_library_sources_ifdef(CONFIG_INTC_ESP32C3 intc_esp32c3.c) 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_NUCLEI_ECLIC intc_nuclei_eclic.c) diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index 97196869703..706aaa2599d 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -73,4 +73,6 @@ source "drivers/interrupt_controller/Kconfig.esp32c3" source "drivers/interrupt_controller/Kconfig.xec" +source "drivers/interrupt_controller/Kconfig.eclic" + endmenu diff --git a/drivers/interrupt_controller/Kconfig.eclic b/drivers/interrupt_controller/Kconfig.eclic new file mode 100644 index 00000000000..e0505372a23 --- /dev/null +++ b/drivers/interrupt_controller/Kconfig.eclic @@ -0,0 +1,18 @@ +# Nuclei ECLIC interrupt-controller configuration + +# Copyright (c) 2021 Tokita, Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_NUCLEI_ECLIC = nuclei,eclic + +config HAS_NUCLEI_ECLIC + bool + help + Indicate that the platform has ECLIC. + +config NUCLEI_ECLIC + bool "Enhanced Core Local Interrupt Controller (ECLIC)" + default $(dt_compat_enabled,$(DT_COMPAT_NUCLEI_ECLIC)) + depends on HAS_NUCLEI_ECLIC + help + Interrupt controller for Nuclei SoC core. diff --git a/drivers/interrupt_controller/intc_nuclei_eclic.c b/drivers/interrupt_controller/intc_nuclei_eclic.c new file mode 100644 index 00000000000..7ad35445335 --- /dev/null +++ b/drivers/interrupt_controller/intc_nuclei_eclic.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Driver for Nuclie's Extended Core Interrupt Controller + */ + +#include +#include +#include +#include +#include + +#include + +union CLICCFG { + struct { + uint8_t _reserved0 : 1; + /** number of interrupt level bits */ + uint8_t nlbits : 4; + uint8_t _reserved1 : 2; + uint8_t _reserved2 : 1; + } b; + uint8_t w; +}; + +union CLICINFO { + struct { + /** number of max supported interrupts */ + uint32_t numint : 13; + /** architecture version */ + uint32_t version : 8; + /** supported bits in the clicintctl */ + uint32_t intctlbits : 4; + uint32_t _reserved0 : 7; + } b; + uint32_t qw; +}; + +union CLICMTH { + uint8_t w; +}; + +union CLICINTIP { + struct { + /** Interrupt Pending */ + uint8_t IP : 1; + uint8_t reserved0 : 7; + } b; + uint8_t w; +}; + +union CLICINTIE { + struct { + /** Interrupt Enabled */ + uint8_t IE : 1; + uint8_t reserved0 : 7; + } b; + uint8_t w; +}; + +union CLICINTATTR { + struct { + /** 0: non-vectored 1:vectored */ + uint8_t shv : 1; + /** 0: level 1: rising edge 2: falling edge */ + uint8_t trg : 2; + uint8_t reserved0 : 3; + uint8_t reserved1 : 2; + } b; + uint8_t w; +}; + +struct CLICCTRL { + volatile union CLICINTIP INTIP; + volatile union CLICINTIE INTIE; + volatile union CLICINTATTR INTATTR; + volatile uint8_t INTCTRL; +}; + +/** ECLIC Mode mask for MTVT CSR Register */ +#define ECLIC_MODE_MTVEC_Msk 3U + +/** CLIC INTATTR: TRIG Position */ +#define CLIC_INTATTR_TRIG_Pos 1U +/** CLIC INTATTR: TRIG Mask */ +#define CLIC_INTATTR_TRIG_Msk (0x3UL << CLIC_INTATTR_TRIG_Pos) + +#define ECLIC_CFG (*((volatile union CLICCFG *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0)))) +#define ECLIC_INFO (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1)))) +#define ECLIC_MTH (*((volatile union CLICMTH *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2)))) +#define ECLIC_CTRL ((volatile struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3))) +#define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3)) + +#if CONFIG_3RD_LEVEL_INTERRUPTS +#define INTERRUPT_LEVEL 2 +#elif CONFIG_2ND_LEVEL_INTERRUPTS +#define INTERRUPT_LEVEL 1 +#else +#define INTERRUPT_LEVEL 0 +#endif + +static uint8_t nlbits; +static uint8_t intctlbits; +static uint8_t max_prio; +static uint8_t max_level; +static uint8_t intctrl_mask; + +static inline uint8_t leftalign8(uint8_t val, uint8_t shift) +{ + return (val << (8U - shift)); +} + +static inline uint8_t mask8(uint8_t len) +{ + return ((1 << len) - 1) & 0xFFFFU; +} + +/** + * @brief Enable interrupt + */ +void nuclei_eclic_irq_enable(uint32_t irq) +{ + ECLIC_CTRL[irq].INTIE.b.IE = 1; +} + +/** + * @brief Disable interrupt + */ +void nuclei_eclic_irq_disable(uint32_t irq) +{ + ECLIC_CTRL[irq].INTIE.b.IE = 0; +} + +/** + * @brief Get enable status of interrupt + */ +int nuclei_eclic_irq_is_enabled(uint32_t irq) +{ + return ECLIC_CTRL[irq].INTIE.b.IE; +} + +/** + * @brief Set priority and level of interrupt + */ +void nuclei_eclic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags) +{ + const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits); + const uint8_t level = leftalign8(MIN((irq_get_level(irq) - 1), max_level), nlbits); + const uint8_t intctrl = (prio | level) | (~intctrl_mask); + + ECLIC_CTRL[irq].INTCTRL = intctrl; + + ECLIC_CTRL[irq].INTATTR.b.shv = 0; + ECLIC_CTRL[irq].INTATTR.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk); +} + +static int nuclei_eclic_init(const struct device *dev) +{ + /* check hardware support required interrupt levels */ + __ASSERT_NO_MSG(ECLIC_INFO.b.intctlbits >= INTERRUPT_LEVEL); + + ECLIC_MTH.w = 0; + ECLIC_CFG.w = 0; + ECLIC_CFG.b.nlbits = INTERRUPT_LEVEL; + for (int i = 0; i < ECLIC_CTRL_SIZE; i++) { + ECLIC_CTRL[i] = (struct CLICCTRL) { 0 }; + } + + csr_write(mtvec, ((csr_read(mtvec) & 0xFFFFFFC0) | ECLIC_MODE_MTVEC_Msk)); + + nlbits = ECLIC_CFG.b.nlbits; + intctlbits = ECLIC_INFO.b.intctlbits; + max_prio = mask8(intctlbits - nlbits); + max_level = mask8(nlbits); + intctrl_mask = leftalign8(mask8(intctlbits), intctlbits); + + return 0; +} + +SYS_INIT(nuclei_eclic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/dts/bindings/interrupt-controller/nuclei,eclic.yaml b/dts/bindings/interrupt-controller/nuclei,eclic.yaml new file mode 100644 index 00000000000..47aee3157df --- /dev/null +++ b/dts/bindings/interrupt-controller/nuclei,eclic.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2021, Tokita, Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: Nuclei ECLIC interrupt controller + +compatible: "nuclei,eclic" + +include: [interrupt-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#interrupt-cells": + const: 2 + +interrupt-cells: + - irq + - priority diff --git a/dts/riscv/gigadevice/gd32vf103.dtsi b/dts/riscv/gigadevice/gd32vf103.dtsi index d6051de8e24..a4eb806b0bb 100644 --- a/dts/riscv/gigadevice/gd32vf103.dtsi +++ b/dts/riscv/gigadevice/gd32vf103.dtsi @@ -40,6 +40,17 @@ clk-divider = ; }; + eclic: interrupt-controller@d2000000 { + #interrupt-cells = <2>; + compatible = "nuclei,eclic"; + interrupt-controller; + reg = <0xd2000000 0x0001 + 0xd2000004 0x0004 + 0xd200000b 0x0001 + 0xd2001000 0x1000>; + label = "NUCLEI_ECLIC"; + }; + fmc: flash-controller@40022000 { compatible = "gd,gd32-flash-controller"; label = "FMC"; diff --git a/include/arch/riscv/arch.h b/include/arch/riscv/arch.h index d0379b614a0..30272905832 100644 --- a/include/arch/riscv/arch.h +++ b/include/arch/riscv/arch.h @@ -291,6 +291,13 @@ void z_irq_spurious(const void *unused); Z_ISR_DECLARE(irq_p, 0, isr_p, isr_param_p); \ arch_irq_priority_set(irq_p, priority_p); \ } +#elif defined(CONFIG_NUCLEI_ECLIC) +void nuclei_eclic_irq_priority_set(unsigned int irq, unsigned int prio, unsigned int flags); +#define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \ +{ \ + Z_ISR_DECLARE(irq_p, 0, isr_p, isr_param_p); \ + nuclei_eclic_irq_priority_set(irq_p, priority_p, flags_p); \ +} #else #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \ { \ diff --git a/modules/hal_gigadevice/Kconfig b/modules/hal_gigadevice/Kconfig index 41f7f8facb3..a26c03b0447 100644 --- a/modules/hal_gigadevice/Kconfig +++ b/modules/hal_gigadevice/Kconfig @@ -106,12 +106,6 @@ config USE_GD32_DMA help Enable GD32 Direct Memory Access controller (DMA) HAL module driver -config USE_GD32_ECLIC - bool - help - Enable GD32 Enhancement Core-Local Interrupt Controller (ECLIC) HAL - module driver - config USE_GD32_ENET bool help diff --git a/soc/riscv/riscv-privilege/common/soc_common.h b/soc/riscv/riscv-privilege/common/soc_common.h index 6d3989b847e..0a3a875e5d8 100644 --- a/soc/riscv/riscv-privilege/common/soc_common.h +++ b/soc/riscv/riscv-privilege/common/soc_common.h @@ -50,6 +50,13 @@ void riscv_plic_set_priority(uint32_t irq, uint32_t priority); int riscv_plic_get_irq(void); #endif +#if defined(CONFIG_NUCLEI_ECLIC) +void nuclei_eclic_irq_enable(uint32_t irq); +void nuclei_eclic_irq_disable(uint32_t irq); +int nuclei_eclic_irq_is_enabled(uint32_t irq); +void nuclei_eclic_set_priority(uint32_t irq, uint32_t priority); +#endif + #endif /* !_ASMLANGUAGE */ #endif /* __SOC_COMMON_H_ */ diff --git a/soc/riscv/riscv-privilege/common/soc_common_irq.c b/soc/riscv/riscv-privilege/common/soc_common_irq.c index 14848094d47..e79d01622db 100644 --- a/soc/riscv/riscv-privilege/common/soc_common_irq.c +++ b/soc/riscv/riscv-privilege/common/soc_common_irq.c @@ -24,6 +24,10 @@ void arch_irq_enable(unsigned int irq) return; } #endif +#if defined(CONFIG_NUCLEI_ECLIC) + nuclei_eclic_irq_enable(irq); + return; +#endif /* * CSR mie register is updated using atomic instruction csrrs @@ -47,6 +51,10 @@ void arch_irq_disable(unsigned int irq) return; } #endif +#if defined(CONFIG_NUCLEI_ECLIC) + nuclei_eclic_irq_disable(irq); + return; +#endif /* * Use atomic instruction csrrc to disable device interrupt in mie CSR. @@ -67,6 +75,9 @@ void arch_irq_priority_set(unsigned int irq, unsigned int prio) riscv_plic_set_priority(irq, prio); } #endif +#if defined(CONFIG_NUCLEI_ECLIC) + nuclei_eclic_set_priority(irq, prio); +#endif return ; } @@ -83,6 +94,9 @@ int arch_irq_is_enabled(unsigned int irq) return riscv_plic_irq_is_enabled(irq); } #endif +#if defined(CONFIG_NUCLEI_ECLIC) + return nuclei_eclic_irq_is_enabled(irq); +#endif __asm__ volatile ("csrr %0, mie" : "=r" (mie)); diff --git a/soc/riscv/riscv-privilege/gd32vf103/Kconfig.defconfig.gd32vf103 b/soc/riscv/riscv-privilege/gd32vf103/Kconfig.defconfig.gd32vf103 index 39837051285..b039891eb5a 100644 --- a/soc/riscv/riscv-privilege/gd32vf103/Kconfig.defconfig.gd32vf103 +++ b/soc/riscv/riscv-privilege/gd32vf103/Kconfig.defconfig.gd32vf103 @@ -31,11 +31,15 @@ config RISCV_HAS_PLIC default n config NUM_IRQS - default 64 + default 87 if NUCLEI_ECLIC + default 16 if !NUCLEI_ECLIC config FLASH_BASE_ADDRESS default $(dt_node_reg_addr_hex,flash0@8000000) +config 2ND_LEVEL_INTERRUPTS + default y + config PINCTRL default y diff --git a/soc/riscv/riscv-privilege/gd32vf103/Kconfig.series b/soc/riscv/riscv-privilege/gd32vf103/Kconfig.series index 18fd2087878..79f4956fc4e 100644 --- a/soc/riscv/riscv-privilege/gd32vf103/Kconfig.series +++ b/soc/riscv/riscv-privilege/gd32vf103/Kconfig.series @@ -14,6 +14,7 @@ config SOC_SERIES_GD32VF103 select XIP select GD32_HAS_AFIO_PINMUX select HAS_GD32_HAL + select HAS_NUCLEI_ECLIC help Enable support for GigaDevice GD32VF1 series SoC