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