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