xref: /optee_os/core/kernel/interrupt.c (revision 1b5c7ca4e5173c4b773c00829a0ce351d2b6bdd4)
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