Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014 Wind River Systems, Inc.
3 : : * Copyright (c) 2017 Oticon A/S
4 : : * Copyright (c) 2023 Nordic Semiconductor ASA
5 : : *
6 : : * SPDX-License-Identifier: Apache-2.0
7 : : *
8 : : * SW side of the IRQ handling
9 : : */
10 : :
11 : : #include <stdint.h>
12 : : #include <zephyr/irq_offload.h>
13 : : #include <zephyr/kernel_structs.h>
14 : : #include "kernel_internal.h"
15 : : #include "kswap.h"
16 : : #include "irq_ctrl.h"
17 : : #include "posix_core.h"
18 : : #include <zephyr/sw_isr_table.h>
19 : : #include "soc.h"
20 : : #include <zephyr/tracing/tracing.h>
21 : : #include "irq_handler.h"
22 : : #include "board_soc.h"
23 : : #include "nsi_cpu_if.h"
24 : :
25 : : typedef void (*normal_irq_f_ptr)(const void *);
26 : : typedef int (*direct_irq_f_ptr)(void);
27 : :
28 : : static struct _isr_list irq_vector_table[N_IRQS] = { { 0 } };
29 : :
30 : : static int currently_running_irq = -1;
31 : :
32 : 0 : static inline void vector_to_irq(int irq_nbr, int *may_swap)
33 : : {
34 : 0 : sys_trace_isr_enter();
35 : :
36 : 0 : if (irq_vector_table[irq_nbr].func == NULL) { /* LCOV_EXCL_BR_LINE */
37 : : /* LCOV_EXCL_START */
38 : : posix_print_error_and_exit("Received irq %i without a "
39 : : "registered handler\n",
40 : : irq_nbr);
41 : : /* LCOV_EXCL_STOP */
42 : : } else {
43 [ # # ]: 0 : if (irq_vector_table[irq_nbr].flags & ISR_FLAG_DIRECT) {
44 : 0 : *may_swap |= ((direct_irq_f_ptr)
45 : : irq_vector_table[irq_nbr].func)();
46 : : } else {
47 : : #ifdef CONFIG_PM
48 : : posix_irq_check_idle_exit();
49 : : #endif
50 : 0 : ((normal_irq_f_ptr)irq_vector_table[irq_nbr].func)
51 : : (irq_vector_table[irq_nbr].param);
52 : 0 : *may_swap = 1;
53 : : }
54 : : }
55 : :
56 : 0 : sys_trace_isr_exit();
57 : 0 : }
58 : :
59 : : /**
60 : : * When an interrupt is raised, this function is called to handle it and, if
61 : : * needed, swap to a re-enabled thread
62 : : *
63 : : * Note that even that this function is executing in a Zephyr thread, it is
64 : : * effectively the model of the interrupt controller passing context to the IRQ
65 : : * handler and therefore its priority handling
66 : : */
67 : 0 : void posix_irq_handler(void)
68 : : {
69 : 0 : uint64_t irq_lock;
70 : 0 : int irq_nbr;
71 : 0 : static int may_swap;
72 : :
73 : 0 : irq_lock = hw_irq_ctrl_get_current_lock();
74 : :
75 [ # # ]: 0 : if (irq_lock) {
76 : : /* "spurious" wakes can happen with interrupts locked */
77 : : return;
78 : : }
79 : :
80 : 0 : irq_nbr = hw_irq_ctrl_get_highest_prio_irq();
81 : :
82 [ # # ]: 0 : if (irq_nbr == -1) {
83 : : /* This is a phony interrupt during a busy wait, no need for more */
84 : : return;
85 : : }
86 : :
87 [ # # ]: 0 : if (_kernel.cpus[0].nested == 0) {
88 : 0 : may_swap = 0;
89 : : }
90 : :
91 : 0 : _kernel.cpus[0].nested++;
92 : :
93 : 0 : do {
94 : 0 : int last_current_running_prio = hw_irq_ctrl_get_cur_prio();
95 : 0 : int last_running_irq = currently_running_irq;
96 : :
97 : 0 : hw_irq_ctrl_set_cur_prio(hw_irq_ctrl_get_prio(irq_nbr));
98 : 0 : hw_irq_ctrl_clear_irq(irq_nbr);
99 : :
100 : 0 : currently_running_irq = irq_nbr;
101 : 0 : vector_to_irq(irq_nbr, &may_swap);
102 : 0 : currently_running_irq = last_running_irq;
103 : :
104 : 0 : hw_irq_ctrl_set_cur_prio(last_current_running_prio);
105 [ # # ]: 0 : } while ((irq_nbr = hw_irq_ctrl_get_highest_prio_irq()) != -1);
106 : :
107 : 0 : _kernel.cpus[0].nested--;
108 : :
109 : : /* Call swap if all the following is true:
110 : : * 1) may_swap was enabled
111 : : * 2) We are not nesting irq_handler calls (interrupts)
112 : : * 3) Next thread to run in the ready queue is not this thread
113 : : */
114 [ # # ]: 0 : if (may_swap
115 [ # # ]: 0 : && (hw_irq_ctrl_get_cur_prio() == 256)
116 [ # # # # ]: 0 : && (_kernel.ready_q.cache) && (_kernel.ready_q.cache != _current)) {
117 : :
118 : 0 : (void)z_swap_irqlock(irq_lock);
119 : : }
120 : : }
121 : :
122 : : /**
123 : : * Thru this function the IRQ controller can raise an immediate interrupt which
124 : : * will interrupt the SW itself
125 : : * (this function should only be called from the HW model code, from SW threads)
126 : : */
127 : 0 : void nsif_cpu0_irq_raised_from_sw(void)
128 : : {
129 : : /*
130 : : * if a higher priority interrupt than the possibly currently running is
131 : : * pending we go immediately into irq_handler() to vector into its
132 : : * handler
133 : : */
134 [ # # ]: 0 : if (hw_irq_ctrl_get_highest_prio_irq() != -1) {
135 : 0 : if (!posix_is_cpu_running()) { /* LCOV_EXCL_BR_LINE */
136 : : /* LCOV_EXCL_START */
137 : : posix_print_error_and_exit("programming error: %s "
138 : : "called from a HW model thread\n",
139 : : __func__);
140 : : /* LCOV_EXCL_STOP */
141 : : }
142 : 0 : posix_irq_handler();
143 : : }
144 : 0 : }
145 : :
146 : : /**
147 : : * @brief Disable all interrupts on the CPU
148 : : *
149 : : * This routine disables interrupts. It can be called from either interrupt,
150 : : * task or fiber level. This routine returns an architecture-dependent
151 : : * lock-out key representing the "interrupt disable state" prior to the call;
152 : : * this key can be passed to irq_unlock() to re-enable interrupts.
153 : : *
154 : : * The lock-out key should only be used as the argument to the irq_unlock()
155 : : * API. It should never be used to manually re-enable interrupts or to inspect
156 : : * or manipulate the contents of the source register.
157 : : *
158 : : * This function can be called recursively: it will return a key to return the
159 : : * state of interrupt locking to the previous level.
160 : : *
161 : : * WARNINGS
162 : : * Invoking a kernel routine with interrupts locked may result in
163 : : * interrupts being re-enabled for an unspecified period of time. If the
164 : : * called routine blocks, interrupts will be re-enabled while another
165 : : * thread executes, or while the system is idle.
166 : : *
167 : : * The "interrupt disable state" is an attribute of a thread. Thus, if a
168 : : * fiber or task disables interrupts and subsequently invokes a kernel
169 : : * routine that causes the calling thread to block, the interrupt
170 : : * disable state will be restored when the thread is later rescheduled
171 : : * for execution.
172 : : *
173 : : * @return An architecture-dependent lock-out key representing the
174 : : * "interrupt disable state" prior to the call.
175 : : *
176 : : */
177 : 606 : unsigned int posix_irq_lock(void)
178 : : {
179 : 606 : return hw_irq_ctrl_change_lock(true);
180 : : }
181 : :
182 : : /**
183 : : * @brief Enable all interrupts on the CPU
184 : : *
185 : : * This routine re-enables interrupts on the CPU. The @a key parameter is a
186 : : * board-dependent lock-out key that is returned by a previous invocation of
187 : : * board_irq_lock().
188 : : *
189 : : * This routine can be called from either interrupt, task or fiber level.
190 : : */
191 : 510 : void posix_irq_unlock(unsigned int key)
192 : : {
193 : 510 : hw_irq_ctrl_change_lock(key);
194 : 510 : }
195 : :
196 : 47 : void posix_irq_full_unlock(void)
197 : : {
198 : 47 : hw_irq_ctrl_change_lock(false);
199 : 47 : }
200 : :
201 : 1 : void posix_irq_enable(unsigned int irq)
202 : : {
203 : 1 : hw_irq_ctrl_enable_irq(irq);
204 : 1 : }
205 : :
206 : 0 : void posix_irq_disable(unsigned int irq)
207 : : {
208 : 0 : hw_irq_ctrl_disable_irq(irq);
209 : 0 : }
210 : :
211 : 0 : int posix_irq_is_enabled(unsigned int irq)
212 : : {
213 : 0 : return hw_irq_ctrl_is_irq_enabled(irq);
214 : : }
215 : :
216 : 0 : int posix_get_current_irq(void)
217 : : {
218 : 0 : return currently_running_irq;
219 : : }
220 : :
221 : : /**
222 : : * Configure a static interrupt.
223 : : *
224 : : * posix_isr_declare will populate the interrupt table table with the
225 : : * interrupt's parameters, the vector table and the software ISR table.
226 : : *
227 : : * We additionally set the priority in the interrupt controller at
228 : : * runtime.
229 : : *
230 : : * @param irq_p IRQ line number
231 : : * @param flags [plug it directly (1), or as a SW managed interrupt (0)]
232 : : * @param isr_p Interrupt service routine
233 : : * @param isr_param_p ISR parameter
234 : : * @param flags_p IRQ options
235 : : */
236 : 1 : void posix_isr_declare(unsigned int irq_p, int flags, void isr_p(const void *),
237 : : const void *isr_param_p)
238 : : {
239 [ - + ]: 1 : if (irq_p >= N_IRQS) {
240 : 0 : posix_print_error_and_exit("Attempted to configure not existent interrupt %u\n",
241 : : irq_p);
242 : 0 : return;
243 : : }
244 : 1 : irq_vector_table[irq_p].irq = irq_p;
245 : 1 : irq_vector_table[irq_p].func = isr_p;
246 : 1 : irq_vector_table[irq_p].param = isr_param_p;
247 : 1 : irq_vector_table[irq_p].flags = flags;
248 : : }
249 : :
250 : : /**
251 : : * @internal
252 : : *
253 : : * @brief Set an interrupt's priority
254 : : *
255 : : * Lower values take priority over higher values.
256 : : */
257 : 1 : void posix_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
258 : : {
259 [ - + ]: 1 : if (irq >= N_IRQS) {
260 : 0 : posix_print_error_and_exit("Attempted to configure not existent interrupt %u\n",
261 : : irq);
262 : 0 : return;
263 : : }
264 : 1 : hw_irq_ctrl_prio_set(irq, prio);
265 : : }
266 : :
267 : : /**
268 : : * Similar to ARM's NVIC_SetPendingIRQ
269 : : * set a pending IRQ from SW
270 : : *
271 : : * Note that this will interrupt immediately if the interrupt is not masked and
272 : : * IRQs are not locked, and this interrupt has higher priority than a possibly
273 : : * currently running interrupt
274 : : */
275 : 0 : void posix_sw_set_pending_IRQ(unsigned int IRQn)
276 : : {
277 : 0 : hw_irq_ctrl_raise_im_from_sw(IRQn);
278 : 0 : }
279 : :
280 : : /**
281 : : * Similar to ARM's NVIC_ClearPendingIRQ
282 : : * clear a pending irq from SW
283 : : */
284 : 0 : void posix_sw_clear_pending_IRQ(unsigned int IRQn)
285 : : {
286 : 0 : hw_irq_ctrl_clear_irq(IRQn);
287 : 0 : }
288 : :
289 : : #ifdef CONFIG_IRQ_OFFLOAD
290 : : /**
291 : : * Storage for functions offloaded to IRQ
292 : : */
293 : : static void (*off_routine)(const void *);
294 : : static const void *off_parameter;
295 : :
296 : : /**
297 : : * IRQ handler for the SW interrupt assigned to irq_offload()
298 : : */
299 : : static void offload_sw_irq_handler(const void *a)
300 : : {
301 : : ARG_UNUSED(a);
302 : : off_routine(off_parameter);
303 : : }
304 : :
305 : : /**
306 : : * @brief Run a function in interrupt context
307 : : *
308 : : * Raise the SW IRQ assigned to handled this
309 : : */
310 : : void posix_irq_offload(void (*routine)(const void *), const void *parameter)
311 : : {
312 : : off_routine = routine;
313 : : off_parameter = parameter;
314 : : posix_isr_declare(OFFLOAD_SW_IRQ, 0, offload_sw_irq_handler, NULL);
315 : : posix_irq_enable(OFFLOAD_SW_IRQ);
316 : : posix_sw_set_pending_IRQ(OFFLOAD_SW_IRQ);
317 : : posix_irq_disable(OFFLOAD_SW_IRQ);
318 : : }
319 : : #endif /* CONFIG_IRQ_OFFLOAD */
|