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 = {0}; 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 itr_handle(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 struct itr_chip saic_chip = { 196 .ops = &saic_ops, 197 .dt_get_irq = &saic_dt_get_irq, 198 }; 199 200 static void saic_clear_aicredir(void) 201 { 202 vaddr_t sfr_base = sam_sfr_base(); 203 uint32_t aicredir_val = 0; 204 205 aicredir_val = io_read32(sfr_base + AT91_SFR_SN1); 206 aicredir_val ^= AT91_SFR_AICREDIR_XOR_KEY; 207 aicredir_val &= AT91_SFR_AICREDIR_KEY_MASK; 208 209 /* 210 * We explicitly don't want to redirect secure interrupts to non secure 211 * AIC. By default, AT91Bootstrap does so on some platforms. 212 */ 213 io_write32(sfr_base + AT91_SFR_AICREDIR, aicredir_val); 214 } 215 216 static void saic_init_external(const void *fdt, int node) 217 { 218 int i = 0; 219 int len = 0; 220 int it_grp = 0; 221 int it_off = 0; 222 size_t it = 0; 223 const uint32_t *external = NULL; 224 225 external = fdt_getprop(fdt, node, "atmel,external-irqs", &len); 226 if (!external) 227 return; 228 229 len /= sizeof(uint32_t); 230 for (i = 0; i < len; i++) { 231 it = fdt32_to_cpu(external[i]); 232 233 DMSG("IRQ %zu is external", it); 234 235 if (it >= saic.nr_irqs) 236 panic(); 237 238 it_grp = it / 32; 239 it_off = it % 32; 240 241 saic.external[it_grp] |= BIT32(it_off); 242 } 243 } 244 245 static void saic_init_hw(void) 246 { 247 unsigned int i = 0; 248 249 saic_clear_aicredir(); 250 251 /* Disable write protect if any */ 252 saic_write_reg(AT91_AIC_WPMR, AT91_AIC_WPKEY); 253 254 /* Pop the (potential) interrupt stack (8 priority) */ 255 for (i = 0; i < 8; i++) 256 saic_write_reg(AT91_AIC_EOICR, 0); 257 258 /* Disable and clear all interrupts initially */ 259 for (i = 0; i < saic.nr_irqs; i++) { 260 saic_write_reg(AT91_AIC_IDCR, 1); 261 saic_write_reg(AT91_AIC_ICCR, 1); 262 /* Set interrupt vector to hold interrupt number */ 263 saic_select_it(i); 264 saic_write_reg(AT91_AIC_SVR, i); 265 } 266 267 saic_write_reg(AT91_AIC_SPU, 0xffffffff); 268 269 /* Disable AIC debugging */ 270 saic_write_reg(AT91_AIC_DCR, 0); 271 } 272 273 TEE_Result atmel_saic_setup(void) 274 { 275 int node = -1; 276 int ret = 0; 277 size_t size = 0; 278 const void *fdt = get_embedded_dt(); 279 280 /* There is only 1 SAIC controller */ 281 if (saic.base) 282 return TEE_ERROR_GENERIC; 283 284 node = fdt_node_offset_by_compatible(fdt, -1, "atmel,sama5d2-saic"); 285 if (node < 0) 286 return TEE_ERROR_GENERIC; 287 288 ret = dt_map_dev(fdt, node, &saic.base, &size, DT_MAP_AUTO); 289 if (ret) { 290 EMSG("Failed to map SAIC\n"); 291 return TEE_ERROR_GENERIC; 292 } 293 294 saic.chip.ops = &saic_ops; 295 saic.nr_irqs = SAMA5D2_AIC_MAX_IRQS; 296 297 saic_init_external(fdt, node); 298 saic_init_hw(); 299 300 interrupt_main_init(&saic_chip); 301 saic_register_pm(); 302 303 return TEE_SUCCESS; 304 } 305 306 #ifdef CFG_PM_ARM32 307 308 static struct { 309 uint8_t smr[SAMA5D2_AIC_MAX_IRQS]; 310 } saic_state; 311 312 static void saic_resume(void) 313 { 314 uint8_t it = 0; 315 316 saic_init_hw(); 317 318 for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) { 319 saic_select_it(it); 320 saic_write_reg(AT91_AIC_SMR, saic_state.smr[it]); 321 } 322 } 323 324 static void saic_suspend(void) 325 { 326 uint8_t it = 0; 327 328 for (it = 0; it < SAMA5D2_AIC_MAX_IRQS; it++) { 329 saic_select_it(it); 330 saic_state.smr[it] = saic_read_reg(AT91_AIC_SMR); 331 } 332 } 333 334 static TEE_Result saic_pm(enum pm_op op, uint32_t pm_hint __unused, 335 const struct pm_callback_handle *hdl __unused) 336 { 337 switch (op) { 338 case PM_OP_RESUME: 339 saic_resume(); 340 break; 341 case PM_OP_SUSPEND: 342 saic_suspend(); 343 break; 344 default: 345 panic("Invalid PM operation"); 346 } 347 348 return TEE_SUCCESS; 349 } 350 351 static void saic_register_pm(void) 352 { 353 register_pm_core_service_cb(saic_pm, NULL, "saic"); 354 } 355 #else 356 static void saic_register_pm(void) 357 { 358 } 359 #endif 360