Branch data Line data Source code
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 : : 31 : 0 : static inline void vector_to_irq(int irq_nbr, int *may_swap) 32 : : { 33 : 0 : sys_trace_isr_enter(); 34 : : 35 : 0 : 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 [ # # ]: 0 : if (irq_vector_table[irq_nbr].flags & ISR_FLAG_DIRECT) { 43 : 0 : *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 : 0 : ((normal_irq_f_ptr)irq_vector_table[irq_nbr].func) 50 : : (irq_vector_table[irq_nbr].param); 51 : 0 : *may_swap = 1; 52 : : } 53 : : } 54 : : 55 : 0 : sys_trace_isr_exit(); 56 : 0 : } 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 : : */ 66 : 0 : void posix_irq_handler(void) 67 : : { 68 : 0 : uint64_t irq_lock; 69 : 0 : int irq_nbr; 70 : 0 : static int may_swap; 71 : : 72 : 0 : irq_lock = hw_irq_ctrl_get_current_lock(); 73 : : 74 [ # # ]: 0 : if (irq_lock) { 75 : : /* "spurious" wakes can happen with interrupts locked */ 76 : : return; 77 : : } 78 : : 79 [ # # ]: 0 : if (_kernel.cpus[0].nested == 0) { 80 : 0 : may_swap = 0; 81 : : } 82 : : 83 : 0 : _kernel.cpus[0].nested++; 84 : : 85 [ # # ]: 0 : while ((irq_nbr = hw_irq_ctrl_get_highest_prio_irq()) != -1) { 86 : 0 : int last_current_running_prio = hw_irq_ctrl_get_cur_prio(); 87 : 0 : int last_running_irq = currently_running_irq; 88 : : 89 : 0 : hw_irq_ctrl_set_cur_prio(hw_irq_ctrl_get_prio(irq_nbr)); 90 : 0 : hw_irq_ctrl_clear_irq(irq_nbr); 91 : : 92 : 0 : currently_running_irq = irq_nbr; 93 : 0 : vector_to_irq(irq_nbr, &may_swap); 94 : 0 : currently_running_irq = last_running_irq; 95 : : 96 : 0 : hw_irq_ctrl_set_cur_prio(last_current_running_prio); 97 : : } 98 : : 99 : 0 : _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 [ # # ]: 0 : if (may_swap 107 [ # # ]: 0 : && (hw_irq_ctrl_get_cur_prio() == 256) 108 [ # # # # ]: 0 : && (_kernel.ready_q.cache) && (_kernel.ready_q.cache != _current)) { 109 : : 110 : 0 : (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 : : */ 119 : 0 : 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 [ # # ]: 0 : if (hw_irq_ctrl_get_highest_prio_irq() != -1) { 127 : 0 : 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 : 0 : posix_irq_handler(); 135 : : } 136 : 0 : } 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 : : */ 169 : 463 : unsigned int posix_irq_lock(void) 170 : : { 171 : 463 : 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 : : */ 183 : 367 : void posix_irq_unlock(unsigned int key) 184 : : { 185 : 367 : hw_irq_ctrl_change_lock(key); 186 : 367 : } 187 : : 188 : 47 : void posix_irq_full_unlock(void) 189 : : { 190 : 47 : hw_irq_ctrl_change_lock(false); 191 : 47 : } 192 : : 193 : 1 : void posix_irq_enable(unsigned int irq) 194 : : { 195 : 1 : hw_irq_ctrl_enable_irq(irq); 196 : 1 : } 197 : : 198 : 0 : void posix_irq_disable(unsigned int irq) 199 : : { 200 : 0 : hw_irq_ctrl_disable_irq(irq); 201 : 0 : } 202 : : 203 : 0 : int posix_irq_is_enabled(unsigned int irq) 204 : : { 205 : 0 : return hw_irq_ctrl_is_irq_enabled(irq); 206 : : } 207 : : 208 : 0 : int posix_get_current_irq(void) 209 : : { 210 : 0 : 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 : : */ 228 : 1 : void posix_isr_declare(unsigned int irq_p, int flags, void isr_p(const void *), 229 : : const void *isr_param_p) 230 : : { 231 [ - + ]: 1 : if (irq_p >= N_IRQS) { 232 : 0 : posix_print_error_and_exit("Attempted to configure not existent interrupt %u\n", 233 : : irq_p); 234 : 0 : return; 235 : : } 236 : 1 : irq_vector_table[irq_p].irq = irq_p; 237 : 1 : irq_vector_table[irq_p].func = isr_p; 238 : 1 : irq_vector_table[irq_p].param = isr_param_p; 239 : 1 : 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 : : */ 249 : 1 : void posix_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags) 250 : : { 251 : 1 : hw_irq_ctrl_prio_set(irq, prio); 252 : 1 : } 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 : : */ 262 : 0 : void posix_sw_set_pending_IRQ(unsigned int IRQn) 263 : : { 264 : 0 : hw_irq_ctrl_raise_im_from_sw(IRQn); 265 : 0 : } 266 : : 267 : : /** 268 : : * Similar to ARM's NVIC_ClearPendingIRQ 269 : : * clear a pending irq from SW 270 : : */ 271 : 0 : void posix_sw_clear_pending_IRQ(unsigned int IRQn) 272 : : { 273 : 0 : hw_irq_ctrl_clear_irq(IRQn); 274 : 0 : } 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 : : */ 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 : : */ 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 */