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