xref: /optee_os/core/drivers/atmel_saic.c (revision 99e2612cb7ec7a9ddced18ad03f602848bb97bce)
154c0b326SClément Léger // SPDX-License-Identifier: BSD-2-Clause
254c0b326SClément Léger /*
354c0b326SClément Léger  * Copyright (c) 2021, Microchip
454c0b326SClément Léger  */
554c0b326SClément Léger 
654c0b326SClément Léger #include <assert.h>
754c0b326SClément Léger #include <drivers/atmel_saic.h>
854c0b326SClément Léger #include <dt-bindings/interrupt-controller/irq.h>
954c0b326SClément Léger #include <io.h>
1054c0b326SClément Léger #include <kernel/boot.h>
1154c0b326SClément Léger #include <kernel/dt.h>
1254c0b326SClément Léger #include <kernel/interrupt.h>
1354c0b326SClément Léger #include <kernel/pm.h>
1454c0b326SClément Léger #include <libfdt.h>
1554c0b326SClément Léger #include <sam_sfr.h>
1654c0b326SClément Léger #include <tee_api_types.h>
1754c0b326SClément Léger #include <trace.h>
1854c0b326SClément Léger 
1954c0b326SClément Léger #define AT91_AIC_MAX_PRIO	8
2054c0b326SClément Léger 
2154c0b326SClément Léger #define SAMA5D2_AIC_MAX_IRQS	77
2254c0b326SClément Léger 
2354c0b326SClément Léger #define SAMA5D2_AIC_MAX_IRQS32	((SAMA5D2_AIC_MAX_IRQS + 31) / 32)
2454c0b326SClément Léger 
2554c0b326SClément Léger struct saic_data {
2654c0b326SClément Léger 	struct itr_chip chip;
2754c0b326SClément Léger 	vaddr_t base;
2854c0b326SClément Léger 	size_t nr_irqs;
2954c0b326SClément Léger 	uint32_t external[SAMA5D2_AIC_MAX_IRQS32];
3054c0b326SClément Léger };
3154c0b326SClément Léger 
32*99e2612cSEtienne Carriere static struct saic_data saic;
3354c0b326SClément Léger 
3454c0b326SClément Léger static void saic_register_pm(void);
3554c0b326SClément Léger 
3654c0b326SClément Léger static void saic_write_reg(uint32_t reg, uint32_t val)
3754c0b326SClément Léger {
3854c0b326SClément Léger 	io_write32(saic.base + reg, val);
3954c0b326SClément Léger }
4054c0b326SClément Léger 
4154c0b326SClément Léger static uint32_t saic_read_reg(uint32_t reg)
4254c0b326SClément Léger {
4354c0b326SClément Léger 	return io_read32(saic.base + reg);
4454c0b326SClément Léger }
4554c0b326SClément Léger 
46358bf47cSEtienne Carriere void interrupt_main_handler(void)
4754c0b326SClément Léger {
4854c0b326SClément Léger 	uint32_t irqnr = saic_read_reg(AT91_AIC_IVR);
4954c0b326SClément Léger 
50*99e2612cSEtienne Carriere 	interrupt_call_handlers(&saic.chip, irqnr);
5154c0b326SClément Léger 	saic_write_reg(AT91_AIC_EOICR, 0);
5254c0b326SClément Léger }
5354c0b326SClément Léger 
5454c0b326SClément Léger static void saic_select_it(size_t it)
5554c0b326SClément Léger {
5654c0b326SClément Léger 	assert(!(it & ~AT91_AIC_SSR_ITSEL_MASK));
5754c0b326SClément Léger 
5854c0b326SClément Léger 	saic_write_reg(AT91_AIC_SSR, it);
5954c0b326SClément Léger }
6054c0b326SClément Léger 
6154c0b326SClément Léger static void saic_configure_it(size_t it, uint32_t src_type, uint32_t priority)
6254c0b326SClément Léger {
6354c0b326SClément Léger 	saic_select_it(it);
6454c0b326SClément Léger 	saic_write_reg(AT91_AIC_SMR, src_type | priority);
6554c0b326SClément Léger }
6654c0b326SClément Léger 
6754c0b326SClément Léger static bool is_external_it(size_t it)
6854c0b326SClément Léger {
6954c0b326SClément Léger 	uint32_t it_grp = it / 32;
7054c0b326SClément Léger 	uint32_t it_off = it % 32;
7154c0b326SClément Léger 
7254c0b326SClément Léger 	if (it >= saic.nr_irqs)
7354c0b326SClément Léger 		panic();
7454c0b326SClément Léger 
7554c0b326SClément Léger 	return saic.external[it_grp] & BIT32(it_off);
7654c0b326SClément Léger }
7754c0b326SClément Léger 
7854c0b326SClément Léger static TEE_Result saic_get_src_type(uint32_t dt_level, size_t it,
7954c0b326SClément Léger 				    uint32_t *src_type)
8054c0b326SClément Léger {
8154c0b326SClément Léger 	switch (dt_level) {
8254c0b326SClément Léger 	case IRQ_TYPE_EDGE_RISING:
8354c0b326SClément Léger 		*src_type = AT91_AIC_SMR_POS_EDGE;
8454c0b326SClément Léger 		break;
8554c0b326SClément Léger 	case IRQ_TYPE_EDGE_FALLING:
8654c0b326SClément Léger 		if (!is_external_it(it))
8754c0b326SClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
8854c0b326SClément Léger 
8954c0b326SClément Léger 		*src_type = AT91_AIC_SMR_NEG_EDGE;
9054c0b326SClément Léger 		break;
9154c0b326SClément Léger 	case IRQ_TYPE_LEVEL_HIGH:
9254c0b326SClément Léger 		*src_type = AT91_AIC_SMR_HIGH_LEVEL;
9354c0b326SClément Léger 		break;
9454c0b326SClément Léger 	case IRQ_TYPE_LEVEL_LOW:
9554c0b326SClément Léger 		if (!is_external_it(it))
9654c0b326SClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
9754c0b326SClément Léger 
9854c0b326SClément Léger 		*src_type = AT91_AIC_SMR_LEVEL;
9954c0b326SClément Léger 		break;
10054c0b326SClément Léger 	default:
10154c0b326SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
10254c0b326SClément Léger 	}
10354c0b326SClément Léger 
10454c0b326SClément Léger 	return TEE_SUCCESS;
10554c0b326SClément Léger }
10654c0b326SClément Léger 
10754c0b326SClément Léger static void saic_add(struct itr_chip *chip __unused, size_t it,
10854c0b326SClément Léger 		     uint32_t type, uint32_t prio)
10954c0b326SClément Léger {
11054c0b326SClément Léger 	uint32_t src_type = AT91_AIC_SMR_HIGH_LEVEL;
11154c0b326SClément Léger 
11254c0b326SClément Léger 	if (it >= saic.nr_irqs)
11354c0b326SClément Léger 		panic();
11454c0b326SClément Léger 
11554c0b326SClément Léger 	if (saic_get_src_type(type, it, &src_type))
11654c0b326SClément Léger 		panic("Invalid interrupt specifier");
11754c0b326SClément Léger 
11854c0b326SClément Léger 	saic_configure_it(it, src_type, prio);
11954c0b326SClément Léger }
12054c0b326SClément Léger 
12154c0b326SClément Léger static void saic_enable(struct itr_chip *chip __unused, size_t it)
12254c0b326SClément Léger {
12354c0b326SClément Léger 	saic_select_it(it);
12454c0b326SClément Léger 	saic_write_reg(AT91_AIC_IECR, 1);
12554c0b326SClément Léger }
12654c0b326SClément Léger 
12754c0b326SClément Léger static void saic_disable(struct itr_chip *chip __unused, size_t it)
12854c0b326SClément Léger {
12954c0b326SClément Léger 	saic_select_it(it);
13054c0b326SClément Léger 	saic_write_reg(AT91_AIC_IDCR, 1);
13154c0b326SClément Léger }
13254c0b326SClément Léger 
13354c0b326SClément Léger static void saic_raise_pi(struct itr_chip *chip __unused, size_t it __unused)
13454c0b326SClément Léger {
13554c0b326SClément Léger 	panic();
13654c0b326SClément Léger }
13754c0b326SClément Léger 
13854c0b326SClément Léger static void saic_raise_sgi(struct itr_chip *chip __unused, size_t it __unused,
13954c0b326SClément Léger 			   uint8_t cpu_mask __unused)
14054c0b326SClément Léger {
14154c0b326SClément Léger 	panic();
14254c0b326SClément Léger }
14354c0b326SClément Léger 
14454c0b326SClément Léger static void saic_set_affinity(struct itr_chip *chip __unused,
14554c0b326SClément Léger 			      size_t it __unused, uint8_t cpu_mask __unused)
14654c0b326SClément Léger {
14754c0b326SClément Léger 	panic();
14854c0b326SClément Léger }
14954c0b326SClément Léger 
15054c0b326SClément Léger static const struct itr_ops saic_ops = {
15154c0b326SClément Léger 	.add = saic_add,
15287db85acSEtienne Carriere 	.mask = saic_disable,
15387db85acSEtienne Carriere 	.unmask = saic_enable,
15454c0b326SClément Léger 	.enable = saic_enable,
15554c0b326SClément Léger 	.disable = saic_disable,
15654c0b326SClément Léger 	.raise_pi = saic_raise_pi,
15754c0b326SClément Léger 	.raise_sgi = saic_raise_sgi,
15854c0b326SClément Léger 	.set_affinity = saic_set_affinity,
15954c0b326SClément Léger };
16054c0b326SClément Léger 
16154c0b326SClément Léger static int saic_dt_get_irq(const uint32_t *properties, int len,
16254c0b326SClément Léger 			   uint32_t *type, uint32_t *prio)
16354c0b326SClément Léger {
16454c0b326SClément Léger 	int it = DT_INFO_INVALID_INTERRUPT;
16554c0b326SClément Léger 	uint32_t src_type = 0;
16654c0b326SClément Léger 	uint32_t priority = 0;
16754c0b326SClément Léger 	uint32_t irq_type = 0;
16854c0b326SClément Léger 
16954c0b326SClément Léger 	len /= sizeof(uint32_t);
17054c0b326SClément Léger 
17154c0b326SClément Léger 	if (len != 3)
17254c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
17354c0b326SClément Léger 
17454c0b326SClément Léger 	it = fdt32_to_cpu(properties[0]);
17554c0b326SClément Léger 	if (it >= (int)saic.nr_irqs)
17654c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
17754c0b326SClément Léger 
17854c0b326SClément Léger 	irq_type = fdt32_to_cpu(properties[1]);
17954c0b326SClément Léger 	if (saic_get_src_type(irq_type, it, &src_type))
18054c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
18154c0b326SClément Léger 
18254c0b326SClément Léger 	priority = fdt32_to_cpu(properties[2]);
18354c0b326SClément Léger 	if (priority >= AT91_AIC_MAX_PRIO)
18454c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
18554c0b326SClément Léger 
18654c0b326SClément Léger 	if (type)
18754c0b326SClément Léger 		*type = irq_type;
18854c0b326SClément Léger 
18954c0b326SClément Léger 	if (prio)
19054c0b326SClément Léger 		*prio = priority;
19154c0b326SClément Léger 
19254c0b326SClément Léger 	return it;
19354c0b326SClément Léger }
19454c0b326SClément Léger 
195*99e2612cSEtienne Carriere static struct saic_data saic = {
196*99e2612cSEtienne Carriere 	.chip = {
19754c0b326SClément Léger 		.ops = &saic_ops,
19854c0b326SClément Léger 		.dt_get_irq = &saic_dt_get_irq,
199*99e2612cSEtienne Carriere 	},
20054c0b326SClément Léger };
20154c0b326SClément Léger 
20254c0b326SClément Léger static void saic_clear_aicredir(void)
20354c0b326SClément Léger {
20454c0b326SClément Léger 	vaddr_t sfr_base = sam_sfr_base();
20554c0b326SClément Léger 	uint32_t aicredir_val = 0;
20654c0b326SClément Léger 
20754c0b326SClément Léger 	aicredir_val = io_read32(sfr_base + AT91_SFR_SN1);
20854c0b326SClément Léger 	aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY;
20954c0b326SClément Léger 	aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK;
21054c0b326SClément Léger 
21154c0b326SClément Léger 	/*
21254c0b326SClément Léger 	 * We explicitly don't want to redirect secure interrupts to non secure
21354c0b326SClément Léger 	 * AIC. By default, AT91Bootstrap does so on some platforms.
21454c0b326SClément Léger 	 */
21554c0b326SClément Léger 	io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val);
21654c0b326SClément Léger }
21754c0b326SClément Léger 
21854c0b326SClément Léger static void saic_init_external(const void *fdt, int node)
21954c0b326SClément Léger {
22054c0b326SClément Léger 	int i = 0;
22154c0b326SClément Léger 	int len = 0;
22254c0b326SClément Léger 	int it_grp = 0;
22354c0b326SClément Léger 	int it_off = 0;
22454c0b326SClément Léger 	size_t it = 0;
22554c0b326SClément Léger 	const uint32_t *external = NULL;
22654c0b326SClément Léger 
22754c0b326SClément Léger 	external = fdt_getprop(fdt, node, "atmel,external-irqs", &len);
22854c0b326SClément Léger 	if (!external)
22954c0b326SClément Léger 		return;
23054c0b326SClément Léger 
23154c0b326SClément Léger 	len /= sizeof(uint32_t);
23254c0b326SClément Léger 	for (i = 0; i < len; i++) {
23354c0b326SClément Léger 		it = fdt32_to_cpu(external[i]);
23454c0b326SClément Léger 
23554c0b326SClément Léger 		DMSG("IRQ %zu is external", it);
23654c0b326SClément Léger 
23754c0b326SClément Léger 		if (it >= saic.nr_irqs)
23854c0b326SClément Léger 			panic();
23954c0b326SClément Léger 
24054c0b326SClément Léger 		it_grp = it / 32;
24154c0b326SClément Léger 		it_off = it % 32;
24254c0b326SClément Léger 
24354c0b326SClément Léger 		saic.external[it_grp] |= BIT32(it_off);
24454c0b326SClément Léger 	}
24554c0b326SClément Léger }
24654c0b326SClément Léger 
24754c0b326SClément Léger static void saic_init_hw(void)
24854c0b326SClément Léger {
24954c0b326SClément Léger 	unsigned int i = 0;
25054c0b326SClément Léger 
25154c0b326SClément Léger 	saic_clear_aicredir();
25254c0b326SClément Léger 
25354c0b326SClément Léger 	/* Disable write protect if any */
25454c0b326SClément Léger 	saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY);
25554c0b326SClément Léger 
25654c0b326SClément Léger 	/* Pop the (potential) interrupt stack (8 priority) */
25754c0b326SClément Léger 	for (i = 0; i < 8; i++)
25854c0b326SClément Léger 		saic_write_reg(AT91_AIC_EOICR, 0);
25954c0b326SClément Léger 
26054c0b326SClément Léger 	/* Disable and clear all interrupts initially */
26154c0b326SClément Léger 	for (i = 0; i < saic.nr_irqs; i++) {
26254c0b326SClément Léger 		saic_write_reg(AT91_AIC_IDCR, 1);
26354c0b326SClément Léger 		saic_write_reg(AT91_AIC_ICCR, 1);
26454c0b326SClément Léger 		/* Set interrupt vector to hold interrupt number */
26554c0b326SClément Léger 		saic_select_it(i);
26654c0b326SClément Léger 		saic_write_reg(AT91_AIC_SVR, i);
26754c0b326SClément Léger 	}
26854c0b326SClément Léger 
26954c0b326SClément Léger 	saic_write_reg(AT91_AIC_SPU, 0xffffffff);
27054c0b326SClément Léger 
27154c0b326SClément Léger 	/* Disable AIC debugging */
27254c0b326SClément Léger 	saic_write_reg(AT91_AIC_DCR, 0);
27354c0b326SClément Léger }
27454c0b326SClément Léger 
27554c0b326SClément Léger TEE_Result atmel_saic_setup(void)
27654c0b326SClément Léger {
27754c0b326SClément Léger 	int node = -1;
27854c0b326SClément Léger 	int ret = 0;
27954c0b326SClément Léger 	size_t size = 0;
28054c0b326SClément Léger 	const void *fdt = get_embedded_dt();
28154c0b326SClément Léger 
28254c0b326SClément Léger 	/* There is only 1 SAIC controller */
28354c0b326SClément Léger 	if (saic.base)
28454c0b326SClément Léger 		return TEE_ERROR_GENERIC;
28554c0b326SClément Léger 
28654c0b326SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic");
28754c0b326SClément Léger 	if (node < 0)
28854c0b326SClément Léger 		return TEE_ERROR_GENERIC;
28954c0b326SClément Léger 
290a5d5bbc8SVesa Jääskeläinen 	ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO);
29154c0b326SClément Léger 	if (ret) {
29254c0b326SClément Léger 		EMSG("Failed to map SAIC\n");
29354c0b326SClément Léger 		return TEE_ERROR_GENERIC;
29454c0b326SClément Léger 	}
29554c0b326SClément Léger 
29654c0b326SClément Léger 	saic.chip.ops = &saic_ops;
29754c0b326SClément Léger 	saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS;
29854c0b326SClément Léger 
29954c0b326SClément Léger 	saic_init_external(fdt, node);
30054c0b326SClément Léger 	saic_init_hw();
30154c0b326SClément Léger 
302*99e2612cSEtienne Carriere 	interrupt_main_init(&saic.chip);
30354c0b326SClément Léger 	saic_register_pm();
30454c0b326SClément Léger 
30554c0b326SClément Léger 	return TEE_SUCCESS;
30654c0b326SClément Léger }
30754c0b326SClément Léger 
30854c0b326SClément Léger #ifdef CFG_PM_ARM32
30954c0b326SClément Léger 
31054c0b326SClément Léger static struct {
31154c0b326SClément Léger 	uint8_t smr[SAMA5D2_AIC_MAX_IRQS];
31254c0b326SClément Léger } saic_state;
31354c0b326SClément Léger 
31454c0b326SClément Léger static void saic_resume(void)
31554c0b326SClément Léger {
31654c0b326SClément Léger 	uint8_t it = 0;
31754c0b326SClément Léger 
31854c0b326SClément Léger 	saic_init_hw();
31954c0b326SClément Léger 
32054c0b326SClément Léger 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
32154c0b326SClément Léger 		saic_select_it(it);
32254c0b326SClément Léger 		saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]);
32354c0b326SClément Léger 	}
32454c0b326SClément Léger }
32554c0b326SClément Léger 
32654c0b326SClément Léger static void saic_suspend(void)
32754c0b326SClément Léger {
32854c0b326SClément Léger 	uint8_t it = 0;
32954c0b326SClément Léger 
33054c0b326SClément Léger 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
33154c0b326SClément Léger 		saic_select_it(it);
33254c0b326SClément Léger 		saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR);
33354c0b326SClément Léger 	}
33454c0b326SClément Léger }
33554c0b326SClément Léger 
33654c0b326SClément Léger static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused,
33754c0b326SClément Léger 			  const struct pm_callback_handle *hdl __unused)
33854c0b326SClément Léger {
33954c0b326SClément Léger 	switch (op) {
34054c0b326SClément Léger 	case PM_OP_RESUME:
34154c0b326SClément Léger 		saic_resume();
34254c0b326SClément Léger 		break;
34354c0b326SClément Léger 	case PM_OP_SUSPEND:
34454c0b326SClément Léger 		saic_suspend();
34554c0b326SClément Léger 		break;
34654c0b326SClément Léger 	default:
34754c0b326SClément Léger 		panic("Invalid PM operation");
34854c0b326SClément Léger 	}
34954c0b326SClément Léger 
35054c0b326SClément Léger 	return TEE_SUCCESS;
35154c0b326SClément Léger }
35254c0b326SClément Léger 
35354c0b326SClément Léger static void saic_register_pm(void)
35454c0b326SClément Léger {
35554c0b326SClément Léger 	register_pm_core_service_cb(saic_pm, NULL, "saic");
35654c0b326SClément Léger }
35754c0b326SClément Léger #else
35854c0b326SClément Léger static void saic_register_pm(void)
35954c0b326SClément Léger {
36054c0b326SClément Léger }
36154c0b326SClément Léger #endif
362