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 "bs_tracing.h"
17 #include "irq_ctrl.h"
18 #include "time_machine_if.h"
19 #include "NRF_HW_model_top.h"
20
21 bs_time_t Timer_irq_ctrl = TIME_NEVER;
22
23 static uint64_t irq_lines; /*Level of interrupt lines from peripherals*/
24 static uint64_t irq_status; /*pended and not masked interrupts*/
25 static uint64_t irq_premask; /*pended interrupts before the mask*/
26
27 /*
28 * Mask of which interrupts will actually cause the cpu to vector into its
29 * irq handler
30 * If an interrupt is masked in this way, it will be pending in the premask in
31 * case it is enabled later before clearing it.
32 * If the irq_mask enables and interrupt pending in irq_premask, it will cause
33 * the controller to raise the interrupt immediately
34 */
35 static uint64_t irq_mask;
36
37 /*
38 * Interrupts lock/disable. When set, interrupts are registered
39 * (in the irq_status) but do not awake the cpu. if when unlocked,
40 * irq_status != 0 an interrupt will be raised immediately
41 */
42 static bool irqs_locked;
43 static bool lock_ignore; /*For the hard fake IRQ, temporarily ignore lock*/
44
45 static uint8_t irq_prio[NRF_HW_NBR_IRQs]; /*Priority of each interrupt*/
46 /*note that prio = 0 == highest, prio=255 == lowest*/
47
48 static int currently_running_prio = 256; /*255 is the lowest prio interrupt*/
49
50 /*
51 * These functions are provided by the board
52 */
53 extern void posix_interrupt_raised(void);
54 extern void posix_irq_handler_im_from_sw(void);
55
56
hw_irq_ctrl_init(void)57 void hw_irq_ctrl_init(void)
58 {
59 irq_mask = 0; /*Let's assume all interrupts are disable at boot*/
60 irq_premask = 0;
61 irqs_locked = false;
62 lock_ignore = false;
63
64 for (int i = 0 ; i < NRF_HW_NBR_IRQs; i++) {
65 irq_prio[i] = 255;
66 }
67 }
68
hw_irq_ctrl_cleanup(void)69 void hw_irq_ctrl_cleanup(void)
70 {
71 /*Nothing to be done*/
72 }
73
hw_irq_ctrl_set_cur_prio(int new)74 void hw_irq_ctrl_set_cur_prio(int new)
75 {
76 currently_running_prio = new;
77 }
78
hw_irq_ctrl_get_cur_prio(void)79 int hw_irq_ctrl_get_cur_prio(void)
80 {
81 return currently_running_prio;
82 }
83
hw_irq_ctrl_prio_set(unsigned int irq,unsigned int prio)84 void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio)
85 {
86 irq_prio[irq] = prio;
87 }
88
hw_irq_ctrl_get_prio(unsigned int irq)89 uint8_t hw_irq_ctrl_get_prio(unsigned int irq)
90 {
91 return irq_prio[irq];
92 }
93
94 /**
95 * Get the currently pending highest priority interrupt which has a priority
96 * higher than a possibly currently running interrupt
97 *
98 * If none, return -1
99 */
hw_irq_ctrl_get_highest_prio_irq(void)100 int hw_irq_ctrl_get_highest_prio_irq(void)
101 {
102 if (irqs_locked) {
103 return -1;
104 }
105
106 uint64_t irq_status = hw_irq_ctrl_get_irq_status();
107 int winner = -1;
108 int winner_prio = 256;
109
110 while (irq_status != 0) {
111 int irq_nbr = __builtin_ffs(irq_status) - 1;
112
113 irq_status &= ~((uint64_t) 1 << irq_nbr);
114 if ((winner_prio > (int)irq_prio[irq_nbr])
115 && (currently_running_prio > (int)irq_prio[irq_nbr])) {
116 winner = irq_nbr;
117 winner_prio = irq_prio[irq_nbr];
118 }
119 }
120 return winner;
121 }
122
hw_irq_ctrl_get_current_lock(void)123 uint32_t hw_irq_ctrl_get_current_lock(void)
124 {
125 return irqs_locked;
126 }
127
128 /*
129 * Change the overall interrupt controller "interrupt lock"
130 * The interrupt lock is a flag that provisionally disables all interrupts
131 * without affecting their status or their ability to be pended in the meanwhile
132 */
hw_irq_ctrl_change_lock(uint32_t new_lock)133 uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock)
134 {
135 uint32_t previous_lock = irqs_locked;
136
137 irqs_locked = new_lock;
138
139 if ((previous_lock == true) && (new_lock == false)) {
140 if (irq_status != 0) {
141 posix_irq_handler_im_from_sw();
142 }
143 }
144 return previous_lock;
145 }
146
hw_irq_ctrl_get_irq_status(void)147 uint64_t hw_irq_ctrl_get_irq_status(void)
148 {
149 return irq_status;
150 }
151
hw_irq_ctrl_clear_all_enabled_irqs(void)152 void hw_irq_ctrl_clear_all_enabled_irqs(void)
153 {
154 irq_status = 0;
155 irq_premask &= ~irq_mask;
156 }
157
hw_irq_ctrl_clear_all_irqs(void)158 void hw_irq_ctrl_clear_all_irqs(void)
159 {
160 irq_status = 0;
161 irq_premask = 0;
162 }
163
hw_irq_ctrl_disable_irq(unsigned int irq)164 void hw_irq_ctrl_disable_irq(unsigned int irq)
165 {
166 irq_mask &= ~((uint64_t)1<<irq);
167 }
168
hw_irq_ctrl_is_irq_enabled(unsigned int irq)169 int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
170 {
171 return (irq_mask & ((uint64_t)1 << irq))?1:0;
172 }
173
174 /**
175 * Get the current interrupt enable mask
176 */
hw_irq_ctrl_get_irq_mask(void)177 uint64_t hw_irq_ctrl_get_irq_mask(void)
178 {
179 return irq_mask;
180 }
181
182 /*
183 * Un-pend an interrupt from the interrupt controller.
184 *
185 * This is an API between the MCU model/IRQ handling side and the IRQ controller
186 * model (NVIC)
187 */
hw_irq_ctrl_clear_irq(unsigned int irq)188 void hw_irq_ctrl_clear_irq(unsigned int irq)
189 {
190 irq_status &= ~((uint64_t)1<<irq);
191 irq_premask &= ~((uint64_t)1<<irq);
192 }
193
194 /*
195 * Reevaluate if an interrupt still has its interrupt line up,
196 * and if it does, re-pend immediately the interrupt
197 *
198 * This is an API between the MCU model/IRQ handling side and the IRQ controller
199 * model (NVIC).
200 * To properly model an NVIC behavior call it only when the MCU is exiting
201 * the interrupt handler for this irq
202 */
hw_irq_ctrl_reeval_level_irq(unsigned int irq)203 void hw_irq_ctrl_reeval_level_irq(unsigned int irq)
204 {
205 uint64_t irq_bit = ((uint64_t)1<<irq);
206
207 if ((irq_lines & irq_bit) != 0) {
208 irq_premask |= irq_bit;
209
210 if (irq_mask & irq_bit) {
211 irq_status |= irq_bit;
212 }
213 }
214 }
215
216 /**
217 *
218 * Enable an interrupt
219 *
220 * This function may only be called from SW threads
221 *
222 * If the enabled interrupt is pending, it will immediately vector to its
223 * interrupt handler and continue (maybe with some swap() before)
224 */
hw_irq_ctrl_enable_irq(unsigned int irq)225 void hw_irq_ctrl_enable_irq(unsigned int irq)
226 {
227 irq_mask |= ((uint64_t)1<<irq);
228 if (irq_premask & ((uint64_t)1<<irq)) { /*if the interrupt is pending*/
229 hw_irq_ctrl_raise_im_from_sw(irq);
230 }
231 }
232
hw_irq_ctrl_irq_raise_prefix(unsigned int irq)233 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq)
234 {
235 if ( irq < NRF_HW_NBR_IRQs ) {
236 irq_premask |= ((uint64_t)1<<irq);
237
238 if (irq_mask & ((uint64_t)1<<irq)) {
239 irq_status |= ((uint64_t)1<<irq);
240 }
241 } else if (irq == PHONY_HARD_IRQ) {
242 lock_ignore = true;
243 }
244 }
245
246 /**
247 * Set/Raise/Pend an interrupt
248 *
249 * This function is meant to be used by either the SW manual IRQ raising
250 * or by HW which wants the IRQ to be raised in one delta cycle from now
251 *
252 * Note that this is equivalent to a HW peripheral sending a *pulse* interrupt
253 * to the interrupt controller
254 */
hw_irq_ctrl_set_irq(unsigned int irq)255 void hw_irq_ctrl_set_irq(unsigned int irq)
256 {
257 hw_irq_ctrl_irq_raise_prefix(irq);
258 if ((irqs_locked == false) || (lock_ignore)) {
259 /*
260 * Awake CPU in 1 delta
261 * Note that we awake the CPU even if the IRQ is disabled
262 * => we assume the CPU is always idling in a WFE() like
263 * instruction and the CPU is allowed to awake just with the irq
264 * being marked as pending
265 */
266 Timer_irq_ctrl = tm_get_hw_time();
267 nrf_hw_find_next_timer_to_trigger();
268 }
269 }
270
271 /**
272 * Raise an interrupt line from a HW peripheral to the interrupt controller
273 *
274 * This function is meant to be used only from a HW model which wants
275 * to emulate level interrupts.
276 * An IRQ will be raised in one delta cycle from now
277 *
278 * Any call from the hardware models to this function must be eventually
279 * followed by a call to hw_irq_ctrl_lower_level_irq_line(), otherwise
280 * the interrupt controller will keep interrupting the CPU and causing it to
281 * re-enter the interrupt handler
282 */
hw_irq_ctrl_raise_level_irq_line(unsigned int irq)283 void hw_irq_ctrl_raise_level_irq_line(unsigned int irq)
284 {
285 if ( irq >= NRF_HW_NBR_IRQs ) {
286 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
287 }
288
289 if ((irq_lines & ((uint64_t)1<<irq)) == 0) {
290 irq_lines |= ((uint64_t)1<<irq);
291 hw_irq_ctrl_set_irq(irq);
292 }
293 }
294
295 /**
296 * Lower an interrupt line from a HW peripheral to the interrupt controller
297 *
298 * This function is meant to be used only from a HW model which wants
299 * to emulate level interrupts.
300 */
hw_irq_ctrl_lower_level_irq_line(unsigned int irq)301 void hw_irq_ctrl_lower_level_irq_line(unsigned int irq)
302 {
303 if ( irq >= NRF_HW_NBR_IRQs ) {
304 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
305 }
306
307 irq_lines &= ~((uint64_t)1<<irq);
308 }
309
310
irq_raising_from_hw_now(void)311 static void irq_raising_from_hw_now(void)
312 {
313 /*
314 * We always awake the CPU even if the IRQ was masked,
315 * but not if irqs are locked unless this is due to a
316 * PHONY_HARD_IRQ
317 */
318 if ((irqs_locked == false) || (lock_ignore)) {
319 lock_ignore = false;
320 posix_interrupt_raised();
321 }
322 }
323
324 /**
325 * Set/Raise/Pend an interrupt immediately.
326 * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
327 * 1 delta cycle
328 *
329 * Call only from HW threads; Should be used with care
330 */
hw_irq_ctrl_raise_im(unsigned int irq)331 void hw_irq_ctrl_raise_im(unsigned int irq)
332 {
333 hw_irq_ctrl_irq_raise_prefix(irq);
334 irq_raising_from_hw_now();
335 }
336
337 /**
338 * Like hw_irq_ctrl_raise_im() but for SW threads
339 *
340 * Call only from SW threads; Should be used with care
341 */
hw_irq_ctrl_raise_im_from_sw(unsigned int irq)342 void hw_irq_ctrl_raise_im_from_sw(unsigned int irq)
343 {
344 hw_irq_ctrl_irq_raise_prefix(irq);
345
346 if (irqs_locked == false) {
347 posix_irq_handler_im_from_sw();
348 }
349 }
350
351 /*
352 * Event timer handler for the IRQ controller HW model
353 */
hw_irq_ctrl_timer_triggered(void)354 void hw_irq_ctrl_timer_triggered(void)
355 {
356 Timer_irq_ctrl = TIME_NEVER;
357 irq_raising_from_hw_now();
358 nrf_hw_find_next_timer_to_trigger();
359 }
360
hw_irq_ctrl_get_name(unsigned int irq)361 const char *hw_irq_ctrl_get_name(unsigned int irq)
362 {
363 /* The names are taken from the IRQn_Type in the MDK header.
364 * with the suffix '_IRQn' removed.
365 */
366 static const char *irqnames_nrf52833[] = {
367 [0] = "POWER_CLOCK",
368 [1] = "RADIO",
369 [2] = "UARTE0_UART0",
370 [3] = "SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0",
371 [4] = "SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1",
372 [5] = "NFCT",
373 [6] = "GPIOTE",
374 [7] = "SAADC",
375 [8] = "TIMER0",
376 [9] = "TIMER1",
377 [10] = "TIMER2",
378 [11] = "RTC0",
379 [12] = "TEMP",
380 [13] = "RNG",
381 [14] = "ECB",
382 [15] = "CCM_AAR",
383 [16] = "WDT",
384 [17] = "RTC1",
385 [18] = "QDEC",
386 [19] = "COMP_LPCOMP",
387 [20] = "SWI0_EGU0",
388 [21] = "SWI1_EGU1",
389 [22] = "SWI2_EGU2",
390 [23] = "SWI3_EGU3",
391 [24] = "SWI4_EGU4",
392 [25] = "SWI5_EGU5",
393 [26] = "TIMER3",
394 [27] = "TIMER4",
395 [28] = "PWM0",
396 [29] = "PDM",
397 [30] = "MWU",
398 [31] = "PWM1",
399 [32] = "PWM2",
400 [33] = "SPIM2_SPIS2_SPI2",
401 [34] = "RTC2",
402 [35] = "I2S",
403 [36] = "FPU",
404 [37] = "USBD",
405 [38] = "UARTE1",
406 [39] = "PWM3",
407 [40] = "SPIM3",
408 };
409
410 if (irq < sizeof(irqnames_nrf52833)/sizeof(irqnames_nrf52833[0])) {
411 return irqnames_nrf52833[irq];
412 } else {
413 return NULL;
414 }
415 }
416