xref: /optee_os/core/kernel/interrupt.c (revision 77bdbf67c42209142ef43129e01113d29d9c62f6)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016-2019, Linaro Limited
4  */
5 
6 #include <kernel/dt.h>
7 #include <kernel/interrupt.h>
8 #include <kernel/panic.h>
9 #include <libfdt.h>
10 #include <stdlib.h>
11 #include <trace.h>
12 #include <assert.h>
13 
14 /*
15  * NOTE!
16  *
17  * We're assuming that there's no concurrent use of this interface, except
18  * delivery of interrupts in parallel. Synchronization will be needed when
19  * we begin to modify settings after boot initialization.
20  */
21 
22 static struct itr_chip *itr_chip __nex_bss;
23 static SLIST_HEAD(, itr_handler) handlers __nex_data =
24 	SLIST_HEAD_INITIALIZER(handlers);
25 
26 void itr_init(struct itr_chip *chip)
27 {
28 	itr_chip = chip;
29 }
30 
31 #ifdef CFG_DT
32 int dt_get_irq(const void *fdt, int node)
33 {
34 	const uint32_t *prop = NULL;
35 	int len = 0;
36 	int it_num = DT_INFO_INVALID_INTERRUPT;
37 
38 	if (!itr_chip || !itr_chip->dt_get_irq)
39 		return it_num;
40 
41 	prop = fdt_getprop(fdt, node, "interrupts", &len);
42 	if (!prop)
43 		return it_num;
44 
45 	return itr_chip->dt_get_irq(prop, len);
46 }
47 #endif
48 
49 void itr_handle(size_t it)
50 {
51 	struct itr_handler *h = NULL;
52 	bool was_handled = false;
53 
54 	SLIST_FOREACH(h, &handlers, link) {
55 		if (h->it == it) {
56 			if (h->handler(h) == ITRR_HANDLED)
57 				was_handled = true;
58 			else if (!(h->flags & ITRF_SHARED))
59 				break;
60 		}
61 	}
62 
63 	if (!was_handled) {
64 		EMSG("Disabling unhandled interrupt %zu", it);
65 		itr_chip->ops->disable(itr_chip, it);
66 	}
67 }
68 
69 struct itr_handler *itr_alloc_add(size_t it, itr_handler_t handler,
70 				  uint32_t flags, void *data)
71 {
72 	struct itr_handler *hdl = calloc(1, sizeof(*hdl));
73 
74 	if (hdl) {
75 		hdl->it = it;
76 		hdl->handler = handler;
77 		hdl->flags = flags;
78 		hdl->data = data;
79 		itr_add(hdl);
80 	}
81 
82 	return hdl;
83 }
84 
85 void itr_free(struct itr_handler *hdl)
86 {
87 	if (!hdl)
88 		return;
89 
90 	itr_chip->ops->disable(itr_chip, hdl->it);
91 
92 	SLIST_REMOVE(&handlers, hdl, itr_handler, link);
93 	free(hdl);
94 }
95 
96 void itr_add(struct itr_handler *h)
97 {
98 	struct itr_handler __maybe_unused *hdl = NULL;
99 
100 	SLIST_FOREACH(hdl, &handlers, link)
101 		if (hdl->it == h->it)
102 			assert((hdl->flags & ITRF_SHARED) &&
103 			       (h->flags & ITRF_SHARED));
104 
105 	itr_chip->ops->add(itr_chip, h->it, h->flags);
106 	SLIST_INSERT_HEAD(&handlers, h, link);
107 }
108 
109 void itr_enable(size_t it)
110 {
111 	itr_chip->ops->enable(itr_chip, it);
112 }
113 
114 void itr_disable(size_t it)
115 {
116 	itr_chip->ops->disable(itr_chip, it);
117 }
118 
119 void itr_raise_pi(size_t it)
120 {
121 	itr_chip->ops->raise_pi(itr_chip, it);
122 }
123 
124 void itr_raise_sgi(size_t it, uint8_t cpu_mask)
125 {
126 	itr_chip->ops->raise_sgi(itr_chip, it, cpu_mask);
127 }
128 
129 void itr_set_affinity(size_t it, uint8_t cpu_mask)
130 {
131 	itr_chip->ops->set_affinity(itr_chip, it, cpu_mask);
132 }
133 
134 /* This function is supposed to be overridden in platform specific code */
135 void __weak __noreturn itr_core_handler(void)
136 {
137 	panic("Secure interrupt handler not defined");
138 }
139