1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2021, Microchip
4 */
5
6 #include <assert.h>
7 #include <drivers/atmel_saic.h>
8 #include <dt-bindings/interrupt-controller/irq.h>
9 #include <io.h>
10 #include <kernel/boot.h>
11 #include <kernel/dt.h>
12 #include <kernel/interrupt.h>
13 #include <kernel/pm.h>
14 #include <libfdt.h>
15 #include <sam_sfr.h>
16 #include <tee_api_types.h>
17 #include <trace.h>
18
19 #define AT91_AIC_MAX_PRIO 8
20
21 #define SAMA5D2_AIC_MAX_IRQS 77
22
23 #define SAMA5D2_AIC_MAX_IRQS32 ((SAMA5D2_AIC_MAX_IRQS + 31) / 32)
24
25 struct saic_data {
26 struct itr_chip chip;
27 vaddr_t base;
28 size_t nr_irqs;
29 uint32_t external[SAMA5D2_AIC_MAX_IRQS32];
30 };
31
32 static struct saic_data saic = {0};
33
34 static void saic_register_pm(void);
35
saic_write_reg(uint32_t reg,uint32_t val)36 static void saic_write_reg(uint32_t reg, uint32_t val)
37 {
38 io_write32(saic.base + reg, val);
39 }
40
saic_read_reg(uint32_t reg)41 static uint32_t saic_read_reg(uint32_t reg)
42 {
43 return io_read32(saic.base + reg);
44 }
45
atmel_saic_it_handle(void)46 void atmel_saic_it_handle(void)
47 {
48 uint32_t irqnr = saic_read_reg(AT91_AIC_IVR);
49
50 itr_handle(irqnr);
51 saic_write_reg(AT91_AIC_EOICR, 0);
52 }
53
saic_select_it(size_t it)54 static void saic_select_it(size_t it)
55 {
56 assert(!(it & ~AT91_AIC_SSR_ITSEL_MASK));
57
58 saic_write_reg(AT91_AIC_SSR, it);
59 }
60
saic_configure_it(size_t it,uint32_t src_type,uint32_t priority)61 static void saic_configure_it(size_t it, uint32_t src_type, uint32_t priority)
62 {
63 saic_select_it(it);
64 saic_write_reg(AT91_AIC_SMR, src_type | priority);
65 }
66
is_external_it(size_t it)67 static bool is_external_it(size_t it)
68 {
69 uint32_t it_grp = it / 32;
70 uint32_t it_off = it % 32;
71
72 if (it >= saic.nr_irqs)
73 panic();
74
75 return saic.external[it_grp] & BIT32(it_off);
76 }
77
saic_get_src_type(uint32_t dt_level,size_t it,uint32_t * src_type)78 static TEE_Result saic_get_src_type(uint32_t dt_level, size_t it,
79 uint32_t *src_type)
80 {
81 switch (dt_level) {
82 case IRQ_TYPE_EDGE_RISING:
83 *src_type = AT91_AIC_SMR_POS_EDGE;
84 break;
85 case IRQ_TYPE_EDGE_FALLING:
86 if (!is_external_it(it))
87 return TEE_ERROR_BAD_PARAMETERS;
88
89 *src_type = AT91_AIC_SMR_NEG_EDGE;
90 break;
91 case IRQ_TYPE_LEVEL_HIGH:
92 *src_type = AT91_AIC_SMR_HIGH_LEVEL;
93 break;
94 case IRQ_TYPE_LEVEL_LOW:
95 if (!is_external_it(it))
96 return TEE_ERROR_BAD_PARAMETERS;
97
98 *src_type = AT91_AIC_SMR_LEVEL;
99 break;
100 default:
101 return TEE_ERROR_BAD_PARAMETERS;
102 }
103
104 return TEE_SUCCESS;
105 }
106
saic_add(struct itr_chip * chip __unused,size_t it,uint32_t type,uint32_t prio)107 static void saic_add(struct itr_chip *chip __unused, size_t it,
108 uint32_t type, uint32_t prio)
109 {
110 uint32_t src_type = AT91_AIC_SMR_HIGH_LEVEL;
111
112 if (it >= saic.nr_irqs)
113 panic();
114
115 if (saic_get_src_type(type, it, &src_type))
116 panic("Invalid interrupt specifier");
117
118 saic_configure_it(it, src_type, prio);
119 }
120
saic_enable(struct itr_chip * chip __unused,size_t it)121 static void saic_enable(struct itr_chip *chip __unused, size_t it)
122 {
123 saic_select_it(it);
124 saic_write_reg(AT91_AIC_IECR, 1);
125 }
126
saic_disable(struct itr_chip * chip __unused,size_t it)127 static void saic_disable(struct itr_chip *chip __unused, size_t it)
128 {
129 saic_select_it(it);
130 saic_write_reg(AT91_AIC_IDCR, 1);
131 }
132
saic_raise_pi(struct itr_chip * chip __unused,size_t it __unused)133 static void saic_raise_pi(struct itr_chip *chip __unused, size_t it __unused)
134 {
135 panic();
136 }
137
saic_raise_sgi(struct itr_chip * chip __unused,size_t it __unused,uint8_t cpu_mask __unused)138 static void saic_raise_sgi(struct itr_chip *chip __unused, size_t it __unused,
139 uint8_t cpu_mask __unused)
140 {
141 panic();
142 }
143
saic_set_affinity(struct itr_chip * chip __unused,size_t it __unused,uint8_t cpu_mask __unused)144 static void saic_set_affinity(struct itr_chip *chip __unused,
145 size_t it __unused, uint8_t cpu_mask __unused)
146 {
147 panic();
148 }
149
150 static const struct itr_ops saic_ops = {
151 .add = saic_add,
152 .enable = saic_enable,
153 .disable = saic_disable,
154 .raise_pi = saic_raise_pi,
155 .raise_sgi = saic_raise_sgi,
156 .set_affinity = saic_set_affinity,
157 };
158
saic_dt_get_irq(const uint32_t * properties,int len,uint32_t * type,uint32_t * prio)159 static int saic_dt_get_irq(const uint32_t *properties, int len,
160 uint32_t *type, uint32_t *prio)
161 {
162 int it = DT_INFO_INVALID_INTERRUPT;
163 uint32_t src_type = 0;
164 uint32_t priority = 0;
165 uint32_t irq_type = 0;
166
167 len /= sizeof(uint32_t);
168
169 if (len != 3)
170 return DT_INFO_INVALID_INTERRUPT;
171
172 it = fdt32_to_cpu(properties[0]);
173 if (it >= (int)saic.nr_irqs)
174 return DT_INFO_INVALID_INTERRUPT;
175
176 irq_type = fdt32_to_cpu(properties[1]);
177 if (saic_get_src_type(irq_type, it, &src_type))
178 return DT_INFO_INVALID_INTERRUPT;
179
180 priority = fdt32_to_cpu(properties[2]);
181 if (priority >= AT91_AIC_MAX_PRIO)
182 return DT_INFO_INVALID_INTERRUPT;
183
184 if (type)
185 *type = irq_type;
186
187 if (prio)
188 *prio = priority;
189
190 return it;
191 }
192
193 struct itr_chip saic_chip = {
194 .ops = &saic_ops,
195 .dt_get_irq = &saic_dt_get_irq,
196 };
197
saic_clear_aicredir(void)198 static void saic_clear_aicredir(void)
199 {
200 vaddr_t sfr_base = sam_sfr_base();
201 uint32_t aicredir_val = 0;
202
203 aicredir_val = io_read32(sfr_base + AT91_SFR_SN1);
204 aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY;
205 aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK;
206
207 /*
208 * We explicitly don't want to redirect secure interrupts to non secure
209 * AIC. By default, AT91Bootstrap does so on some platforms.
210 */
211 io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val);
212 }
213
saic_init_external(const void * fdt,int node)214 static void saic_init_external(const void *fdt, int node)
215 {
216 int i = 0;
217 int len = 0;
218 int it_grp = 0;
219 int it_off = 0;
220 size_t it = 0;
221 const uint32_t *external = NULL;
222
223 external = fdt_getprop(fdt, node, "atmel,external-irqs", &len);
224 if (!external)
225 return;
226
227 len /= sizeof(uint32_t);
228 for (i = 0; i < len; i++) {
229 it = fdt32_to_cpu(external[i]);
230
231 DMSG("IRQ %zu is external", it);
232
233 if (it >= saic.nr_irqs)
234 panic();
235
236 it_grp = it / 32;
237 it_off = it % 32;
238
239 saic.external[it_grp] |= BIT32(it_off);
240 }
241 }
242
saic_init_hw(void)243 static void saic_init_hw(void)
244 {
245 unsigned int i = 0;
246
247 saic_clear_aicredir();
248
249 /* Disable write protect if any */
250 saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY);
251
252 /* Pop the (potential) interrupt stack (8 priority) */
253 for (i = 0; i < 8; i++)
254 saic_write_reg(AT91_AIC_EOICR, 0);
255
256 /* Disable and clear all interrupts initially */
257 for (i = 0; i < saic.nr_irqs; i++) {
258 saic_write_reg(AT91_AIC_IDCR, 1);
259 saic_write_reg(AT91_AIC_ICCR, 1);
260 /* Set interrupt vector to hold interrupt number */
261 saic_select_it(i);
262 saic_write_reg(AT91_AIC_SVR, i);
263 }
264
265 saic_write_reg(AT91_AIC_SPU, 0xffffffff);
266
267 /* Disable AIC debugging */
268 saic_write_reg(AT91_AIC_DCR, 0);
269 }
270
atmel_saic_setup(void)271 TEE_Result atmel_saic_setup(void)
272 {
273 int node = -1;
274 int ret = 0;
275 size_t size = 0;
276 const void *fdt = get_embedded_dt();
277
278 /* There is only 1 SAIC controller */
279 if (saic.base)
280 return TEE_ERROR_GENERIC;
281
282 node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic");
283 if (node < 0)
284 return TEE_ERROR_GENERIC;
285
286 ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO);
287 if (ret) {
288 EMSG("Failed to map SAIC\n");
289 return TEE_ERROR_GENERIC;
290 }
291
292 saic.chip.ops = &saic_ops;
293 saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS;
294
295 saic_init_external(fdt, node);
296 saic_init_hw();
297
298 itr_init(&saic_chip);
299 saic_register_pm();
300
301 return TEE_SUCCESS;
302 }
303
304 #ifdef CFG_PM_ARM32
305
306 static struct {
307 uint8_t smr[SAMA5D2_AIC_MAX_IRQS];
308 } saic_state;
309
saic_resume(void)310 static void saic_resume(void)
311 {
312 uint8_t it = 0;
313
314 saic_init_hw();
315
316 for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
317 saic_select_it(it);
318 saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]);
319 }
320 }
321
saic_suspend(void)322 static void saic_suspend(void)
323 {
324 uint8_t it = 0;
325
326 for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
327 saic_select_it(it);
328 saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR);
329 }
330 }
331
saic_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl __unused)332 static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused,
333 const struct pm_callback_handle *hdl __unused)
334 {
335 switch (op) {
336 case PM_OP_RESUME:
337 saic_resume();
338 break;
339 case PM_OP_SUSPEND:
340 saic_suspend();
341 break;
342 default:
343 panic("Invalid PM operation");
344 }
345
346 return TEE_SUCCESS;
347 }
348
saic_register_pm(void)349 static void saic_register_pm(void)
350 {
351 register_pm_core_service_cb(saic_pm, NULL, "saic");
352 }
353 #else
saic_register_pm(void)354 static void saic_register_pm(void)
355 {
356 }
357 #endif
358