xref: /optee_os/core/drivers/atmel_saic.c (revision 99e2612cb7ec7a9ddced18ad03f602848bb97bce)
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;
33 
34 static void saic_register_pm(void);
35 
36 static void saic_write_reg(uint32_t reg, uint32_t val)
37 {
38 	io_write32(saic.base + reg, val);
39 }
40 
41 static uint32_t saic_read_reg(uint32_t reg)
42 {
43 	return io_read32(saic.base + reg);
44 }
45 
46 void interrupt_main_handler(void)
47 {
48 	uint32_t irqnr = saic_read_reg(AT91_AIC_IVR);
49 
50 	interrupt_call_handlers(&saic.chip, irqnr);
51 	saic_write_reg(AT91_AIC_EOICR, 0);
52 }
53 
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 
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 
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 
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 
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 
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 
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 
133 static void saic_raise_pi(struct itr_chip *chip __unused, size_t it __unused)
134 {
135 	panic();
136 }
137 
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 
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 	.mask = saic_disable,
153 	.unmask = saic_enable,
154 	.enable = saic_enable,
155 	.disable = saic_disable,
156 	.raise_pi = saic_raise_pi,
157 	.raise_sgi = saic_raise_sgi,
158 	.set_affinity = saic_set_affinity,
159 };
160 
161 static int saic_dt_get_irq(const uint32_t *properties, int len,
162 			   uint32_t *type, uint32_t *prio)
163 {
164 	int it = DT_INFO_INVALID_INTERRUPT;
165 	uint32_t src_type = 0;
166 	uint32_t priority = 0;
167 	uint32_t irq_type = 0;
168 
169 	len /= sizeof(uint32_t);
170 
171 	if (len != 3)
172 		return DT_INFO_INVALID_INTERRUPT;
173 
174 	it = fdt32_to_cpu(properties[0]);
175 	if (it >= (int)saic.nr_irqs)
176 		return DT_INFO_INVALID_INTERRUPT;
177 
178 	irq_type = fdt32_to_cpu(properties[1]);
179 	if (saic_get_src_type(irq_type, it, &src_type))
180 		return DT_INFO_INVALID_INTERRUPT;
181 
182 	priority = fdt32_to_cpu(properties[2]);
183 	if (priority >= AT91_AIC_MAX_PRIO)
184 		return DT_INFO_INVALID_INTERRUPT;
185 
186 	if (type)
187 		*type = irq_type;
188 
189 	if (prio)
190 		*prio = priority;
191 
192 	return it;
193 }
194 
195 static struct saic_data saic = {
196 	.chip = {
197 		.ops = &saic_ops,
198 		.dt_get_irq = &saic_dt_get_irq,
199 	},
200 };
201 
202 static void saic_clear_aicredir(void)
203 {
204 	vaddr_t sfr_base = sam_sfr_base();
205 	uint32_t aicredir_val = 0;
206 
207 	aicredir_val = io_read32(sfr_base + AT91_SFR_SN1);
208 	aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY;
209 	aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK;
210 
211 	/*
212 	 * We explicitly don't want to redirect secure interrupts to non secure
213 	 * AIC. By default, AT91Bootstrap does so on some platforms.
214 	 */
215 	io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val);
216 }
217 
218 static void saic_init_external(const void *fdt, int node)
219 {
220 	int i = 0;
221 	int len = 0;
222 	int it_grp = 0;
223 	int it_off = 0;
224 	size_t it = 0;
225 	const uint32_t *external = NULL;
226 
227 	external = fdt_getprop(fdt, node, "atmel,external-irqs", &len);
228 	if (!external)
229 		return;
230 
231 	len /= sizeof(uint32_t);
232 	for (i = 0; i < len; i++) {
233 		it = fdt32_to_cpu(external[i]);
234 
235 		DMSG("IRQ %zu is external", it);
236 
237 		if (it >= saic.nr_irqs)
238 			panic();
239 
240 		it_grp = it / 32;
241 		it_off = it % 32;
242 
243 		saic.external[it_grp] |= BIT32(it_off);
244 	}
245 }
246 
247 static void saic_init_hw(void)
248 {
249 	unsigned int i = 0;
250 
251 	saic_clear_aicredir();
252 
253 	/* Disable write protect if any */
254 	saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY);
255 
256 	/* Pop the (potential) interrupt stack (8 priority) */
257 	for (i = 0; i < 8; i++)
258 		saic_write_reg(AT91_AIC_EOICR, 0);
259 
260 	/* Disable and clear all interrupts initially */
261 	for (i = 0; i < saic.nr_irqs; i++) {
262 		saic_write_reg(AT91_AIC_IDCR, 1);
263 		saic_write_reg(AT91_AIC_ICCR, 1);
264 		/* Set interrupt vector to hold interrupt number */
265 		saic_select_it(i);
266 		saic_write_reg(AT91_AIC_SVR, i);
267 	}
268 
269 	saic_write_reg(AT91_AIC_SPU, 0xffffffff);
270 
271 	/* Disable AIC debugging */
272 	saic_write_reg(AT91_AIC_DCR, 0);
273 }
274 
275 TEE_Result atmel_saic_setup(void)
276 {
277 	int node = -1;
278 	int ret = 0;
279 	size_t size = 0;
280 	const void *fdt = get_embedded_dt();
281 
282 	/* There is only 1 SAIC controller */
283 	if (saic.base)
284 		return TEE_ERROR_GENERIC;
285 
286 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic");
287 	if (node < 0)
288 		return TEE_ERROR_GENERIC;
289 
290 	ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO);
291 	if (ret) {
292 		EMSG("Failed to map SAIC\n");
293 		return TEE_ERROR_GENERIC;
294 	}
295 
296 	saic.chip.ops = &saic_ops;
297 	saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS;
298 
299 	saic_init_external(fdt, node);
300 	saic_init_hw();
301 
302 	interrupt_main_init(&saic.chip);
303 	saic_register_pm();
304 
305 	return TEE_SUCCESS;
306 }
307 
308 #ifdef CFG_PM_ARM32
309 
310 static struct {
311 	uint8_t smr[SAMA5D2_AIC_MAX_IRQS];
312 } saic_state;
313 
314 static void saic_resume(void)
315 {
316 	uint8_t it = 0;
317 
318 	saic_init_hw();
319 
320 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
321 		saic_select_it(it);
322 		saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]);
323 	}
324 }
325 
326 static void saic_suspend(void)
327 {
328 	uint8_t it = 0;
329 
330 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
331 		saic_select_it(it);
332 		saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR);
333 	}
334 }
335 
336 static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused,
337 			  const struct pm_callback_handle *hdl __unused)
338 {
339 	switch (op) {
340 	case PM_OP_RESUME:
341 		saic_resume();
342 		break;
343 	case PM_OP_SUSPEND:
344 		saic_suspend();
345 		break;
346 	default:
347 		panic("Invalid PM operation");
348 	}
349 
350 	return TEE_SUCCESS;
351 }
352 
353 static void saic_register_pm(void)
354 {
355 	register_pm_core_service_cb(saic_pm, NULL, "saic");
356 }
357 #else
358 static void saic_register_pm(void)
359 {
360 }
361 #endif
362