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