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 25*df7874b5SEtienne Carriere static bool itr_chip_is_valid(struct itr_chip *chip) 26*df7874b5SEtienne Carriere { 27*df7874b5SEtienne Carriere return chip && is_unpaged(chip) && chip->ops && 28*df7874b5SEtienne Carriere is_unpaged((void *)chip->ops) && 29*df7874b5SEtienne Carriere chip->ops->mask && is_unpaged(chip->ops->mask) && 30*df7874b5SEtienne Carriere chip->ops->unmask && is_unpaged(chip->ops->unmask) && 31*df7874b5SEtienne Carriere chip->ops->enable && chip->ops->disable; 32*df7874b5SEtienne Carriere } 33*df7874b5SEtienne Carriere 34*df7874b5SEtienne Carriere static void __itr_chip_init(struct itr_chip *chip) 35*df7874b5SEtienne Carriere { 36*df7874b5SEtienne Carriere SLIST_INIT(&chip->handlers); 37*df7874b5SEtienne Carriere } 38*df7874b5SEtienne Carriere 39f932e355SEtienne Carriere TEE_Result itr_chip_init(struct itr_chip *chip) 40f932e355SEtienne Carriere { 41*df7874b5SEtienne Carriere /* 42*df7874b5SEtienne Carriere * Interrupt chips not using only the DT to configure 43*df7874b5SEtienne Carriere * consumers interrupts require configure handler. 44*df7874b5SEtienne Carriere */ 45*df7874b5SEtienne Carriere if (!itr_chip_is_valid(chip) || !chip->ops->configure) 46*df7874b5SEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 47*df7874b5SEtienne Carriere 48*df7874b5SEtienne Carriere __itr_chip_init(chip); 49*df7874b5SEtienne Carriere 50*df7874b5SEtienne Carriere return TEE_SUCCESS; 51*df7874b5SEtienne Carriere } 52*df7874b5SEtienne Carriere 53*df7874b5SEtienne Carriere TEE_Result itr_chip_dt_only_init(struct itr_chip *chip) 54*df7874b5SEtienne Carriere { 55f932e355SEtienne Carriere if (!itr_chip_is_valid(chip)) 56f932e355SEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 57f932e355SEtienne Carriere 58*df7874b5SEtienne Carriere __itr_chip_init(chip); 59f932e355SEtienne Carriere 60f932e355SEtienne Carriere return TEE_SUCCESS; 61f932e355SEtienne Carriere } 62f932e355SEtienne Carriere 6301980f3fSEtienne Carriere void interrupt_main_init(struct itr_chip *chip) 647315b7b4SJens Wiklander { 655f21fda6SEtienne Carriere if (itr_chip_init(chip)) 665f21fda6SEtienne Carriere panic(); 675f21fda6SEtienne Carriere 683475549bSEtienne Carriere itr_main_chip = chip; 697315b7b4SJens Wiklander } 707315b7b4SJens Wiklander 71e050e0a7SEtienne Carriere struct itr_chip *interrupt_get_main_chip(void) 72e050e0a7SEtienne Carriere { 733475549bSEtienne Carriere assert(itr_main_chip); 743475549bSEtienne Carriere return itr_main_chip; 75e050e0a7SEtienne Carriere } 76e050e0a7SEtienne Carriere 77245a552cSJens Wiklander struct itr_chip *interrupt_get_main_chip_may_fail(void) 78245a552cSJens Wiklander { 79245a552cSJens Wiklander return itr_main_chip; 80245a552cSJens Wiklander } 81245a552cSJens Wiklander 8267729d8dSLudovic Barre #ifdef CFG_DT 83702fe5a7SClément Léger int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type, 84702fe5a7SClément Léger uint32_t *prio) 8567729d8dSLudovic Barre { 8667729d8dSLudovic Barre const uint32_t *prop = NULL; 87888bb63dSClément Léger int count = 0; 8867729d8dSLudovic Barre int it_num = DT_INFO_INVALID_INTERRUPT; 8967729d8dSLudovic Barre 903475549bSEtienne Carriere if (!itr_main_chip || !itr_main_chip->dt_get_irq) 9167729d8dSLudovic Barre return it_num; 9267729d8dSLudovic Barre 93888bb63dSClément Léger prop = fdt_getprop(fdt, node, "interrupts", &count); 9467729d8dSLudovic Barre if (!prop) 9567729d8dSLudovic Barre return it_num; 9667729d8dSLudovic Barre 9763873401SEtienne Carriere return itr_main_chip->dt_get_irq(prop, count / sizeof(uint32_t), type, 9863873401SEtienne Carriere prio); 9967729d8dSLudovic Barre } 10067729d8dSLudovic Barre #endif 10167729d8dSLudovic Barre 102e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */ 103358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void) 104e9f46c74SJens Wiklander { 105e9f46c74SJens Wiklander panic("Secure interrupt handler not defined"); 106e9f46c74SJens Wiklander } 107f932e355SEtienne Carriere 108f932e355SEtienne Carriere /* 109f932e355SEtienne Carriere * Interrupt controller chip support 110f932e355SEtienne Carriere */ 111f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num) 112f932e355SEtienne Carriere { 113f932e355SEtienne Carriere struct itr_handler *h = NULL; 114f932e355SEtienne Carriere bool was_handled = false; 115f932e355SEtienne Carriere 116f932e355SEtienne Carriere assert(chip); 117f932e355SEtienne Carriere 118f932e355SEtienne Carriere SLIST_FOREACH(h, &chip->handlers, link) { 119f932e355SEtienne Carriere if (h->it == itr_num) { 120f932e355SEtienne Carriere if (h->handler(h) == ITRR_HANDLED) 121f932e355SEtienne Carriere was_handled = true; 122f932e355SEtienne Carriere else if (!(h->flags & ITRF_SHARED)) 123f932e355SEtienne Carriere break; 124f932e355SEtienne Carriere } 125f932e355SEtienne Carriere } 126f932e355SEtienne Carriere 127f932e355SEtienne Carriere if (!was_handled) { 128f932e355SEtienne Carriere EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num); 129f932e355SEtienne Carriere interrupt_mask(chip, itr_num); 130f932e355SEtienne Carriere } 131f932e355SEtienne Carriere } 132f932e355SEtienne Carriere 133f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 134f932e355SEtienne Carriere uint32_t type, uint32_t prio) 135f932e355SEtienne Carriere { 136*df7874b5SEtienne Carriere if (!chip->ops->configure) { 137*df7874b5SEtienne Carriere EMSG("No configure handler in itr_chip %s", chip->name); 138*df7874b5SEtienne Carriere return TEE_ERROR_NOT_IMPLEMENTED; 139*df7874b5SEtienne Carriere } 140*df7874b5SEtienne Carriere 1412a50ce7dSEtienne Carriere chip->ops->configure(chip, itr_num, type, prio); 142f932e355SEtienne Carriere 143f932e355SEtienne Carriere return TEE_SUCCESS; 144f932e355SEtienne Carriere } 145f932e355SEtienne Carriere 146e9376d02SEtienne Carriere static TEE_Result add_configure_handler(struct itr_handler *hdl, 147e9376d02SEtienne Carriere uint32_t type, uint32_t prio, 148e9376d02SEtienne Carriere bool configure) 149f932e355SEtienne Carriere { 150*df7874b5SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 151f932e355SEtienne Carriere struct itr_handler *h = NULL; 152f932e355SEtienne Carriere 153f932e355SEtienne Carriere assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 154f932e355SEtienne Carriere hdl->handler && is_unpaged(hdl->handler)); 155f932e355SEtienne Carriere 156f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 157f932e355SEtienne Carriere if (h->it == hdl->it && 158f932e355SEtienne Carriere (!(hdl->flags & ITRF_SHARED) || 159f932e355SEtienne Carriere !(h->flags & ITRF_SHARED))) { 160f932e355SEtienne Carriere EMSG("Shared and non-shared flags on interrupt %s#%zu", 161f932e355SEtienne Carriere hdl->chip->name, hdl->it); 162f932e355SEtienne Carriere return TEE_ERROR_GENERIC; 163f932e355SEtienne Carriere } 164f932e355SEtienne Carriere } 165f932e355SEtienne Carriere 166*df7874b5SEtienne Carriere if (configure) { 167*df7874b5SEtienne Carriere res = interrupt_configure(hdl->chip, hdl->it, type, prio); 168*df7874b5SEtienne Carriere if (res) 169*df7874b5SEtienne Carriere return res; 170*df7874b5SEtienne Carriere } 171f932e355SEtienne Carriere 172f932e355SEtienne Carriere SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 173f932e355SEtienne Carriere 174f932e355SEtienne Carriere return TEE_SUCCESS; 175f932e355SEtienne Carriere } 176f932e355SEtienne Carriere 177e9376d02SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 178e9376d02SEtienne Carriere uint32_t type, uint32_t prio) 179e9376d02SEtienne Carriere { 180e9376d02SEtienne Carriere return add_configure_handler(hdl, type, prio, true /* configure */); 181e9376d02SEtienne Carriere } 182e9376d02SEtienne Carriere 183e9376d02SEtienne Carriere TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num, 184e9376d02SEtienne Carriere itr_handler_t callback, void *priv, 185e9376d02SEtienne Carriere uint32_t flags, 186e9376d02SEtienne Carriere struct itr_handler **out_hdl) 187e9376d02SEtienne Carriere { 188e9376d02SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 189e9376d02SEtienne Carriere struct itr_handler *itr_hdl = NULL; 190e9376d02SEtienne Carriere 191e9376d02SEtienne Carriere itr_hdl = calloc(1, sizeof(*itr_hdl)); 192e9376d02SEtienne Carriere if (!itr_hdl) 193e9376d02SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 194e9376d02SEtienne Carriere 195e9376d02SEtienne Carriere *itr_hdl = (struct itr_handler){ 196e9376d02SEtienne Carriere .chip = itr_chip, 197e9376d02SEtienne Carriere .it = itr_num, 198e9376d02SEtienne Carriere .flags = flags, 199e9376d02SEtienne Carriere .handler = callback, 200e9376d02SEtienne Carriere .data = priv, 201e9376d02SEtienne Carriere }; 202e9376d02SEtienne Carriere 203e9376d02SEtienne Carriere res = add_configure_handler(itr_hdl, 0, 0, false /* configure */); 204e9376d02SEtienne Carriere if (res) { 205e9376d02SEtienne Carriere free(itr_hdl); 206e9376d02SEtienne Carriere return res; 207e9376d02SEtienne Carriere } 208e9376d02SEtienne Carriere 209e9376d02SEtienne Carriere if (out_hdl) 210e9376d02SEtienne Carriere *out_hdl = itr_hdl; 211e9376d02SEtienne Carriere 212e9376d02SEtienne Carriere return TEE_SUCCESS; 213e9376d02SEtienne Carriere } 214e9376d02SEtienne Carriere 215f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl) 216f932e355SEtienne Carriere { 217f932e355SEtienne Carriere struct itr_handler *h = NULL; 218f932e355SEtienne Carriere bool disable_itr = true; 219f932e355SEtienne Carriere 220f932e355SEtienne Carriere if (!hdl) 221f932e355SEtienne Carriere return; 222f932e355SEtienne Carriere 223f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) 224f932e355SEtienne Carriere if (h == hdl) 225f932e355SEtienne Carriere break; 226f932e355SEtienne Carriere if (!h) { 227f932e355SEtienne Carriere DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 228f932e355SEtienne Carriere assert(false); 229f932e355SEtienne Carriere return; 230f932e355SEtienne Carriere } 231f932e355SEtienne Carriere 232f932e355SEtienne Carriere if (hdl->flags & ITRF_SHARED) { 233f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 234f932e355SEtienne Carriere if (h != hdl && h->it == hdl->it) { 235f932e355SEtienne Carriere disable_itr = false; 236f932e355SEtienne Carriere break; 237f932e355SEtienne Carriere } 238f932e355SEtienne Carriere } 239f932e355SEtienne Carriere } 240f932e355SEtienne Carriere 241f932e355SEtienne Carriere if (disable_itr) 242f932e355SEtienne Carriere interrupt_disable(hdl->chip, hdl->it); 243f932e355SEtienne Carriere 244f932e355SEtienne Carriere SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 245f932e355SEtienne Carriere } 246f932e355SEtienne Carriere 2471b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 2481b5c7ca4SEtienne Carriere size_t itr_num, 2491b5c7ca4SEtienne Carriere itr_handler_t handler, 2501b5c7ca4SEtienne Carriere uint32_t flags, void *data, 2511b5c7ca4SEtienne Carriere uint32_t type, uint32_t prio, 2521b5c7ca4SEtienne Carriere struct itr_handler **out_hdl) 253f932e355SEtienne Carriere { 254f932e355SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 255f932e355SEtienne Carriere struct itr_handler *hdl = NULL; 256f932e355SEtienne Carriere 257f932e355SEtienne Carriere hdl = calloc(1, sizeof(*hdl)); 258f932e355SEtienne Carriere if (!hdl) 259f932e355SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 260f932e355SEtienne Carriere 261f932e355SEtienne Carriere *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 262f932e355SEtienne Carriere 2631b5c7ca4SEtienne Carriere res = interrupt_add_configure_handler(hdl, type, prio); 264f932e355SEtienne Carriere if (res) { 265f932e355SEtienne Carriere free(hdl); 266f932e355SEtienne Carriere return res; 267f932e355SEtienne Carriere } 268f932e355SEtienne Carriere 269f932e355SEtienne Carriere if (out_hdl) 270f932e355SEtienne Carriere *out_hdl = hdl; 271f932e355SEtienne Carriere 272f932e355SEtienne Carriere return TEE_SUCCESS; 273f932e355SEtienne Carriere } 274f932e355SEtienne Carriere 275f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl) 276f932e355SEtienne Carriere { 277f932e355SEtienne Carriere if (hdl) { 278f932e355SEtienne Carriere interrupt_remove_handler(hdl); 279f932e355SEtienne Carriere free(hdl); 280f932e355SEtienne Carriere } 281f932e355SEtienne Carriere } 28233a0c835SEtienne Carriere 28333a0c835SEtienne Carriere #ifdef CFG_DT 28433a0c835SEtienne Carriere TEE_Result interrupt_register_provider(const void *fdt, int node, 28533a0c835SEtienne Carriere itr_dt_get_func dt_get_itr, void *data) 28633a0c835SEtienne Carriere { 28733a0c835SEtienne Carriere return dt_driver_register_provider(fdt, node, 28833a0c835SEtienne Carriere (get_of_device_func)dt_get_itr, 28933a0c835SEtienne Carriere data, DT_DRIVER_INTERRUPT); 29033a0c835SEtienne Carriere } 29133a0c835SEtienne Carriere 29233a0c835SEtienne Carriere /* 29333a0c835SEtienne Carriere * Fills an itr_desc reference based on "interrupts" property bindings. 29433a0c835SEtienne Carriere * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but 29533a0c835SEtienne Carriere * not yet initialized. 29633a0c835SEtienne Carriere */ 29733a0c835SEtienne Carriere static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node, 29833a0c835SEtienne Carriere unsigned int index, 29933a0c835SEtienne Carriere struct itr_desc *itr_desc) 30033a0c835SEtienne Carriere { 30133a0c835SEtienne Carriere const uint32_t *prop = NULL; 30233a0c835SEtienne Carriere uint32_t phandle = 0; 30333a0c835SEtienne Carriere int pnode = 0; 30433a0c835SEtienne Carriere int len = 0; 30533a0c835SEtienne Carriere 30633a0c835SEtienne Carriere prop = fdt_getprop(fdt, node, "interrupts", &len); 30733a0c835SEtienne Carriere if (!prop) 30833a0c835SEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 30933a0c835SEtienne Carriere 31033a0c835SEtienne Carriere /* Find "interrupt-parent" in node or its parents */ 31133a0c835SEtienne Carriere pnode = node; 31233a0c835SEtienne Carriere prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 31333a0c835SEtienne Carriere 31433a0c835SEtienne Carriere while (!prop) { 31533a0c835SEtienne Carriere pnode = fdt_parent_offset(fdt, pnode); 31633a0c835SEtienne Carriere if (pnode < 0) 31733a0c835SEtienne Carriere break; 31833a0c835SEtienne Carriere 31933a0c835SEtienne Carriere prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len); 32033a0c835SEtienne Carriere if (!prop && len != -FDT_ERR_NOTFOUND) 32133a0c835SEtienne Carriere break; 32233a0c835SEtienne Carriere } 32333a0c835SEtienne Carriere if (!prop) { 32433a0c835SEtienne Carriere DMSG("No interrupt parent for node %s", 32533a0c835SEtienne Carriere fdt_get_name(fdt, node, NULL)); 32633a0c835SEtienne Carriere return TEE_ERROR_GENERIC; 32733a0c835SEtienne Carriere } 32833a0c835SEtienne Carriere 32933a0c835SEtienne Carriere /* "interrupt-parent" provides interrupt controller phandle */ 33033a0c835SEtienne Carriere phandle = fdt32_to_cpu(prop[0]); 33133a0c835SEtienne Carriere 33233a0c835SEtienne Carriere /* Get interrupt chip/number from phandle and "interrupts" property */ 33333a0c835SEtienne Carriere return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt, 33433a0c835SEtienne Carriere node, index, 33533a0c835SEtienne Carriere DT_DRIVER_INTERRUPT, 33633a0c835SEtienne Carriere phandle, 33733a0c835SEtienne Carriere itr_desc); 33833a0c835SEtienne Carriere } 33933a0c835SEtienne Carriere 34033a0c835SEtienne Carriere /* 34133a0c835SEtienne Carriere * Fills an itr_desc based on "interrupts-extended" property bindings. 34233a0c835SEtienne Carriere * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found 34333a0c835SEtienne Carriere * but not yet initialized. 34433a0c835SEtienne Carriere */ 34533a0c835SEtienne Carriere static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node, 34633a0c835SEtienne Carriere unsigned int index, 34733a0c835SEtienne Carriere struct itr_desc *itr_desc) 34833a0c835SEtienne Carriere { 34933a0c835SEtienne Carriere return dt_driver_device_from_node_idx_prop("interrupts-extended", 35033a0c835SEtienne Carriere fdt, node, index, 35133a0c835SEtienne Carriere DT_DRIVER_INTERRUPT, 35233a0c835SEtienne Carriere itr_desc); 35333a0c835SEtienne Carriere } 35433a0c835SEtienne Carriere 35533a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_index(const void *fdt, int node, 35633a0c835SEtienne Carriere unsigned int index, struct itr_chip **chip, 35733a0c835SEtienne Carriere size_t *itr_num) 35833a0c835SEtienne Carriere { 35933a0c835SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 36033a0c835SEtienne Carriere struct itr_desc desc = { }; 36133a0c835SEtienne Carriere 36233a0c835SEtienne Carriere assert(chip && itr_num); 36333a0c835SEtienne Carriere 36433a0c835SEtienne Carriere /* "interrupts-extended" takes precedence over "interrupts" */ 36533a0c835SEtienne Carriere if (fdt_getprop(fdt, node, "interrupts-extended", NULL)) 36633a0c835SEtienne Carriere res = get_extended_interrupt_by_index(fdt, node, index, &desc); 36733a0c835SEtienne Carriere else 36833a0c835SEtienne Carriere res = get_legacy_interrupt_by_index(fdt, node, index, &desc); 36933a0c835SEtienne Carriere 37033a0c835SEtienne Carriere if (!res) { 371941de178SEtienne Carriere assert(itr_chip_is_valid(desc.chip)); 37233a0c835SEtienne Carriere *chip = desc.chip; 37333a0c835SEtienne Carriere *itr_num = desc.itr_num; 37433a0c835SEtienne Carriere } 37533a0c835SEtienne Carriere 37633a0c835SEtienne Carriere return res; 37733a0c835SEtienne Carriere } 37833a0c835SEtienne Carriere 37933a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name, 38033a0c835SEtienne Carriere struct itr_chip **chip, size_t *itr_num) 38133a0c835SEtienne Carriere { 38233a0c835SEtienne Carriere int idx = 0; 38333a0c835SEtienne Carriere 38433a0c835SEtienne Carriere idx = fdt_stringlist_search(fdt, node, "interrupt-names", name); 38533a0c835SEtienne Carriere if (idx < 0) 38633a0c835SEtienne Carriere return TEE_ERROR_GENERIC; 38733a0c835SEtienne Carriere 38833a0c835SEtienne Carriere return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num); 38933a0c835SEtienne Carriere } 39033a0c835SEtienne Carriere #endif /*CFG_DT*/ 391