Branch data Line data Source code
1 : : /* 2 : : * Copyright (c) 2017 Oticon A/S 3 : : * 4 : : * SPDX-License-Identifier: Apache-2.0 5 : : * 6 : : * HW IRQ controller model 7 : : */ 8 : : 9 : : #include <stdint.h> 10 : : #include <stdbool.h> 11 : : #include "hw_models_top.h" 12 : : #include "irq_ctrl.h" 13 : : #include "irq_handler.h" 14 : : #include <zephyr/arch/posix/arch.h> /* for find_lsb_set() */ 15 : : #include "board_soc.h" 16 : : #include "posix_soc.h" 17 : : #include <zephyr/types.h> 18 : : 19 : : uint64_t irq_ctrl_timer = NEVER; 20 : : 21 : : 22 : : static uint64_t irq_status; /* pending interrupts */ 23 : : static uint64_t irq_premask; /* interrupts before the mask */ 24 : : 25 : : /* 26 : : * Mask of which interrupts will actually cause the cpu to vector into its 27 : : * irq handler 28 : : * If an interrupt is masked in this way, it will be pending in the premask in 29 : : * case it is enabled later before clearing it. 30 : : * If the irq_mask enables and interrupt pending in irq_premask, it will cause 31 : : * the controller to raise the interrupt immediately 32 : : */ 33 : : static uint64_t irq_mask; 34 : : 35 : : /* 36 : : * Interrupts lock/disable. When set, interrupts are registered 37 : : * (in the irq_status) but do not awake the cpu. if when unlocked, 38 : : * irq_status != 0 an interrupt will be raised immediately 39 : : */ 40 : : static bool irqs_locked; 41 : : static bool lock_ignore; /* For the hard fake IRQ, temporarily ignore lock */ 42 : : 43 : : static uint8_t irq_prio[N_IRQS]; /* Priority of each interrupt */ 44 : : /* note that prio = 0 == highest, prio=255 == lowest */ 45 : : 46 : : static int currently_running_prio = 256; /* 255 is the lowest prio interrupt */ 47 : : 48 : 1 : void hw_irq_ctrl_init(void) 49 : : { 50 : 1 : irq_mask = 0U; /* Let's assume all interrupts are disable at boot */ 51 : 1 : irq_premask = 0U; 52 : 1 : irqs_locked = false; 53 : 1 : lock_ignore = false; 54 : : 55 [ + + ]: 33 : for (int i = 0 ; i < N_IRQS; i++) { 56 : 32 : irq_prio[i] = 255U; 57 : : } 58 : 1 : } 59 : : 60 : 1 : void hw_irq_ctrl_cleanup(void) 61 : : { 62 : : /* Nothing to be done */ 63 : 1 : } 64 : : 65 : 0 : void hw_irq_ctrl_set_cur_prio(int new) 66 : : { 67 : 0 : currently_running_prio = new; 68 : 0 : } 69 : : 70 : 0 : int hw_irq_ctrl_get_cur_prio(void) 71 : : { 72 : 0 : return currently_running_prio; 73 : : } 74 : : 75 : 1 : void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio) 76 : : { 77 : 1 : irq_prio[irq] = prio; 78 : 1 : } 79 : : 80 : 0 : uint8_t hw_irq_ctrl_get_prio(unsigned int irq) 81 : : { 82 : 0 : return irq_prio[irq]; 83 : : } 84 : : 85 : : /** 86 : : * Get the currently pending highest priority interrupt which has a priority 87 : : * higher than a possibly currently running interrupt 88 : : * 89 : : * If none, return -1 90 : : */ 91 : 0 : int hw_irq_ctrl_get_highest_prio_irq(void) 92 : : { 93 [ # # ]: 0 : if (irqs_locked) { 94 : : return -1; 95 : : } 96 : : 97 : 0 : uint64_t hw_irq_status = hw_irq_ctrl_get_irq_status(); 98 : 0 : int winner = -1; 99 : 0 : int winner_prio = 256; 100 : : 101 [ # # ]: 0 : while (hw_irq_status != 0U) { 102 : 0 : int irq_nbr = find_lsb_set(hw_irq_status) - 1; 103 : : 104 : 0 : hw_irq_status &= ~((uint64_t) 1 << irq_nbr); 105 [ # # ]: 0 : if ((winner_prio > (int)irq_prio[irq_nbr]) 106 [ # # ]: 0 : && (currently_running_prio > (int)irq_prio[irq_nbr])) { 107 : 0 : winner = irq_nbr; 108 : 0 : winner_prio = irq_prio[irq_nbr]; 109 : : } 110 : : } 111 : : return winner; 112 : : } 113 : : 114 : : 115 : 0 : uint32_t hw_irq_ctrl_get_current_lock(void) 116 : : { 117 : 0 : return irqs_locked; 118 : : } 119 : : 120 : 877 : uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock) 121 : : { 122 : 877 : uint32_t previous_lock = irqs_locked; 123 : : 124 : 877 : irqs_locked = new_lock; 125 : : 126 [ + + ]: 877 : if ((previous_lock == true) && (new_lock == false)) { 127 [ - + ]: 200 : if (irq_status != 0U) { 128 : 0 : posix_irq_handler_im_from_sw(); 129 : : } 130 : : } 131 : 877 : return previous_lock; 132 : : } 133 : : 134 : 0 : uint64_t hw_irq_ctrl_get_irq_status(void) 135 : : { 136 : 0 : return irq_status; 137 : : } 138 : : 139 : 0 : void hw_irq_ctrl_clear_all_enabled_irqs(void) 140 : : { 141 : 0 : irq_status = 0U; 142 : 0 : irq_premask &= ~irq_mask; 143 : 0 : } 144 : : 145 : 0 : void hw_irq_ctrl_clear_all_irqs(void) 146 : : { 147 : 0 : irq_status = 0U; 148 : 0 : irq_premask = 0U; 149 : 0 : } 150 : : 151 : 0 : void hw_irq_ctrl_disable_irq(unsigned int irq) 152 : : { 153 : 0 : irq_mask &= ~((uint64_t)1<<irq); 154 : 0 : } 155 : : 156 : 0 : int hw_irq_ctrl_is_irq_enabled(unsigned int irq) 157 : : { 158 : 0 : return (irq_mask & ((uint64_t)1 << irq))?1:0; 159 : : } 160 : : 161 : 0 : uint64_t hw_irq_ctrl_get_irq_mask(void) 162 : : { 163 : 0 : return irq_mask; 164 : : } 165 : : 166 : 0 : void hw_irq_ctrl_clear_irq(unsigned int irq) 167 : : { 168 : 0 : irq_status &= ~((uint64_t)1<<irq); 169 : 0 : irq_premask &= ~((uint64_t)1<<irq); 170 : 0 : } 171 : : 172 : : 173 : : /** 174 : : * Enable an interrupt 175 : : * 176 : : * This function may only be called from SW threads 177 : : * 178 : : * If the enabled interrupt is pending, it will immediately vector to its 179 : : * interrupt handler and continue (maybe with some swap() before) 180 : : */ 181 : 1 : void hw_irq_ctrl_enable_irq(unsigned int irq) 182 : : { 183 : 1 : irq_mask |= ((uint64_t)1<<irq); 184 [ - + ]: 1 : if (irq_premask & ((uint64_t)1<<irq)) { /* if IRQ is pending */ 185 : 0 : hw_irq_ctrl_raise_im_from_sw(irq); 186 : : } 187 : 1 : } 188 : : 189 : : 190 : 0 : static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq) 191 : : { 192 [ # # ]: 0 : if (irq < N_IRQS) { 193 : 0 : irq_premask |= ((uint64_t)1<<irq); 194 : : 195 [ # # ]: 0 : if (irq_mask & (1 << irq)) { 196 : 0 : irq_status |= ((uint64_t)1<<irq); 197 : : } 198 [ # # ]: 0 : } else if (irq == PHONY_HARD_IRQ) { 199 : 0 : lock_ignore = true; 200 : : } 201 : 0 : } 202 : : 203 : : /** 204 : : * Set/Raise an interrupt 205 : : * 206 : : * This function is meant to be used by either the SW manual IRQ raising 207 : : * or by HW which wants the IRQ to be raised in one delta cycle from now 208 : : */ 209 : 0 : void hw_irq_ctrl_set_irq(unsigned int irq) 210 : : { 211 : 0 : hw_irq_ctrl_irq_raise_prefix(irq); 212 [ # # # # ]: 0 : if ((irqs_locked == false) || (lock_ignore)) { 213 : : /* 214 : : * Awake CPU in 1 delta 215 : : * Note that we awake the CPU even if the IRQ is disabled 216 : : * => we assume the CPU is always idling in a WFE() like 217 : : * instruction and the CPU is allowed to awake just with the irq 218 : : * being marked as pending 219 : : */ 220 : 0 : irq_ctrl_timer = hwm_get_time(); 221 : 0 : hwm_find_next_timer(); 222 : : } 223 : 0 : } 224 : : 225 : : 226 : : 227 : 0 : static void irq_raising_from_hw_now(void) 228 : : { 229 : : /* 230 : : * We always awake the CPU even if the IRQ was masked, 231 : : * but not if irqs are locked unless this is due to a 232 : : * PHONY_HARD_IRQ 233 : : */ 234 [ # # # # ]: 0 : if ((irqs_locked == false) || (lock_ignore)) { 235 : 0 : lock_ignore = false; 236 : 0 : posix_interrupt_raised(); 237 : : } 238 : 0 : } 239 : : 240 : : /** 241 : : * Set/Raise an interrupt immediately. 242 : : * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in 243 : : * 1 delta cycle 244 : : * 245 : : * Call only from HW threads 246 : : */ 247 : 0 : void hw_irq_ctrl_raise_im(unsigned int irq) 248 : : { 249 : 0 : hw_irq_ctrl_irq_raise_prefix(irq); 250 : 0 : irq_raising_from_hw_now(); 251 : 0 : } 252 : : 253 : : /** 254 : : * Like hw_irq_ctrl_raise_im() but for SW threads 255 : : * 256 : : * Call only from SW threads 257 : : */ 258 : 0 : void hw_irq_ctrl_raise_im_from_sw(unsigned int irq) 259 : : { 260 : 0 : hw_irq_ctrl_irq_raise_prefix(irq); 261 : : 262 [ # # ]: 0 : if (irqs_locked == false) { 263 : 0 : posix_irq_handler_im_from_sw(); 264 : : } 265 : 0 : } 266 : : 267 : 0 : void hw_irq_ctrl_timer_triggered(void) 268 : : { 269 : 0 : irq_ctrl_timer = NEVER; 270 : 0 : irq_raising_from_hw_now(); 271 : 0 : }