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