driver: interrupt_controller: intc_clic: rework to standard CLIC driver
Rework intc_clic to standard CLIC driver with Nuclei ECLIC extention. Signed-off-by: Jimmy Zheng <jimmyzhe@andestech.com>
This commit is contained in:
parent
21f0ee0383
commit
9349d54074
7 changed files with 166 additions and 75 deletions
|
@ -31,8 +31,8 @@ zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.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_VIM intc_vim.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_clic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_clic.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_CLIC intc_clic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_CLIC intc_clic.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_NRFX_CLIC intc_nrfx_clic.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NRFX_CLIC intc_nrfx_clic.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_NXP_S32_EIRQ intc_eirq_nxp_s32.c)
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
# Copyright (c) 2021 Tokita, Hiroshi <tokita.hiroshi@gmail.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config CLIC
|
||||
bool "RISC-V Core Local Interrupt Controller (CLIC)"
|
||||
default y
|
||||
depends on DT_HAS_NUCLEI_ECLIC_ENABLED || DT_HAS_RISCV_CLIC_ENABLED
|
||||
select RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
|
||||
help
|
||||
Core Local Interrupt Controller provide low-latency, vectored,
|
||||
preemptive interrupts for RISC-V systems.
|
||||
|
||||
config NUCLEI_ECLIC
|
||||
bool "Enhanced Core Local Interrupt Controller (ECLIC)"
|
||||
default y
|
||||
depends on DT_HAS_NUCLEI_ECLIC_ENABLED
|
||||
select RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
|
||||
select CLIC
|
||||
select CLIC_SMCLICSHV_EXT if RISCV_VECTORED_MODE
|
||||
help
|
||||
Interrupt controller for Nuclei SoC core.
|
||||
|
@ -18,6 +27,8 @@ config NRFX_CLIC
|
|||
help
|
||||
Interrupt controller for Nordic VPR cores.
|
||||
|
||||
if CLIC
|
||||
|
||||
config CLIC_SMCLICSHV_EXT
|
||||
bool
|
||||
help
|
||||
|
@ -25,8 +36,6 @@ config CLIC_SMCLICSHV_EXT
|
|||
to select the behavior for each interrupt. The CLIC driver needs to
|
||||
implement the riscv_clic_irq_vector_set() function.
|
||||
|
||||
if NUCLEI_ECLIC
|
||||
|
||||
config LEGACY_CLIC
|
||||
bool "Use the legacy clic specification"
|
||||
depends on RISCV_HAS_CLIC
|
||||
|
@ -34,4 +43,4 @@ config LEGACY_CLIC
|
|||
Enables legacy clic, where smclicshv extension is not supported and
|
||||
hardware vectoring is set via mode bits of mtvec.
|
||||
|
||||
endif # NUCLEI_ECLIC
|
||||
endif # CLIC
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @brief Assembler-hooks specific to Nuclei's Extended Core Interrupt Controller
|
||||
* @brief Assembler-hooks specific to RISC-V Core Local Interrupt Controller
|
||||
*/
|
||||
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include "intc_clic.h"
|
||||
|
||||
|
||||
GTEXT(__soc_handle_irq)
|
||||
/*
|
||||
* In an ECLIC, pending interrupts don't have to be cleared by hand.
|
||||
* In an CLIC, 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).
|
||||
|
@ -31,7 +32,7 @@ GTEXT(sys_trace_isr_exit)
|
|||
#endif
|
||||
|
||||
/*
|
||||
* This function services and clears all pending interrupts for an ECLIC in non-vectored mode.
|
||||
* This function services and clears all pending interrupts for an CLIC in non-vectored mode.
|
||||
*/
|
||||
SECTION_FUNC(exception.other, __soc_handle_all_irqs)
|
||||
addi sp, sp, -16
|
||||
|
@ -39,7 +40,7 @@ SECTION_FUNC(exception.other, __soc_handle_all_irqs)
|
|||
|
||||
/* 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
|
||||
csrrci a0, CSR_MNXTI, MSTATUS_IEN
|
||||
beqz a0, irq_done /* Check if original interrupt vanished. */
|
||||
|
||||
irq_loop:
|
||||
|
@ -50,7 +51,7 @@ irq_loop:
|
|||
|
||||
/* Call corresponding registered function in _sw_isr_table. a0 is offset in pointer with
|
||||
* the mtvt, sw irq table is 2-pointer wide -> shift by one. */
|
||||
csrr t0, 0x307 /* mtvt */
|
||||
csrr t0, CSR_MTVT
|
||||
sub a0, a0, t0
|
||||
la t0, _sw_isr_table
|
||||
slli a0, a0, (1)
|
||||
|
@ -70,7 +71,7 @@ irq_loop:
|
|||
#endif
|
||||
|
||||
/* Read and clear mnxti to get highest current interrupt and enable interrupts. */
|
||||
csrrci a0, 0x345, MSTATUS_IEN
|
||||
csrrci a0, CSR_MNXTI, MSTATUS_IEN
|
||||
bnez a0, irq_loop
|
||||
|
||||
irq_done:
|
||||
|
|
|
@ -1,55 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Tokita, Hiroshi <tokita.hiroshi@gmail.com>
|
||||
* Copyright (c) 2025 Andes Technology Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Driver for Nuclie's Extended Core Interrupt Controller
|
||||
* @brief Driver for Core Local Interrupt Controller
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/arch/riscv/csr.h>
|
||||
#include <zephyr/device.h>
|
||||
|
||||
#include <zephyr/sw_isr_table.h>
|
||||
#include <zephyr/drivers/interrupt_controller/riscv_clic.h>
|
||||
#include "intc_clic.h"
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(riscv_clic)
|
||||
#define DT_DRV_COMPAT riscv_clic
|
||||
#elif DT_HAS_COMPAT_STATUS_OKAY(nuclei_eclic)
|
||||
#define DT_DRV_COMPAT nuclei_eclic
|
||||
#else
|
||||
#error "Unknown CLIC controller compatible for this configuration"
|
||||
#endif
|
||||
|
||||
/** CLIC INTATTR: TRIG Mask */
|
||||
#define CLIC_INTATTR_TRIG_Msk 0x3U
|
||||
struct clic_data {
|
||||
uint8_t nlbits;
|
||||
uint8_t intctlbits;
|
||||
};
|
||||
|
||||
#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))
|
||||
|
||||
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;
|
||||
}
|
||||
struct clic_config {
|
||||
mem_addr_t base;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enable interrupt
|
||||
*/
|
||||
void riscv_clic_irq_enable(uint32_t irq)
|
||||
{
|
||||
ECLIC_CTRL[irq].INTIE.b.IE = 1;
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
union CLICINTIE clicintie = {.b = {.IE = 0x1}};
|
||||
|
||||
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +49,11 @@ void riscv_clic_irq_enable(uint32_t irq)
|
|||
*/
|
||||
void riscv_clic_irq_disable(uint32_t irq)
|
||||
{
|
||||
ECLIC_CTRL[irq].INTIE.b.IE = 0;
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
union CLICINTIE clicintie = {.b = {.IE = 0x0}};
|
||||
|
||||
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +61,11 @@ void riscv_clic_irq_disable(uint32_t irq)
|
|||
*/
|
||||
int riscv_clic_irq_is_enabled(uint32_t irq)
|
||||
{
|
||||
return ECLIC_CTRL[irq].INTIE.b.IE;
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
union CLICINTIE clicintie = {.w = sys_read8(config->base + CLIC_INTIE(irq))};
|
||||
|
||||
return clicintie.b.IE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,18 +73,35 @@ int riscv_clic_irq_is_enabled(uint32_t irq)
|
|||
*/
|
||||
void riscv_clic_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(max_level, nlbits);
|
||||
const uint8_t intctrl = (prio | level) | (~intctrl_mask);
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
const struct clic_data *data = dev->data;
|
||||
|
||||
ECLIC_CTRL[irq].INTCTRL = intctrl;
|
||||
/*
|
||||
* Set the interrupt level and the interrupt priority.
|
||||
* Examples of mcliccfg settings:
|
||||
* CLICINTCTLBITS mnlbits clicintctl[i] interrupt levels
|
||||
* 0 2 ........ 255
|
||||
* 1 2 l....... 127,255
|
||||
* 2 2 ll...... 63,127,191,255
|
||||
* 3 3 lll..... 31,63,95,127,159,191,223,255
|
||||
* 4 1 lppp.... 127,255
|
||||
* "." bits are non-existent bits for level encoding, assumed to be 1
|
||||
* "l" bits are available variable bits in level specification
|
||||
* "p" bits are available variable bits in priority specification
|
||||
*/
|
||||
const uint8_t max_level = BIT_MASK(data->nlbits);
|
||||
const uint8_t max_prio = BIT_MASK(data->intctlbits - data->nlbits);
|
||||
uint8_t intctrl = (MIN(pri, max_prio) << (8U - data->intctlbits)) |
|
||||
(MIN(pri, max_level) << (8U - data->nlbits)) |
|
||||
BIT_MASK(8U - data->intctlbits);
|
||||
|
||||
union CLICINTATTR intattr = {.w = 0};
|
||||
sys_write8(intctrl, config->base + CLIC_INTCTRL(irq));
|
||||
|
||||
/* Set non-vectoring as default. */
|
||||
intattr.b.shv = 0;
|
||||
intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
|
||||
ECLIC_CTRL[irq].INTATTR = intattr;
|
||||
/* Set the IRQ operates in machine mode, non-vectoring and the trigger type. */
|
||||
union CLICINTATTR clicattr = {.b = {.mode = 0x3, .shv = 0x0, .trg = flags & BIT_MASK(3)}};
|
||||
|
||||
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,11 +109,13 @@ void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
|
|||
*/
|
||||
void riscv_clic_irq_vector_set(uint32_t irq)
|
||||
{
|
||||
/* Set Selective Hardware Vectoring. */
|
||||
union CLICINTATTR intattr = ECLIC_CTRL[irq].INTATTR;
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
union CLICINTATTR clicattr = {.w = sys_read8(config->base + CLIC_INTATTR(irq))};
|
||||
|
||||
intattr.b.shv = 1;
|
||||
ECLIC_CTRL[irq].INTATTR = intattr;
|
||||
/* Set Selective Hardware Vectoring. */
|
||||
clicattr.b.shv = 1;
|
||||
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,26 +123,64 @@ void riscv_clic_irq_vector_set(uint32_t irq)
|
|||
*/
|
||||
void riscv_clic_irq_set_pending(uint32_t irq)
|
||||
{
|
||||
ECLIC_CTRL[irq].INTIP.b.IP = 1;
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
const struct clic_config *config = dev->config;
|
||||
union CLICINTIP clicintip = {.b = {.IP = 0x1}};
|
||||
|
||||
sys_write8(clicintip.w, config->base + CLIC_INTIP(irq));
|
||||
}
|
||||
|
||||
static int nuclei_eclic_init(const struct device *dev)
|
||||
static int clic_init(const struct device *dev)
|
||||
{
|
||||
ECLIC_MTH.w = 0;
|
||||
ECLIC_CFG.w = 0;
|
||||
ECLIC_CFG.b.nlbits = 0;
|
||||
for (int i = 0; i < ECLIC_CTRL_SIZE; i++) {
|
||||
ECLIC_CTRL[i] = (struct CLICCTRL) { 0 };
|
||||
const struct clic_config *config = dev->config;
|
||||
struct clic_data *data = dev->data;
|
||||
|
||||
if (IS_ENABLED(CONFIG_NUCLEI_ECLIC)) {
|
||||
/* Configure the interrupt level threshold. */
|
||||
union CLICMTH clicmth = {.b = {.mth = 0x0}};
|
||||
|
||||
sys_write32(clicmth.qw, config->base + CLIC_MTH);
|
||||
|
||||
/* Detect the number of bits for the clicintctl register. */
|
||||
union CLICINFO clicinfo = {.qw = sys_read32(config->base + CLIC_INFO)};
|
||||
|
||||
data->intctlbits = clicinfo.b.intctlbits;
|
||||
|
||||
if (data->nlbits > data->intctlbits) {
|
||||
data->nlbits = data->intctlbits;
|
||||
}
|
||||
|
||||
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);
|
||||
/* Configure the number of bits assigned to interrupt levels. */
|
||||
union CLICCFG cliccfg = {.qw = sys_read32(config->base + CLIC_CFG)};
|
||||
|
||||
cliccfg.w.nlbits = data->nlbits;
|
||||
sys_write32(cliccfg.qw, config->base + CLIC_CFG);
|
||||
} else {
|
||||
/* Configure the interrupt level threshold by CSR mintthresh. */
|
||||
csr_write(CSR_MINTTHRESH, 0x0);
|
||||
}
|
||||
|
||||
/* Reset all interrupt control register */
|
||||
for (int i = 0; i < CONFIG_NUM_IRQS; i++) {
|
||||
sys_write32(0, config->base + CLIC_CTRL(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL,
|
||||
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
|
||||
#define CLIC_INTC_DATA_INIT(n) \
|
||||
static struct clic_data clic_data_##n = { \
|
||||
.nlbits = 0, \
|
||||
.intctlbits = 8, \
|
||||
};
|
||||
#define CLIC_INTC_CONFIG_INIT(n) \
|
||||
const static struct clic_config clic_config_##n = { \
|
||||
.base = DT_REG_ADDR(DT_DRV_INST(n)), \
|
||||
};
|
||||
#define CLIC_INTC_DEVICE_INIT(n) \
|
||||
CLIC_INTC_DATA_INIT(n) \
|
||||
CLIC_INTC_CONFIG_INIT(n) \
|
||||
DEVICE_DT_INST_DEFINE(n, &clic_init, NULL, &clic_data_##n, &clic_config_##n, PRE_KERNEL_1, \
|
||||
CONFIG_INTC_INIT_PRIORITY, NULL);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(CLIC_INTC_DEVICE_INIT)
|
||||
|
|
|
@ -8,9 +8,33 @@
|
|||
#ifndef ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_CLIC_H_
|
||||
#define ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_CLIC_H_
|
||||
|
||||
/* CLIC relative CSR number */
|
||||
#define CSR_MTVT (0x307)
|
||||
#define CSR_MNXTI (0x345)
|
||||
#define CSR_MINTTHRESH (0x347)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* CLIC Memory mapped register offset */
|
||||
#define CLIC_CFG (0x0)
|
||||
#define CLIC_CTRL(irq) (0x1000 + 4 * (irq))
|
||||
#define CLIC_INTIP(irq) (CLIC_CTRL(irq) + offsetof(union CLICCTRL, w.INTIP))
|
||||
#define CLIC_INTIE(irq) (CLIC_CTRL(irq) + offsetof(union CLICCTRL, w.INTIE))
|
||||
#define CLIC_INTATTR(irq) (CLIC_CTRL(irq) + offsetof(union CLICCTRL, w.INTATTR))
|
||||
#define CLIC_INTCTRL(irq) (CLIC_CTRL(irq) + offsetof(union CLICCTRL, w.INTCTRL))
|
||||
|
||||
/* Nuclei ECLIC memory mapped register offset */
|
||||
#define CLIC_INFO (0x4)
|
||||
#define CLIC_MTH (0x8)
|
||||
|
||||
/* CLIC register structure */
|
||||
union CLICCFG {
|
||||
struct {
|
||||
#ifdef CONFIG_NUCLEI_ECLIC
|
||||
uint32_t _reserved0: 1;
|
||||
#endif /* CONFIG_NUCLEI_ECLIC */
|
||||
/** number of interrupt level bits */
|
||||
uint32_t nlbits: 4;
|
||||
/** number of clicintattr[i].MODE bits */
|
||||
|
@ -60,6 +84,7 @@ union CLICCTRL {
|
|||
uint32_t qw;
|
||||
};
|
||||
|
||||
/* Nuclei ECLIC register structure */
|
||||
union CLICINFO {
|
||||
struct {
|
||||
/** number of max supported interrupts */
|
||||
|
@ -82,4 +107,6 @@ union CLICMTH {
|
|||
uint32_t qw;
|
||||
};
|
||||
|
||||
#endif /*__ASSEMBLER__*/
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_INTC_CLIC_H_ */
|
||||
|
|
|
@ -52,10 +52,7 @@
|
|||
#address-cells = <0>;
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
reg = <0xd2000000 0x0001
|
||||
0xd2000004 0x0004
|
||||
0xd200000b 0x0001
|
||||
0xd2001000 0x1000>;
|
||||
reg = <0xd2000000 0x2000>;
|
||||
};
|
||||
|
||||
rcu: reset-clock-controller@40021000 {
|
||||
|
|
|
@ -164,7 +164,7 @@ static inline void trigger_irq(int irq)
|
|||
}
|
||||
|
||||
#elif defined(CONFIG_RISCV)
|
||||
#if defined(CONFIG_NUCLEI_ECLIC) || defined(CONFIG_NRFX_CLIC)
|
||||
#if defined(CONFIG_CLIC) || defined(CONFIG_NRFX_CLIC)
|
||||
void riscv_clic_irq_set_pending(uint32_t irq);
|
||||
static inline void trigger_irq(int irq)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue