1 /*
2 * Copyright (c) 2017 Oticon A/S
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7 /*
8 * HW Interrupt Controller model
9 *
10 * Note: In principle this should have been a model of the ARM NVIC
11 * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100166_0001_00_en/ric1417175922867.html
12 *
13 * But so far it is just a general INT controller that provides
14 * a reasonable emulation of the NVIC functionality used in Zephyr
15 *
16 * Implementation spec:
17 * This file provides the implementation of a generic interrupt controller,
18 * and instantiates N of them (and initializes them at startup),
19 * as described in the configuration (NHW_config.h).
20 *
21 * This interrupt controller must be paired with dedicated interrupt handling
22 * code.
23 *
24 * Each interrupt has a configurable priority (256 levels).
25 * Interrupts can be individually masked, apart from interrupts globally locked.
26 * With the appropriate interrupt handling code, interrupt nesting is supported.
27 *
28 * The interrupt controller supports both level and edge/pulse interrupts.
29 * The level interrupts "lines" must be raised and *lowered* by the peripherals.
30 * ( hw_irq_ctrl_{raise|lower}_level_irq_line() )
31 * Pulse/edge interrupts are just events that pend the interrupt
32 * (calls to hw_irq_ctrl_set_irq()).
33 *
34 * NOTE: At this point an interrupt controller may have at most 64 interrupt lines.
35 * Generalizing this is pending.
36 */
37
38 #include <stdint.h>
39 #include <string.h>
40 #include "NHW_common_types.h"
41 #include "NHW_config.h"
42 #include "nsi_internal.h"
43 #include "nsi_cpun_if.h"
44 #include "bs_types.h"
45 #include "bs_tracing.h"
46 #include "irq_ctrl.h"
47 #include "nsi_hw_scheduler.h"
48 #include "nsi_tasks.h"
49 #include "nsi_hws_models_if.h"
50
51 #define IRQ_64s ((NHW_INTCTRL_MAX_INTLINES+63)/64)
52
53 struct intctrl_status {
54 uint64_t irq_lines[IRQ_64s]; /*Level of interrupt lines from peripherals*/
55 uint64_t irq_status[IRQ_64s]; /*pended and not masked interrupts*/
56 uint64_t irq_premask[IRQ_64s]; /*pended interrupts before the mask*/
57 /*
58 * Mask of which interrupts will actually cause the cpu to vector into its
59 * irq handler
60 * If an interrupt is masked in this way, it will be pending in the premask in
61 * case it is enabled later before clearing it.
62 * If the irq_mask enables and interrupt pending in irq_premask, it will cause
63 * the controller to raise the interrupt immediately
64 */
65 uint64_t irq_mask[IRQ_64s];
66
67 uint8_t irq_prio[NHW_INTCTRL_MAX_INTLINES]; /*Priority of each interrupt*/
68 /*note that prio = 0 == highest, prio=255 == lowest*/
69
70 int currently_running_prio;
71
72 /*
73 * Interrupts lock/disable. When set, interrupts are registered
74 * (in the irq_status) but do not awake the cpu. if when unlocked,
75 * irq_status != 0 an interrupt will be raised immediately
76 */
77 bool irqs_locked;
78 bool wake_even_if_lock; /* Equivalent to SCB.SCR SEVONPEND */
79 bool lock_ignore; /*For the hard fake IRQ, temporarily ignore lock*/
80
81 bool awaking_CPU; /* Is this instance raising an interrupt to the CPU in a delta cycle */
82 };
83
84 static struct intctrl_status nhw_intctrl_st[NHW_INTCTRL_TOTAL_INST];
85
86 static bs_time_t Timer_irq_ctrl = TIME_NEVER;
87
hw_irq_ctrl_init(void)88 static void hw_irq_ctrl_init(void)
89 {
90 for (int i= 0; i < NHW_INTCTRL_TOTAL_INST; i++) {
91 struct intctrl_status *el = &nhw_intctrl_st[i];
92
93 memset(el->irq_prio, 255, NHW_INTCTRL_MAX_INTLINES);
94 el->currently_running_prio = 256; /*255 is the lowest prio interrupt*/
95 }
96 }
97
98 NSI_TASK(hw_irq_ctrl_init, HW_INIT, 100);
99
hw_irq_ctrl_set_cur_prio(unsigned int inst,int new)100 void hw_irq_ctrl_set_cur_prio(unsigned int inst, int new)
101 {
102 nhw_intctrl_st[inst].currently_running_prio = new;
103 }
104
hw_irq_ctrl_get_cur_prio(unsigned int inst)105 int hw_irq_ctrl_get_cur_prio(unsigned int inst)
106 {
107 return nhw_intctrl_st[inst].currently_running_prio;
108 }
109
hw_irq_ctrl_prio_set(unsigned int inst,unsigned int irq,unsigned int prio)110 void hw_irq_ctrl_prio_set(unsigned int inst, unsigned int irq, unsigned int prio)
111 {
112 nhw_intctrl_st[inst].irq_prio[irq] = prio;
113 }
114
hw_irq_ctrl_get_prio(unsigned int inst,unsigned int irq)115 uint8_t hw_irq_ctrl_get_prio(unsigned int inst, unsigned int irq)
116 {
117 return nhw_intctrl_st[inst].irq_prio[irq];
118 }
119
irq_status_not_zero(unsigned int inst)120 static inline bool irq_status_not_zero(unsigned int inst) {
121 for (int i = 0; i < IRQ_64s; i++) {
122 if (nhw_intctrl_st[inst].irq_status[i] != 0) {
123 return true;
124 }
125 }
126 return false;
127 }
128
129 /**
130 * Get the currently pending highest priority interrupt which has a priority
131 * higher than a possibly currently running interrupt
132 *
133 * If none, return -1
134 */
hw_irq_ctrl_get_highest_prio_irq(unsigned int inst)135 int hw_irq_ctrl_get_highest_prio_irq(unsigned int inst)
136 {
137 struct intctrl_status *this = &nhw_intctrl_st[inst];
138
139 if (this->irqs_locked) {
140 return -1;
141 }
142
143 int winner = -1;
144 int winner_prio = 256;
145
146 for (int i = 0; i < IRQ_64s; i++) {
147 uint64_t irq_status = this->irq_status[i];
148 while (irq_status != 0U) {
149 int irq_bit = nsi_find_lsb_set64(irq_status) - 1;
150 int irq_nbr = irq_bit + i*64;
151
152 irq_status &= ~((uint64_t) 1 << irq_bit);
153 if ((winner_prio > (int)this->irq_prio[irq_nbr])
154 && (this->currently_running_prio > (int)this->irq_prio[irq_nbr])) {
155 winner = irq_nbr;
156 winner_prio = this->irq_prio[irq_nbr];
157 }
158 }
159 }
160 return winner;
161 }
162
hw_irq_ctrl_get_current_lock(unsigned int inst)163 uint32_t hw_irq_ctrl_get_current_lock(unsigned int inst)
164 {
165 return nhw_intctrl_st[inst].irqs_locked;
166 }
167
168 /*
169 * Change the overall interrupt controller "interrupt lock"
170 * The interrupt lock is a flag that provisionally disables all interrupts
171 * without affecting their status or their ability to be pended in the meanwhile
172 */
hw_irq_ctrl_change_lock(unsigned int inst,uint32_t new_lock)173 uint32_t hw_irq_ctrl_change_lock(unsigned int inst, uint32_t new_lock)
174 {
175 struct intctrl_status *this = &nhw_intctrl_st[inst];
176 uint32_t previous_lock = this->irqs_locked;
177
178 this->irqs_locked = new_lock;
179
180 if ((previous_lock == true) && (new_lock == false)) {
181 if (irq_status_not_zero(inst)) {
182 nsif_cpun_irq_raised_from_sw(inst);
183 }
184 }
185 return previous_lock;
186 }
187
hw_irq_ctrl_clear_all_enabled_irqs(unsigned int inst)188 void hw_irq_ctrl_clear_all_enabled_irqs(unsigned int inst)
189 {
190 struct intctrl_status *this = &nhw_intctrl_st[inst];
191
192 for (int i=0; i < IRQ_64s; i++) {
193 this->irq_status[i] = 0U;
194 this->irq_premask[i] &= ~this->irq_mask[i];
195 }
196 }
197
198 /*
199 * Un-pend all interrupt from the interrupt controller.
200 * Note: For level interrupts, the interrupts may be re-pended soon after.
201 *
202 * This is an API between the MCU model/IRQ handling side and the IRQ controller
203 * model (NVIC)
204 */
hw_irq_ctrl_clear_all_irqs(unsigned int inst)205 void hw_irq_ctrl_clear_all_irqs(unsigned int inst)
206 {
207 struct intctrl_status *this = &nhw_intctrl_st[inst];
208
209 for (int i=0; i < IRQ_64s; i++) {
210 this->irq_status[i] = 0U;
211 this->irq_premask[i] = 0U;
212 }
213 }
214
215 /*
216 * Disable (mask) one interrupt
217 *
218 * This is an API between the MCU model/IRQ handling side and the IRQ controller
219 * model (NVIC)
220 */
hw_irq_ctrl_disable_irq(unsigned int inst,unsigned int irq)221 void hw_irq_ctrl_disable_irq(unsigned int inst, unsigned int irq)
222 {
223 nhw_intctrl_st[inst].irq_mask[irq/64] &= ~((uint64_t)1<<(irq%64));
224 }
225
226 /*
227 * Check if an interrupt is enabled (not masked) (But not necessarily pended)
228 * return 1 is enabled, 0 is disabled.
229 *
230 * This is an API between the MCU model/IRQ handling side and the IRQ controller
231 * model (NVIC)
232 */
hw_irq_ctrl_is_irq_enabled(unsigned int inst,unsigned int irq)233 int hw_irq_ctrl_is_irq_enabled(unsigned int inst, unsigned int irq)
234 {
235 return (nhw_intctrl_st[inst].irq_mask[irq/64] & ((uint64_t)1 << (irq%64)))?1:0;
236 }
237
238 /*
239 * Check if an interrupt is pending (it may be masked or not)
240 * return 1 if it is pending, 0 otherwise
241 *
242 * This is an API between the MCU model/IRQ handling side and the IRQ controller
243 * model (NVIC)
244 */
hw_irq_ctrl_is_irq_pending(unsigned int inst,unsigned int irq)245 int hw_irq_ctrl_is_irq_pending(unsigned int inst, unsigned int irq)
246 {
247 return (nhw_intctrl_st[inst].irq_premask[irq/64] & ((uint64_t)1 << (irq%64)))?1:0;
248 }
249
250 /*
251 * Un-pend an interrupt from the interrupt controller.
252 * Note: For level interrupts, the interrupt may be repended soon after.
253 *
254 * This is an API between the MCU model/IRQ handling side and the IRQ controller
255 * model (NVIC)
256 */
hw_irq_ctrl_clear_irq(unsigned int inst,unsigned int irq)257 void hw_irq_ctrl_clear_irq(unsigned int inst, unsigned int irq)
258 {
259 struct intctrl_status *this = &nhw_intctrl_st[inst];
260 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
261 uint irq_idx = irq/64;
262
263 this->irq_status[irq_idx] &= ~irq_bit;
264 this->irq_premask[irq_idx] &= ~irq_bit;
265 }
266
267 /*
268 * Reevaluate if an interrupt still has its interrupt line up,
269 * and if it does, re-pend immediately the interrupt
270 *
271 * This is an API between the MCU model/IRQ handling side and the IRQ controller
272 * model (NVIC).
273 * To properly model an NVIC behavior call it only when the MCU is exiting
274 * the interrupt handler for this irq
275 */
hw_irq_ctrl_reeval_level_irq(unsigned int inst,unsigned int irq)276 void hw_irq_ctrl_reeval_level_irq(unsigned int inst, unsigned int irq)
277 {
278 struct intctrl_status *this = &nhw_intctrl_st[inst];
279 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
280 uint irq_idx = irq/64;
281
282 if ((this->irq_lines[irq_idx] & irq_bit) != 0) {
283 this->irq_premask[irq_idx] |= irq_bit;
284
285 if (this->irq_mask[irq_idx] & irq_bit) {
286 this->irq_status[irq_idx] |= irq_bit;
287 }
288 }
289 }
290
291 /**
292 * Enable an interrupt
293 *
294 * This function may only be called from SW threads
295 *
296 * If the enabled interrupt is pending, it will immediately vector to its
297 * interrupt handler and continue (maybe with some swap() before)
298 *
299 * This is an API between the MCU model/IRQ handling side and the IRQ controller
300 * model (NVIC).
301 */
hw_irq_ctrl_enable_irq(unsigned int inst,unsigned int irq)302 void hw_irq_ctrl_enable_irq(unsigned int inst, unsigned int irq)
303 {
304 struct intctrl_status *this = &nhw_intctrl_st[inst];
305 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
306 uint irq_idx = irq/64;
307
308 this->irq_mask[irq_idx] |= irq_bit;
309 if (this->irq_premask[irq_idx] & irq_bit) { /*if the interrupt is pending*/
310 hw_irq_ctrl_raise_im_from_sw(inst, irq);
311 }
312 }
313
314 /**
315 * If an interrupt comes, wake the processor even if interrupts were locked.
316 */
hw_irq_ctrl_set_wake_even_if_lock(unsigned int inst,int value)317 int hw_irq_ctrl_set_wake_even_if_lock(unsigned int inst, int value)
318 {
319 int current = nhw_intctrl_st[inst].wake_even_if_lock;
320 nhw_intctrl_st[inst].wake_even_if_lock = (value!=0);
321 return current;
322 }
323
hw_irq_ctrl_irq_raise_prefix(unsigned int inst,unsigned int irq)324 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int inst, unsigned int irq)
325 {
326 struct intctrl_status *this = &nhw_intctrl_st[inst];
327
328 if ( irq < NHW_INTCTRL_MAX_INTLINES ) {
329 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
330 uint irq_idx = irq/64;
331
332 this->irq_premask[irq_idx] |= irq_bit;
333
334 if (this->irq_mask[irq_idx] & irq_bit) {
335 this->irq_status[irq_idx] |= irq_bit;
336 }
337 } else if (irq == PHONY_HARD_IRQ) {
338 this->lock_ignore = true;
339 }
340 }
341
342 /**
343 * Set/Raise/Pend an interrupt
344 *
345 * This function is meant to be used by either the SW manual IRQ raising
346 * or by HW which wants the IRQ to be raised in one delta cycle from now
347 *
348 * Note that this is equivalent to a HW peripheral sending a *pulse* interrupt
349 * to the interrupt controller
350 *
351 * This is an API towards the HW models. Embedded SW may not call it.
352 */
hw_irq_ctrl_set_irq(unsigned int inst,unsigned int irq)353 void hw_irq_ctrl_set_irq(unsigned int inst, unsigned int irq)
354 {
355 struct intctrl_status *this = &nhw_intctrl_st[inst];
356
357 hw_irq_ctrl_irq_raise_prefix(inst, irq);
358 if ((this->irqs_locked == false) || (this->lock_ignore) || (this->wake_even_if_lock)) {
359 /*
360 * Awake CPU in 1 delta
361 * Note that we awake the CPU even if the IRQ is disabled
362 * => we assume the CPU is always idling in a WFE() like
363 * instruction and the CPU is allowed to awake just with the irq
364 * being marked as pending
365 */
366 this->awaking_CPU = true;
367 Timer_irq_ctrl = nsi_hws_get_time();
368 nsi_hws_find_next_event();
369 }
370 }
371
372 /**
373 * Raise an interrupt line from a HW peripheral to the interrupt controller
374 *
375 * This function is meant to be used only from a HW model which wants
376 * to emulate level interrupts.
377 * An IRQ will be raised in one delta cycle from now
378 *
379 * Any call from the hardware models to this function must be eventually
380 * followed by a call to hw_irq_ctrl_lower_level_irq_line(), otherwise
381 * the interrupt controller will keep interrupting the CPU and causing it to
382 * re-enter the interrupt handler
383 *
384 * This is an API towards the HW models. Embedded SW may not call it.
385 */
hw_irq_ctrl_raise_level_irq_line(unsigned int inst,unsigned int irq)386 void hw_irq_ctrl_raise_level_irq_line(unsigned int inst, unsigned int irq)
387 {
388 struct intctrl_status *this = &nhw_intctrl_st[inst];
389
390 if ( irq >= NHW_INTCTRL_MAX_INTLINES ) {
391 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
392 }
393
394 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
395 uint irq_idx = irq/64;
396
397 if ((this->irq_lines[irq_idx] & irq_bit) == 0) {
398 this->irq_lines[irq_idx] |= irq_bit;
399 hw_irq_ctrl_set_irq(inst, irq);
400 }
401 }
402
403 /**
404 * Lower an interrupt line from a HW peripheral to the interrupt controller
405 *
406 * This function is meant to be used only from a HW model which wants
407 * to emulate level interrupts.
408 *
409 * This is an API towards the HW models. Embedded SW may not call it.
410 */
hw_irq_ctrl_lower_level_irq_line(unsigned int inst,unsigned int irq)411 void hw_irq_ctrl_lower_level_irq_line(unsigned int inst, unsigned int irq)
412 {
413 if ( irq >= NHW_INTCTRL_MAX_INTLINES ) {
414 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
415 }
416 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
417 uint irq_idx = irq/64;
418
419 nhw_intctrl_st[inst].irq_lines[irq_idx] &= ~irq_bit;
420 }
421
422
irq_raising_from_hw_now(unsigned int inst)423 static void irq_raising_from_hw_now(unsigned int inst)
424 {
425 struct intctrl_status *this = &nhw_intctrl_st[inst];
426 /*
427 * We always awake the CPU even if the IRQ was masked,
428 * but not if irqs are locked unless this is due to a
429 * PHONY_HARD_IRQ
430 */
431 if ((this->irqs_locked == false) || (this->lock_ignore) || (this->wake_even_if_lock)) {
432 this->lock_ignore = false;
433 nsif_cpun_irq_raised(inst);
434 }
435 }
436
437 /**
438 * Set/Raise/Pend an interrupt immediately.
439 * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
440 * 1 delta cycle
441 *
442 * Call only from HW threads; Should be used with care
443 *
444 * This is an API towards the HW models. Embedded SW may not call it.
445 */
hw_irq_ctrl_raise_im(unsigned int inst,unsigned int irq)446 void hw_irq_ctrl_raise_im(unsigned int inst, unsigned int irq)
447 {
448 hw_irq_ctrl_irq_raise_prefix(inst, irq);
449 irq_raising_from_hw_now(inst);
450 }
451
452 /**
453 * Like hw_irq_ctrl_raise_im() but for SW threads
454 *
455 * Call only from SW threads; Should be used with care
456 */
hw_irq_ctrl_raise_im_from_sw(unsigned int inst,unsigned int irq)457 void hw_irq_ctrl_raise_im_from_sw(unsigned int inst, unsigned int irq)
458 {
459 hw_irq_ctrl_irq_raise_prefix(inst, irq);
460
461 struct intctrl_status *this = &nhw_intctrl_st[inst];
462
463 if ((this->irqs_locked == false) || (this->wake_even_if_lock)) {
464 nsif_cpun_irq_raised_from_sw(inst);
465 }
466 }
467
468 /*
469 * Event timer handler for the IRQ controller HW model
470 */
hw_irq_ctrl_timer_triggered(void)471 static void hw_irq_ctrl_timer_triggered(void)
472 {
473 Timer_irq_ctrl = TIME_NEVER;
474 for (int i = 0; i < NHW_INTCTRL_TOTAL_INST; i++) {
475 if (nhw_intctrl_st[i].awaking_CPU) {
476 nhw_intctrl_st[i].awaking_CPU = false;
477 irq_raising_from_hw_now(i);
478 }
479 }
480 nsi_hws_find_next_event();
481 }
482
483 NSI_HW_EVENT(Timer_irq_ctrl, hw_irq_ctrl_timer_triggered, 10 /* Purposely a low value */);
484
485 /*
486 * Get the name of an interrupt given the interrupt controller instance
487 * and the int line number.
488 *
489 * Only for debugging/logging/tracing purposes.
490 */
hw_irq_ctrl_get_name(unsigned int inst,unsigned int irq)491 const char *hw_irq_ctrl_get_name(unsigned int inst, unsigned int irq)
492 {
493 static const char *irqnames[NHW_INTCTRL_TOTAL_INST][NHW_INTCTRL_MAX_INTLINES] = NHW_INT_NAMES;
494
495 if ((inst < NHW_INTCTRL_TOTAL_INST) && (irq < NHW_INTCTRL_MAX_INTLINES)) {
496 return irqnames[inst][irq];
497 } else {
498 return NULL;
499 }
500 }
501
502 /*
503 * NOTE: This is not a Interrupt controller function per se, but a common function
504 * for all peripherals to evaluate if they want to raise or not
505 * their interrupt line
506 *
507 * int_line Previous/Current interrutp line level
508 * new_int_line new interrupt line level
509 * irq_map Interrupt mapping (interrupt controller and line)
510 */
hw_irq_ctrl_toggle_level_irq_line_if(bool * int_line,bool new_int_line,struct nhw_irq_mapping * irq_map)511 void hw_irq_ctrl_toggle_level_irq_line_if(bool *int_line,
512 bool new_int_line,
513 struct nhw_irq_mapping *irq_map)
514 {
515 if (*int_line == false && new_int_line == true) {
516 *int_line = true;
517 hw_irq_ctrl_raise_level_irq_line(irq_map->cntl_inst,
518 irq_map->int_nbr);
519 } else if (*int_line == true && new_int_line == false) {
520 *int_line = false;
521 hw_irq_ctrl_lower_level_irq_line(irq_map->cntl_inst,
522 irq_map->int_nbr);
523 }
524
525 }
526