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