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