11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause 27315b7b4SJens Wiklander /* 3e9f46c74SJens Wiklander * Copyright (c) 2016-2019, Linaro Limited 47315b7b4SJens Wiklander */ 57315b7b4SJens Wiklander 667729d8dSLudovic Barre #include <kernel/dt.h> 77315b7b4SJens Wiklander #include <kernel/interrupt.h> 8e9f46c74SJens Wiklander #include <kernel/panic.h> 967729d8dSLudovic Barre #include <libfdt.h> 10f932e355SEtienne Carriere #include <mm/core_memprot.h> 11acc5dd21SLudovic Barre #include <stdlib.h> 127315b7b4SJens Wiklander #include <trace.h> 131c832d7cSdavidwang #include <assert.h> 147315b7b4SJens Wiklander 157315b7b4SJens Wiklander /* 167315b7b4SJens Wiklander * NOTE! 177315b7b4SJens Wiklander * 187315b7b4SJens Wiklander * We're assuming that there's no concurrent use of this interface, except 197315b7b4SJens Wiklander * delivery of interrupts in parallel. Synchronization will be needed when 207315b7b4SJens Wiklander * we begin to modify settings after boot initialization. 217315b7b4SJens Wiklander */ 227315b7b4SJens Wiklander 233475549bSEtienne Carriere static struct itr_chip *itr_main_chip __nex_bss; 247315b7b4SJens Wiklander 25f932e355SEtienne Carriere TEE_Result itr_chip_init(struct itr_chip *chip) 26f932e355SEtienne Carriere { 27f932e355SEtienne Carriere if (!itr_chip_is_valid(chip)) 28f932e355SEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 29f932e355SEtienne Carriere 30f932e355SEtienne Carriere SLIST_INIT(&chip->handlers); 31f932e355SEtienne Carriere 32f932e355SEtienne Carriere return TEE_SUCCESS; 33f932e355SEtienne Carriere } 34f932e355SEtienne Carriere 3501980f3fSEtienne Carriere void interrupt_main_init(struct itr_chip *chip) 367315b7b4SJens Wiklander { 375f21fda6SEtienne Carriere if (itr_chip_init(chip)) 385f21fda6SEtienne Carriere panic(); 395f21fda6SEtienne Carriere 403475549bSEtienne Carriere itr_main_chip = chip; 417315b7b4SJens Wiklander } 427315b7b4SJens Wiklander 43e050e0a7SEtienne Carriere struct itr_chip *interrupt_get_main_chip(void) 44e050e0a7SEtienne Carriere { 453475549bSEtienne Carriere assert(itr_main_chip); 463475549bSEtienne Carriere return itr_main_chip; 47e050e0a7SEtienne Carriere } 48e050e0a7SEtienne Carriere 4967729d8dSLudovic Barre #ifdef CFG_DT 50702fe5a7SClément Léger int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type, 51702fe5a7SClément Léger uint32_t *prio) 5267729d8dSLudovic Barre { 5367729d8dSLudovic Barre const uint32_t *prop = NULL; 54888bb63dSClément Léger int count = 0; 5567729d8dSLudovic Barre int it_num = DT_INFO_INVALID_INTERRUPT; 5667729d8dSLudovic Barre 573475549bSEtienne Carriere if (!itr_main_chip || !itr_main_chip->dt_get_irq) 5867729d8dSLudovic Barre return it_num; 5967729d8dSLudovic Barre 60888bb63dSClément Léger prop = fdt_getprop(fdt, node, "interrupts", &count); 6167729d8dSLudovic Barre if (!prop) 6267729d8dSLudovic Barre return it_num; 6367729d8dSLudovic Barre 643475549bSEtienne Carriere return itr_main_chip->dt_get_irq(prop, count, type, prio); 6567729d8dSLudovic Barre } 6667729d8dSLudovic Barre #endif 6767729d8dSLudovic Barre 68e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */ 69358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void) 70e9f46c74SJens Wiklander { 71e9f46c74SJens Wiklander panic("Secure interrupt handler not defined"); 72e9f46c74SJens Wiklander } 73f932e355SEtienne Carriere 74f932e355SEtienne Carriere /* 75f932e355SEtienne Carriere * Interrupt controller chip support 76f932e355SEtienne Carriere */ 77f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num) 78f932e355SEtienne Carriere { 79f932e355SEtienne Carriere struct itr_handler *h = NULL; 80f932e355SEtienne Carriere bool was_handled = false; 81f932e355SEtienne Carriere 82f932e355SEtienne Carriere assert(chip); 83f932e355SEtienne Carriere 84f932e355SEtienne Carriere SLIST_FOREACH(h, &chip->handlers, link) { 85f932e355SEtienne Carriere if (h->it == itr_num) { 86f932e355SEtienne Carriere if (h->handler(h) == ITRR_HANDLED) 87f932e355SEtienne Carriere was_handled = true; 88f932e355SEtienne Carriere else if (!(h->flags & ITRF_SHARED)) 89f932e355SEtienne Carriere break; 90f932e355SEtienne Carriere } 91f932e355SEtienne Carriere } 92f932e355SEtienne Carriere 93f932e355SEtienne Carriere if (!was_handled) { 94f932e355SEtienne Carriere EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num); 95f932e355SEtienne Carriere interrupt_mask(chip, itr_num); 96f932e355SEtienne Carriere } 97f932e355SEtienne Carriere } 98f932e355SEtienne Carriere 99f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 100f932e355SEtienne Carriere uint32_t type, uint32_t prio) 101f932e355SEtienne Carriere { 102f932e355SEtienne Carriere chip->ops->add(chip, itr_num, type, prio); 103f932e355SEtienne Carriere 104f932e355SEtienne Carriere return TEE_SUCCESS; 105f932e355SEtienne Carriere } 106f932e355SEtienne Carriere 107f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 108f932e355SEtienne Carriere uint32_t type, uint32_t prio) 109f932e355SEtienne Carriere { 110f932e355SEtienne Carriere struct itr_handler *h = NULL; 111f932e355SEtienne Carriere 112f932e355SEtienne Carriere assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 113f932e355SEtienne Carriere hdl->handler && is_unpaged(hdl->handler)); 114f932e355SEtienne Carriere 115f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 116f932e355SEtienne Carriere if (h->it == hdl->it && 117f932e355SEtienne Carriere (!(hdl->flags & ITRF_SHARED) || 118f932e355SEtienne Carriere !(h->flags & ITRF_SHARED))) { 119f932e355SEtienne Carriere EMSG("Shared and non-shared flags on interrupt %s#%zu", 120f932e355SEtienne Carriere hdl->chip->name, hdl->it); 121f932e355SEtienne Carriere return TEE_ERROR_GENERIC; 122f932e355SEtienne Carriere } 123f932e355SEtienne Carriere } 124f932e355SEtienne Carriere 125f932e355SEtienne Carriere interrupt_configure(hdl->chip, hdl->it, type, prio); 126f932e355SEtienne Carriere 127f932e355SEtienne Carriere SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 128f932e355SEtienne Carriere 129f932e355SEtienne Carriere return TEE_SUCCESS; 130f932e355SEtienne Carriere } 131f932e355SEtienne Carriere 132f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl) 133f932e355SEtienne Carriere { 134f932e355SEtienne Carriere struct itr_handler *h = NULL; 135f932e355SEtienne Carriere bool disable_itr = true; 136f932e355SEtienne Carriere 137f932e355SEtienne Carriere if (!hdl) 138f932e355SEtienne Carriere return; 139f932e355SEtienne Carriere 140f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) 141f932e355SEtienne Carriere if (h == hdl) 142f932e355SEtienne Carriere break; 143f932e355SEtienne Carriere if (!h) { 144f932e355SEtienne Carriere DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 145f932e355SEtienne Carriere assert(false); 146f932e355SEtienne Carriere return; 147f932e355SEtienne Carriere } 148f932e355SEtienne Carriere 149f932e355SEtienne Carriere if (hdl->flags & ITRF_SHARED) { 150f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 151f932e355SEtienne Carriere if (h != hdl && h->it == hdl->it) { 152f932e355SEtienne Carriere disable_itr = false; 153f932e355SEtienne Carriere break; 154f932e355SEtienne Carriere } 155f932e355SEtienne Carriere } 156f932e355SEtienne Carriere } 157f932e355SEtienne Carriere 158f932e355SEtienne Carriere if (disable_itr) 159f932e355SEtienne Carriere interrupt_disable(hdl->chip, hdl->it); 160f932e355SEtienne Carriere 161f932e355SEtienne Carriere SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 162f932e355SEtienne Carriere } 163f932e355SEtienne Carriere 1641b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 1651b5c7ca4SEtienne Carriere size_t itr_num, 1661b5c7ca4SEtienne Carriere itr_handler_t handler, 1671b5c7ca4SEtienne Carriere uint32_t flags, void *data, 1681b5c7ca4SEtienne Carriere uint32_t type, uint32_t prio, 1691b5c7ca4SEtienne Carriere struct itr_handler **out_hdl) 170f932e355SEtienne Carriere { 171f932e355SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 172f932e355SEtienne Carriere struct itr_handler *hdl = NULL; 173f932e355SEtienne Carriere 174f932e355SEtienne Carriere hdl = calloc(1, sizeof(*hdl)); 175f932e355SEtienne Carriere if (!hdl) 176f932e355SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 177f932e355SEtienne Carriere 178f932e355SEtienne Carriere *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 179f932e355SEtienne Carriere 1801b5c7ca4SEtienne Carriere res = interrupt_add_configure_handler(hdl, type, prio); 181f932e355SEtienne Carriere if (res) { 182f932e355SEtienne Carriere free(hdl); 183f932e355SEtienne Carriere return res; 184f932e355SEtienne Carriere } 185f932e355SEtienne Carriere 186f932e355SEtienne Carriere if (out_hdl) 187f932e355SEtienne Carriere *out_hdl = hdl; 188f932e355SEtienne Carriere 189f932e355SEtienne Carriere return TEE_SUCCESS; 190f932e355SEtienne Carriere } 191f932e355SEtienne Carriere 192f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl) 193f932e355SEtienne Carriere { 194f932e355SEtienne Carriere if (hdl) { 195f932e355SEtienne Carriere interrupt_remove_handler(hdl); 196f932e355SEtienne Carriere free(hdl); 197f932e355SEtienne Carriere } 198f932e355SEtienne Carriere } 199*33a0c835SEtienne Carriere 200*33a0c835SEtienne Carriere #ifdef CFG_DT 201*33a0c835SEtienne Carriere TEE_Result interrupt_register_provider(const void *fdt, int node, 202*33a0c835SEtienne Carriere itr_dt_get_func dt_get_itr, void *data) 203*33a0c835SEtienne Carriere { 204*33a0c835SEtienne Carriere return dt_driver_register_provider(fdt, node, 205*33a0c835SEtienne Carriere (get_of_device_func)dt_get_itr, 206*33a0c835SEtienne Carriere data, DT_DRIVER_INTERRUPT); 207*33a0c835SEtienne Carriere } 208*33a0c835SEtienne Carriere 209*33a0c835SEtienne Carriere /* 210*33a0c835SEtienne Carriere * Fills an itr_desc reference based on "interrupts" property bindings. 211*33a0c835SEtienne Carriere * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but 212*33a0c835SEtienne Carriere * not yet initialized. 213*33a0c835SEtienne Carriere */ 214*33a0c835SEtienne Carriere static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node, 215*33a0c835SEtienne Carriere unsigned int index, 216*33a0c835SEtienne Carriere struct itr_desc *itr_desc) 217*33a0c835SEtienne Carriere { 218*33a0c835SEtienne Carriere const uint32_t *prop = NULL; 219*33a0c835SEtienne Carriere uint32_t phandle = 0; 220*33a0c835SEtienne Carriere int pnode = 0; 221*33a0c835SEtienne Carriere int len = 0; 222*33a0c835SEtienne Carriere 223*33a0c835SEtienne Carriere prop = fdt_getprop(fdt, node, "interrupts", &len); 224*33a0c835SEtienne Carriere if (!prop) 225*33a0c835SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 226*33a0c835SEtienne Carriere 227*33a0c835SEtienne Carriere /* Find "interrupt-parent" in node or its parents */ 228*33a0c835SEtienne Carriere pnode = node; 229*33a0c835SEtienne Carriere prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 230*33a0c835SEtienne Carriere 231*33a0c835SEtienne Carriere while (!prop) { 232*33a0c835SEtienne Carriere pnode = fdt_parent_offset(fdt, pnode); 233*33a0c835SEtienne Carriere if (pnode < 0) 234*33a0c835SEtienne Carriere break; 235*33a0c835SEtienne Carriere 236*33a0c835SEtienne Carriere prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 237*33a0c835SEtienne Carriere if (!prop && len != -FDT_ERR_NOTFOUND) 238*33a0c835SEtienne Carriere break; 239*33a0c835SEtienne Carriere } 240*33a0c835SEtienne Carriere if (!prop) { 241*33a0c835SEtienne Carriere DMSG("No interrupt parent for node %s", 242*33a0c835SEtienne Carriere fdt_get_name(fdt, node, NULL)); 243*33a0c835SEtienne Carriere return TEE_ERROR_GENERIC; 244*33a0c835SEtienne Carriere } 245*33a0c835SEtienne Carriere 246*33a0c835SEtienne Carriere /* "interrupt-parent" provides interrupt controller phandle */ 247*33a0c835SEtienne Carriere phandle = fdt32_to_cpu(prop[0]); 248*33a0c835SEtienne Carriere 249*33a0c835SEtienne Carriere /* Get interrupt chip/number from phandle and "interrupts" property */ 250*33a0c835SEtienne Carriere return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt, 251*33a0c835SEtienne Carriere node, index, 252*33a0c835SEtienne Carriere DT_DRIVER_INTERRUPT, 253*33a0c835SEtienne Carriere phandle, 254*33a0c835SEtienne Carriere itr_desc); 255*33a0c835SEtienne Carriere } 256*33a0c835SEtienne Carriere 257*33a0c835SEtienne Carriere /* 258*33a0c835SEtienne Carriere * Fills an itr_desc based on "interrupts-extended" property bindings. 259*33a0c835SEtienne Carriere * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found 260*33a0c835SEtienne Carriere * but not yet initialized. 261*33a0c835SEtienne Carriere */ 262*33a0c835SEtienne Carriere static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node, 263*33a0c835SEtienne Carriere unsigned int index, 264*33a0c835SEtienne Carriere struct itr_desc *itr_desc) 265*33a0c835SEtienne Carriere { 266*33a0c835SEtienne Carriere return dt_driver_device_from_node_idx_prop("interrupts-extended", 267*33a0c835SEtienne Carriere fdt, node, index, 268*33a0c835SEtienne Carriere DT_DRIVER_INTERRUPT, 269*33a0c835SEtienne Carriere itr_desc); 270*33a0c835SEtienne Carriere } 271*33a0c835SEtienne Carriere 272*33a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_index(const void *fdt, int node, 273*33a0c835SEtienne Carriere unsigned int index, struct itr_chip **chip, 274*33a0c835SEtienne Carriere size_t *itr_num) 275*33a0c835SEtienne Carriere { 276*33a0c835SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 277*33a0c835SEtienne Carriere struct itr_desc desc = { }; 278*33a0c835SEtienne Carriere 279*33a0c835SEtienne Carriere assert(chip && itr_num); 280*33a0c835SEtienne Carriere 281*33a0c835SEtienne Carriere /* "interrupts-extended" takes precedence over "interrupts" */ 282*33a0c835SEtienne Carriere if (fdt_getprop(fdt, node, "interrupts-extended", NULL)) 283*33a0c835SEtienne Carriere res = get_extended_interrupt_by_index(fdt, node, index, &desc); 284*33a0c835SEtienne Carriere else 285*33a0c835SEtienne Carriere res = get_legacy_interrupt_by_index(fdt, node, index, &desc); 286*33a0c835SEtienne Carriere 287*33a0c835SEtienne Carriere if (!res) { 288*33a0c835SEtienne Carriere assert(desc.chip); 289*33a0c835SEtienne Carriere *chip = desc.chip; 290*33a0c835SEtienne Carriere *itr_num = desc.itr_num; 291*33a0c835SEtienne Carriere } 292*33a0c835SEtienne Carriere 293*33a0c835SEtienne Carriere return res; 294*33a0c835SEtienne Carriere } 295*33a0c835SEtienne Carriere 296*33a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name, 297*33a0c835SEtienne Carriere struct itr_chip **chip, size_t *itr_num) 298*33a0c835SEtienne Carriere { 299*33a0c835SEtienne Carriere int idx = 0; 300*33a0c835SEtienne Carriere 301*33a0c835SEtienne Carriere idx = fdt_stringlist_search(fdt, node, "interrupt-names", name); 302*33a0c835SEtienne Carriere if (idx < 0) 303*33a0c835SEtienne Carriere return TEE_ERROR_GENERIC; 304*33a0c835SEtienne Carriere 305*33a0c835SEtienne Carriere return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num); 306*33a0c835SEtienne Carriere } 307*33a0c835SEtienne Carriere #endif /*CFG_DT*/ 308