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
vector_to_irq(int irq_nbr,int * may_swap)32 static inline void vector_to_irq(int irq_nbr, int *may_swap)
33 {
34 sys_trace_isr_enter();
35
36 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 if (irq_vector_table[irq_nbr].flags & ISR_FLAG_DIRECT) {
44 *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 ((normal_irq_f_ptr)irq_vector_table[irq_nbr].func)
51 (irq_vector_table[irq_nbr].param);
52 *may_swap = 1;
53 }
54 }
55
56 sys_trace_isr_exit();
57 }
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 */
posix_irq_handler(void)67 void posix_irq_handler(void)
68 {
69 uint64_t irq_lock;
70 int irq_nbr;
71 static int may_swap;
72
73 irq_lock = hw_irq_ctrl_get_current_lock();
74
75 if (irq_lock) {
76 /* "spurious" wakes can happen with interrupts locked */
77 return;
78 }
79
80 irq_nbr = hw_irq_ctrl_get_highest_prio_irq();
81
82 if (irq_nbr == -1) {
83 /* This is a phony interrupt during a busy wait, no need for more */
84 return;
85 }
86
87 if (_kernel.cpus[0].nested == 0) {
88 may_swap = 0;
89 }
90
91 _kernel.cpus[0].nested++;
92
93 do {
94 int last_current_running_prio = hw_irq_ctrl_get_cur_prio();
95 int last_running_irq = currently_running_irq;
96
97 hw_irq_ctrl_set_cur_prio(hw_irq_ctrl_get_prio(irq_nbr));
98 hw_irq_ctrl_clear_irq(irq_nbr);
99
100 currently_running_irq = irq_nbr;
101 vector_to_irq(irq_nbr, &may_swap);
102 currently_running_irq = last_running_irq;
103
104 hw_irq_ctrl_set_cur_prio(last_current_running_prio);
105 } while ((irq_nbr = hw_irq_ctrl_get_highest_prio_irq()) != -1);
106
107 _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 if (may_swap
115 && (hw_irq_ctrl_get_cur_prio() == 256)
116 && (_kernel.ready_q.cache) && (_kernel.ready_q.cache != arch_current_thread())) {
117
118 (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 */
nsif_cpu0_irq_raised_from_sw(void)127 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 if (hw_irq_ctrl_get_highest_prio_irq() != -1) {
135 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 posix_irq_handler();
143 }
144 }
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 */
posix_irq_lock(void)177 unsigned int posix_irq_lock(void)
178 {
179 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 */
posix_irq_unlock(unsigned int key)191 void posix_irq_unlock(unsigned int key)
192 {
193 hw_irq_ctrl_change_lock(key);
194 }
195
posix_irq_full_unlock(void)196 void posix_irq_full_unlock(void)
197 {
198 hw_irq_ctrl_change_lock(false);
199 }
200
posix_irq_enable(unsigned int irq)201 void posix_irq_enable(unsigned int irq)
202 {
203 hw_irq_ctrl_enable_irq(irq);
204 }
205
posix_irq_disable(unsigned int irq)206 void posix_irq_disable(unsigned int irq)
207 {
208 hw_irq_ctrl_disable_irq(irq);
209 }
210
posix_irq_is_enabled(unsigned int irq)211 int posix_irq_is_enabled(unsigned int irq)
212 {
213 return hw_irq_ctrl_is_irq_enabled(irq);
214 }
215
posix_get_current_irq(void)216 int posix_get_current_irq(void)
217 {
218 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 */
posix_isr_declare(unsigned int irq_p,int flags,void isr_p (const void *),const void * isr_param_p)236 void posix_isr_declare(unsigned int irq_p, int flags, void isr_p(const void *),
237 const void *isr_param_p)
238 {
239 if (irq_p >= N_IRQS) {
240 posix_print_error_and_exit("Attempted to configure not existent interrupt %u\n",
241 irq_p);
242 return;
243 }
244 irq_vector_table[irq_p].irq = irq_p;
245 irq_vector_table[irq_p].func = isr_p;
246 irq_vector_table[irq_p].param = isr_param_p;
247 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 */
posix_irq_priority_set(unsigned int irq,unsigned int prio,uint32_t flags)257 void posix_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
258 {
259 hw_irq_ctrl_prio_set(irq, prio);
260 }
261
262 /**
263 * Similar to ARM's NVIC_SetPendingIRQ
264 * set a pending IRQ from SW
265 *
266 * Note that this will interrupt immediately if the interrupt is not masked and
267 * IRQs are not locked, and this interrupt has higher priority than a possibly
268 * currently running interrupt
269 */
posix_sw_set_pending_IRQ(unsigned int IRQn)270 void posix_sw_set_pending_IRQ(unsigned int IRQn)
271 {
272 hw_irq_ctrl_raise_im_from_sw(IRQn);
273 }
274
275 /**
276 * Similar to ARM's NVIC_ClearPendingIRQ
277 * clear a pending irq from SW
278 */
posix_sw_clear_pending_IRQ(unsigned int IRQn)279 void posix_sw_clear_pending_IRQ(unsigned int IRQn)
280 {
281 hw_irq_ctrl_clear_irq(IRQn);
282 }
283
284 #ifdef CONFIG_IRQ_OFFLOAD
285 /**
286 * Storage for functions offloaded to IRQ
287 */
288 static void (*off_routine)(const void *);
289 static const void *off_parameter;
290
291 /**
292 * IRQ handler for the SW interrupt assigned to irq_offload()
293 */
offload_sw_irq_handler(const void * a)294 static void offload_sw_irq_handler(const void *a)
295 {
296 ARG_UNUSED(a);
297 off_routine(off_parameter);
298 }
299
300 /**
301 * @brief Run a function in interrupt context
302 *
303 * Raise the SW IRQ assigned to handled this
304 */
posix_irq_offload(void (* routine)(const void *),const void * parameter)305 void posix_irq_offload(void (*routine)(const void *), const void *parameter)
306 {
307 off_routine = routine;
308 off_parameter = parameter;
309 posix_isr_declare(OFFLOAD_SW_IRQ, 0, offload_sw_irq_handler, NULL);
310 posix_irq_enable(OFFLOAD_SW_IRQ);
311 posix_sw_set_pending_IRQ(OFFLOAD_SW_IRQ);
312 posix_irq_disable(OFFLOAD_SW_IRQ);
313 }
314 #endif /* CONFIG_IRQ_OFFLOAD */
315