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 
16 #include <zephyr/sw_isr_table.h>
17 #include <zephyr/drivers/interrupt_controller/riscv_clic.h>
18 
19 #define DT_DRV_COMPAT nuclei_eclic
20 
21 union CLICCFG {
22 	struct {
23 		uint8_t _reserved0 : 1;
24 		/** number of interrupt level bits */
25 		uint8_t nlbits : 4;
26 		uint8_t _reserved1 : 2;
27 		uint8_t _reserved2 : 1;
28 	} b;
29 	uint8_t w;
30 };
31 
32 union CLICINFO {
33 	struct {
34 		/** number of max supported interrupts */
35 		uint32_t numint : 13;
36 		/** architecture version */
37 		uint32_t version : 8;
38 		/** supported bits in the clicintctl */
39 		uint32_t intctlbits : 4;
40 		uint32_t _reserved0 : 7;
41 	} b;
42 	uint32_t qw;
43 };
44 
45 union CLICMTH {
46 	uint8_t w;
47 };
48 
49 union CLICINTIP {
50 	struct {
51 		/** Interrupt Pending */
52 		uint8_t IP : 1;
53 		uint8_t reserved0 : 7;
54 	} b;
55 	uint8_t w;
56 };
57 
58 union CLICINTIE {
59 	struct {
60 		/** Interrupt Enabled */
61 		uint8_t IE : 1;
62 		uint8_t reserved0 : 7;
63 	} b;
64 	uint8_t w;
65 };
66 
67 union CLICINTATTR {
68 	struct {
69 		/** 0: non-vectored 1:vectored */
70 		uint8_t shv : 1;
71 		/** 0: level 1: rising edge 2: falling edge */
72 		uint8_t trg : 2;
73 		uint8_t reserved0 : 3;
74 		uint8_t reserved1 : 2;
75 	} b;
76 	uint8_t w;
77 };
78 
79 struct CLICCTRL {
80 	volatile union CLICINTIP INTIP;
81 	volatile union CLICINTIE INTIE;
82 	volatile union CLICINTATTR INTATTR;
83 	volatile uint8_t INTCTRL;
84 };
85 
86 /** CLIC INTATTR: TRIG Mask */
87 #define CLIC_INTATTR_TRIG_Msk  0x3U
88 
89 #define ECLIC_CFG       (*((volatile union CLICCFG  *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 0))))
90 #define ECLIC_INFO      (*((volatile union CLICINFO *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 1))))
91 #define ECLIC_MTH       (*((volatile union CLICMTH  *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 2))))
92 #define ECLIC_CTRL      ((volatile  struct CLICCTRL *)(DT_REG_ADDR_BY_IDX(DT_NODELABEL(eclic), 3)))
93 #define ECLIC_CTRL_SIZE (DT_REG_SIZE_BY_IDX(DT_NODELABEL(eclic), 3))
94 
95 static uint8_t nlbits;
96 static uint8_t intctlbits;
97 static uint8_t max_prio;
98 static uint8_t max_level;
99 static uint8_t intctrl_mask;
100 
leftalign8(uint8_t val,uint8_t shift)101 static inline uint8_t leftalign8(uint8_t val, uint8_t shift)
102 {
103 	return (val << (8U - shift));
104 }
105 
mask8(uint8_t len)106 static inline uint8_t mask8(uint8_t len)
107 {
108 	return ((1 << len) - 1) & 0xFFFFU;
109 }
110 
111 /**
112  * @brief Enable interrupt
113  */
riscv_clic_irq_enable(uint32_t irq)114 void riscv_clic_irq_enable(uint32_t irq)
115 {
116 	ECLIC_CTRL[irq].INTIE.b.IE = 1;
117 }
118 
119 /**
120  * @brief Disable interrupt
121  */
riscv_clic_irq_disable(uint32_t irq)122 void riscv_clic_irq_disable(uint32_t irq)
123 {
124 	ECLIC_CTRL[irq].INTIE.b.IE = 0;
125 }
126 
127 /**
128  * @brief Get enable status of interrupt
129  */
riscv_clic_irq_is_enabled(uint32_t irq)130 int riscv_clic_irq_is_enabled(uint32_t irq)
131 {
132 	return ECLIC_CTRL[irq].INTIE.b.IE;
133 }
134 
135 /**
136  * @brief Set priority and level of interrupt
137  */
riscv_clic_irq_priority_set(uint32_t irq,uint32_t pri,uint32_t flags)138 void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
139 {
140 	const uint8_t prio = leftalign8(MIN(pri, max_prio), intctlbits);
141 	const uint8_t level =  leftalign8(max_level, nlbits);
142 	const uint8_t intctrl = (prio | level) | (~intctrl_mask);
143 
144 	ECLIC_CTRL[irq].INTCTRL = intctrl;
145 
146 	union CLICINTATTR intattr = {.w = 0};
147 
148 	/* Set non-vectoring as default. */
149 	intattr.b.shv = 0;
150 	intattr.b.trg = (uint8_t)(flags & CLIC_INTATTR_TRIG_Msk);
151 	ECLIC_CTRL[irq].INTATTR = intattr;
152 }
153 
154 /**
155  * @brief Set vector mode of interrupt
156  */
riscv_clic_irq_vector_set(uint32_t irq)157 void riscv_clic_irq_vector_set(uint32_t irq)
158 {
159 	/* Set Selective Hardware Vectoring. */
160 	union CLICINTATTR intattr = ECLIC_CTRL[irq].INTATTR;
161 
162 	intattr.b.shv = 1;
163 	ECLIC_CTRL[irq].INTATTR = intattr;
164 }
165 
166 /**
167  * @brief Set pending bit of an interrupt
168  */
riscv_clic_irq_set_pending(uint32_t irq)169 void riscv_clic_irq_set_pending(uint32_t irq)
170 {
171 	ECLIC_CTRL[irq].INTIP.b.IP = 1;
172 }
173 
nuclei_eclic_init(const struct device * dev)174 static int nuclei_eclic_init(const struct device *dev)
175 {
176 	ECLIC_MTH.w = 0;
177 	ECLIC_CFG.w = 0;
178 	ECLIC_CFG.b.nlbits = 0;
179 	for (int i = 0; i < ECLIC_CTRL_SIZE; i++) {
180 		ECLIC_CTRL[i] = (struct CLICCTRL) { 0 };
181 	}
182 
183 	nlbits = ECLIC_CFG.b.nlbits;
184 	intctlbits = ECLIC_INFO.b.intctlbits;
185 	max_prio = mask8(intctlbits - nlbits);
186 	max_level = mask8(nlbits);
187 	intctrl_mask = leftalign8(mask8(intctlbits), intctlbits);
188 
189 	return 0;
190 }
191 
192 DEVICE_DT_INST_DEFINE(0, nuclei_eclic_init, NULL, NULL, NULL,
193 		      PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
194