1*54c0b326SClément Léger // SPDX-License-Identifier: BSD-2-Clause 2*54c0b326SClément Léger /* 3*54c0b326SClément Léger * Copyright (c) 2021, Microchip 4*54c0b326SClément Léger */ 5*54c0b326SClément Léger 6*54c0b326SClément Léger #include <assert.h> 7*54c0b326SClément Léger #include <drivers/atmel_saic.h> 8*54c0b326SClément Léger #include <dt-bindings/interrupt-controller/irq.h> 9*54c0b326SClément Léger #include <io.h> 10*54c0b326SClément Léger #include <kernel/boot.h> 11*54c0b326SClément Léger #include <kernel/dt.h> 12*54c0b326SClément Léger #include <kernel/interrupt.h> 13*54c0b326SClément Léger #include <kernel/pm.h> 14*54c0b326SClément Léger #include <libfdt.h> 15*54c0b326SClément Léger #include <sam_sfr.h> 16*54c0b326SClément Léger #include <tee_api_types.h> 17*54c0b326SClément Léger #include <trace.h> 18*54c0b326SClément Léger 19*54c0b326SClément Léger #define AT91_AIC_MAX_PRIO 8 20*54c0b326SClément Léger 21*54c0b326SClément Léger #define SAMA5D2_AIC_MAX_IRQS 77 22*54c0b326SClément Léger 23*54c0b326SClément Léger #define SAMA5D2_AIC_MAX_IRQS32 ((SAMA5D2_AIC_MAX_IRQS + 31) / 32) 24*54c0b326SClément Léger 25*54c0b326SClément Léger struct saic_data { 26*54c0b326SClément Léger struct itr_chip chip; 27*54c0b326SClément Léger vaddr_t base; 28*54c0b326SClément Léger size_t nr_irqs; 29*54c0b326SClément Léger uint32_t external[SAMA5D2_AIC_MAX_IRQS32]; 30*54c0b326SClément Léger }; 31*54c0b326SClément Léger 32*54c0b326SClément Léger static struct saic_data saic = {0}; 33*54c0b326SClément Léger 34*54c0b326SClément Léger static void saic_register_pm(void); 35*54c0b326SClément Léger 36*54c0b326SClément Léger static void saic_write_reg(uint32_t reg, uint32_t val) 37*54c0b326SClément Léger { 38*54c0b326SClément Léger io_write32(saic.base + reg, val); 39*54c0b326SClément Léger } 40*54c0b326SClément Léger 41*54c0b326SClément Léger static uint32_t saic_read_reg(uint32_t reg) 42*54c0b326SClément Léger { 43*54c0b326SClément Léger return io_read32(saic.base + reg); 44*54c0b326SClément Léger } 45*54c0b326SClément Léger 46*54c0b326SClément Léger void atmel_saic_it_handle(void) 47*54c0b326SClément Léger { 48*54c0b326SClément Léger uint32_t irqnr = saic_read_reg(AT91_AIC_IVR); 49*54c0b326SClément Léger 50*54c0b326SClément Léger itr_handle(irqnr); 51*54c0b326SClément Léger saic_write_reg(AT91_AIC_EOICR, 0); 52*54c0b326SClément Léger } 53*54c0b326SClément Léger 54*54c0b326SClément Léger static void saic_select_it(size_t it) 55*54c0b326SClément Léger { 56*54c0b326SClément Léger assert(!(it & ~AT91_AIC_SSR_ITSEL_MASK)); 57*54c0b326SClément Léger 58*54c0b326SClément Léger saic_write_reg(AT91_AIC_SSR, it); 59*54c0b326SClément Léger } 60*54c0b326SClément Léger 61*54c0b326SClément Léger static void saic_configure_it(size_t it, uint32_t src_type, uint32_t priority) 62*54c0b326SClément Léger { 63*54c0b326SClément Léger saic_select_it(it); 64*54c0b326SClément Léger saic_write_reg(AT91_AIC_SMR, src_type | priority); 65*54c0b326SClément Léger } 66*54c0b326SClément Léger 67*54c0b326SClément Léger static bool is_external_it(size_t it) 68*54c0b326SClément Léger { 69*54c0b326SClément Léger uint32_t it_grp = it / 32; 70*54c0b326SClément Léger uint32_t it_off = it % 32; 71*54c0b326SClément Léger 72*54c0b326SClément Léger if (it >= saic.nr_irqs) 73*54c0b326SClément Léger panic(); 74*54c0b326SClément Léger 75*54c0b326SClément Léger return saic.external[it_grp] & BIT32(it_off); 76*54c0b326SClément Léger } 77*54c0b326SClément Léger 78*54c0b326SClément Léger static TEE_Result saic_get_src_type(uint32_t dt_level, size_t it, 79*54c0b326SClément Léger uint32_t *src_type) 80*54c0b326SClément Léger { 81*54c0b326SClément Léger switch (dt_level) { 82*54c0b326SClément Léger case IRQ_TYPE_EDGE_RISING: 83*54c0b326SClément Léger *src_type = AT91_AIC_SMR_POS_EDGE; 84*54c0b326SClément Léger break; 85*54c0b326SClément Léger case IRQ_TYPE_EDGE_FALLING: 86*54c0b326SClément Léger if (!is_external_it(it)) 87*54c0b326SClément Léger return TEE_ERROR_BAD_PARAMETERS; 88*54c0b326SClément Léger 89*54c0b326SClément Léger *src_type = AT91_AIC_SMR_NEG_EDGE; 90*54c0b326SClément Léger break; 91*54c0b326SClément Léger case IRQ_TYPE_LEVEL_HIGH: 92*54c0b326SClément Léger *src_type = AT91_AIC_SMR_HIGH_LEVEL; 93*54c0b326SClément Léger break; 94*54c0b326SClément Léger case IRQ_TYPE_LEVEL_LOW: 95*54c0b326SClément Léger if (!is_external_it(it)) 96*54c0b326SClément Léger return TEE_ERROR_BAD_PARAMETERS; 97*54c0b326SClément Léger 98*54c0b326SClément Léger *src_type = AT91_AIC_SMR_LEVEL; 99*54c0b326SClément Léger break; 100*54c0b326SClément Léger default: 101*54c0b326SClément Léger return TEE_ERROR_BAD_PARAMETERS; 102*54c0b326SClément Léger } 103*54c0b326SClément Léger 104*54c0b326SClément Léger return TEE_SUCCESS; 105*54c0b326SClément Léger } 106*54c0b326SClément Léger 107*54c0b326SClément Léger static void saic_add(struct itr_chip *chip __unused, size_t it, 108*54c0b326SClément Léger uint32_t type, uint32_t prio) 109*54c0b326SClément Léger { 110*54c0b326SClément Léger uint32_t src_type = AT91_AIC_SMR_HIGH_LEVEL; 111*54c0b326SClément Léger 112*54c0b326SClément Léger if (it >= saic.nr_irqs) 113*54c0b326SClément Léger panic(); 114*54c0b326SClément Léger 115*54c0b326SClément Léger if (saic_get_src_type(type, it, &src_type)) 116*54c0b326SClément Léger panic("Invalid interrupt specifier"); 117*54c0b326SClément Léger 118*54c0b326SClément Léger saic_configure_it(it, src_type, prio); 119*54c0b326SClément Léger } 120*54c0b326SClément Léger 121*54c0b326SClément Léger static void saic_enable(struct itr_chip *chip __unused, size_t it) 122*54c0b326SClément Léger { 123*54c0b326SClément Léger saic_select_it(it); 124*54c0b326SClément Léger saic_write_reg(AT91_AIC_IECR, 1); 125*54c0b326SClément Léger } 126*54c0b326SClément Léger 127*54c0b326SClément Léger static void saic_disable(struct itr_chip *chip __unused, size_t it) 128*54c0b326SClément Léger { 129*54c0b326SClément Léger saic_select_it(it); 130*54c0b326SClément Léger saic_write_reg(AT91_AIC_IDCR, 1); 131*54c0b326SClément Léger } 132*54c0b326SClément Léger 133*54c0b326SClément Léger static void saic_raise_pi(struct itr_chip *chip __unused, size_t it __unused) 134*54c0b326SClément Léger { 135*54c0b326SClément Léger panic(); 136*54c0b326SClément Léger } 137*54c0b326SClément Léger 138*54c0b326SClément Léger static void saic_raise_sgi(struct itr_chip *chip __unused, size_t it __unused, 139*54c0b326SClément Léger uint8_t cpu_mask __unused) 140*54c0b326SClément Léger { 141*54c0b326SClément Léger panic(); 142*54c0b326SClément Léger } 143*54c0b326SClément Léger 144*54c0b326SClément Léger static void saic_set_affinity(struct itr_chip *chip __unused, 145*54c0b326SClément Léger size_t it __unused, uint8_t cpu_mask __unused) 146*54c0b326SClément Léger { 147*54c0b326SClément Léger panic(); 148*54c0b326SClément Léger } 149*54c0b326SClément Léger 150*54c0b326SClément Léger static const struct itr_ops saic_ops = { 151*54c0b326SClément Léger .add = saic_add, 152*54c0b326SClément Léger .enable = saic_enable, 153*54c0b326SClément Léger .disable = saic_disable, 154*54c0b326SClément Léger .raise_pi = saic_raise_pi, 155*54c0b326SClément Léger .raise_sgi = saic_raise_sgi, 156*54c0b326SClément Léger .set_affinity = saic_set_affinity, 157*54c0b326SClément Léger }; 158*54c0b326SClément Léger 159*54c0b326SClément Léger static int saic_dt_get_irq(const uint32_t *properties, int len, 160*54c0b326SClément Léger uint32_t *type, uint32_t *prio) 161*54c0b326SClément Léger { 162*54c0b326SClément Léger int it = DT_INFO_INVALID_INTERRUPT; 163*54c0b326SClément Léger uint32_t src_type = 0; 164*54c0b326SClément Léger uint32_t priority = 0; 165*54c0b326SClément Léger uint32_t irq_type = 0; 166*54c0b326SClément Léger 167*54c0b326SClément Léger len /= sizeof(uint32_t); 168*54c0b326SClément Léger 169*54c0b326SClément Léger if (len != 3) 170*54c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT; 171*54c0b326SClément Léger 172*54c0b326SClément Léger it = fdt32_to_cpu(properties[0]); 173*54c0b326SClément Léger if (it >= (int)saic.nr_irqs) 174*54c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT; 175*54c0b326SClément Léger 176*54c0b326SClément Léger irq_type = fdt32_to_cpu(properties[1]); 177*54c0b326SClément Léger if (saic_get_src_type(irq_type, it, &src_type)) 178*54c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT; 179*54c0b326SClément Léger 180*54c0b326SClément Léger priority = fdt32_to_cpu(properties[2]); 181*54c0b326SClément Léger if (priority >= AT91_AIC_MAX_PRIO) 182*54c0b326SClément Léger return DT_INFO_INVALID_INTERRUPT; 183*54c0b326SClément Léger 184*54c0b326SClément Léger if (type) 185*54c0b326SClément Léger *type = irq_type; 186*54c0b326SClément Léger 187*54c0b326SClément Léger if (prio) 188*54c0b326SClément Léger *prio = priority; 189*54c0b326SClément Léger 190*54c0b326SClément Léger return it; 191*54c0b326SClément Léger } 192*54c0b326SClément Léger 193*54c0b326SClément Léger struct itr_chip saic_chip = { 194*54c0b326SClément Léger .ops = &saic_ops, 195*54c0b326SClément Léger .dt_get_irq = &saic_dt_get_irq, 196*54c0b326SClément Léger }; 197*54c0b326SClément Léger 198*54c0b326SClément Léger static void saic_clear_aicredir(void) 199*54c0b326SClément Léger { 200*54c0b326SClément Léger vaddr_t sfr_base = sam_sfr_base(); 201*54c0b326SClément Léger uint32_t aicredir_val = 0; 202*54c0b326SClément Léger 203*54c0b326SClément Léger aicredir_val = io_read32(sfr_base + AT91_SFR_SN1); 204*54c0b326SClément Léger aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY; 205*54c0b326SClément Léger aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK; 206*54c0b326SClément Léger 207*54c0b326SClément Léger /* 208*54c0b326SClément Léger * We explicitly don't want to redirect secure interrupts to non secure 209*54c0b326SClément Léger * AIC. By default, AT91Bootstrap does so on some platforms. 210*54c0b326SClément Léger */ 211*54c0b326SClément Léger io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val); 212*54c0b326SClément Léger } 213*54c0b326SClément Léger 214*54c0b326SClément Léger static void saic_init_external(const void *fdt, int node) 215*54c0b326SClément Léger { 216*54c0b326SClément Léger int i = 0; 217*54c0b326SClément Léger int len = 0; 218*54c0b326SClément Léger int it_grp = 0; 219*54c0b326SClément Léger int it_off = 0; 220*54c0b326SClément Léger size_t it = 0; 221*54c0b326SClément Léger const uint32_t *external = NULL; 222*54c0b326SClément Léger 223*54c0b326SClément Léger external = fdt_getprop(fdt, node, "atmel,external-irqs", &len); 224*54c0b326SClément Léger if (!external) 225*54c0b326SClément Léger return; 226*54c0b326SClément Léger 227*54c0b326SClément Léger len /= sizeof(uint32_t); 228*54c0b326SClément Léger for (i = 0; i < len; i++) { 229*54c0b326SClément Léger it = fdt32_to_cpu(external[i]); 230*54c0b326SClément Léger 231*54c0b326SClément Léger DMSG("IRQ %zu is external", it); 232*54c0b326SClément Léger 233*54c0b326SClément Léger if (it >= saic.nr_irqs) 234*54c0b326SClément Léger panic(); 235*54c0b326SClément Léger 236*54c0b326SClément Léger it_grp = it / 32; 237*54c0b326SClément Léger it_off = it % 32; 238*54c0b326SClément Léger 239*54c0b326SClément Léger saic.external[it_grp] |= BIT32(it_off); 240*54c0b326SClément Léger } 241*54c0b326SClément Léger } 242*54c0b326SClément Léger 243*54c0b326SClément Léger static void saic_init_hw(void) 244*54c0b326SClément Léger { 245*54c0b326SClément Léger unsigned int i = 0; 246*54c0b326SClément Léger 247*54c0b326SClément Léger saic_clear_aicredir(); 248*54c0b326SClément Léger 249*54c0b326SClément Léger /* Disable write protect if any */ 250*54c0b326SClément Léger saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY); 251*54c0b326SClément Léger 252*54c0b326SClément Léger /* Pop the (potential) interrupt stack (8 priority) */ 253*54c0b326SClément Léger for (i = 0; i < 8; i++) 254*54c0b326SClément Léger saic_write_reg(AT91_AIC_EOICR, 0); 255*54c0b326SClément Léger 256*54c0b326SClément Léger /* Disable and clear all interrupts initially */ 257*54c0b326SClément Léger for (i = 0; i < saic.nr_irqs; i++) { 258*54c0b326SClément Léger saic_write_reg(AT91_AIC_IDCR, 1); 259*54c0b326SClément Léger saic_write_reg(AT91_AIC_ICCR, 1); 260*54c0b326SClément Léger /* Set interrupt vector to hold interrupt number */ 261*54c0b326SClément Léger saic_select_it(i); 262*54c0b326SClément Léger saic_write_reg(AT91_AIC_SVR, i); 263*54c0b326SClément Léger } 264*54c0b326SClément Léger 265*54c0b326SClément Léger saic_write_reg(AT91_AIC_SPU, 0xffffffff); 266*54c0b326SClément Léger 267*54c0b326SClément Léger /* Disable AIC debugging */ 268*54c0b326SClément Léger saic_write_reg(AT91_AIC_DCR, 0); 269*54c0b326SClément Léger } 270*54c0b326SClément Léger 271*54c0b326SClément Léger TEE_Result atmel_saic_setup(void) 272*54c0b326SClément Léger { 273*54c0b326SClément Léger int node = -1; 274*54c0b326SClément Léger int ret = 0; 275*54c0b326SClément Léger size_t size = 0; 276*54c0b326SClément Léger const void *fdt = get_embedded_dt(); 277*54c0b326SClément Léger 278*54c0b326SClément Léger /* There is only 1 SAIC controller */ 279*54c0b326SClément Léger if (saic.base) 280*54c0b326SClément Léger return TEE_ERROR_GENERIC; 281*54c0b326SClément Léger 282*54c0b326SClément Léger node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic"); 283*54c0b326SClément Léger if (node < 0) 284*54c0b326SClément Léger return TEE_ERROR_GENERIC; 285*54c0b326SClément Léger 286*54c0b326SClément Léger ret = dt_map_dev(fdt, node, &saic.base, &size); 287*54c0b326SClément Léger if (ret) { 288*54c0b326SClément Léger EMSG("Failed to map SAIC\n"); 289*54c0b326SClément Léger return TEE_ERROR_GENERIC; 290*54c0b326SClément Léger } 291*54c0b326SClément Léger 292*54c0b326SClément Léger saic.chip.ops = &saic_ops; 293*54c0b326SClément Léger saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS; 294*54c0b326SClément Léger 295*54c0b326SClément Léger saic_init_external(fdt, node); 296*54c0b326SClément Léger saic_init_hw(); 297*54c0b326SClément Léger 298*54c0b326SClément Léger itr_init(&saic_chip); 299*54c0b326SClément Léger saic_register_pm(); 300*54c0b326SClément Léger 301*54c0b326SClément Léger return TEE_SUCCESS; 302*54c0b326SClément Léger } 303*54c0b326SClément Léger 304*54c0b326SClément Léger #ifdef CFG_PM_ARM32 305*54c0b326SClément Léger 306*54c0b326SClément Léger static struct { 307*54c0b326SClément Léger uint8_t smr[SAMA5D2_AIC_MAX_IRQS]; 308*54c0b326SClément Léger } saic_state; 309*54c0b326SClément Léger 310*54c0b326SClément Léger static void saic_resume(void) 311*54c0b326SClément Léger { 312*54c0b326SClément Léger uint8_t it = 0; 313*54c0b326SClément Léger 314*54c0b326SClément Léger saic_init_hw(); 315*54c0b326SClément Léger 316*54c0b326SClément Léger for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) { 317*54c0b326SClément Léger saic_select_it(it); 318*54c0b326SClément Léger saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]); 319*54c0b326SClément Léger } 320*54c0b326SClément Léger } 321*54c0b326SClément Léger 322*54c0b326SClément Léger static void saic_suspend(void) 323*54c0b326SClément Léger { 324*54c0b326SClément Léger uint8_t it = 0; 325*54c0b326SClément Léger 326*54c0b326SClément Léger for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) { 327*54c0b326SClément Léger saic_select_it(it); 328*54c0b326SClément Léger saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR); 329*54c0b326SClément Léger } 330*54c0b326SClément Léger } 331*54c0b326SClément Léger 332*54c0b326SClément Léger static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused, 333*54c0b326SClément Léger const struct pm_callback_handle *hdl __unused) 334*54c0b326SClément Léger { 335*54c0b326SClément Léger switch (op) { 336*54c0b326SClément Léger case PM_OP_RESUME: 337*54c0b326SClément Léger saic_resume(); 338*54c0b326SClément Léger break; 339*54c0b326SClément Léger case PM_OP_SUSPEND: 340*54c0b326SClément Léger saic_suspend(); 341*54c0b326SClément Léger break; 342*54c0b326SClément Léger default: 343*54c0b326SClément Léger panic("Invalid PM operation"); 344*54c0b326SClément Léger } 345*54c0b326SClément Léger 346*54c0b326SClément Léger return TEE_SUCCESS; 347*54c0b326SClément Léger } 348*54c0b326SClément Léger 349*54c0b326SClément Léger static void saic_register_pm(void) 350*54c0b326SClément Léger { 351*54c0b326SClément Léger register_pm_core_service_cb(saic_pm, NULL, "saic"); 352*54c0b326SClément Léger } 353*54c0b326SClément Léger #else 354*54c0b326SClément Léger static void saic_register_pm(void) 355*54c0b326SClément Léger { 356*54c0b326SClément Léger } 357*54c0b326SClément Léger #endif 358