xref: /optee_os/core/kernel/interrupt.c (revision 33a0c8350ac15de18e20ca47c5267b803fed76f8)
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 
68e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */
69358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void)
70e9f46c74SJens Wiklander {
71e9f46c74SJens Wiklander 	panic("Secure interrupt handler not defined");
72e9f46c74SJens Wiklander }
73f932e355SEtienne Carriere 
74f932e355SEtienne Carriere /*
75f932e355SEtienne Carriere  * Interrupt controller chip support
76f932e355SEtienne Carriere  */
77f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num)
78f932e355SEtienne Carriere {
79f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
80f932e355SEtienne Carriere 	bool was_handled = false;
81f932e355SEtienne Carriere 
82f932e355SEtienne Carriere 	assert(chip);
83f932e355SEtienne Carriere 
84f932e355SEtienne Carriere 	SLIST_FOREACH(h, &chip->handlers, link) {
85f932e355SEtienne Carriere 		if (h->it == itr_num) {
86f932e355SEtienne Carriere 			if (h->handler(h) == ITRR_HANDLED)
87f932e355SEtienne Carriere 				was_handled = true;
88f932e355SEtienne Carriere 			else if (!(h->flags & ITRF_SHARED))
89f932e355SEtienne Carriere 				break;
90f932e355SEtienne Carriere 		}
91f932e355SEtienne Carriere 	}
92f932e355SEtienne Carriere 
93f932e355SEtienne Carriere 	if (!was_handled) {
94f932e355SEtienne Carriere 		EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num);
95f932e355SEtienne Carriere 		interrupt_mask(chip, itr_num);
96f932e355SEtienne Carriere 	}
97f932e355SEtienne Carriere }
98f932e355SEtienne Carriere 
99f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num,
100f932e355SEtienne Carriere 			       uint32_t type, uint32_t prio)
101f932e355SEtienne Carriere {
102f932e355SEtienne Carriere 	chip->ops->add(chip, itr_num, type, prio);
103f932e355SEtienne Carriere 
104f932e355SEtienne Carriere 	return TEE_SUCCESS;
105f932e355SEtienne Carriere }
106f932e355SEtienne Carriere 
107f932e355SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
108f932e355SEtienne Carriere 					   uint32_t type, uint32_t prio)
109f932e355SEtienne Carriere {
110f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
111f932e355SEtienne Carriere 
112f932e355SEtienne Carriere 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
113f932e355SEtienne Carriere 	       hdl->handler && is_unpaged(hdl->handler));
114f932e355SEtienne Carriere 
115f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
116f932e355SEtienne Carriere 		if (h->it == hdl->it &&
117f932e355SEtienne Carriere 		    (!(hdl->flags & ITRF_SHARED) ||
118f932e355SEtienne Carriere 		     !(h->flags & ITRF_SHARED))) {
119f932e355SEtienne Carriere 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
120f932e355SEtienne Carriere 			     hdl->chip->name, hdl->it);
121f932e355SEtienne Carriere 			return TEE_ERROR_GENERIC;
122f932e355SEtienne Carriere 		}
123f932e355SEtienne Carriere 	}
124f932e355SEtienne Carriere 
125f932e355SEtienne Carriere 	interrupt_configure(hdl->chip, hdl->it, type, prio);
126f932e355SEtienne Carriere 
127f932e355SEtienne Carriere 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
128f932e355SEtienne Carriere 
129f932e355SEtienne Carriere 	return TEE_SUCCESS;
130f932e355SEtienne Carriere }
131f932e355SEtienne Carriere 
132f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl)
133f932e355SEtienne Carriere {
134f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
135f932e355SEtienne Carriere 	bool disable_itr = true;
136f932e355SEtienne Carriere 
137f932e355SEtienne Carriere 	if (!hdl)
138f932e355SEtienne Carriere 		return;
139f932e355SEtienne Carriere 
140f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
141f932e355SEtienne Carriere 		if (h == hdl)
142f932e355SEtienne Carriere 			break;
143f932e355SEtienne Carriere 	if (!h) {
144f932e355SEtienne Carriere 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
145f932e355SEtienne Carriere 		assert(false);
146f932e355SEtienne Carriere 		return;
147f932e355SEtienne Carriere 	}
148f932e355SEtienne Carriere 
149f932e355SEtienne Carriere 	if (hdl->flags & ITRF_SHARED) {
150f932e355SEtienne Carriere 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
151f932e355SEtienne Carriere 			if (h != hdl && h->it == hdl->it) {
152f932e355SEtienne Carriere 				disable_itr = false;
153f932e355SEtienne Carriere 				break;
154f932e355SEtienne Carriere 			}
155f932e355SEtienne Carriere 		}
156f932e355SEtienne Carriere 	}
157f932e355SEtienne Carriere 
158f932e355SEtienne Carriere 	if (disable_itr)
159f932e355SEtienne Carriere 		interrupt_disable(hdl->chip, hdl->it);
160f932e355SEtienne Carriere 
161f932e355SEtienne Carriere 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
162f932e355SEtienne Carriere }
163f932e355SEtienne Carriere 
1641b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip,
1651b5c7ca4SEtienne Carriere 					    size_t itr_num,
1661b5c7ca4SEtienne Carriere 					    itr_handler_t handler,
1671b5c7ca4SEtienne Carriere 					    uint32_t flags, void *data,
1681b5c7ca4SEtienne Carriere 					    uint32_t type, uint32_t prio,
1691b5c7ca4SEtienne Carriere 					    struct itr_handler **out_hdl)
170f932e355SEtienne Carriere {
171f932e355SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
172f932e355SEtienne Carriere 	struct itr_handler *hdl = NULL;
173f932e355SEtienne Carriere 
174f932e355SEtienne Carriere 	hdl = calloc(1, sizeof(*hdl));
175f932e355SEtienne Carriere 	if (!hdl)
176f932e355SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
177f932e355SEtienne Carriere 
178f932e355SEtienne Carriere 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
179f932e355SEtienne Carriere 
1801b5c7ca4SEtienne Carriere 	res = interrupt_add_configure_handler(hdl, type, prio);
181f932e355SEtienne Carriere 	if (res) {
182f932e355SEtienne Carriere 		free(hdl);
183f932e355SEtienne Carriere 		return res;
184f932e355SEtienne Carriere 	}
185f932e355SEtienne Carriere 
186f932e355SEtienne Carriere 	if (out_hdl)
187f932e355SEtienne Carriere 		*out_hdl = hdl;
188f932e355SEtienne Carriere 
189f932e355SEtienne Carriere 	return TEE_SUCCESS;
190f932e355SEtienne Carriere }
191f932e355SEtienne Carriere 
192f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl)
193f932e355SEtienne Carriere {
194f932e355SEtienne Carriere 	if (hdl) {
195f932e355SEtienne Carriere 		interrupt_remove_handler(hdl);
196f932e355SEtienne Carriere 		free(hdl);
197f932e355SEtienne Carriere 	}
198f932e355SEtienne Carriere }
199*33a0c835SEtienne Carriere 
200*33a0c835SEtienne Carriere #ifdef CFG_DT
201*33a0c835SEtienne Carriere TEE_Result interrupt_register_provider(const void *fdt, int node,
202*33a0c835SEtienne Carriere 				       itr_dt_get_func dt_get_itr, void *data)
203*33a0c835SEtienne Carriere {
204*33a0c835SEtienne Carriere 	return dt_driver_register_provider(fdt, node,
205*33a0c835SEtienne Carriere 					   (get_of_device_func)dt_get_itr,
206*33a0c835SEtienne Carriere 					   data, DT_DRIVER_INTERRUPT);
207*33a0c835SEtienne Carriere }
208*33a0c835SEtienne Carriere 
209*33a0c835SEtienne Carriere /*
210*33a0c835SEtienne Carriere  * Fills an itr_desc reference based on "interrupts" property bindings.
211*33a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but
212*33a0c835SEtienne Carriere  * not yet initialized.
213*33a0c835SEtienne Carriere  */
214*33a0c835SEtienne Carriere static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node,
215*33a0c835SEtienne Carriere 						unsigned int index,
216*33a0c835SEtienne Carriere 						struct itr_desc *itr_desc)
217*33a0c835SEtienne Carriere {
218*33a0c835SEtienne Carriere 	const uint32_t *prop = NULL;
219*33a0c835SEtienne Carriere 	uint32_t phandle = 0;
220*33a0c835SEtienne Carriere 	int pnode = 0;
221*33a0c835SEtienne Carriere 	int len = 0;
222*33a0c835SEtienne Carriere 
223*33a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, node, "interrupts", &len);
224*33a0c835SEtienne Carriere 	if (!prop)
225*33a0c835SEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
226*33a0c835SEtienne Carriere 
227*33a0c835SEtienne Carriere 	/* Find "interrupt-parent" in node or its parents */
228*33a0c835SEtienne Carriere 	pnode = node;
229*33a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
230*33a0c835SEtienne Carriere 
231*33a0c835SEtienne Carriere 	while (!prop) {
232*33a0c835SEtienne Carriere 		pnode = fdt_parent_offset(fdt, pnode);
233*33a0c835SEtienne Carriere 		if (pnode < 0)
234*33a0c835SEtienne Carriere 			break;
235*33a0c835SEtienne Carriere 
236*33a0c835SEtienne Carriere 		prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
237*33a0c835SEtienne Carriere 		if (!prop && len != -FDT_ERR_NOTFOUND)
238*33a0c835SEtienne Carriere 			break;
239*33a0c835SEtienne Carriere 	}
240*33a0c835SEtienne Carriere 	if (!prop) {
241*33a0c835SEtienne Carriere 		DMSG("No interrupt parent for node %s",
242*33a0c835SEtienne Carriere 		     fdt_get_name(fdt, node, NULL));
243*33a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
244*33a0c835SEtienne Carriere 	}
245*33a0c835SEtienne Carriere 
246*33a0c835SEtienne Carriere 	/* "interrupt-parent" provides interrupt controller phandle */
247*33a0c835SEtienne Carriere 	phandle = fdt32_to_cpu(prop[0]);
248*33a0c835SEtienne Carriere 
249*33a0c835SEtienne Carriere 	/* Get interrupt chip/number from phandle and "interrupts" property */
250*33a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
251*33a0c835SEtienne Carriere 							   node, index,
252*33a0c835SEtienne Carriere 							   DT_DRIVER_INTERRUPT,
253*33a0c835SEtienne Carriere 							   phandle,
254*33a0c835SEtienne Carriere 							   itr_desc);
255*33a0c835SEtienne Carriere }
256*33a0c835SEtienne Carriere 
257*33a0c835SEtienne Carriere /*
258*33a0c835SEtienne Carriere  * Fills an itr_desc based on "interrupts-extended" property bindings.
259*33a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found
260*33a0c835SEtienne Carriere  * but not yet initialized.
261*33a0c835SEtienne Carriere  */
262*33a0c835SEtienne Carriere static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
263*33a0c835SEtienne Carriere 						  unsigned int index,
264*33a0c835SEtienne Carriere 						  struct itr_desc *itr_desc)
265*33a0c835SEtienne Carriere {
266*33a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop("interrupts-extended",
267*33a0c835SEtienne Carriere 						   fdt, node, index,
268*33a0c835SEtienne Carriere 						   DT_DRIVER_INTERRUPT,
269*33a0c835SEtienne Carriere 						   itr_desc);
270*33a0c835SEtienne Carriere }
271*33a0c835SEtienne Carriere 
272*33a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
273*33a0c835SEtienne Carriere 				     unsigned int index, struct itr_chip **chip,
274*33a0c835SEtienne Carriere 				     size_t *itr_num)
275*33a0c835SEtienne Carriere {
276*33a0c835SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
277*33a0c835SEtienne Carriere 	struct itr_desc desc = { };
278*33a0c835SEtienne Carriere 
279*33a0c835SEtienne Carriere 	assert(chip && itr_num);
280*33a0c835SEtienne Carriere 
281*33a0c835SEtienne Carriere 	/* "interrupts-extended" takes precedence over "interrupts" */
282*33a0c835SEtienne Carriere 	if (fdt_getprop(fdt, node, "interrupts-extended", NULL))
283*33a0c835SEtienne Carriere 		res = get_extended_interrupt_by_index(fdt, node, index, &desc);
284*33a0c835SEtienne Carriere 	else
285*33a0c835SEtienne Carriere 		res = get_legacy_interrupt_by_index(fdt, node, index, &desc);
286*33a0c835SEtienne Carriere 
287*33a0c835SEtienne Carriere 	if (!res) {
288*33a0c835SEtienne Carriere 		assert(desc.chip);
289*33a0c835SEtienne Carriere 		*chip = desc.chip;
290*33a0c835SEtienne Carriere 		*itr_num = desc.itr_num;
291*33a0c835SEtienne Carriere 	}
292*33a0c835SEtienne Carriere 
293*33a0c835SEtienne Carriere 	return res;
294*33a0c835SEtienne Carriere }
295*33a0c835SEtienne Carriere 
296*33a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
297*33a0c835SEtienne Carriere 				    struct itr_chip **chip, size_t *itr_num)
298*33a0c835SEtienne Carriere {
299*33a0c835SEtienne Carriere 	int idx = 0;
300*33a0c835SEtienne Carriere 
301*33a0c835SEtienne Carriere 	idx = fdt_stringlist_search(fdt, node, "interrupt-names", name);
302*33a0c835SEtienne Carriere 	if (idx < 0)
303*33a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
304*33a0c835SEtienne Carriere 
305*33a0c835SEtienne Carriere 	return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num);
306*33a0c835SEtienne Carriere }
307*33a0c835SEtienne Carriere #endif /*CFG_DT*/
308