Skip to content

Commit 9349d54

Browse files
jimmyzhekartben
authored andcommitted
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 <[email protected]>
1 parent 21f0ee0 commit 9349d54

File tree

7 files changed

+169
-78
lines changed

7 files changed

+169
-78
lines changed

drivers/interrupt_controller/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.c)
3131
zephyr_library_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
3232
zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)
3333
zephyr_library_sources_ifdef(CONFIG_VIM intc_vim.c)
34-
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_clic.c)
35-
zephyr_library_sources_ifdef(CONFIG_NUCLEI_ECLIC intc_clic.S)
34+
zephyr_library_sources_ifdef(CONFIG_CLIC intc_clic.c)
35+
zephyr_library_sources_ifdef(CONFIG_CLIC intc_clic.S)
3636
zephyr_library_sources_ifdef(CONFIG_NRFX_CLIC intc_nrfx_clic.c)
3737
zephyr_library_sources_ifdef(CONFIG_NRFX_CLIC intc_nrfx_clic.S)
3838
zephyr_library_sources_ifdef(CONFIG_NXP_S32_EIRQ intc_eirq_nxp_s32.c)

drivers/interrupt_controller/Kconfig.clic

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
# Copyright (c) 2021 Tokita, Hiroshi <[email protected]>
22
# SPDX-License-Identifier: Apache-2.0
33

4+
config CLIC
5+
bool "RISC-V Core Local Interrupt Controller (CLIC)"
6+
default y
7+
depends on DT_HAS_NUCLEI_ECLIC_ENABLED || DT_HAS_RISCV_CLIC_ENABLED
8+
select RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
9+
help
10+
Core Local Interrupt Controller provide low-latency, vectored,
11+
preemptive interrupts for RISC-V systems.
12+
413
config NUCLEI_ECLIC
514
bool "Enhanced Core Local Interrupt Controller (ECLIC)"
615
default y
716
depends on DT_HAS_NUCLEI_ECLIC_ENABLED
8-
select RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING
17+
select CLIC
918
select CLIC_SMCLICSHV_EXT if RISCV_VECTORED_MODE
1019
help
1120
Interrupt controller for Nuclei SoC core.
@@ -18,20 +27,20 @@ config NRFX_CLIC
1827
help
1928
Interrupt controller for Nordic VPR cores.
2029

30+
if CLIC
31+
2132
config CLIC_SMCLICSHV_EXT
2233
bool
2334
help
2435
The selective hardware vectoring extension gives users the flexibility
2536
to select the behavior for each interrupt. The CLIC driver needs to
2637
implement the riscv_clic_irq_vector_set() function.
2738

28-
if NUCLEI_ECLIC
29-
3039
config LEGACY_CLIC
3140
bool "Use the legacy clic specification"
3241
depends on RISCV_HAS_CLIC
3342
help
3443
Enables legacy clic, where smclicshv extension is not supported and
3544
hardware vectoring is set via mode bits of mtvec.
3645

37-
endif # NUCLEI_ECLIC
46+
endif # CLIC

drivers/interrupt_controller/intc_clic.S

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
*/
66

77
/**
8-
* @brief Assembler-hooks specific to Nuclei's Extended Core Interrupt Controller
8+
* @brief Assembler-hooks specific to RISC-V Core Local Interrupt Controller
99
*/
1010

1111
#include <zephyr/arch/cpu.h>
12+
#include "intc_clic.h"
1213

1314

1415
GTEXT(__soc_handle_irq)
1516
/*
16-
* In an ECLIC, pending interrupts don't have to be cleared by hand.
17+
* In an CLIC, pending interrupts don't have to be cleared by hand.
1718
* In vectored mode, interrupts are cleared automatically.
1819
* In non-vectored mode, interrupts are cleared when writing the mnxti register (done in
1920
* __soc_handle_all_irqs).
@@ -31,15 +32,15 @@ GTEXT(sys_trace_isr_exit)
3132
#endif
3233

3334
/*
34-
* This function services and clears all pending interrupts for an ECLIC in non-vectored mode.
35+
* This function services and clears all pending interrupts for an CLIC in non-vectored mode.
3536
*/
3637
SECTION_FUNC(exception.other, __soc_handle_all_irqs)
3738
addi sp, sp, -16
3839
sw ra, 0(sp)
3940

4041
/* Read and clear mnxti to get highest current interrupt and enable interrupts. Will return
4142
* original interrupt if no others appear. */
42-
csrrci a0, 0x345, MSTATUS_IEN
43+
csrrci a0, CSR_MNXTI, MSTATUS_IEN
4344
beqz a0, irq_done /* Check if original interrupt vanished. */
4445

4546
irq_loop:
@@ -50,7 +51,7 @@ irq_loop:
5051

5152
/* Call corresponding registered function in _sw_isr_table. a0 is offset in pointer with
5253
* the mtvt, sw irq table is 2-pointer wide -> shift by one. */
53-
csrr t0, 0x307 /* mtvt */
54+
csrr t0, CSR_MTVT
5455
sub a0, a0, t0
5556
la t0, _sw_isr_table
5657
slli a0, a0, (1)
@@ -70,7 +71,7 @@ irq_loop:
7071
#endif
7172

7273
/* Read and clear mnxti to get highest current interrupt and enable interrupts. */
73-
csrrci a0, 0x345, MSTATUS_IEN
74+
csrrci a0, CSR_MNXTI, MSTATUS_IEN
7475
bnez a0, irq_loop
7576

7677
irq_done:
Lines changed: 118 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,186 @@
11
/*
22
* Copyright (c) 2021 Tokita, Hiroshi <[email protected]>
3+
* Copyright (c) 2025 Andes Technology Corporation
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
67

78
/**
8-
* @brief Driver for Nuclie's Extended Core Interrupt Controller
9+
* @brief Driver for Core Local Interrupt Controller
910
*/
1011

1112
#include <zephyr/kernel.h>
12-
#include <zephyr/arch/cpu.h>
13-
#include <zephyr/sys/util.h>
13+
#include <zephyr/arch/riscv/csr.h>
1414
#include <zephyr/device.h>
15-
16-
#include <zephyr/sw_isr_table.h>
1715
#include <zephyr/drivers/interrupt_controller/riscv_clic.h>
1816
#include "intc_clic.h"
1917

18+
#if DT_HAS_COMPAT_STATUS_OKAY(riscv_clic)
19+
#define DT_DRV_COMPAT riscv_clic
20+
#elif DT_HAS_COMPAT_STATUS_OKAY(nuclei_eclic)
2021
#define DT_DRV_COMPAT nuclei_eclic
22+
#else
23+
#error "Unknown CLIC controller compatible for this configuration"
24+
#endif
2125

22-
/** CLIC INTATTR: TRIG Mask */
23-
#define CLIC_INTATTR_TRIG_Msk 0x3U
24-
25-
#define ECLIC_CFG (*((volatile union CLICCFG *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0))))
26-
#define ECLIC_INFO (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1))))
27-
#define ECLIC_MTH (*((volatile union CLICMTH *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2))))
28-
#define ECLIC_CTRL ((volatile struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3)))
29-
#define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3))
26+
struct clic_data {
27+
uint8_t nlbits;
28+
uint8_t intctlbits;
29+
};
3030

31-
static uint8_t nlbits;
32-
static uint8_t intctlbits;
33-
static uint8_t max_prio;
34-
static uint8_t max_level;
35-
static uint8_t intctrl_mask;
36-
37-
static inline uint8_t leftalign8(uint8_t val, uint8_t shift)
38-
{
39-
return (val << (8U - shift));
40-
}
41-
42-
static inline uint8_t mask8(uint8_t len)
43-
{
44-
return ((1 << len) - 1) & 0xFFFFU;
45-
}
31+
struct clic_config {
32+
mem_addr_t base;
33+
};
4634

4735
/**
4836
* @brief Enable interrupt
4937
*/
5038
void riscv_clic_irq_enable(uint32_t irq)
5139
{
52-
ECLIC_CTRL[irq].INTIE.b.IE = 1;
40+
const struct device *dev = DEVICE_DT_INST_GET(0);
41+
const struct clic_config *config = dev->config;
42+
union CLICINTIE clicintie = {.b = {.IE = 0x1}};
43+
44+
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
5345
}
5446

5547
/**
5648
* @brief Disable interrupt
5749
*/
5850
void riscv_clic_irq_disable(uint32_t irq)
5951
{
60-
ECLIC_CTRL[irq].INTIE.b.IE = 0;
52+
const struct device *dev = DEVICE_DT_INST_GET(0);
53+
const struct clic_config *config = dev->config;
54+
union CLICINTIE clicintie = {.b = {.IE = 0x0}};
55+
56+
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
6157
}
6258

6359
/**
6460
* @brief Get enable status of interrupt
6561
*/
6662
int riscv_clic_irq_is_enabled(uint32_t irq)
6763
{
68-
return ECLIC_CTRL[irq].INTIE.b.IE;
64+
const struct device *dev = DEVICE_DT_INST_GET(0);
65+
const struct clic_config *config = dev->config;
66+
union CLICINTIE clicintie = {.w = sys_read8(config->base + CLIC_INTIE(irq))};
67+
68+
return clicintie.b.IE;
6969
}
7070

7171
/**
7272
* @brief Set priority and level of interrupt
7373
*/
7474
void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
7575
{
76-
const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits);
77-
const uint8_t level = leftalign8(max_level, nlbits);
78-
const uint8_t intctrl = (prio | level) | (~intctrl_mask);
79-
80-
ECLIC_CTRL[irq].INTCTRL = intctrl;
81-
82-
union CLICINTATTR intattr = {.w = 0};
83-
84-
/* Set non-vectoring as default. */
85-
intattr.b.shv = 0;
86-
intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
87-
ECLIC_CTRL[irq].INTATTR = intattr;
76+
const struct device *dev = DEVICE_DT_INST_GET(0);
77+
const struct clic_config *config = dev->config;
78+
const struct clic_data *data = dev->data;
79+
80+
/*
81+
* Set the interrupt level and the interrupt priority.
82+
* Examples of mcliccfg settings:
83+
* CLICINTCTLBITS mnlbits clicintctl[i] interrupt levels
84+
* 0 2 ........ 255
85+
* 1 2 l....... 127,255
86+
* 2 2 ll...... 63,127,191,255
87+
* 3 3 lll..... 31,63,95,127,159,191,223,255
88+
* 4 1 lppp.... 127,255
89+
* "." bits are non-existent bits for level encoding, assumed to be 1
90+
* "l" bits are available variable bits in level specification
91+
* "p" bits are available variable bits in priority specification
92+
*/
93+
const uint8_t max_level = BIT_MASK(data->nlbits);
94+
const uint8_t max_prio = BIT_MASK(data->intctlbits - data->nlbits);
95+
uint8_t intctrl = (MIN(pri, max_prio) << (8U - data->intctlbits)) |
96+
(MIN(pri, max_level) << (8U - data->nlbits)) |
97+
BIT_MASK(8U - data->intctlbits);
98+
99+
sys_write8(intctrl, config->base + CLIC_INTCTRL(irq));
100+
101+
/* Set the IRQ operates in machine mode, non-vectoring and the trigger type. */
102+
union CLICINTATTR clicattr = {.b = {.mode = 0x3, .shv = 0x0, .trg = flags & BIT_MASK(3)}};
103+
104+
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
88105
}
89106

90107
/**
91108
* @brief Set vector mode of interrupt
92109
*/
93110
void riscv_clic_irq_vector_set(uint32_t irq)
94111
{
95-
/* Set Selective Hardware Vectoring. */
96-
union CLICINTATTR intattr = ECLIC_CTRL[irq].INTATTR;
112+
const struct device *dev = DEVICE_DT_INST_GET(0);
113+
const struct clic_config *config = dev->config;
114+
union CLICINTATTR clicattr = {.w = sys_read8(config->base + CLIC_INTATTR(irq))};
97115

98-
intattr.b.shv = 1;
99-
ECLIC_CTRL[irq].INTATTR = intattr;
116+
/* Set Selective Hardware Vectoring. */
117+
clicattr.b.shv = 1;
118+
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
100119
}
101120

102121
/**
103122
* @brief Set pending bit of an interrupt
104123
*/
105124
void riscv_clic_irq_set_pending(uint32_t irq)
106125
{
107-
ECLIC_CTRL[irq].INTIP.b.IP = 1;
126+
const struct device *dev = DEVICE_DT_INST_GET(0);
127+
const struct clic_config *config = dev->config;
128+
union CLICINTIP clicintip = {.b = {.IP = 0x1}};
129+
130+
sys_write8(clicintip.w, config->base + CLIC_INTIP(irq));
108131
}
109132

110-
static int nuclei_eclic_init(const struct device *dev)
133+
static int clic_init(const struct device *dev)
111134
{
112-
ECLIC_MTH.w = 0;
113-
ECLIC_CFG.w = 0;
114-
ECLIC_CFG.b.nlbits = 0;
115-
for (int i = 0; i < ECLIC_CTRL_SIZE; i++) {
116-
ECLIC_CTRL[i] = (struct CLICCTRL) { 0 };
135+
const struct clic_config *config = dev->config;
136+
struct clic_data *data = dev->data;
137+
138+
if (IS_ENABLED(CONFIG_NUCLEI_ECLIC)) {
139+
/* Configure the interrupt level threshold. */
140+
union CLICMTH clicmth = {.b = {.mth = 0x0}};
141+
142+
sys_write32(clicmth.qw, config->base + CLIC_MTH);
143+
144+
/* Detect the number of bits for the clicintctl register. */
145+
union CLICINFO clicinfo = {.qw = sys_read32(config->base + CLIC_INFO)};
146+
147+
data->intctlbits = clicinfo.b.intctlbits;
148+
149+
if (data->nlbits > data->intctlbits) {
150+
data->nlbits = data->intctlbits;
151+
}
152+
153+
/* Configure the number of bits assigned to interrupt levels. */
154+
union CLICCFG cliccfg = {.qw = sys_read32(config->base + CLIC_CFG)};
155+
156+
cliccfg.w.nlbits = data->nlbits;
157+
sys_write32(cliccfg.qw, config->base + CLIC_CFG);
158+
} else {
159+
/* Configure the interrupt level threshold by CSR mintthresh. */
160+
csr_write(CSR_MINTTHRESH, 0x0);
117161
}
118162

119-
nlbits = ECLIC_CFG.b.nlbits;
120-
intctlbits = ECLIC_INFO.b.intctlbits;
121-
max_prio = mask8(intctlbits - nlbits);
122-
max_level = mask8(nlbits);
123-
intctrl_mask = leftalign8(mask8(intctlbits), intctlbits);
163+
/* Reset all interrupt control register */
164+
for (int i = 0; i < CONFIG_NUM_IRQS; i++) {
165+
sys_write32(0, config->base + CLIC_CTRL(i));
166+
}
124167

125168
return 0;
126169
}
127170

128-
DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL,
129-
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
171+
#define CLIC_INTC_DATA_INIT(n) \
172+
static struct clic_data clic_data_##n = { \
173+
.nlbits = 0, \
174+
.intctlbits = 8, \
175+
};
176+
#define CLIC_INTC_CONFIG_INIT(n) \
177+
const static struct clic_config clic_config_##n = { \
178+
.base = DT_REG_ADDR(DT_DRV_INST(n)), \
179+
};
180+
#define CLIC_INTC_DEVICE_INIT(n) \
181+
CLIC_INTC_DATA_INIT(n) \
182+
CLIC_INTC_CONFIG_INIT(n) \
183+
DEVICE_DT_INST_DEFINE(n, &clic_init, NULL, &clic_data_##n, &clic_config_##n, PRE_KERNEL_1, \
184+
CONFIG_INTC_INIT_PRIORITY, NULL);
185+
186+
DT_INST_FOREACH_STATUS_OKAY(CLIC_INTC_DEVICE_INIT)

0 commit comments

Comments
 (0)