1 /*
2  * Copyright (c) 2021 Tokita, Hiroshi <tokita.hiroshi@gmail.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @brief Driver for Nuclie's Extended Core Interrupt Controller
9  */
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/arch/cpu.h>
13 #include <zephyr/sys/util.h>
14 #include <zephyr/device.h>
15 #include <zephyr/irq_multilevel.h>
16 
17 #include <zephyr/sw_isr_table.h>
18 #include <zephyr/drivers/interrupt_controller/riscv_clic.h>
19 
20 #define DT_DRV_COMPAT nuclei_eclic
21 
22 union CLICCFG {
23 	struct {
24 		uint8_t _reserved0 : 1;
25 		/** number of interrupt level bits */
26 		uint8_t nlbits : 4;
27 		uint8_t _reserved1 : 2;
28 		uint8_t _reserved2 : 1;
29 	} b;
30 	uint8_t w;
31 };
32 
33 union CLICINFO {
34 	struct {
35 		/** number of max supported interrupts */
36 		uint32_t numint : 13;
37 		/** architecture version */
38 		uint32_t version : 8;
39 		/** supported bits in the clicintctl */
40 		uint32_t intctlbits : 4;
41 		uint32_t _reserved0 : 7;
42 	} b;
43 	uint32_t qw;
44 };
45 
46 union CLICMTH {
47 	uint8_t w;
48 };
49 
50 union CLICINTIP {
51 	struct {
52 		/** Interrupt Pending */
53 		uint8_t IP : 1;
54 		uint8_t reserved0 : 7;
55 	} b;
56 	uint8_t w;
57 };
58 
59 union CLICINTIE {
60 	struct {
61 		/** Interrupt Enabled */
62 		uint8_t IE : 1;
63 		uint8_t reserved0 : 7;
64 	} b;
65 	uint8_t w;
66 };
67 
68 union CLICINTATTR {
69 	struct {
70 		/** 0: non-vectored 1:vectored */
71 		uint8_t shv : 1;
72 		/** 0: level 1: rising edge 2: falling edge */
73 		uint8_t trg : 2;
74 		uint8_t reserved0 : 3;
75 		uint8_t reserved1 : 2;
76 	} b;
77 	uint8_t w;
78 };
79 
80 struct CLICCTRL {
81 	volatile union CLICINTIP INTIP;
82 	volatile union CLICINTIE INTIE;
83 	volatile union CLICINTATTR INTATTR;
84 	volatile uint8_t INTCTRL;
85 };
86 
87 /** ECLIC Mode mask for MTVT CSR Register */
88 #define ECLIC_MODE_MTVEC_Msk   3U
89 
90 /** CLIC INTATTR: TRIG Mask */
91 #define CLIC_INTATTR_TRIG_Msk  0x3U
92 
93 #define ECLIC_CFG       (*((volatile union CLICCFG  *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0))))
94 #define ECLIC_INFO      (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1))))
95 #define ECLIC_MTH       (*((volatile union CLICMTH  *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2))))
96 #define ECLIC_CTRL      ((volatile  struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3)))
97 #define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3))
98 
99 #if CONFIG_3RD_LEVEL_INTERRUPTS
100 #define INTERRUPT_LEVEL 2
101 #elif CONFIG_2ND_LEVEL_INTERRUPTS
102 #define INTERRUPT_LEVEL 1
103 #else
104 #define INTERRUPT_LEVEL 0
105 #endif
106 
107 static uint8_t nlbits;
108 static uint8_t intctlbits;
109 static uint8_t max_prio;
110 static uint8_t max_level;
111 static uint8_t intctrl_mask;
112 
leftalign8(uint8_t val,uint8_t shift)113 static inline uint8_t leftalign8(uint8_t val, uint8_t shift)
114 {
115 	return (val << (8U - shift));
116 }
117 
mask8(uint8_t len)118 static inline uint8_t mask8(uint8_t len)
119 {
120 	return ((1 << len) - 1) & 0xFFFFU;
121 }
122 
123 /**
124  * @brief Enable interrupt
125  */
riscv_clic_irq_enable(uint32_t irq)126 void riscv_clic_irq_enable(uint32_t irq)
127 {
128 	ECLIC_CTRL[irq].INTIE.b.IE = 1;
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 	ECLIC_CTRL[irq].INTIE.b.IE = 0;
137 }
138 
139 /**
140  * @brief Get enable status of interrupt
141  */
riscv_clic_irq_is_enabled(uint32_t irq)142 int riscv_clic_irq_is_enabled(uint32_t irq)
143 {
144 	return ECLIC_CTRL[irq].INTIE.b.IE;
145 }
146 
147 /**
148  * @brief Set priority and level of interrupt
149  */
riscv_clic_irq_priority_set(uint32_t irq,uint32_t pri,uint32_t flags)150 void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
151 {
152 	const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits);
153 	const uint8_t level =  leftalign8(MIN((irq_get_level(irq) - 1), max_level), nlbits);
154 	const uint8_t intctrl = (prio | level) | (~intctrl_mask);
155 
156 	ECLIC_CTRL[irq].INTCTRL = intctrl;
157 
158 	union CLICINTATTR intattr = {.w = 0};
159 #if defined(CONFIG_RISCV_VECTORED_MODE) && !defined(CONFIG_LEGACY_CLIC)
160 	/*
161 	 * Set Selective Hardware Vectoring.
162 	 * Legacy SiFive does not implement smclicshv extension and vectoring is
163 	 * enabled in the mode bits of mtvec.
164 	 */
165 	intattr.b.shv = 1;
166 #else
167 	intattr.b.shv = 0;
168 #endif
169 	intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
170 	ECLIC_CTRL[irq].INTATTR = intattr;
171 }
172 
173 /**
174  * @brief Set pending bit of an interrupt
175  */
riscv_clic_irq_set_pending(uint32_t irq)176 void riscv_clic_irq_set_pending(uint32_t irq)
177 {
178 	ECLIC_CTRL[irq].INTIP.b.IP = 1;
179 }
180 
nuclei_eclic_init(const struct device * dev)181 static int nuclei_eclic_init(const struct device *dev)
182 {
183 	/* check hardware support required interrupt levels */
184 	__ASSERT_NO_MSG(ECLIC_INFO.b.intctlbits >= INTERRUPT_LEVEL);
185 
186 	ECLIC_MTH.w = 0;
187 	ECLIC_CFG.w = 0;
188 	ECLIC_CFG.b.nlbits = INTERRUPT_LEVEL;
189 	for (int i = 0; i < ECLIC_CTRL_SIZE; i++) {
190 		ECLIC_CTRL[i] = (struct CLICCTRL) { 0 };
191 	}
192 
193 	csr_write(mtvec, ((csr_read(mtvec) & 0xFFFFFFC0) | ECLIC_MODE_MTVEC_Msk));
194 
195 	nlbits = ECLIC_CFG.b.nlbits;
196 	intctlbits = ECLIC_INFO.b.intctlbits;
197 	max_prio = mask8(intctlbits - nlbits);
198 	max_level = mask8(nlbits);
199 	intctrl_mask = leftalign8(mask8(intctlbits), intctlbits);
200 
201 	return 0;
202 }
203 
204 DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL,
205 		      PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
206