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