xref: /optee_os/core/kernel/interrupt.c (revision 245a552c4164e6803517ea796a1deacfa8259574)
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 
49*245a552cSJens Wiklander struct itr_chip *interrupt_get_main_chip_may_fail(void)
50*245a552cSJens Wiklander {
51*245a552cSJens Wiklander 	return itr_main_chip;
52*245a552cSJens Wiklander }
53*245a552cSJens Wiklander 
5467729d8dSLudovic Barre #ifdef CFG_DT
55702fe5a7SClément Léger int dt_get_irq_type_prio(const void *fdt, int node, uint32_t *type,
56702fe5a7SClément Léger 			 uint32_t *prio)
5767729d8dSLudovic Barre {
5867729d8dSLudovic Barre 	const uint32_t *prop = NULL;
59888bb63dSClément Léger 	int count = 0;
6067729d8dSLudovic Barre 	int it_num = DT_INFO_INVALID_INTERRUPT;
6167729d8dSLudovic Barre 
623475549bSEtienne Carriere 	if (!itr_main_chip || !itr_main_chip->dt_get_irq)
6367729d8dSLudovic Barre 		return it_num;
6467729d8dSLudovic Barre 
65888bb63dSClément Léger 	prop = fdt_getprop(fdt, node, "interrupts", &count);
6667729d8dSLudovic Barre 	if (!prop)
6767729d8dSLudovic Barre 		return it_num;
6867729d8dSLudovic Barre 
693475549bSEtienne Carriere 	return itr_main_chip->dt_get_irq(prop, count, type, prio);
7067729d8dSLudovic Barre }
7167729d8dSLudovic Barre #endif
7267729d8dSLudovic Barre 
73e9f46c74SJens Wiklander /* This function is supposed to be overridden in platform specific code */
74358bf47cSEtienne Carriere void __weak __noreturn interrupt_main_handler(void)
75e9f46c74SJens Wiklander {
76e9f46c74SJens Wiklander 	panic("Secure interrupt handler not defined");
77e9f46c74SJens Wiklander }
78f932e355SEtienne Carriere 
79f932e355SEtienne Carriere /*
80f932e355SEtienne Carriere  * Interrupt controller chip support
81f932e355SEtienne Carriere  */
82f932e355SEtienne Carriere void interrupt_call_handlers(struct itr_chip *chip, size_t itr_num)
83f932e355SEtienne Carriere {
84f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
85f932e355SEtienne Carriere 	bool was_handled = false;
86f932e355SEtienne Carriere 
87f932e355SEtienne Carriere 	assert(chip);
88f932e355SEtienne Carriere 
89f932e355SEtienne Carriere 	SLIST_FOREACH(h, &chip->handlers, link) {
90f932e355SEtienne Carriere 		if (h->it == itr_num) {
91f932e355SEtienne Carriere 			if (h->handler(h) == ITRR_HANDLED)
92f932e355SEtienne Carriere 				was_handled = true;
93f932e355SEtienne Carriere 			else if (!(h->flags & ITRF_SHARED))
94f932e355SEtienne Carriere 				break;
95f932e355SEtienne Carriere 		}
96f932e355SEtienne Carriere 	}
97f932e355SEtienne Carriere 
98f932e355SEtienne Carriere 	if (!was_handled) {
99f932e355SEtienne Carriere 		EMSG("Mask unhandled interrupt %s:%zu", chip->name, itr_num);
100f932e355SEtienne Carriere 		interrupt_mask(chip, itr_num);
101f932e355SEtienne Carriere 	}
102f932e355SEtienne Carriere }
103f932e355SEtienne Carriere 
104f932e355SEtienne Carriere TEE_Result interrupt_configure(struct itr_chip *chip, size_t itr_num,
105f932e355SEtienne Carriere 			       uint32_t type, uint32_t prio)
106f932e355SEtienne Carriere {
107f932e355SEtienne Carriere 	chip->ops->add(chip, itr_num, type, prio);
108f932e355SEtienne Carriere 
109f932e355SEtienne Carriere 	return TEE_SUCCESS;
110f932e355SEtienne Carriere }
111f932e355SEtienne Carriere 
112e9376d02SEtienne Carriere static TEE_Result add_configure_handler(struct itr_handler *hdl,
113e9376d02SEtienne Carriere 					uint32_t type, uint32_t prio,
114e9376d02SEtienne Carriere 					bool configure)
115f932e355SEtienne Carriere {
116f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
117f932e355SEtienne Carriere 
118f932e355SEtienne Carriere 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
119f932e355SEtienne Carriere 	       hdl->handler && is_unpaged(hdl->handler));
120f932e355SEtienne Carriere 
121f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
122f932e355SEtienne Carriere 		if (h->it == hdl->it &&
123f932e355SEtienne Carriere 		    (!(hdl->flags & ITRF_SHARED) ||
124f932e355SEtienne Carriere 		     !(h->flags & ITRF_SHARED))) {
125f932e355SEtienne Carriere 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
126f932e355SEtienne Carriere 			     hdl->chip->name, hdl->it);
127f932e355SEtienne Carriere 			return TEE_ERROR_GENERIC;
128f932e355SEtienne Carriere 		}
129f932e355SEtienne Carriere 	}
130f932e355SEtienne Carriere 
131e9376d02SEtienne Carriere 	if (configure)
132f932e355SEtienne Carriere 		interrupt_configure(hdl->chip, hdl->it, type, prio);
133f932e355SEtienne Carriere 
134f932e355SEtienne Carriere 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
135f932e355SEtienne Carriere 
136f932e355SEtienne Carriere 	return TEE_SUCCESS;
137f932e355SEtienne Carriere }
138f932e355SEtienne Carriere 
139e9376d02SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
140e9376d02SEtienne Carriere 					   uint32_t type, uint32_t prio)
141e9376d02SEtienne Carriere {
142e9376d02SEtienne Carriere 	return add_configure_handler(hdl, type, prio, true /* configure */);
143e9376d02SEtienne Carriere }
144e9376d02SEtienne Carriere 
145e9376d02SEtienne Carriere TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num,
146e9376d02SEtienne Carriere 				    itr_handler_t callback, void *priv,
147e9376d02SEtienne Carriere 				    uint32_t flags,
148e9376d02SEtienne Carriere 				    struct itr_handler **out_hdl)
149e9376d02SEtienne Carriere {
150e9376d02SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
151e9376d02SEtienne Carriere 	struct itr_handler *itr_hdl = NULL;
152e9376d02SEtienne Carriere 
153e9376d02SEtienne Carriere 	itr_hdl = calloc(1, sizeof(*itr_hdl));
154e9376d02SEtienne Carriere 	if (!itr_hdl)
155e9376d02SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
156e9376d02SEtienne Carriere 
157e9376d02SEtienne Carriere 	*itr_hdl = (struct itr_handler){
158e9376d02SEtienne Carriere 		.chip = itr_chip,
159e9376d02SEtienne Carriere 		.it = itr_num,
160e9376d02SEtienne Carriere 		.flags = flags,
161e9376d02SEtienne Carriere 		.handler = callback,
162e9376d02SEtienne Carriere 		.data = priv,
163e9376d02SEtienne Carriere 	};
164e9376d02SEtienne Carriere 
165e9376d02SEtienne Carriere 	res = add_configure_handler(itr_hdl, 0, 0, false /* configure */);
166e9376d02SEtienne Carriere 	if (res) {
167e9376d02SEtienne Carriere 		free(itr_hdl);
168e9376d02SEtienne Carriere 		return res;
169e9376d02SEtienne Carriere 	}
170e9376d02SEtienne Carriere 
171e9376d02SEtienne Carriere 	if (out_hdl)
172e9376d02SEtienne Carriere 		*out_hdl = itr_hdl;
173e9376d02SEtienne Carriere 
174e9376d02SEtienne Carriere 	return TEE_SUCCESS;
175e9376d02SEtienne Carriere }
176e9376d02SEtienne Carriere 
177f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl)
178f932e355SEtienne Carriere {
179f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
180f932e355SEtienne Carriere 	bool disable_itr = true;
181f932e355SEtienne Carriere 
182f932e355SEtienne Carriere 	if (!hdl)
183f932e355SEtienne Carriere 		return;
184f932e355SEtienne Carriere 
185f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
186f932e355SEtienne Carriere 		if (h == hdl)
187f932e355SEtienne Carriere 			break;
188f932e355SEtienne Carriere 	if (!h) {
189f932e355SEtienne Carriere 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
190f932e355SEtienne Carriere 		assert(false);
191f932e355SEtienne Carriere 		return;
192f932e355SEtienne Carriere 	}
193f932e355SEtienne Carriere 
194f932e355SEtienne Carriere 	if (hdl->flags & ITRF_SHARED) {
195f932e355SEtienne Carriere 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
196f932e355SEtienne Carriere 			if (h != hdl && h->it == hdl->it) {
197f932e355SEtienne Carriere 				disable_itr = false;
198f932e355SEtienne Carriere 				break;
199f932e355SEtienne Carriere 			}
200f932e355SEtienne Carriere 		}
201f932e355SEtienne Carriere 	}
202f932e355SEtienne Carriere 
203f932e355SEtienne Carriere 	if (disable_itr)
204f932e355SEtienne Carriere 		interrupt_disable(hdl->chip, hdl->it);
205f932e355SEtienne Carriere 
206f932e355SEtienne Carriere 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
207f932e355SEtienne Carriere }
208f932e355SEtienne Carriere 
2091b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip,
2101b5c7ca4SEtienne Carriere 					    size_t itr_num,
2111b5c7ca4SEtienne Carriere 					    itr_handler_t handler,
2121b5c7ca4SEtienne Carriere 					    uint32_t flags, void *data,
2131b5c7ca4SEtienne Carriere 					    uint32_t type, uint32_t prio,
2141b5c7ca4SEtienne Carriere 					    struct itr_handler **out_hdl)
215f932e355SEtienne Carriere {
216f932e355SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
217f932e355SEtienne Carriere 	struct itr_handler *hdl = NULL;
218f932e355SEtienne Carriere 
219f932e355SEtienne Carriere 	hdl = calloc(1, sizeof(*hdl));
220f932e355SEtienne Carriere 	if (!hdl)
221f932e355SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
222f932e355SEtienne Carriere 
223f932e355SEtienne Carriere 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
224f932e355SEtienne Carriere 
2251b5c7ca4SEtienne Carriere 	res = interrupt_add_configure_handler(hdl, type, prio);
226f932e355SEtienne Carriere 	if (res) {
227f932e355SEtienne Carriere 		free(hdl);
228f932e355SEtienne Carriere 		return res;
229f932e355SEtienne Carriere 	}
230f932e355SEtienne Carriere 
231f932e355SEtienne Carriere 	if (out_hdl)
232f932e355SEtienne Carriere 		*out_hdl = hdl;
233f932e355SEtienne Carriere 
234f932e355SEtienne Carriere 	return TEE_SUCCESS;
235f932e355SEtienne Carriere }
236f932e355SEtienne Carriere 
237f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl)
238f932e355SEtienne Carriere {
239f932e355SEtienne Carriere 	if (hdl) {
240f932e355SEtienne Carriere 		interrupt_remove_handler(hdl);
241f932e355SEtienne Carriere 		free(hdl);
242f932e355SEtienne Carriere 	}
243f932e355SEtienne Carriere }
24433a0c835SEtienne Carriere 
24533a0c835SEtienne Carriere #ifdef CFG_DT
24633a0c835SEtienne Carriere TEE_Result interrupt_register_provider(const void *fdt, int node,
24733a0c835SEtienne Carriere 				       itr_dt_get_func dt_get_itr, void *data)
24833a0c835SEtienne Carriere {
24933a0c835SEtienne Carriere 	return dt_driver_register_provider(fdt, node,
25033a0c835SEtienne Carriere 					   (get_of_device_func)dt_get_itr,
25133a0c835SEtienne Carriere 					   data, DT_DRIVER_INTERRUPT);
25233a0c835SEtienne Carriere }
25333a0c835SEtienne Carriere 
25433a0c835SEtienne Carriere /*
25533a0c835SEtienne Carriere  * Fills an itr_desc reference based on "interrupts" property bindings.
25633a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but
25733a0c835SEtienne Carriere  * not yet initialized.
25833a0c835SEtienne Carriere  */
25933a0c835SEtienne Carriere static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node,
26033a0c835SEtienne Carriere 						unsigned int index,
26133a0c835SEtienne Carriere 						struct itr_desc *itr_desc)
26233a0c835SEtienne Carriere {
26333a0c835SEtienne Carriere 	const uint32_t *prop = NULL;
26433a0c835SEtienne Carriere 	uint32_t phandle = 0;
26533a0c835SEtienne Carriere 	int pnode = 0;
26633a0c835SEtienne Carriere 	int len = 0;
26733a0c835SEtienne Carriere 
26833a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, node, "interrupts", &len);
26933a0c835SEtienne Carriere 	if (!prop)
27033a0c835SEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
27133a0c835SEtienne Carriere 
27233a0c835SEtienne Carriere 	/* Find "interrupt-parent" in node or its parents */
27333a0c835SEtienne Carriere 	pnode = node;
27433a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
27533a0c835SEtienne Carriere 
27633a0c835SEtienne Carriere 	while (!prop) {
27733a0c835SEtienne Carriere 		pnode = fdt_parent_offset(fdt, pnode);
27833a0c835SEtienne Carriere 		if (pnode < 0)
27933a0c835SEtienne Carriere 			break;
28033a0c835SEtienne Carriere 
28133a0c835SEtienne Carriere 		prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
28233a0c835SEtienne Carriere 		if (!prop && len != -FDT_ERR_NOTFOUND)
28333a0c835SEtienne Carriere 			break;
28433a0c835SEtienne Carriere 	}
28533a0c835SEtienne Carriere 	if (!prop) {
28633a0c835SEtienne Carriere 		DMSG("No interrupt parent for node %s",
28733a0c835SEtienne Carriere 		     fdt_get_name(fdt, node, NULL));
28833a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
28933a0c835SEtienne Carriere 	}
29033a0c835SEtienne Carriere 
29133a0c835SEtienne Carriere 	/* "interrupt-parent" provides interrupt controller phandle */
29233a0c835SEtienne Carriere 	phandle = fdt32_to_cpu(prop[0]);
29333a0c835SEtienne Carriere 
29433a0c835SEtienne Carriere 	/* Get interrupt chip/number from phandle and "interrupts" property */
29533a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
29633a0c835SEtienne Carriere 							   node, index,
29733a0c835SEtienne Carriere 							   DT_DRIVER_INTERRUPT,
29833a0c835SEtienne Carriere 							   phandle,
29933a0c835SEtienne Carriere 							   itr_desc);
30033a0c835SEtienne Carriere }
30133a0c835SEtienne Carriere 
30233a0c835SEtienne Carriere /*
30333a0c835SEtienne Carriere  * Fills an itr_desc based on "interrupts-extended" property bindings.
30433a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found
30533a0c835SEtienne Carriere  * but not yet initialized.
30633a0c835SEtienne Carriere  */
30733a0c835SEtienne Carriere static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
30833a0c835SEtienne Carriere 						  unsigned int index,
30933a0c835SEtienne Carriere 						  struct itr_desc *itr_desc)
31033a0c835SEtienne Carriere {
31133a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop("interrupts-extended",
31233a0c835SEtienne Carriere 						   fdt, node, index,
31333a0c835SEtienne Carriere 						   DT_DRIVER_INTERRUPT,
31433a0c835SEtienne Carriere 						   itr_desc);
31533a0c835SEtienne Carriere }
31633a0c835SEtienne Carriere 
31733a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
31833a0c835SEtienne Carriere 				     unsigned int index, struct itr_chip **chip,
31933a0c835SEtienne Carriere 				     size_t *itr_num)
32033a0c835SEtienne Carriere {
32133a0c835SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
32233a0c835SEtienne Carriere 	struct itr_desc desc = { };
32333a0c835SEtienne Carriere 
32433a0c835SEtienne Carriere 	assert(chip && itr_num);
32533a0c835SEtienne Carriere 
32633a0c835SEtienne Carriere 	/* "interrupts-extended" takes precedence over "interrupts" */
32733a0c835SEtienne Carriere 	if (fdt_getprop(fdt, node, "interrupts-extended", NULL))
32833a0c835SEtienne Carriere 		res = get_extended_interrupt_by_index(fdt, node, index, &desc);
32933a0c835SEtienne Carriere 	else
33033a0c835SEtienne Carriere 		res = get_legacy_interrupt_by_index(fdt, node, index, &desc);
33133a0c835SEtienne Carriere 
33233a0c835SEtienne Carriere 	if (!res) {
33333a0c835SEtienne Carriere 		assert(desc.chip);
33433a0c835SEtienne Carriere 		*chip = desc.chip;
33533a0c835SEtienne Carriere 		*itr_num = desc.itr_num;
33633a0c835SEtienne Carriere 	}
33733a0c835SEtienne Carriere 
33833a0c835SEtienne Carriere 	return res;
33933a0c835SEtienne Carriere }
34033a0c835SEtienne Carriere 
34133a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
34233a0c835SEtienne Carriere 				    struct itr_chip **chip, size_t *itr_num)
34333a0c835SEtienne Carriere {
34433a0c835SEtienne Carriere 	int idx = 0;
34533a0c835SEtienne Carriere 
34633a0c835SEtienne Carriere 	idx = fdt_stringlist_search(fdt, node, "interrupt-names", name);
34733a0c835SEtienne Carriere 	if (idx < 0)
34833a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
34933a0c835SEtienne Carriere 
35033a0c835SEtienne Carriere 	return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num);
35133a0c835SEtienne Carriere }
35233a0c835SEtienne Carriere #endif /*CFG_DT*/
353