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