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 TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 108 uint32_t type, uint32_t prio) 109 { 110 struct itr_handler *h = NULL; 111 112 assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 113 hdl->handler && is_unpaged(hdl->handler)); 114 115 SLIST_FOREACH(h, &hdl->chip->handlers, link) { 116 if (h->it == hdl->it && 117 (!(hdl->flags & ITRF_SHARED) || 118 !(h->flags & ITRF_SHARED))) { 119 EMSG("Shared and non-shared flags on interrupt %s#%zu", 120 hdl->chip->name, hdl->it); 121 return TEE_ERROR_GENERIC; 122 } 123 } 124 125 interrupt_configure(hdl->chip, hdl->it, type, prio); 126 127 SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 128 129 return TEE_SUCCESS; 130 } 131 132 void interrupt_remove_handler(struct itr_handler *hdl) 133 { 134 struct itr_handler *h = NULL; 135 bool disable_itr = true; 136 137 if (!hdl) 138 return; 139 140 SLIST_FOREACH(h, &hdl->chip->handlers, link) 141 if (h == hdl) 142 break; 143 if (!h) { 144 DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 145 assert(false); 146 return; 147 } 148 149 if (hdl->flags & ITRF_SHARED) { 150 SLIST_FOREACH(h, &hdl->chip->handlers, link) { 151 if (h != hdl && h->it == hdl->it) { 152 disable_itr = false; 153 break; 154 } 155 } 156 } 157 158 if (disable_itr) 159 interrupt_disable(hdl->chip, hdl->it); 160 161 SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 162 } 163 164 TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 165 size_t itr_num, 166 itr_handler_t handler, 167 uint32_t flags, void *data, 168 uint32_t type, uint32_t prio, 169 struct itr_handler **out_hdl) 170 { 171 TEE_Result res = TEE_ERROR_GENERIC; 172 struct itr_handler *hdl = NULL; 173 174 hdl = calloc(1, sizeof(*hdl)); 175 if (!hdl) 176 return TEE_ERROR_OUT_OF_MEMORY; 177 178 *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 179 180 res = interrupt_add_configure_handler(hdl, type, prio); 181 if (res) { 182 free(hdl); 183 return res; 184 } 185 186 if (out_hdl) 187 *out_hdl = hdl; 188 189 return TEE_SUCCESS; 190 } 191 192 void interrupt_remove_free_handler(struct itr_handler *hdl) 193 { 194 if (hdl) { 195 interrupt_remove_handler(hdl); 196 free(hdl); 197 } 198 } 199 200 #ifdef CFG_DT 201 TEE_Result interrupt_register_provider(const void *fdt, int node, 202 itr_dt_get_func dt_get_itr, void *data) 203 { 204 return dt_driver_register_provider(fdt, node, 205 (get_of_device_func)dt_get_itr, 206 data, DT_DRIVER_INTERRUPT); 207 } 208 209 /* 210 * Fills an itr_desc reference based on "interrupts" property bindings. 211 * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but 212 * not yet initialized. 213 */ 214 static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node, 215 unsigned int index, 216 struct itr_desc *itr_desc) 217 { 218 const uint32_t *prop = NULL; 219 uint32_t phandle = 0; 220 int pnode = 0; 221 int len = 0; 222 223 prop = fdt_getprop(fdt, node, "interrupts", &len); 224 if (!prop) 225 return TEE_ERROR_ITEM_NOT_FOUND; 226 227 /* Find "interrupt-parent" in node or its parents */ 228 pnode = node; 229 prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 230 231 while (!prop) { 232 pnode = fdt_parent_offset(fdt, pnode); 233 if (pnode < 0) 234 break; 235 236 prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 237 if (!prop && len != -FDT_ERR_NOTFOUND) 238 break; 239 } 240 if (!prop) { 241 DMSG("No interrupt parent for node %s", 242 fdt_get_name(fdt, node, NULL)); 243 return TEE_ERROR_GENERIC; 244 } 245 246 /* "interrupt-parent" provides interrupt controller phandle */ 247 phandle = fdt32_to_cpu(prop[0]); 248 249 /* Get interrupt chip/number from phandle and "interrupts" property */ 250 return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt, 251 node, index, 252 DT_DRIVER_INTERRUPT, 253 phandle, 254 itr_desc); 255 } 256 257 /* 258 * Fills an itr_desc based on "interrupts-extended" property bindings. 259 * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found 260 * but not yet initialized. 261 */ 262 static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node, 263 unsigned int index, 264 struct itr_desc *itr_desc) 265 { 266 return dt_driver_device_from_node_idx_prop("interrupts-extended", 267 fdt, node, index, 268 DT_DRIVER_INTERRUPT, 269 itr_desc); 270 } 271 272 TEE_Result interrupt_dt_get_by_index(const void *fdt, int node, 273 unsigned int index, struct itr_chip **chip, 274 size_t *itr_num) 275 { 276 TEE_Result res = TEE_ERROR_GENERIC; 277 struct itr_desc desc = { }; 278 279 assert(chip && itr_num); 280 281 /* "interrupts-extended" takes precedence over "interrupts" */ 282 if (fdt_getprop(fdt, node, "interrupts-extended", NULL)) 283 res = get_extended_interrupt_by_index(fdt, node, index, &desc); 284 else 285 res = get_legacy_interrupt_by_index(fdt, node, index, &desc); 286 287 if (!res) { 288 assert(desc.chip); 289 *chip = desc.chip; 290 *itr_num = desc.itr_num; 291 } 292 293 return res; 294 } 295 296 TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name, 297 struct itr_chip **chip, size_t *itr_num) 298 { 299 int idx = 0; 300 301 idx = fdt_stringlist_search(fdt, node, "interrupt-names", name); 302 if (idx < 0) 303 return TEE_ERROR_GENERIC; 304 305 return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num); 306 } 307 #endif /*CFG_DT*/ 308