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