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