xref: /optee_os/core/kernel/interrupt.c (revision e9376d025eb57424056b012335aaa5f812691259)
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 
107*e9376d02SEtienne Carriere static TEE_Result add_configure_handler(struct itr_handler *hdl,
108*e9376d02SEtienne Carriere 					uint32_t type, uint32_t prio,
109*e9376d02SEtienne Carriere 					bool configure)
110f932e355SEtienne Carriere {
111f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
112f932e355SEtienne Carriere 
113f932e355SEtienne Carriere 	assert(hdl && hdl->chip->ops && is_unpaged(hdl) &&
114f932e355SEtienne Carriere 	       hdl->handler && is_unpaged(hdl->handler));
115f932e355SEtienne Carriere 
116f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link) {
117f932e355SEtienne Carriere 		if (h->it == hdl->it &&
118f932e355SEtienne Carriere 		    (!(hdl->flags & ITRF_SHARED) ||
119f932e355SEtienne Carriere 		     !(h->flags & ITRF_SHARED))) {
120f932e355SEtienne Carriere 			EMSG("Shared and non-shared flags on interrupt %s#%zu",
121f932e355SEtienne Carriere 			     hdl->chip->name, hdl->it);
122f932e355SEtienne Carriere 			return TEE_ERROR_GENERIC;
123f932e355SEtienne Carriere 		}
124f932e355SEtienne Carriere 	}
125f932e355SEtienne Carriere 
126*e9376d02SEtienne Carriere 	if (configure)
127f932e355SEtienne Carriere 		interrupt_configure(hdl->chip, hdl->it, type, prio);
128f932e355SEtienne Carriere 
129f932e355SEtienne Carriere 	SLIST_INSERT_HEAD(&hdl->chip->handlers, hdl, link);
130f932e355SEtienne Carriere 
131f932e355SEtienne Carriere 	return TEE_SUCCESS;
132f932e355SEtienne Carriere }
133f932e355SEtienne Carriere 
134*e9376d02SEtienne Carriere TEE_Result interrupt_add_configure_handler(struct itr_handler *hdl,
135*e9376d02SEtienne Carriere 					   uint32_t type, uint32_t prio)
136*e9376d02SEtienne Carriere {
137*e9376d02SEtienne Carriere 	return add_configure_handler(hdl, type, prio, true /* configure */);
138*e9376d02SEtienne Carriere }
139*e9376d02SEtienne Carriere 
140*e9376d02SEtienne Carriere TEE_Result interrupt_create_handler(struct itr_chip *itr_chip, size_t itr_num,
141*e9376d02SEtienne Carriere 				    itr_handler_t callback, void *priv,
142*e9376d02SEtienne Carriere 				    uint32_t flags,
143*e9376d02SEtienne Carriere 				    struct itr_handler **out_hdl)
144*e9376d02SEtienne Carriere {
145*e9376d02SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
146*e9376d02SEtienne Carriere 	struct itr_handler *itr_hdl = NULL;
147*e9376d02SEtienne Carriere 
148*e9376d02SEtienne Carriere 	itr_hdl = calloc(1, sizeof(*itr_hdl));
149*e9376d02SEtienne Carriere 	if (!itr_hdl)
150*e9376d02SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
151*e9376d02SEtienne Carriere 
152*e9376d02SEtienne Carriere 	*itr_hdl = (struct itr_handler){
153*e9376d02SEtienne Carriere 		.chip = itr_chip,
154*e9376d02SEtienne Carriere 		.it = itr_num,
155*e9376d02SEtienne Carriere 		.flags = flags,
156*e9376d02SEtienne Carriere 		.handler = callback,
157*e9376d02SEtienne Carriere 		.data = priv,
158*e9376d02SEtienne Carriere 	};
159*e9376d02SEtienne Carriere 
160*e9376d02SEtienne Carriere 	res = add_configure_handler(itr_hdl, 0, 0, false /* configure */);
161*e9376d02SEtienne Carriere 	if (res) {
162*e9376d02SEtienne Carriere 		free(itr_hdl);
163*e9376d02SEtienne Carriere 		return res;
164*e9376d02SEtienne Carriere 	}
165*e9376d02SEtienne Carriere 
166*e9376d02SEtienne Carriere 	if (out_hdl)
167*e9376d02SEtienne Carriere 		*out_hdl = itr_hdl;
168*e9376d02SEtienne Carriere 
169*e9376d02SEtienne Carriere 	return TEE_SUCCESS;
170*e9376d02SEtienne Carriere }
171*e9376d02SEtienne Carriere 
172f932e355SEtienne Carriere void interrupt_remove_handler(struct itr_handler *hdl)
173f932e355SEtienne Carriere {
174f932e355SEtienne Carriere 	struct itr_handler *h = NULL;
175f932e355SEtienne Carriere 	bool disable_itr = true;
176f932e355SEtienne Carriere 
177f932e355SEtienne Carriere 	if (!hdl)
178f932e355SEtienne Carriere 		return;
179f932e355SEtienne Carriere 
180f932e355SEtienne Carriere 	SLIST_FOREACH(h, &hdl->chip->handlers, link)
181f932e355SEtienne Carriere 		if (h == hdl)
182f932e355SEtienne Carriere 			break;
183f932e355SEtienne Carriere 	if (!h) {
184f932e355SEtienne Carriere 		DMSG("Invalid %s:%zu", hdl->chip->name, hdl->it);
185f932e355SEtienne Carriere 		assert(false);
186f932e355SEtienne Carriere 		return;
187f932e355SEtienne Carriere 	}
188f932e355SEtienne Carriere 
189f932e355SEtienne Carriere 	if (hdl->flags & ITRF_SHARED) {
190f932e355SEtienne Carriere 		SLIST_FOREACH(h, &hdl->chip->handlers, link) {
191f932e355SEtienne Carriere 			if (h != hdl && h->it == hdl->it) {
192f932e355SEtienne Carriere 				disable_itr = false;
193f932e355SEtienne Carriere 				break;
194f932e355SEtienne Carriere 			}
195f932e355SEtienne Carriere 		}
196f932e355SEtienne Carriere 	}
197f932e355SEtienne Carriere 
198f932e355SEtienne Carriere 	if (disable_itr)
199f932e355SEtienne Carriere 		interrupt_disable(hdl->chip, hdl->it);
200f932e355SEtienne Carriere 
201f932e355SEtienne Carriere 	SLIST_REMOVE(&hdl->chip->handlers, hdl, itr_handler, link);
202f932e355SEtienne Carriere }
203f932e355SEtienne Carriere 
2041b5c7ca4SEtienne Carriere TEE_Result interrupt_alloc_add_conf_handler(struct itr_chip *chip,
2051b5c7ca4SEtienne Carriere 					    size_t itr_num,
2061b5c7ca4SEtienne Carriere 					    itr_handler_t handler,
2071b5c7ca4SEtienne Carriere 					    uint32_t flags, void *data,
2081b5c7ca4SEtienne Carriere 					    uint32_t type, uint32_t prio,
2091b5c7ca4SEtienne Carriere 					    struct itr_handler **out_hdl)
210f932e355SEtienne Carriere {
211f932e355SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
212f932e355SEtienne Carriere 	struct itr_handler *hdl = NULL;
213f932e355SEtienne Carriere 
214f932e355SEtienne Carriere 	hdl = calloc(1, sizeof(*hdl));
215f932e355SEtienne Carriere 	if (!hdl)
216f932e355SEtienne Carriere 		return TEE_ERROR_OUT_OF_MEMORY;
217f932e355SEtienne Carriere 
218f932e355SEtienne Carriere 	*hdl = ITR_HANDLER(chip, itr_num, flags, handler, data);
219f932e355SEtienne Carriere 
2201b5c7ca4SEtienne Carriere 	res = interrupt_add_configure_handler(hdl, type, prio);
221f932e355SEtienne Carriere 	if (res) {
222f932e355SEtienne Carriere 		free(hdl);
223f932e355SEtienne Carriere 		return res;
224f932e355SEtienne Carriere 	}
225f932e355SEtienne Carriere 
226f932e355SEtienne Carriere 	if (out_hdl)
227f932e355SEtienne Carriere 		*out_hdl = hdl;
228f932e355SEtienne Carriere 
229f932e355SEtienne Carriere 	return TEE_SUCCESS;
230f932e355SEtienne Carriere }
231f932e355SEtienne Carriere 
232f932e355SEtienne Carriere void interrupt_remove_free_handler(struct itr_handler *hdl)
233f932e355SEtienne Carriere {
234f932e355SEtienne Carriere 	if (hdl) {
235f932e355SEtienne Carriere 		interrupt_remove_handler(hdl);
236f932e355SEtienne Carriere 		free(hdl);
237f932e355SEtienne Carriere 	}
238f932e355SEtienne Carriere }
23933a0c835SEtienne Carriere 
24033a0c835SEtienne Carriere #ifdef CFG_DT
24133a0c835SEtienne Carriere TEE_Result interrupt_register_provider(const void *fdt, int node,
24233a0c835SEtienne Carriere 				       itr_dt_get_func dt_get_itr, void *data)
24333a0c835SEtienne Carriere {
24433a0c835SEtienne Carriere 	return dt_driver_register_provider(fdt, node,
24533a0c835SEtienne Carriere 					   (get_of_device_func)dt_get_itr,
24633a0c835SEtienne Carriere 					   data, DT_DRIVER_INTERRUPT);
24733a0c835SEtienne Carriere }
24833a0c835SEtienne Carriere 
24933a0c835SEtienne Carriere /*
25033a0c835SEtienne Carriere  * Fills an itr_desc reference based on "interrupts" property bindings.
25133a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found but
25233a0c835SEtienne Carriere  * not yet initialized.
25333a0c835SEtienne Carriere  */
25433a0c835SEtienne Carriere static TEE_Result get_legacy_interrupt_by_index(const void *fdt, int node,
25533a0c835SEtienne Carriere 						unsigned int index,
25633a0c835SEtienne Carriere 						struct itr_desc *itr_desc)
25733a0c835SEtienne Carriere {
25833a0c835SEtienne Carriere 	const uint32_t *prop = NULL;
25933a0c835SEtienne Carriere 	uint32_t phandle = 0;
26033a0c835SEtienne Carriere 	int pnode = 0;
26133a0c835SEtienne Carriere 	int len = 0;
26233a0c835SEtienne Carriere 
26333a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, node, "interrupts", &len);
26433a0c835SEtienne Carriere 	if (!prop)
26533a0c835SEtienne Carriere 		return TEE_ERROR_ITEM_NOT_FOUND;
26633a0c835SEtienne Carriere 
26733a0c835SEtienne Carriere 	/* Find "interrupt-parent" in node or its parents */
26833a0c835SEtienne Carriere 	pnode = node;
26933a0c835SEtienne Carriere 	prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
27033a0c835SEtienne Carriere 
27133a0c835SEtienne Carriere 	while (!prop) {
27233a0c835SEtienne Carriere 		pnode = fdt_parent_offset(fdt, pnode);
27333a0c835SEtienne Carriere 		if (pnode < 0)
27433a0c835SEtienne Carriere 			break;
27533a0c835SEtienne Carriere 
27633a0c835SEtienne Carriere 		prop = fdt_getprop(fdt, pnode, "interrupt-parent", &len);
27733a0c835SEtienne Carriere 		if (!prop && len != -FDT_ERR_NOTFOUND)
27833a0c835SEtienne Carriere 			break;
27933a0c835SEtienne Carriere 	}
28033a0c835SEtienne Carriere 	if (!prop) {
28133a0c835SEtienne Carriere 		DMSG("No interrupt parent for node %s",
28233a0c835SEtienne Carriere 		     fdt_get_name(fdt, node, NULL));
28333a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
28433a0c835SEtienne Carriere 	}
28533a0c835SEtienne Carriere 
28633a0c835SEtienne Carriere 	/* "interrupt-parent" provides interrupt controller phandle */
28733a0c835SEtienne Carriere 	phandle = fdt32_to_cpu(prop[0]);
28833a0c835SEtienne Carriere 
28933a0c835SEtienne Carriere 	/* Get interrupt chip/number from phandle and "interrupts" property */
29033a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop_phandle("interrupts", fdt,
29133a0c835SEtienne Carriere 							   node, index,
29233a0c835SEtienne Carriere 							   DT_DRIVER_INTERRUPT,
29333a0c835SEtienne Carriere 							   phandle,
29433a0c835SEtienne Carriere 							   itr_desc);
29533a0c835SEtienne Carriere }
29633a0c835SEtienne Carriere 
29733a0c835SEtienne Carriere /*
29833a0c835SEtienne Carriere  * Fills an itr_desc based on "interrupts-extended" property bindings.
29933a0c835SEtienne Carriere  * May return TEE_ERROR_DEFER_DRIVER_INIT if parent controller is found
30033a0c835SEtienne Carriere  * but not yet initialized.
30133a0c835SEtienne Carriere  */
30233a0c835SEtienne Carriere static TEE_Result get_extended_interrupt_by_index(const void *fdt, int node,
30333a0c835SEtienne Carriere 						  unsigned int index,
30433a0c835SEtienne Carriere 						  struct itr_desc *itr_desc)
30533a0c835SEtienne Carriere {
30633a0c835SEtienne Carriere 	return dt_driver_device_from_node_idx_prop("interrupts-extended",
30733a0c835SEtienne Carriere 						   fdt, node, index,
30833a0c835SEtienne Carriere 						   DT_DRIVER_INTERRUPT,
30933a0c835SEtienne Carriere 						   itr_desc);
31033a0c835SEtienne Carriere }
31133a0c835SEtienne Carriere 
31233a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_index(const void *fdt, int node,
31333a0c835SEtienne Carriere 				     unsigned int index, struct itr_chip **chip,
31433a0c835SEtienne Carriere 				     size_t *itr_num)
31533a0c835SEtienne Carriere {
31633a0c835SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
31733a0c835SEtienne Carriere 	struct itr_desc desc = { };
31833a0c835SEtienne Carriere 
31933a0c835SEtienne Carriere 	assert(chip && itr_num);
32033a0c835SEtienne Carriere 
32133a0c835SEtienne Carriere 	/* "interrupts-extended" takes precedence over "interrupts" */
32233a0c835SEtienne Carriere 	if (fdt_getprop(fdt, node, "interrupts-extended", NULL))
32333a0c835SEtienne Carriere 		res = get_extended_interrupt_by_index(fdt, node, index, &desc);
32433a0c835SEtienne Carriere 	else
32533a0c835SEtienne Carriere 		res = get_legacy_interrupt_by_index(fdt, node, index, &desc);
32633a0c835SEtienne Carriere 
32733a0c835SEtienne Carriere 	if (!res) {
32833a0c835SEtienne Carriere 		assert(desc.chip);
32933a0c835SEtienne Carriere 		*chip = desc.chip;
33033a0c835SEtienne Carriere 		*itr_num = desc.itr_num;
33133a0c835SEtienne Carriere 	}
33233a0c835SEtienne Carriere 
33333a0c835SEtienne Carriere 	return res;
33433a0c835SEtienne Carriere }
33533a0c835SEtienne Carriere 
33633a0c835SEtienne Carriere TEE_Result interrupt_dt_get_by_name(const void *fdt, int node, const char *name,
33733a0c835SEtienne Carriere 				    struct itr_chip **chip, size_t *itr_num)
33833a0c835SEtienne Carriere {
33933a0c835SEtienne Carriere 	int idx = 0;
34033a0c835SEtienne Carriere 
34133a0c835SEtienne Carriere 	idx = fdt_stringlist_search(fdt, node, "interrupt-names", name);
34233a0c835SEtienne Carriere 	if (idx < 0)
34333a0c835SEtienne Carriere 		return TEE_ERROR_GENERIC;
34433a0c835SEtienne Carriere 
34533a0c835SEtienne Carriere 	return interrupt_dt_get_by_index(fdt, node, idx, chip, itr_num);
34633a0c835SEtienne Carriere }
34733a0c835SEtienne Carriere #endif /*CFG_DT*/
348