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 68702fe5a7SClément Léger struct itr_handler *itr_alloc_add_type_prio(size_t it, itr_handler_t handler, 69702fe5a7SClément Léger uint32_t flags, void *data, 70702fe5a7SClément Léger uint32_t type, uint32_t prio) 71acc5dd21SLudovic Barre { 72acc5dd21SLudovic Barre struct itr_handler *hdl = calloc(1, sizeof(*hdl)); 73acc5dd21SLudovic Barre 74acc5dd21SLudovic Barre if (hdl) { 75acc5dd21SLudovic Barre hdl->it = it; 76acc5dd21SLudovic Barre hdl->handler = handler; 77acc5dd21SLudovic Barre hdl->flags = flags; 78acc5dd21SLudovic Barre hdl->data = data; 79702fe5a7SClément Léger itr_add_type_prio(hdl, type, prio); 80acc5dd21SLudovic Barre } 81acc5dd21SLudovic Barre 82acc5dd21SLudovic Barre return hdl; 83acc5dd21SLudovic Barre } 84acc5dd21SLudovic Barre 85acc5dd21SLudovic Barre void itr_free(struct itr_handler *hdl) 86acc5dd21SLudovic Barre { 87acc5dd21SLudovic Barre if (!hdl) 88acc5dd21SLudovic Barre return; 89acc5dd21SLudovic Barre 903475549bSEtienne Carriere itr_main_chip->ops->disable(itr_main_chip, hdl->it); 91acc5dd21SLudovic Barre 925f21fda6SEtienne Carriere SLIST_REMOVE(&itr_main_chip->handlers, hdl, itr_handler, link); 93acc5dd21SLudovic Barre free(hdl); 94acc5dd21SLudovic Barre } 95acc5dd21SLudovic Barre 96702fe5a7SClément Léger void itr_add_type_prio(struct itr_handler *h, uint32_t type, uint32_t prio) 977315b7b4SJens Wiklander { 981c832d7cSdavidwang struct itr_handler __maybe_unused *hdl = NULL; 991c832d7cSdavidwang 1005f21fda6SEtienne Carriere SLIST_FOREACH(hdl, &itr_main_chip->handlers, link) 1011c832d7cSdavidwang if (hdl->it == h->it) 1021c832d7cSdavidwang assert((hdl->flags & ITRF_SHARED) && 1031c832d7cSdavidwang (h->flags & ITRF_SHARED)); 1041c832d7cSdavidwang 1053475549bSEtienne Carriere itr_main_chip->ops->add(itr_main_chip, h->it, type, prio); 1065f21fda6SEtienne Carriere SLIST_INSERT_HEAD(&itr_main_chip->handlers, h, link); 1077315b7b4SJens Wiklander } 1087315b7b4SJens Wiklander 10926ed70ecSGuanchao Liang void itr_enable(size_t it) 1107315b7b4SJens Wiklander { 1113475549bSEtienne Carriere itr_main_chip->ops->enable(itr_main_chip, it); 1127315b7b4SJens Wiklander } 1137315b7b4SJens Wiklander 11426ed70ecSGuanchao Liang void itr_disable(size_t it) 1157315b7b4SJens Wiklander { 1163475549bSEtienne Carriere itr_main_chip->ops->disable(itr_main_chip, it); 11726ed70ecSGuanchao Liang } 11826ed70ecSGuanchao Liang 11926ed70ecSGuanchao Liang void itr_raise_pi(size_t it) 12026ed70ecSGuanchao Liang { 1213475549bSEtienne Carriere itr_main_chip->ops->raise_pi(itr_main_chip, it); 12226ed70ecSGuanchao Liang } 12326ed70ecSGuanchao Liang 12426ed70ecSGuanchao Liang void itr_raise_sgi(size_t it, uint8_t cpu_mask) 12526ed70ecSGuanchao Liang { 1263475549bSEtienne Carriere itr_main_chip->ops->raise_sgi(itr_main_chip, it, cpu_mask); 12726ed70ecSGuanchao Liang } 12826ed70ecSGuanchao Liang 12926ed70ecSGuanchao Liang void itr_set_affinity(size_t it, uint8_t cpu_mask) 13026ed70ecSGuanchao Liang { 1313475549bSEtienne Carriere itr_main_chip->ops->set_affinity(itr_main_chip, it, cpu_mask); 1327315b7b4SJens Wiklander } 133e9f46c74SJens Wiklander 134e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */ 135358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void) 136e9f46c74SJens Wiklander { 137e9f46c74SJens Wiklander panic("Secure interrupt handler not defined"); 138e9f46c74SJens Wiklander } 139f932e355SEtienne Carriere 140f932e355SEtienne Carriere /* 141f932e355SEtienne Carriere * Interrupt controller chip support 142f932e355SEtienne Carriere */ 143f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num) 144f932e355SEtienne Carriere { 145f932e355SEtienne Carriere struct itr_handler *h = NULL; 146f932e355SEtienne Carriere bool was_handled = false; 147f932e355SEtienne Carriere 148f932e355SEtienne Carriere assert(chip); 149f932e355SEtienne Carriere 150f932e355SEtienne Carriere SLIST_FOREACH(h, &chip->handlers, link) { 151f932e355SEtienne Carriere if (h->it == itr_num) { 152f932e355SEtienne Carriere if (h->handler(h) == ITRR_HANDLED) 153f932e355SEtienne Carriere was_handled = true; 154f932e355SEtienne Carriere else if (!(h->flags & ITRF_SHARED)) 155f932e355SEtienne Carriere break; 156f932e355SEtienne Carriere } 157f932e355SEtienne Carriere } 158f932e355SEtienne Carriere 159f932e355SEtienne Carriere if (!was_handled) { 160f932e355SEtienne Carriere EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num); 161f932e355SEtienne Carriere interrupt_mask(chip, itr_num); 162f932e355SEtienne Carriere } 163f932e355SEtienne Carriere } 164f932e355SEtienne Carriere 165f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num, 166f932e355SEtienne Carriere uint32_t type, uint32_t prio) 167f932e355SEtienne Carriere { 168f932e355SEtienne Carriere chip->ops->add(chip, itr_num, type, prio); 169f932e355SEtienne Carriere 170f932e355SEtienne Carriere return TEE_SUCCESS; 171f932e355SEtienne Carriere } 172f932e355SEtienne Carriere 173f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl, 174f932e355SEtienne Carriere uint32_t type, uint32_t prio) 175f932e355SEtienne Carriere { 176f932e355SEtienne Carriere struct itr_handler *h = NULL; 177f932e355SEtienne Carriere 178f932e355SEtienne Carriere assert(hdl && hdl->chip->ops && is_unpaged(hdl) && 179f932e355SEtienne Carriere hdl->handler && is_unpaged(hdl->handler)); 180f932e355SEtienne Carriere 181f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 182f932e355SEtienne Carriere if (h->it == hdl->it && 183f932e355SEtienne Carriere (!(hdl->flags & ITRF_SHARED) || 184f932e355SEtienne Carriere !(h->flags & ITRF_SHARED))) { 185f932e355SEtienne Carriere EMSG("Shared and non-shared flags on interrupt %s#%zu", 186f932e355SEtienne Carriere hdl->chip->name, hdl->it); 187f932e355SEtienne Carriere return TEE_ERROR_GENERIC; 188f932e355SEtienne Carriere } 189f932e355SEtienne Carriere } 190f932e355SEtienne Carriere 191f932e355SEtienne Carriere interrupt_configure(hdl->chip, hdl->it, type, prio); 192f932e355SEtienne Carriere 193f932e355SEtienne Carriere SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link); 194f932e355SEtienne Carriere 195f932e355SEtienne Carriere return TEE_SUCCESS; 196f932e355SEtienne Carriere } 197f932e355SEtienne Carriere 198f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl) 199f932e355SEtienne Carriere { 200f932e355SEtienne Carriere struct itr_handler *h = NULL; 201f932e355SEtienne Carriere bool disable_itr = true; 202f932e355SEtienne Carriere 203f932e355SEtienne Carriere if (!hdl) 204f932e355SEtienne Carriere return; 205f932e355SEtienne Carriere 206f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) 207f932e355SEtienne Carriere if (h == hdl) 208f932e355SEtienne Carriere break; 209f932e355SEtienne Carriere if (!h) { 210f932e355SEtienne Carriere DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it); 211f932e355SEtienne Carriere assert(false); 212f932e355SEtienne Carriere return; 213f932e355SEtienne Carriere } 214f932e355SEtienne Carriere 215f932e355SEtienne Carriere if (hdl->flags & ITRF_SHARED) { 216f932e355SEtienne Carriere SLIST_FOREACH(h, &hdl->chip->handlers, link) { 217f932e355SEtienne Carriere if (h != hdl && h->it == hdl->it) { 218f932e355SEtienne Carriere disable_itr = false; 219f932e355SEtienne Carriere break; 220f932e355SEtienne Carriere } 221f932e355SEtienne Carriere } 222f932e355SEtienne Carriere } 223f932e355SEtienne Carriere 224f932e355SEtienne Carriere if (disable_itr) 225f932e355SEtienne Carriere interrupt_disable(hdl->chip, hdl->it); 226f932e355SEtienne Carriere 227f932e355SEtienne Carriere SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link); 228f932e355SEtienne Carriere } 229f932e355SEtienne Carriere 230*1b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip, 231*1b5c7ca4SEtienne Carriere size_t itr_num, 232*1b5c7ca4SEtienne Carriere itr_handler_t handler, 233*1b5c7ca4SEtienne Carriere uint32_t flags, void *data, 234*1b5c7ca4SEtienne Carriere uint32_t type, uint32_t prio, 235*1b5c7ca4SEtienne Carriere struct itr_handler **out_hdl) 236f932e355SEtienne Carriere { 237f932e355SEtienne Carriere TEE_Result res = TEE_ERROR_GENERIC; 238f932e355SEtienne Carriere struct itr_handler *hdl = NULL; 239f932e355SEtienne Carriere 240f932e355SEtienne Carriere hdl = calloc(1, sizeof(*hdl)); 241f932e355SEtienne Carriere if (!hdl) 242f932e355SEtienne Carriere return TEE_ERROR_OUT_OF_MEMORY; 243f932e355SEtienne Carriere 244f932e355SEtienne Carriere *hdl = ITR_HANDLER(chip, itr_num, flags, handler, data); 245f932e355SEtienne Carriere 246*1b5c7ca4SEtienne Carriere res = interrupt_add_configure_handler(hdl, type, prio); 247f932e355SEtienne Carriere if (res) { 248f932e355SEtienne Carriere free(hdl); 249f932e355SEtienne Carriere return res; 250f932e355SEtienne Carriere } 251f932e355SEtienne Carriere 252f932e355SEtienne Carriere if (out_hdl) 253f932e355SEtienne Carriere *out_hdl = hdl; 254f932e355SEtienne Carriere 255f932e355SEtienne Carriere return TEE_SUCCESS; 256f932e355SEtienne Carriere } 257f932e355SEtienne Carriere 258f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl) 259f932e355SEtienne Carriere { 260f932e355SEtienne Carriere if (hdl) { 261f932e355SEtienne Carriere interrupt_remove_handler(hdl); 262f932e355SEtienne Carriere free(hdl); 263f932e355SEtienne Carriere } 264f932e355SEtienne Carriere } 265