xref: /optee_os/core/drivers/atmel_saic.c (revision a5d5bbc82de95f531512fafa76a42c879e81b4c4)
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 
3254c0b326SClément Léger static struct saic_data saic = {0};
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 
4654c0b326SClément Léger void atmel_saic_it_handle(void)
4754c0b326SClément Léger {
4854c0b326SClément Léger 	uint32_t irqnr = saic_read_reg(AT91_AIC_IVR);
4954c0b326SClément Léger 
5054c0b326SClément Léger 	itr_handle(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,
15254c0b326SClément Léger 	.enable = saic_enable,
15354c0b326SClément Léger 	.disable = saic_disable,
15454c0b326SClément Léger 	.raise_pi = saic_raise_pi,
15554c0b326SClément Léger 	.raise_sgi = saic_raise_sgi,
15654c0b326SClément Léger 	.set_affinity = saic_set_affinity,
15754c0b326SClément Léger };
15854c0b326SClément Léger 
15954c0b326SClément Léger static int saic_dt_get_irq(const uint32_t *properties, int len,
16054c0b326SClément Léger 			   uint32_t *type, uint32_t *prio)
16154c0b326SClément Léger {
16254c0b326SClément Léger 	int it = DT_INFO_INVALID_INTERRUPT;
16354c0b326SClément Léger 	uint32_t src_type = 0;
16454c0b326SClément Léger 	uint32_t priority = 0;
16554c0b326SClément Léger 	uint32_t irq_type = 0;
16654c0b326SClément Léger 
16754c0b326SClément Léger 	len /= sizeof(uint32_t);
16854c0b326SClément Léger 
16954c0b326SClément Léger 	if (len != 3)
17054c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
17154c0b326SClément Léger 
17254c0b326SClément Léger 	it = fdt32_to_cpu(properties[0]);
17354c0b326SClément Léger 	if (it >= (int)saic.nr_irqs)
17454c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
17554c0b326SClément Léger 
17654c0b326SClément Léger 	irq_type = fdt32_to_cpu(properties[1]);
17754c0b326SClément Léger 	if (saic_get_src_type(irq_type, it, &src_type))
17854c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
17954c0b326SClément Léger 
18054c0b326SClément Léger 	priority = fdt32_to_cpu(properties[2]);
18154c0b326SClément Léger 	if (priority >= AT91_AIC_MAX_PRIO)
18254c0b326SClément Léger 		return DT_INFO_INVALID_INTERRUPT;
18354c0b326SClément Léger 
18454c0b326SClément Léger 	if (type)
18554c0b326SClément Léger 		*type = irq_type;
18654c0b326SClément Léger 
18754c0b326SClément Léger 	if (prio)
18854c0b326SClément Léger 		*prio = priority;
18954c0b326SClément Léger 
19054c0b326SClément Léger 	return it;
19154c0b326SClément Léger }
19254c0b326SClément Léger 
19354c0b326SClément Léger struct itr_chip saic_chip = {
19454c0b326SClément Léger 	.ops = &saic_ops,
19554c0b326SClément Léger 	.dt_get_irq = &saic_dt_get_irq,
19654c0b326SClément Léger };
19754c0b326SClément Léger 
19854c0b326SClément Léger static void saic_clear_aicredir(void)
19954c0b326SClément Léger {
20054c0b326SClément Léger 	vaddr_t sfr_base = sam_sfr_base();
20154c0b326SClément Léger 	uint32_t aicredir_val = 0;
20254c0b326SClément Léger 
20354c0b326SClément Léger 	aicredir_val = io_read32(sfr_base + AT91_SFR_SN1);
20454c0b326SClément Léger 	aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY;
20554c0b326SClément Léger 	aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK;
20654c0b326SClément Léger 
20754c0b326SClément Léger 	/*
20854c0b326SClément Léger 	 * We explicitly don't want to redirect secure interrupts to non secure
20954c0b326SClément Léger 	 * AIC. By default, AT91Bootstrap does so on some platforms.
21054c0b326SClément Léger 	 */
21154c0b326SClément Léger 	io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val);
21254c0b326SClément Léger }
21354c0b326SClément Léger 
21454c0b326SClément Léger static void saic_init_external(const void *fdt, int node)
21554c0b326SClément Léger {
21654c0b326SClément Léger 	int i = 0;
21754c0b326SClément Léger 	int len = 0;
21854c0b326SClément Léger 	int it_grp = 0;
21954c0b326SClément Léger 	int it_off = 0;
22054c0b326SClément Léger 	size_t it = 0;
22154c0b326SClément Léger 	const uint32_t *external = NULL;
22254c0b326SClément Léger 
22354c0b326SClément Léger 	external = fdt_getprop(fdt, node, "atmel,external-irqs", &len);
22454c0b326SClément Léger 	if (!external)
22554c0b326SClément Léger 		return;
22654c0b326SClément Léger 
22754c0b326SClément Léger 	len /= sizeof(uint32_t);
22854c0b326SClément Léger 	for (i = 0; i < len; i++) {
22954c0b326SClément Léger 		it = fdt32_to_cpu(external[i]);
23054c0b326SClément Léger 
23154c0b326SClément Léger 		DMSG("IRQ %zu is external", it);
23254c0b326SClément Léger 
23354c0b326SClément Léger 		if (it >= saic.nr_irqs)
23454c0b326SClément Léger 			panic();
23554c0b326SClément Léger 
23654c0b326SClément Léger 		it_grp = it / 32;
23754c0b326SClément Léger 		it_off = it % 32;
23854c0b326SClément Léger 
23954c0b326SClément Léger 		saic.external[it_grp] |= BIT32(it_off);
24054c0b326SClément Léger 	}
24154c0b326SClément Léger }
24254c0b326SClément Léger 
24354c0b326SClément Léger static void saic_init_hw(void)
24454c0b326SClément Léger {
24554c0b326SClément Léger 	unsigned int i = 0;
24654c0b326SClément Léger 
24754c0b326SClément Léger 	saic_clear_aicredir();
24854c0b326SClément Léger 
24954c0b326SClément Léger 	/* Disable write protect if any */
25054c0b326SClément Léger 	saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY);
25154c0b326SClément Léger 
25254c0b326SClément Léger 	/* Pop the (potential) interrupt stack (8 priority) */
25354c0b326SClément Léger 	for (i = 0; i < 8; i++)
25454c0b326SClément Léger 		saic_write_reg(AT91_AIC_EOICR, 0);
25554c0b326SClément Léger 
25654c0b326SClément Léger 	/* Disable and clear all interrupts initially */
25754c0b326SClément Léger 	for (i = 0; i < saic.nr_irqs; i++) {
25854c0b326SClément Léger 		saic_write_reg(AT91_AIC_IDCR, 1);
25954c0b326SClément Léger 		saic_write_reg(AT91_AIC_ICCR, 1);
26054c0b326SClément Léger 		/* Set interrupt vector to hold interrupt number */
26154c0b326SClément Léger 		saic_select_it(i);
26254c0b326SClément Léger 		saic_write_reg(AT91_AIC_SVR, i);
26354c0b326SClément Léger 	}
26454c0b326SClément Léger 
26554c0b326SClément Léger 	saic_write_reg(AT91_AIC_SPU, 0xffffffff);
26654c0b326SClément Léger 
26754c0b326SClément Léger 	/* Disable AIC debugging */
26854c0b326SClément Léger 	saic_write_reg(AT91_AIC_DCR, 0);
26954c0b326SClément Léger }
27054c0b326SClément Léger 
27154c0b326SClément Léger TEE_Result atmel_saic_setup(void)
27254c0b326SClément Léger {
27354c0b326SClément Léger 	int node = -1;
27454c0b326SClément Léger 	int ret = 0;
27554c0b326SClément Léger 	size_t size = 0;
27654c0b326SClément Léger 	const void *fdt = get_embedded_dt();
27754c0b326SClément Léger 
27854c0b326SClément Léger 	/* There is only 1 SAIC controller */
27954c0b326SClément Léger 	if (saic.base)
28054c0b326SClément Léger 		return TEE_ERROR_GENERIC;
28154c0b326SClément Léger 
28254c0b326SClément Léger 	node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic");
28354c0b326SClément Léger 	if (node < 0)
28454c0b326SClément Léger 		return TEE_ERROR_GENERIC;
28554c0b326SClément Léger 
286*a5d5bbc8SVesa Jääskeläinen 	ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO);
28754c0b326SClément Léger 	if (ret) {
28854c0b326SClément Léger 		EMSG("Failed to map SAIC\n");
28954c0b326SClément Léger 		return TEE_ERROR_GENERIC;
29054c0b326SClément Léger 	}
29154c0b326SClément Léger 
29254c0b326SClément Léger 	saic.chip.ops = &saic_ops;
29354c0b326SClément Léger 	saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS;
29454c0b326SClément Léger 
29554c0b326SClément Léger 	saic_init_external(fdt, node);
29654c0b326SClément Léger 	saic_init_hw();
29754c0b326SClément Léger 
29854c0b326SClément Léger 	itr_init(&saic_chip);
29954c0b326SClément Léger 	saic_register_pm();
30054c0b326SClément Léger 
30154c0b326SClément Léger 	return TEE_SUCCESS;
30254c0b326SClément Léger }
30354c0b326SClément Léger 
30454c0b326SClément Léger #ifdef CFG_PM_ARM32
30554c0b326SClément Léger 
30654c0b326SClément Léger static struct {
30754c0b326SClément Léger 	uint8_t smr[SAMA5D2_AIC_MAX_IRQS];
30854c0b326SClément Léger } saic_state;
30954c0b326SClément Léger 
31054c0b326SClément Léger static void saic_resume(void)
31154c0b326SClément Léger {
31254c0b326SClément Léger 	uint8_t it = 0;
31354c0b326SClément Léger 
31454c0b326SClément Léger 	saic_init_hw();
31554c0b326SClément Léger 
31654c0b326SClément Léger 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
31754c0b326SClément Léger 		saic_select_it(it);
31854c0b326SClément Léger 		saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]);
31954c0b326SClément Léger 	}
32054c0b326SClément Léger }
32154c0b326SClément Léger 
32254c0b326SClément Léger static void saic_suspend(void)
32354c0b326SClément Léger {
32454c0b326SClément Léger 	uint8_t it = 0;
32554c0b326SClément Léger 
32654c0b326SClément Léger 	for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
32754c0b326SClément Léger 		saic_select_it(it);
32854c0b326SClément Léger 		saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR);
32954c0b326SClément Léger 	}
33054c0b326SClément Léger }
33154c0b326SClément Léger 
33254c0b326SClément Léger static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused,
33354c0b326SClément Léger 			  const struct pm_callback_handle *hdl __unused)
33454c0b326SClément Léger {
33554c0b326SClément Léger 	switch (op) {
33654c0b326SClément Léger 	case PM_OP_RESUME:
33754c0b326SClément Léger 		saic_resume();
33854c0b326SClément Léger 		break;
33954c0b326SClément Léger 	case PM_OP_SUSPEND:
34054c0b326SClément Léger 		saic_suspend();
34154c0b326SClément Léger 		break;
34254c0b326SClément Léger 	default:
34354c0b326SClément Léger 		panic("Invalid PM operation");
34454c0b326SClément Léger 	}
34554c0b326SClément Léger 
34654c0b326SClément Léger 	return TEE_SUCCESS;
34754c0b326SClément Léger }
34854c0b326SClément Léger 
34954c0b326SClément Léger static void saic_register_pm(void)
35054c0b326SClément Léger {
35154c0b326SClément Léger 	register_pm_core_service_cb(saic_pm, NULL, "saic");
35254c0b326SClément Léger }
35354c0b326SClément Léger #else
35454c0b326SClément Léger static void saic_register_pm(void)
35554c0b326SClément Léger {
35654c0b326SClément Léger }
35754c0b326SClément Léger #endif
358