1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016-2019, Linaro Limited 4 */ 5 6 #include <kernel/dt.h> 7 #include <kernel/interrupt.h> 8 #include <kernel/panic.h> 9 #include <libfdt.h> 10 #include <mm/core_memprot.h> 11 #include <stdlib.h> 12 #include <trace.h> 13 #include <assert.h> 14 15 /* 16 * NOTE! 17 * 18 * We're assuming that there's no concurrent use of this interface, except 19 * delivery of interrupts in parallel. Synchronization will be needed when 20 * we begin to modify settings after boot initialization. 21 */ 22 23 static struct itr_chip *itr_main_chip __nex_bss; 24 25 static bool itr_chip_is_valid(struct itr_chip *chip) 26 { 27 return chip && is_unpaged(chip) && chip->ops && 28 is_unpaged((void *)chip->ops) && 29 chip->ops->mask && is_unpaged(chip->ops->mask) && 30 chip->ops->unmask && is_unpaged(chip->ops->unmask) && 31 chip->ops->enable && chip->ops->disable; 32 } 33 34 static void __itr_chip_init(struct itr_chip *chip) 35 { 36 SLIST_INIT(&chip->handlers); 37 } 38 39 TEE_Result itr_chip_init(struct itr_chip *chip) 40 { 41 /* 42 * Interrupt chips not using only the DT to configure 43 * consumers interrupts require configure handler. 44 */ 45 if (!itr_chip_is_valid(chip) || !chip->ops->configure) 46 return TEE_ERROR_BAD_PARAMETERS; 47 48 __itr_chip_init(chip); 49 50 return TEE_SUCCESS; 51 } 52 53 TEE_Result itr_chip_dt_only_init(struct itr_chip *chip) 54 { 55 if (!itr_chip_is_valid(chip)) 56 return TEE_ERROR_BAD_PARAMETERS; 57 58 __itr_chip_init(chip); 59 60 return TEE_SUCCESS; 61 } 62 63 void interrupt_main_init(struct itr_chip *chip) 64 { 65 if (itr_chip_init(chip)) 66 panic(); 67 68 itr_main_chip = chip; 69 } 70 71 struct itr_chip *interrupt_get_main_chip(void) 72 { 73 assert(itr_main_chip); 74 return itr_main_chip; 75 } 76 77 struct itr_chip *interrupt_get_main_chip_may_fail(void) 78 { 79 return itr_main_chip; 80 } 81 82 #ifdef CFG_DT 83 int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type, 84 uint32_t *prio) 85 { 86 const uint32_t *prop = NULL; 87 int count = 0; 88 int it_num = DT_INFO_INVALID_INTERRUPT; 89 90 if (!itr_main_chip || !itr_main_chip->dt_get_irq) 91 return it_num; 92 93 prop = fdt_getprop(fdt, node, "interrupts", &count); 94 if (!prop) 95 return it_num; 96 97 return itr_main_chip->dt_get_irq(prop, count / sizeof(uint32_t), type, 98 prio); 99 } 100 #endif 101 102 /* This function is supposed to be overridden in platform specific code */ 103 void __weak __noreturn interrupt_main_handler(void) 104 { 105 panic("Secure interrupt handler not defined"); 106 } 107 108 /* 109 * Interrupt controller chip support 110 */ 111 void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num) 112 { 113 struct itr_handler *h = NULL; 114 bool was_handled = false; 115 116 assert(chip); 117 118 SLIST_FOREACH(h, &chip->handlers, link) { 119 if (h->it == itr_num) { 120 if (h->handler(h) == ITRR_HANDLED) 121 was_handled = true; 122 else if (!(h->flags & ITRF_SHARED)) 123 break; 124 } 125 } 126 127 if (!was_handled) { 128 EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num); 129 interrupt_mask(chip, itr_num); 130 } 131 } 132 133 TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 134 uint32_t type, uint32_t prio) 135 { 136 if (!chip->ops->configure) { 137 EMSG("No configure handler in itr_chip %s", chip->name); 138 return TEE_ERROR_NOT_IMPLEMENTED; 139 } 140 141 chip->ops->configure(chip, itr_num, type, prio); 142 143 return TEE_SUCCESS; 144 } 145 146 static TEE_Result add_configure_handler(struct itr_handler *hdl, 147 uint32_t type, uint32_t prio, 148 bool configure) 149 { 150 TEE_Result res = TEE_ERROR_GENERIC; 151 struct itr_handler *h = NULL; 152 153 assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 154 hdl->handler && is_unpaged(hdl->handler)); 155 156 SLIST_FOREACH(h, &hdl->chip->handlers, link) { 157 if (h->it == hdl->it && 158 (!(hdl->flags & ITRF_SHARED) || 159 !(h->flags & ITRF_SHARED))) { 160 EMSG("Shared and non-shared flags on interrupt %s#%zu", 161 hdl->chip->name, hdl->it); 162 return TEE_ERROR_GENERIC; 163 } 164 } 165 166 if (configure) { 167 res = interrupt_configure(hdl->chip, hdl->it, type, prio); 168 if (res) 169 return res; 170 } 171 172 SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 173 174 return TEE_SUCCESS; 175 } 176 177 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 178 uint32_t type, uint32_t prio) 179 { 180 return add_configure_handler(hdl, type, prio, true /* configure */); 181 } 182 183 TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num, 184 itr_handler_t callback, void *priv, 185 uint32_t flags, 186 struct itr_handler **out_hdl) 187 { 188 TEE_Result res = TEE_ERROR_GENERIC; 189 struct itr_handler *itr_hdl = NULL; 190 191 itr_hdl = calloc(1, sizeof(*itr_hdl)); 192 if (!itr_hdl) 193 return TEE_ERROR_OUT_OF_MEMORY; 194 195 *itr_hdl = (struct itr_handler){ 196 .chip = itr_chip, 197 .it = itr_num, 198 .flags = flags, 199 .handler = callback, 200 .data = priv, 201 }; 202 203 res = add_configure_handler(itr_hdl, 0, 0, false /* !configure */); 204 if (res) { 205 free(itr_hdl); 206 return res; 207 } 208 209 if (out_hdl) 210 *out_hdl = itr_hdl; 211 212 return TEE_SUCCESS; 213 } 214 215 void interrupt_remove_handler(struct itr_handler *hdl) 216 { 217 struct itr_handler *h = NULL; 218 bool disable_itr = true; 219 220 if (!hdl) 221 return; 222 223 SLIST_FOREACH(h, &hdl->chip->handlers, link) 224 if (h == hdl) 225 break; 226 if (!h) { 227 DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 228 assert(false); 229 return; 230 } 231 232 if (hdl->flags & ITRF_SHARED) { 233 SLIST_FOREACH(h, &hdl->chip->handlers, link) { 234 if (h != hdl && h->it == hdl->it) { 235 disable_itr = false; 236 break; 237 } 238 } 239 } 240 241 if (disable_itr) 242 interrupt_disable(hdl->chip, hdl->it); 243 244 SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 245 } 246 247 TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 248 size_t itr_num, 249 itr_handler_t handler, 250 uint32_t flags, void *data, 251 uint32_t type, uint32_t prio, 252 struct itr_handler **out_hdl) 253 { 254 TEE_Result res = TEE_ERROR_GENERIC; 255 struct itr_handler *hdl = NULL; 256 257 hdl = calloc(1, sizeof(*hdl)); 258 if (!hdl) 259 return TEE_ERROR_OUT_OF_MEMORY; 260 261 *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 262 263 res = interrupt_add_configure_handler(hdl, type, prio); 264 if (res) { 265 free(hdl); 266 return res; 267 } 268 269 if (out_hdl) 270 *out_hdl = hdl; 271 272 return TEE_SUCCESS; 273 } 274 275 void interrupt_remove_free_handler(struct itr_handler *hdl) 276 { 277 if (hdl) { 278 interrupt_remove_handler(hdl); 279 free(hdl); 280 } 281 } 282 283 #ifdef CFG_DT 284 TEE_Result interrupt_register_provider(const void *fdt, int node, 285 itr_dt_get_func dt_get_itr, void *data) 286 { 287 return dt_driver_register_provider(fdt, node, 288 (get_of_device_func)dt_get_itr, 289 data, DT_DRIVER_INTERRUPT); 290 } 291 292 /* 293 * Fills an itr_desc reference based on "interrupts" property bindings. 294 * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but 295 * not yet initialized. 296 */ 297 static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node, 298 unsigned int index, 299 struct itr_desc *itr_desc) 300 { 301 const uint32_t *prop = NULL; 302 uint32_t phandle = 0; 303 int pnode = 0; 304 int len = 0; 305 306 prop = fdt_getprop(fdt, node, "interrupts", &len); 307 if (!prop) 308 return TEE_ERROR_ITEM_NOT_FOUND; 309 310 /* Find "interrupt-parent" in node or its parents */ 311 pnode = node; 312 prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 313 314 while (!prop) { 315 pnode = fdt_parent_offset(fdt, pnode); 316 if (pnode < 0) 317 break; 318 319 prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 320 if (!prop && len != -FDT_ERR_NOTFOUND) 321 break; 322 } 323 if (!prop) { 324 DMSG("No interrupt parent for node %s", 325 fdt_get_name(fdt, node, NULL)); 326 return TEE_ERROR_GENERIC; 327 } 328 329 /* "interrupt-parent" provides interrupt controller phandle */ 330 phandle = fdt32_to_cpu(prop[0]); 331 332 /* Get interrupt chip/number from phandle and "interrupts" property */ 333 return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt, 334 node, index, 335 DT_DRIVER_INTERRUPT, 336 phandle, 337 itr_desc); 338 } 339 340 /* 341 * Fills an itr_desc based on "interrupts-extended" property bindings. 342 * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found 343 * but not yet initialized. 344 */ 345 static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node, 346 unsigned int index, 347 struct itr_desc *itr_desc) 348 { 349 return dt_driver_device_from_node_idx_prop("interrupts-extended", 350 fdt, node, index, 351 DT_DRIVER_INTERRUPT, 352 itr_desc); 353 } 354 355 TEE_Result interrupt_dt_get_by_index(const void *fdt, int node, 356 unsigned int index, struct itr_chip **chip, 357 size_t *itr_num) 358 { 359 TEE_Result res = TEE_ERROR_GENERIC; 360 struct itr_desc desc = { }; 361 362 assert(chip && itr_num); 363 364 /* "interrupts-extended" takes precedence over "interrupts" */ 365 if (fdt_getprop(fdt, node, "interrupts-extended", NULL)) 366 res = get_extended_interrupt_by_index(fdt, node, index, &desc); 367 else 368 res = get_legacy_interrupt_by_index(fdt, node, index, &desc); 369 370 if (!res) { 371 assert(itr_chip_is_valid(desc.chip)); 372 *chip = desc.chip; 373 *itr_num = desc.itr_num; 374 } 375 376 return res; 377 } 378 379 TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name, 380 struct itr_chip **chip, size_t *itr_num) 381 { 382 int idx = 0; 383 384 idx = fdt_stringlist_search(fdt, node, "interrupt-names", name); 385 if (idx < 0) 386 return TEE_ERROR_GENERIC; 387 388 return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num); 389 } 390 #endif /*CFG_DT*/ 391