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
3299e2612cSEtienne Carriere static struct saic_data saic;
3354c0b326SClément Léger
3454c0b326SClément Léger static void saic_register_pm(void);
3554c0b326SClément Léger
saic_write_reg(uint32_t reg,uint32_t val)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
saic_read_reg(uint32_t reg)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
interrupt_main_handler(void)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
5099e2612cSEtienne 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
saic_select_it(size_t it)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
saic_configure_it(size_t it,uint32_t src_type,uint32_t priority)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
is_external_it(size_t it)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
saic_get_src_type(uint32_t dt_level,size_t it,uint32_t * src_type)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
saic_configure(struct itr_chip * chip __unused,size_t it,uint32_t type,uint32_t prio)107*2a50ce7dSEtienne Carriere static void saic_configure(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
saic_enable(struct itr_chip * chip __unused,size_t it)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
saic_disable(struct itr_chip * chip __unused,size_t it)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 const struct itr_ops saic_ops = {
134*2a50ce7dSEtienne Carriere .configure = saic_configure,
13587db85acSEtienne Carriere .mask = saic_disable,
13687db85acSEtienne Carriere .unmask = saic_enable,
13754c0b326SClément Léger .enable = saic_enable,
13854c0b326SClément Léger .disable = saic_disable,
13954c0b326SClément Léger };
14054c0b326SClément Léger
saic_dt_get_irq(const uint32_t * properties,int len,uint32_t * type,uint32_t * prio)14154c0b326SClément Léger static int saic_dt_get_irq(const uint32_t *properties, int len,
14254c0b326SClément Léger uint32_t *type, uint32_t *prio)
14354c0b326SClément Léger {
14454c0b326SClément Léger int it = DT_INFO_INVALID_INTERRUPT;
14554c0b326SClément Léger uint32_t src_type = 0;
14654c0b326SClément Léger uint32_t priority = 0;
14754c0b326SClément Léger uint32_t irq_type = 0;
14854c0b326SClément Léger
14954c0b326SClément Léger if (len != 3)
15054c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT;
15154c0b326SClément Léger
15254c0b326SClément Léger it = fdt32_to_cpu(properties[0]);
15354c0b326SClément Léger if (it >= (int)saic.nr_irqs)
15454c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT;
15554c0b326SClément Léger
15654c0b326SClément Léger irq_type = fdt32_to_cpu(properties[1]);
15754c0b326SClément Léger if (saic_get_src_type(irq_type, it, &src_type))
15854c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT;
15954c0b326SClément Léger
16054c0b326SClément Léger priority = fdt32_to_cpu(properties[2]);
16154c0b326SClément Léger if (priority >= AT91_AIC_MAX_PRIO)
16254c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT;
16354c0b326SClément Léger
16454c0b326SClément Léger if (type)
16554c0b326SClément Léger *type = irq_type;
16654c0b326SClément Léger
16754c0b326SClément Léger if (prio)
16854c0b326SClément Léger *prio = priority;
16954c0b326SClément Léger
17054c0b326SClément Léger return it;
17154c0b326SClément Léger }
17254c0b326SClément Léger
17399e2612cSEtienne Carriere static struct saic_data saic = {
17499e2612cSEtienne Carriere .chip = {
17554c0b326SClément Léger .ops = &saic_ops,
17654c0b326SClément Léger .dt_get_irq = &saic_dt_get_irq,
17799e2612cSEtienne Carriere },
17854c0b326SClément Léger };
17954c0b326SClément Léger
saic_clear_aicredir(void)18054c0b326SClément Léger static void saic_clear_aicredir(void)
18154c0b326SClément Léger {
18254c0b326SClément Léger vaddr_t sfr_base = sam_sfr_base();
18354c0b326SClément Léger uint32_t aicredir_val = 0;
18454c0b326SClément Léger
18554c0b326SClément Léger aicredir_val = io_read32(sfr_base + AT91_SFR_SN1);
18654c0b326SClément Léger aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY;
18754c0b326SClément Léger aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK;
18854c0b326SClément Léger
18954c0b326SClément Léger /*
19054c0b326SClément Léger * We explicitly don't want to redirect secure interrupts to non secure
19154c0b326SClément Léger * AIC. By default, AT91Bootstrap does so on some platforms.
19254c0b326SClément Léger */
19354c0b326SClément Léger io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val);
19454c0b326SClément Léger }
19554c0b326SClément Léger
saic_init_external(const void * fdt,int node)19654c0b326SClément Léger static void saic_init_external(const void *fdt, int node)
19754c0b326SClément Léger {
19854c0b326SClément Léger int i = 0;
19954c0b326SClément Léger int len = 0;
20054c0b326SClément Léger int it_grp = 0;
20154c0b326SClément Léger int it_off = 0;
20254c0b326SClément Léger size_t it = 0;
20354c0b326SClément Léger const uint32_t *external = NULL;
20454c0b326SClément Léger
20554c0b326SClément Léger external = fdt_getprop(fdt, node, "atmel,external-irqs", &len);
20654c0b326SClément Léger if (!external)
20754c0b326SClément Léger return;
20854c0b326SClément Léger
20954c0b326SClément Léger len /= sizeof(uint32_t);
21054c0b326SClément Léger for (i = 0; i < len; i++) {
21154c0b326SClément Léger it = fdt32_to_cpu(external[i]);
21254c0b326SClément Léger
21354c0b326SClément Léger DMSG("IRQ %zu is external", it);
21454c0b326SClément Léger
21554c0b326SClément Léger if (it >= saic.nr_irqs)
21654c0b326SClément Léger panic();
21754c0b326SClément Léger
21854c0b326SClément Léger it_grp = it / 32;
21954c0b326SClément Léger it_off = it % 32;
22054c0b326SClément Léger
22154c0b326SClément Léger saic.external[it_grp] |= BIT32(it_off);
22254c0b326SClément Léger }
22354c0b326SClément Léger }
22454c0b326SClément Léger
saic_init_hw(void)22554c0b326SClément Léger static void saic_init_hw(void)
22654c0b326SClément Léger {
22754c0b326SClément Léger unsigned int i = 0;
22854c0b326SClément Léger
22954c0b326SClément Léger saic_clear_aicredir();
23054c0b326SClément Léger
23154c0b326SClément Léger /* Disable write protect if any */
23254c0b326SClément Léger saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY);
23354c0b326SClément Léger
23454c0b326SClément Léger /* Pop the (potential) interrupt stack (8 priority) */
23554c0b326SClément Léger for (i = 0; i < 8; i++)
23654c0b326SClément Léger saic_write_reg(AT91_AIC_EOICR, 0);
23754c0b326SClément Léger
23854c0b326SClément Léger /* Disable and clear all interrupts initially */
23954c0b326SClément Léger for (i = 0; i < saic.nr_irqs; i++) {
24054c0b326SClément Léger saic_write_reg(AT91_AIC_IDCR, 1);
24154c0b326SClément Léger saic_write_reg(AT91_AIC_ICCR, 1);
24254c0b326SClément Léger /* Set interrupt vector to hold interrupt number */
24354c0b326SClément Léger saic_select_it(i);
24454c0b326SClément Léger saic_write_reg(AT91_AIC_SVR, i);
24554c0b326SClément Léger }
24654c0b326SClément Léger
24754c0b326SClément Léger saic_write_reg(AT91_AIC_SPU, 0xffffffff);
24854c0b326SClément Léger
24954c0b326SClément Léger /* Disable AIC debugging */
25054c0b326SClément Léger saic_write_reg(AT91_AIC_DCR, 0);
25154c0b326SClément Léger }
25254c0b326SClément Léger
atmel_saic_setup(void)25354c0b326SClément Léger TEE_Result atmel_saic_setup(void)
25454c0b326SClément Léger {
25554c0b326SClément Léger int node = -1;
25654c0b326SClément Léger int ret = 0;
25754c0b326SClément Léger size_t size = 0;
25854c0b326SClément Léger const void *fdt = get_embedded_dt();
25954c0b326SClément Léger
26054c0b326SClément Léger /* There is only 1 SAIC controller */
26154c0b326SClément Léger if (saic.base)
26254c0b326SClément Léger return TEE_ERROR_GENERIC;
26354c0b326SClément Léger
26454c0b326SClément Léger node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic");
26554c0b326SClément Léger if (node < 0)
26654c0b326SClément Léger return TEE_ERROR_GENERIC;
26754c0b326SClément Léger
268a5d5bbc8SVesa Jääskeläinen ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO);
26954c0b326SClément Léger if (ret) {
270bce2f88aSVincent Mailhol EMSG("Failed to map SAIC");
27154c0b326SClément Léger return TEE_ERROR_GENERIC;
27254c0b326SClément Léger }
27354c0b326SClément Léger
27454c0b326SClément Léger saic.chip.ops = &saic_ops;
27554c0b326SClément Léger saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS;
27654c0b326SClément Léger
27754c0b326SClément Léger saic_init_external(fdt, node);
27854c0b326SClément Léger saic_init_hw();
27954c0b326SClément Léger
28099e2612cSEtienne Carriere interrupt_main_init(&saic.chip);
28154c0b326SClément Léger saic_register_pm();
28254c0b326SClément Léger
28354c0b326SClément Léger return TEE_SUCCESS;
28454c0b326SClément Léger }
28554c0b326SClément Léger
28654c0b326SClément Léger #ifdef CFG_PM_ARM32
28754c0b326SClément Léger
28854c0b326SClément Léger static struct {
28954c0b326SClément Léger uint8_t smr[SAMA5D2_AIC_MAX_IRQS];
29054c0b326SClément Léger } saic_state;
29154c0b326SClément Léger
saic_resume(void)29254c0b326SClément Léger static void saic_resume(void)
29354c0b326SClément Léger {
29454c0b326SClément Léger uint8_t it = 0;
29554c0b326SClément Léger
29654c0b326SClément Léger saic_init_hw();
29754c0b326SClément Léger
29854c0b326SClément Léger for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
29954c0b326SClément Léger saic_select_it(it);
30054c0b326SClément Léger saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]);
30154c0b326SClément Léger }
30254c0b326SClément Léger }
30354c0b326SClément Léger
saic_suspend(void)30454c0b326SClément Léger static void saic_suspend(void)
30554c0b326SClément Léger {
30654c0b326SClément Léger uint8_t it = 0;
30754c0b326SClément Léger
30854c0b326SClément Léger for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) {
30954c0b326SClément Léger saic_select_it(it);
31054c0b326SClément Léger saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR);
31154c0b326SClément Léger }
31254c0b326SClément Léger }
31354c0b326SClément Léger
saic_pm(enum pm_op op,uint32_t pm_hint __unused,const struct pm_callback_handle * hdl __unused)31454c0b326SClément Léger static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused,
31554c0b326SClément Léger const struct pm_callback_handle *hdl __unused)
31654c0b326SClément Léger {
31754c0b326SClément Léger switch (op) {
31854c0b326SClément Léger case PM_OP_RESUME:
31954c0b326SClément Léger saic_resume();
32054c0b326SClément Léger break;
32154c0b326SClément Léger case PM_OP_SUSPEND:
32254c0b326SClément Léger saic_suspend();
32354c0b326SClément Léger break;
32454c0b326SClément Léger default:
32554c0b326SClément Léger panic("Invalid PM operation");
32654c0b326SClément Léger }
32754c0b326SClément Léger
32854c0b326SClément Léger return TEE_SUCCESS;
32954c0b326SClément Léger }
33054c0b326SClément Léger
saic_register_pm(void)33154c0b326SClément Léger static void saic_register_pm(void)
33254c0b326SClément Léger {
33354c0b326SClément Léger register_pm_core_service_cb(saic_pm, NULL, "saic");
33454c0b326SClément Léger }
33554c0b326SClément Léger #else
saic_register_pm(void)33654c0b326SClément Léger static void saic_register_pm(void)
33754c0b326SClément Léger {
33854c0b326SClément Léger }
33954c0b326SClément Léger #endif
340