1 /*
2 * Copyright (c) 2017 Oticon A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 /*
7 * HW IRQ controller model
8 * Note: In principle this should have been a model of the ARM NVIC
9 * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100166_0001_00_en/ric1417175922867.html
10 * But so far it is just a quick thing that provides a reasonable emulation of its
11 * used functionality in Zephyr
12 */
13
14 #include <stdint.h>
15 #include "bs_types.h"
16 #include "irq_ctrl.h"
17 #include "time_machine_if.h"
18 #include "NRF_HW_model_top.h"
19
20 bs_time_t Timer_irq_ctrl = TIME_NEVER;
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[NRF_HW_NBR_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 /*
49 * These functions are provided by the board
50 */
51 extern void posix_interrupt_raised(void);
52 extern void posix_irq_handler_im_from_sw(void);
53
54
hw_irq_ctrl_init(void)55 void hw_irq_ctrl_init(void)
56 {
57 irq_mask = 0; /*Let's assume all interrupts are disable at boot*/
58 irq_premask = 0;
59 irqs_locked = false;
60 lock_ignore = false;
61
62 for (int i = 0 ; i < NRF_HW_NBR_IRQs; i++) {
63 irq_prio[i] = 255;
64 }
65 }
66
hw_irq_ctrl_cleanup(void)67 void hw_irq_ctrl_cleanup(void)
68 {
69 /*Nothing to be done*/
70 }
71
hw_irq_ctrl_set_cur_prio(int new)72 void hw_irq_ctrl_set_cur_prio(int new)
73 {
74 currently_running_prio = new;
75 }
76
hw_irq_ctrl_get_cur_prio(void)77 int hw_irq_ctrl_get_cur_prio(void)
78 {
79 return currently_running_prio;
80 }
81
hw_irq_ctrl_prio_set(unsigned int irq,unsigned int prio)82 void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio)
83 {
84 irq_prio[irq] = prio;
85 }
86
hw_irq_ctrl_get_prio(unsigned int irq)87 uint8_t hw_irq_ctrl_get_prio(unsigned int irq)
88 {
89 return irq_prio[irq];
90 }
91
92 /**
93 * Get the currently pending highest priority interrupt which has a priority
94 * higher than a possibly currently running interrupt
95 *
96 * If none, return -1
97 */
hw_irq_ctrl_get_highest_prio_irq(void)98 int hw_irq_ctrl_get_highest_prio_irq(void)
99 {
100 if (irqs_locked) {
101 return -1;
102 }
103
104 uint64_t irq_status = hw_irq_ctrl_get_irq_status();
105 int winner = -1;
106 int winner_prio = 256;
107
108 while (irq_status != 0) {
109 int irq_nbr = __builtin_ffs(irq_status) - 1;
110
111 irq_status &= ~((uint64_t) 1 << irq_nbr);
112 if ((winner_prio > (int)irq_prio[irq_nbr])
113 && (currently_running_prio > (int)irq_prio[irq_nbr])) {
114 winner = irq_nbr;
115 winner_prio = irq_prio[irq_nbr];
116 }
117 }
118 return winner;
119 }
120
hw_irq_ctrl_get_current_lock(void)121 uint32_t hw_irq_ctrl_get_current_lock(void)
122 {
123 return irqs_locked;
124 }
125
hw_irq_ctrl_change_lock(uint32_t new_lock)126 uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock)
127 {
128 uint32_t previous_lock = irqs_locked;
129
130 irqs_locked = new_lock;
131
132 if ((previous_lock == true) && (new_lock == false)) {
133 if (irq_status != 0) {
134 posix_irq_handler_im_from_sw();
135 }
136 }
137 return previous_lock;
138 }
139
hw_irq_ctrl_get_irq_status(void)140 uint64_t hw_irq_ctrl_get_irq_status(void)
141 {
142 return irq_status;
143 }
144
hw_irq_ctrl_clear_all_enabled_irqs(void)145 void hw_irq_ctrl_clear_all_enabled_irqs(void)
146 {
147 irq_status = 0;
148 irq_premask &= ~irq_mask;
149 }
150
hw_irq_ctrl_clear_all_irqs(void)151 void hw_irq_ctrl_clear_all_irqs(void)
152 {
153 irq_status = 0;
154 irq_premask = 0;
155 }
156
hw_irq_ctrl_disable_irq(unsigned int irq)157 void hw_irq_ctrl_disable_irq(unsigned int irq)
158 {
159 irq_mask &= ~((uint64_t)1<<irq);
160 }
161
hw_irq_ctrl_is_irq_enabled(unsigned int irq)162 int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
163 {
164 return (irq_mask & ((uint64_t)1 << irq))?1:0;
165 }
166
hw_irq_ctrl_get_irq_mask(void)167 uint64_t hw_irq_ctrl_get_irq_mask(void)
168 {
169 return irq_mask;
170 }
171
hw_irq_ctrl_clear_irq(unsigned int irq)172 void hw_irq_ctrl_clear_irq(unsigned int irq)
173 {
174 irq_status &= ~((uint64_t)1<<irq);
175 irq_premask &= ~((uint64_t)1<<irq);
176 }
177
178 /**
179 * Enable an interrupt
180 *
181 * This function may only be called from SW threads
182 *
183 * If the enabled interrupt is pending, it will immediately vector to its
184 * interrupt handler and continue (maybe with some swap() before)
185 */
hw_irq_ctrl_enable_irq(unsigned int irq)186 void hw_irq_ctrl_enable_irq(unsigned int irq)
187 {
188 irq_mask |= ((uint64_t)1<<irq);
189 if (irq_premask & ((uint64_t)1<<irq)) { /*if the interrupt is pending*/
190 hw_irq_ctrl_raise_im_from_sw(irq);
191 }
192 }
193
hw_irq_ctrl_irq_raise_prefix(unsigned int irq)194 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq)
195 {
196 if ( irq < NRF_HW_NBR_IRQs ) {
197 irq_premask |= ((uint64_t)1<<irq);
198
199 if (irq_mask & (1 << irq)) {
200 irq_status |= ((uint64_t)1<<irq);
201 }
202 } else if (irq == PHONY_HARD_IRQ) {
203 lock_ignore = true;
204 }
205 }
206
207 /**
208 * Set/Raise an interrupt
209 *
210 * This function is meant to be used by either the SW manual IRQ raising
211 * or by HW which wants the IRQ to be raised in one delta cycle from now
212 */
hw_irq_ctrl_set_irq(unsigned int irq)213 void hw_irq_ctrl_set_irq(unsigned int irq)
214 {
215 hw_irq_ctrl_irq_raise_prefix(irq);
216 if ((irqs_locked == false) || (lock_ignore)) {
217 /*
218 * Awake CPU in 1 delta
219 * Note that we awake the CPU even if the IRQ is disabled
220 * => we assume the CPU is always idling in a WFE() like
221 * instruction and the CPU is allowed to awake just with the irq
222 * being marked as pending
223 */
224 Timer_irq_ctrl = tm_get_hw_time();
225 nrf_hw_find_next_timer_to_trigger();
226 }
227 }
228
irq_raising_from_hw_now(void)229 static void irq_raising_from_hw_now(void)
230 {
231 /*
232 * We always awake the CPU even if the IRQ was masked,
233 * but not if irqs are locked unless this is due to a
234 * PHONY_HARD_IRQ
235 */
236 if ((irqs_locked == false) || (lock_ignore)) {
237 lock_ignore = false;
238 posix_interrupt_raised();
239 }
240 }
241
242 /**
243 * Set/Raise an interrupt inmediately.
244 * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
245 * 1 delta cycle
246 *
247 * Call only from HW threads
248 */
hw_irq_ctrl_raise_im(unsigned int irq)249 void hw_irq_ctrl_raise_im(unsigned int irq)
250 {
251 hw_irq_ctrl_irq_raise_prefix(irq);
252 irq_raising_from_hw_now();
253 }
254
255 /**
256 * Like hw_irq_ctrl_raise_im() but for SW threads
257 *
258 * Call only from SW threads
259 */
hw_irq_ctrl_raise_im_from_sw(unsigned int irq)260 void hw_irq_ctrl_raise_im_from_sw(unsigned int irq)
261 {
262 hw_irq_ctrl_irq_raise_prefix(irq);
263
264 if (irqs_locked == false) {
265 posix_irq_handler_im_from_sw();
266 }
267 }
268
hw_irq_ctrl_timer_triggered(void)269 void hw_irq_ctrl_timer_triggered(void)
270 {
271 Timer_irq_ctrl = TIME_NEVER;
272 irq_raising_from_hw_now();
273 nrf_hw_find_next_timer_to_trigger();
274 }
275