1 /*
2  * Copyright (c) 2021 Tokita, Hiroshi <tokita.hiroshi@gmail.com>
3  * Copyright (c) 2025 Andes Technology Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @brief Driver for Core Local Interrupt Controller
10  */
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/arch/riscv/csr.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/interrupt_controller/riscv_clic.h>
16 #include "intc_clic.h"
17 
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)
21 #define DT_DRV_COMPAT nuclei_eclic
22 #else
23 #error "Unknown CLIC controller compatible for this configuration"
24 #endif
25 
26 struct clic_data {
27 	uint8_t nlbits;
28 	uint8_t intctlbits;
29 };
30 
31 struct clic_config {
32 	mem_addr_t base;
33 };
34 
35 struct pmp_stack_guard_key_t {
36 	unsigned long mstatus;
37 	unsigned int irq_key;
38 };
39 
40 /*
41  * M-mode CLIC memory-mapped registers are accessible only in M-mode.
42  * Temporarily disable the PMP stack guard (set mstatus.MPRV = 0) to configure
43  * CLIC registers, then restore the PMP stack guard using these functions.
44  */
disable_pmp_stack_guard(struct pmp_stack_guard_key_t * key)45 static ALWAYS_INLINE void disable_pmp_stack_guard(struct pmp_stack_guard_key_t *key)
46 {
47 	if (IS_ENABLED(CONFIG_PMP_STACK_GUARD)) {
48 		key->irq_key = irq_lock();
49 		key->mstatus = csr_read_clear(mstatus, MSTATUS_MPRV);
50 	} else {
51 		ARG_UNUSED(key);
52 	}
53 }
54 
restore_pmp_stack_guard(struct pmp_stack_guard_key_t key)55 static ALWAYS_INLINE void restore_pmp_stack_guard(struct pmp_stack_guard_key_t key)
56 {
57 	if (IS_ENABLED(CONFIG_PMP_STACK_GUARD)) {
58 		csr_write(mstatus, key.mstatus);
59 		irq_unlock(key.irq_key);
60 	} else {
61 		ARG_UNUSED(key);
62 	}
63 }
64 
write_clic32(const struct device * dev,uint32_t offset,uint32_t value)65 static ALWAYS_INLINE void write_clic32(const struct device *dev, uint32_t offset, uint32_t value)
66 {
67 	const struct clic_config *config = dev->config;
68 	mem_addr_t reg_addr = config->base + offset;
69 	struct pmp_stack_guard_key_t key;
70 
71 	disable_pmp_stack_guard(&key);
72 	sys_write32(value, reg_addr);
73 	restore_pmp_stack_guard(key);
74 }
75 
read_clic32(const struct device * dev,uint32_t offset)76 static ALWAYS_INLINE uint32_t read_clic32(const struct device *dev, uint32_t offset)
77 {
78 	const struct clic_config *config = dev->config;
79 	mem_addr_t reg_addr = config->base + offset;
80 	struct pmp_stack_guard_key_t key;
81 	uint32_t reg;
82 
83 	disable_pmp_stack_guard(&key);
84 	reg = sys_read32(reg_addr);
85 	restore_pmp_stack_guard(key);
86 
87 	return reg;
88 }
89 
write_clic8(const struct device * dev,uint32_t offset,uint8_t value)90 static ALWAYS_INLINE void write_clic8(const struct device *dev, uint32_t offset, uint8_t value)
91 {
92 	const struct clic_config *config = dev->config;
93 	mem_addr_t reg_addr = config->base + offset;
94 	struct pmp_stack_guard_key_t key;
95 
96 	disable_pmp_stack_guard(&key);
97 	sys_write8(value, reg_addr);
98 	restore_pmp_stack_guard(key);
99 }
100 
read_clic8(const struct device * dev,uint32_t offset)101 static ALWAYS_INLINE uint8_t read_clic8(const struct device *dev, uint32_t offset)
102 {
103 	const struct clic_config *config = dev->config;
104 	mem_addr_t reg_addr = config->base + offset;
105 	struct pmp_stack_guard_key_t key;
106 	uint32_t reg;
107 
108 	disable_pmp_stack_guard(&key);
109 	reg = sys_read8(reg_addr);
110 	restore_pmp_stack_guard(key);
111 
112 	return reg;
113 }
114 
115 /**
116  * @brief Enable interrupt
117  */
riscv_clic_irq_enable(uint32_t irq)118 void riscv_clic_irq_enable(uint32_t irq)
119 {
120 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
121 		const struct device *dev = DEVICE_DT_INST_GET(0);
122 		union CLICINTIE clicintie = {.b = {.IE = 0x1}};
123 
124 		write_clic8(dev, CLIC_INTIE(irq), clicintie.w);
125 	} else {
126 		csr_write(CSR_MISELECT, CLIC_INTIE(irq));
127 		csr_set(CSR_MIREG2, BIT(irq % 32));
128 	}
129 }
130 
131 /**
132  * @brief Disable interrupt
133  */
riscv_clic_irq_disable(uint32_t irq)134 void riscv_clic_irq_disable(uint32_t irq)
135 {
136 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
137 		const struct device *dev = DEVICE_DT_INST_GET(0);
138 		union CLICINTIE clicintie = {.b = {.IE = 0x0}};
139 
140 		write_clic8(dev, CLIC_INTIE(irq), clicintie.w);
141 	} else {
142 		csr_write(CSR_MISELECT, CLIC_INTIE(irq));
143 		csr_clear(CSR_MIREG2, BIT(irq % 32));
144 	}
145 }
146 
147 /**
148  * @brief Get enable status of interrupt
149  */
riscv_clic_irq_is_enabled(uint32_t irq)150 int riscv_clic_irq_is_enabled(uint32_t irq)
151 {
152 	int is_enabled = 0;
153 
154 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
155 		const struct device *dev = DEVICE_DT_INST_GET(0);
156 		union CLICINTIE clicintie = {.w = read_clic8(dev, CLIC_INTIE(irq))};
157 
158 		is_enabled = clicintie.b.IE;
159 	} else {
160 		csr_write(CSR_MISELECT, CLIC_INTIE(irq));
161 		is_enabled = csr_read(CSR_MIREG2) & BIT(irq % 32);
162 	}
163 
164 	return !!is_enabled;
165 }
166 
167 /**
168  * @brief Set priority and level of interrupt
169  */
riscv_clic_irq_priority_set(uint32_t irq,uint32_t pri,uint32_t flags)170 void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
171 {
172 	const struct device *dev = DEVICE_DT_INST_GET(0);
173 	const struct clic_data *data = dev->data;
174 
175 	/*
176 	 * Set the interrupt level and the interrupt priority.
177 	 * Examples of mcliccfg settings:
178 	 * CLICINTCTLBITS mnlbits clicintctl[i] interrupt levels
179 	 *       0         2      ........      255
180 	 *       1         2      l.......      127,255
181 	 *       2         2      ll......      63,127,191,255
182 	 *       3         3      lll.....      31,63,95,127,159,191,223,255
183 	 *       4         1      lppp....      127,255
184 	 * "." bits are non-existent bits for level encoding, assumed to be 1
185 	 * "l" bits are available variable bits in level specification
186 	 * "p" bits are available variable bits in priority specification
187 	 */
188 	const uint8_t max_level = BIT_MASK(data->nlbits);
189 	const uint8_t max_prio = BIT_MASK(data->intctlbits - data->nlbits);
190 	uint8_t intctrl = (MIN(pri, max_prio) << (8U - data->intctlbits)) |
191 			  (MIN(pri, max_level) << (8U - data->nlbits)) |
192 			  BIT_MASK(8U - data->intctlbits);
193 
194 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
195 		write_clic8(dev, CLIC_INTCTRL(irq), intctrl);
196 	} else {
197 		uint32_t clicintctl, bit_offset = 8 * (irq % 4);
198 
199 		csr_write(CSR_MISELECT, CLIC_INTCTRL(irq));
200 		clicintctl = csr_read(CSR_MIREG);
201 		clicintctl &= ~GENMASK(bit_offset + 7, bit_offset);
202 		clicintctl |= intctrl << bit_offset;
203 		csr_write(CSR_MIREG, clicintctl);
204 	}
205 
206 	/* Set the IRQ operates in machine mode, non-vectoring and the trigger type. */
207 	union CLICINTATTR clicattr = {.b = {.mode = 0x3, .shv = 0x0, .trg = flags & BIT_MASK(3)}};
208 
209 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
210 		write_clic8(dev, CLIC_INTATTR(irq), clicattr.w);
211 	} else {
212 		uint32_t clicintattr, bit_offset = 8 * (irq % 4);
213 
214 		csr_write(CSR_MISELECT, CLIC_INTATTR(irq));
215 		clicintattr = csr_read(CSR_MIREG2);
216 		clicintattr &= ~GENMASK(bit_offset + 7, bit_offset);
217 		clicintattr |= clicattr.w << bit_offset;
218 		csr_write(CSR_MIREG2, clicintattr);
219 	}
220 }
221 
222 /**
223  * @brief Set vector mode of interrupt
224  */
riscv_clic_irq_vector_set(uint32_t irq)225 void riscv_clic_irq_vector_set(uint32_t irq)
226 {
227 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
228 		const struct device *dev = DEVICE_DT_INST_GET(0);
229 		union CLICINTATTR clicattr = {.w = read_clic8(dev, CLIC_INTATTR(irq))};
230 
231 		/* Set Selective Hardware Vectoring. */
232 		clicattr.b.shv = 1;
233 		write_clic8(dev, CLIC_INTATTR(irq), clicattr.w);
234 	} else {
235 		uint32_t clicintattr, bit_offset = 8 * (irq % 4);
236 		union CLICINTATTR clicattr = {.b = {.shv = 1}};
237 
238 		csr_write(CSR_MISELECT, CLIC_INTATTR(irq));
239 		clicintattr = csr_read(CSR_MIREG2);
240 		clicintattr |= clicattr.w << bit_offset;
241 		csr_write(CSR_MIREG2, clicintattr);
242 	}
243 }
244 
245 /**
246  * @brief Set pending bit of an interrupt
247  */
riscv_clic_irq_set_pending(uint32_t irq)248 void riscv_clic_irq_set_pending(uint32_t irq)
249 {
250 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
251 		const struct device *dev = DEVICE_DT_INST_GET(0);
252 		union CLICINTIP clicintip = {.b = {.IP = 0x1}};
253 
254 		write_clic8(dev, CLIC_INTIP(irq), clicintip.w);
255 	} else {
256 		csr_write(CSR_MISELECT, CLIC_INTIP(irq));
257 		csr_set(CSR_MIREG, BIT(irq % 32));
258 	}
259 }
260 
clic_init(const struct device * dev)261 static int clic_init(const struct device *dev)
262 {
263 	struct clic_data *data = dev->data;
264 
265 	if (IS_ENABLED(CONFIG_NUCLEI_ECLIC)) {
266 		/* Configure the interrupt level threshold. */
267 		union CLICMTH clicmth = {.b = {.mth = 0x0}};
268 
269 		write_clic32(dev, CLIC_MTH, clicmth.qw);
270 
271 		/* Detect the number of bits for the clicintctl register. */
272 		union CLICINFO clicinfo = {.qw = read_clic32(dev, CLIC_INFO)};
273 
274 		data->intctlbits = clicinfo.b.intctlbits;
275 
276 		if (data->nlbits > data->intctlbits) {
277 			data->nlbits = data->intctlbits;
278 		}
279 	} else {
280 		/* Configure the interrupt level threshold by CSR mintthresh. */
281 		csr_write(CSR_MINTTHRESH, 0x0);
282 	}
283 
284 	if (IS_ENABLED(CONFIG_CLIC_SMCLICCONFIG_EXT)) {
285 		if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
286 			/* Configure the number of bits assigned to interrupt levels. */
287 			union CLICCFG cliccfg = {.qw = read_clic32(dev, CLIC_CFG)};
288 
289 			cliccfg.w.nlbits = data->nlbits;
290 			write_clic32(dev, CLIC_CFG, cliccfg.qw);
291 		} else {
292 			csr_write(CSR_MISELECT, CLIC_CFG);
293 			union CLICCFG cliccfg = {.qw = csr_read(CSR_MIREG)};
294 
295 			cliccfg.w.nlbits = data->nlbits;
296 			csr_write(CSR_MIREG, cliccfg.qw);
297 		}
298 	}
299 
300 	if (IS_ENABLED(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS)) {
301 		/* Reset all interrupt control register. */
302 		for (int i = 0; i < CONFIG_NUM_IRQS; i++) {
303 			write_clic32(dev, CLIC_CTRL(i), 0);
304 		}
305 	} else {
306 		/* Reset all clicintip, clicintie register. */
307 		for (int i = 0; i < CONFIG_NUM_IRQS; i += 32) {
308 			csr_write(CSR_MISELECT, CLIC_INTIP(i));
309 			csr_write(CSR_MIREG, 0);
310 			csr_write(CSR_MIREG2, 0);
311 		}
312 
313 		/* Reset all clicintctl, clicintattr register. */
314 		for (int i = 0; i < CONFIG_NUM_IRQS; i += 4) {
315 			csr_write(CSR_MISELECT, CLIC_INTCTRL(i));
316 			csr_write(CSR_MIREG, 0);
317 			csr_write(CSR_MIREG2, 0);
318 		}
319 	}
320 
321 	return 0;
322 }
323 
324 #define CLIC_INTC_DATA_INIT(n)                                                                     \
325 	static struct clic_data clic_data_##n = {                                                  \
326 		.nlbits = CONFIG_CLIC_PARAMETER_MNLBITS,                                           \
327 		.intctlbits = CONFIG_CLIC_PARAMETER_INTCTLBITS,                                    \
328 	};
329 #define CLIC_INTC_CONFIG_INIT(n)                                                                   \
330 	const static struct clic_config clic_config_##n = {                                        \
331 		.base = COND_CODE_1(CONFIG_LEGACY_CLIC_MEMORYMAP_ACCESS,                           \
332 				   (DT_REG_ADDR(DT_DRV_INST(n))), (0)),                            \
333 	};
334 #define CLIC_INTC_DEVICE_INIT(n)                                                                   \
335 	CLIC_INTC_DATA_INIT(n)                                                                     \
336 	CLIC_INTC_CONFIG_INIT(n)                                                                   \
337 	DEVICE_DT_INST_DEFINE(n, &clic_init, NULL, &clic_data_##n, &clic_config_##n, PRE_KERNEL_1, \
338 			      CONFIG_INTC_INIT_PRIORITY, NULL);
339 
340 DT_INST_FOREACH_STATUS_OKAY(CLIC_INTC_DEVICE_INIT)
341