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> 10*f932e355SEtienne 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; 240c1be93bSVolodymyr Babchuk static SLIST_HEAD(, itr_handler) handlers __nex_data = 250c1be93bSVolodymyr Babchuk SLIST_HEAD_INITIALIZER(handlers); 267315b7b4SJens Wiklander 27*f932e355SEtienne Carriere TEE_Result itr_chip_init(struct itr_chip *chip) 28*f932e355SEtienne Carriere { 29*f932e355SEtienne Carriere if (!itr_chip_is_valid(chip)) 30*f932e355SEtienne Carriere return TEE_ERROR_BAD_PARAMETERS; 31*f932e355SEtienne Carriere 32*f932e355SEtienne Carriere SLIST_INIT(&chip->handlers); 33*f932e355SEtienne Carriere 34*f932e355SEtienne Carriere return TEE_SUCCESS; 35*f932e355SEtienne Carriere } 36*f932e355SEtienne Carriere 3701980f3fSEtienne Carriere void interrupt_main_init(struct itr_chip *chip) 387315b7b4SJens Wiklander { 39*f932e355SEtienne Carriere assert(itr_chip_is_valid(chip)); 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 687315b7b4SJens Wiklander void itr_handle(size_t it) 697315b7b4SJens Wiklander { 701c832d7cSdavidwang struct itr_handler *h = NULL; 711c832d7cSdavidwang bool was_handled = false; 727315b7b4SJens Wiklander 731c832d7cSdavidwang SLIST_FOREACH(h, &handlers, link) { 741c832d7cSdavidwang if (h->it == it) { 751c832d7cSdavidwang if (h->handler(h) == ITRR_HANDLED) 761c832d7cSdavidwang was_handled = true; 771c832d7cSdavidwang else if (!(h->flags & ITRF_SHARED)) 781c832d7cSdavidwang break; 791c832d7cSdavidwang } 807315b7b4SJens Wiklander } 817315b7b4SJens Wiklander 821c832d7cSdavidwang if (!was_handled) { 831c832d7cSdavidwang EMSG("Disabling unhandled interrupt %zu", it); 843475549bSEtienne Carriere itr_main_chip->ops->disable(itr_main_chip, it); 857315b7b4SJens Wiklander } 867315b7b4SJens Wiklander } 877315b7b4SJens Wiklander 88702fe5a7SClément Léger struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler, 89702fe5a7SClément Léger uint32_t flags, void *data, 90702fe5a7SClément Léger uint32_t type, uint32_t prio) 91acc5dd21SLudovic Barre { 92acc5dd21SLudovic Barre struct itr_handler *hdl = calloc(1, sizeof(*hdl)); 93acc5dd21SLudovic Barre 94acc5dd21SLudovic Barre if (hdl) { 95acc5dd21SLudovic Barre hdl->it = it; 96acc5dd21SLudovic Barre hdl->handler = handler; 97acc5dd21SLudovic Barre hdl->flags = flags; 98acc5dd21SLudovic Barre hdl->data = data; 99702fe5a7SClément Léger itr_add_type_prio(hdl, type, prio); 100acc5dd21SLudovic Barre } 101acc5dd21SLudovic Barre 102acc5dd21SLudovic Barre return hdl; 103acc5dd21SLudovic Barre } 104acc5dd21SLudovic Barre 105acc5dd21SLudovic Barre void itr_free(struct itr_handler *hdl) 106acc5dd21SLudovic Barre { 107acc5dd21SLudovic Barre if (!hdl) 108acc5dd21SLudovic Barre return; 109acc5dd21SLudovic Barre 1103475549bSEtienne Carriere itr_main_chip->ops->disable(itr_main_chip, hdl->it); 111acc5dd21SLudovic Barre 112acc5dd21SLudovic Barre SLIST_REMOVE(&handlers, hdl, itr_handler, link); 113acc5dd21SLudovic Barre free(hdl); 114acc5dd21SLudovic Barre } 115acc5dd21SLudovic Barre 116702fe5a7SClément Léger void itr_add_type_prio(struct itr_handler *h, uint32_t type, uint32_t prio) 1177315b7b4SJens Wiklander { 1181c832d7cSdavidwang struct itr_handler __maybe_unused *hdl = NULL; 1191c832d7cSdavidwang 1201c832d7cSdavidwang SLIST_FOREACH(hdl, &handlers, link) 1211c832d7cSdavidwang if (hdl->it == h->it) 1221c832d7cSdavidwang assert((hdl->flags & ITRF_SHARED) && 1231c832d7cSdavidwang (h->flags & ITRF_SHARED)); 1241c832d7cSdavidwang 1253475549bSEtienne Carriere itr_main_chip->ops->add(itr_main_chip, h->it, type, prio); 1267315b7b4SJens Wiklander SLIST_INSERT_HEAD(&handlers, h, link); 1277315b7b4SJens Wiklander } 1287315b7b4SJens Wiklander 12926ed70ecSGuanchao Liang void itr_enable(size_t it) 1307315b7b4SJens Wiklander { 1313475549bSEtienne Carriere itr_main_chip->ops->enable(itr_main_chip, it); 1327315b7b4SJens Wiklander } 1337315b7b4SJens Wiklander 13426ed70ecSGuanchao Liang void itr_disable(size_t it) 1357315b7b4SJens Wiklander { 1363475549bSEtienne Carriere itr_main_chip->ops->disable(itr_main_chip, it); 13726ed70ecSGuanchao Liang } 13826ed70ecSGuanchao Liang 13926ed70ecSGuanchao Liang void itr_raise_pi(size_t it) 14026ed70ecSGuanchao Liang { 1413475549bSEtienne Carriere itr_main_chip->ops->raise_pi(itr_main_chip, it); 14226ed70ecSGuanchao Liang } 14326ed70ecSGuanchao Liang 14426ed70ecSGuanchao Liang void itr_raise_sgi(size_t it, uint8_t cpu_mask) 14526ed70ecSGuanchao Liang { 1463475549bSEtienne Carriere itr_main_chip->ops->raise_sgi(itr_main_chip, it, cpu_mask); 14726ed70ecSGuanchao Liang } 14826ed70ecSGuanchao Liang 14926ed70ecSGuanchao Liang void itr_set_affinity(size_t it, uint8_t cpu_mask) 15026ed70ecSGuanchao Liang { 1513475549bSEtienne Carriere itr_main_chip->ops->set_affinity(itr_main_chip, it, cpu_mask); 1527315b7b4SJens Wiklander } 153e9f46c74SJens Wiklander 154e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */ 155358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void) 156e9f46c74SJens Wiklander { 157e9f46c74SJens Wiklander panic("Secure interrupt handler not defined"); 158e9f46c74SJens Wiklander } 159*f932e355SEtienne Carriere 160*f932e355SEtienne Carriere /* 161*f932e355SEtienne Carriere * Interrupt controller chip support 162*f932e355SEtienne Carriere */ 163*f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num) 164*f932e355SEtienne Carriere { 165*f932e355SEtienne Carriere struct itr_handler *h = NULL; 166*f932e355SEtienne Carriere bool was_handled = false; 167*f932e355SEtienne Carriere 168*f932e355SEtienne Carriere assert(chip); 169*f932e355SEtienne Carriere 170*f932e355SEtienne Carriere SLIST_FOREACH(h, &chip->handlers, link) { 171*f932e355SEtienne Carriere if (h->it == itr_num) { 172*f932e355SEtienne Carriere if (h->handler(h) == ITRR_HANDLED) 173*f932e355SEtienne Carriere was_handled = true; 174*f932e355SEtienne Carriere else if (!(h->flags & ITRF_SHARED)) 175*f932e355SEtienne Carriere break; 176*f932e355SEtienne Carriere } 177*f932e355SEtienne Carriere } 178*f932e355SEtienne Carriere 179*f932e355SEtienne Carriere if (!was_handled) { 180*f932e355SEtienne Carriere EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num); 181*f932e355SEtienne Carriere interrupt_mask(chip, itr_num); 182*f932e355SEtienne Carriere } 183*f932e355SEtienne Carriere } 184*f932e355SEtienne Carriere 185*f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 186*f932e355SEtienne Carriere uint32_t type, uint32_t prio) 187*f932e355SEtienne Carriere { 188*f932e355SEtienne Carriere chip->ops->add(chip, itr_num, type, prio); 189*f932e355SEtienne Carriere 190*f932e355SEtienne Carriere return TEE_SUCCESS; 191*f932e355SEtienne Carriere } 192*f932e355SEtienne Carriere 193*f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 194*f932e355SEtienne Carriere uint32_t type, uint32_t prio) 195*f932e355SEtienne Carriere { 196*f932e355SEtienne Carriere struct itr_handler *h = NULL; 197*f932e355SEtienne Carriere 198*f932e355SEtienne Carriere assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 199*f932e355SEtienne Carriere hdl->handler && is_unpaged(hdl->handler)); 200*f932e355SEtienne Carriere 201*f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 202*f932e355SEtienne Carriere if (h->it == hdl->it && 203*f932e355SEtienne Carriere (!(hdl->flags & ITRF_SHARED) || 204*f932e355SEtienne Carriere !(h->flags & ITRF_SHARED))) { 205*f932e355SEtienne Carriere EMSG("Shared and non-shared flags on interrupt %s#%zu", 206*f932e355SEtienne Carriere hdl->chip->name, hdl->it); 207*f932e355SEtienne Carriere return TEE_ERROR_GENERIC; 208*f932e355SEtienne Carriere } 209*f932e355SEtienne Carriere } 210*f932e355SEtienne Carriere 211*f932e355SEtienne Carriere interrupt_configure(hdl->chip, hdl->it, type, prio); 212*f932e355SEtienne Carriere 213*f932e355SEtienne Carriere SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 214*f932e355SEtienne Carriere 215*f932e355SEtienne Carriere return TEE_SUCCESS; 216*f932e355SEtienne Carriere } 217*f932e355SEtienne Carriere 218*f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl) 219*f932e355SEtienne Carriere { 220*f932e355SEtienne Carriere struct itr_handler *h = NULL; 221*f932e355SEtienne Carriere bool disable_itr = true; 222*f932e355SEtienne Carriere 223*f932e355SEtienne Carriere if (!hdl) 224*f932e355SEtienne Carriere return; 225*f932e355SEtienne Carriere 226*f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) 227*f932e355SEtienne Carriere if (h == hdl) 228*f932e355SEtienne Carriere break; 229*f932e355SEtienne Carriere if (!h) { 230*f932e355SEtienne Carriere DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 231*f932e355SEtienne Carriere assert(false); 232*f932e355SEtienne Carriere return; 233*f932e355SEtienne Carriere } 234*f932e355SEtienne Carriere 235*f932e355SEtienne Carriere if (hdl->flags & ITRF_SHARED) { 236*f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 237*f932e355SEtienne Carriere if (h != hdl && h->it == hdl->it) { 238*f932e355SEtienne Carriere disable_itr = false; 239*f932e355SEtienne Carriere break; 240*f932e355SEtienne Carriere } 241*f932e355SEtienne Carriere } 242*f932e355SEtienne Carriere } 243*f932e355SEtienne Carriere 244*f932e355SEtienne Carriere if (disable_itr) 245*f932e355SEtienne Carriere interrupt_disable(hdl->chip, hdl->it); 246*f932e355SEtienne Carriere 247*f932e355SEtienne Carriere SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 248*f932e355SEtienne Carriere } 249*f932e355SEtienne Carriere 250*f932e355SEtienne Carriere TEE_Result interrupt_alloc_add_handler(struct itr_chip *chip, size_t itr_num, 251*f932e355SEtienne Carriere itr_handler_t handler, uint32_t flags, 252*f932e355SEtienne Carriere void *data, struct itr_handler **out_hdl) 253*f932e355SEtienne Carriere { 254*f932e355SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 255*f932e355SEtienne Carriere struct itr_handler *hdl = NULL; 256*f932e355SEtienne Carriere 257*f932e355SEtienne Carriere hdl = calloc(1, sizeof(*hdl)); 258*f932e355SEtienne Carriere if (!hdl) 259*f932e355SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 260*f932e355SEtienne Carriere 261*f932e355SEtienne Carriere *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 262*f932e355SEtienne Carriere 263*f932e355SEtienne Carriere res = interrupt_add_handler(hdl); 264*f932e355SEtienne Carriere if (res) { 265*f932e355SEtienne Carriere free(hdl); 266*f932e355SEtienne Carriere return res; 267*f932e355SEtienne Carriere } 268*f932e355SEtienne Carriere 269*f932e355SEtienne Carriere if (out_hdl) 270*f932e355SEtienne Carriere *out_hdl = hdl; 271*f932e355SEtienne Carriere 272*f932e355SEtienne Carriere return TEE_SUCCESS; 273*f932e355SEtienne Carriere } 274*f932e355SEtienne Carriere 275*f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl) 276*f932e355SEtienne Carriere { 277*f932e355SEtienne Carriere if (hdl) { 278*f932e355SEtienne Carriere interrupt_remove_handler(hdl); 279*f932e355SEtienne Carriere free(hdl); 280*f932e355SEtienne Carriere } 281*f932e355SEtienne Carriere } 282