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 lock_ignore; /*For the hard fake IRQ, temporarily ignore lock*/
79
80 bool awaking_CPU; /* Is this instance raising an interrupt to the CPU in a delta cycle */
81 };
82
83 static struct intctrl_status nhw_intctrl_st[NHW_INTCTRL_TOTAL_INST];
84
85 static bs_time_t Timer_irq_ctrl = TIME_NEVER;
86
hw_irq_ctrl_init(void)87 static void hw_irq_ctrl_init(void)
88 {
89 for (int i= 0; i < NHW_INTCTRL_TOTAL_INST; i++) {
90 struct intctrl_status *el = &nhw_intctrl_st[i];
91
92 memset(el->irq_prio, 255, NHW_INTCTRL_MAX_INTLINES);
93 el->currently_running_prio = 256; /*255 is the lowest prio interrupt*/
94 }
95 }
96
97 NSI_TASK(hw_irq_ctrl_init, HW_INIT, 100);
98
hw_irq_ctrl_set_cur_prio(unsigned int inst,int new)99 void hw_irq_ctrl_set_cur_prio(unsigned int inst, int new)
100 {
101 nhw_intctrl_st[inst].currently_running_prio = new;
102 }
103
hw_irq_ctrl_get_cur_prio(unsigned int inst)104 int hw_irq_ctrl_get_cur_prio(unsigned int inst)
105 {
106 return nhw_intctrl_st[inst].currently_running_prio;
107 }
108
hw_irq_ctrl_prio_set(unsigned int inst,unsigned int irq,unsigned int prio)109 void hw_irq_ctrl_prio_set(unsigned int inst, unsigned int irq, unsigned int prio)
110 {
111 nhw_intctrl_st[inst].irq_prio[irq] = prio;
112 }
113
hw_irq_ctrl_get_prio(unsigned int inst,unsigned int irq)114 uint8_t hw_irq_ctrl_get_prio(unsigned int inst, unsigned int irq)
115 {
116 return nhw_intctrl_st[inst].irq_prio[irq];
117 }
118
irq_status_not_zero(unsigned int inst)119 static inline bool irq_status_not_zero(unsigned int inst) {
120 for (int i = 0; i < IRQ_64s; i++) {
121 if (nhw_intctrl_st[inst].irq_status[i] != 0) {
122 return true;
123 }
124 }
125 return false;
126 }
127
128 /**
129 * Get the currently pending highest priority interrupt which has a priority
130 * higher than a possibly currently running interrupt
131 *
132 * If none, return -1
133 */
hw_irq_ctrl_get_highest_prio_irq(unsigned int inst)134 int hw_irq_ctrl_get_highest_prio_irq(unsigned int inst)
135 {
136 struct intctrl_status *this = &nhw_intctrl_st[inst];
137
138 if (this->irqs_locked) {
139 return -1;
140 }
141
142 int winner = -1;
143 int winner_prio = 256;
144
145 for (int i = 0; i < IRQ_64s; i++) {
146 uint64_t irq_status = this->irq_status[i];
147 while (irq_status != 0U) {
148 int irq_bit = nsi_find_lsb_set64(irq_status) - 1;
149 int irq_nbr = irq_bit + i*64;
150
151 irq_status &= ~((uint64_t) 1 << irq_bit);
152 if ((winner_prio > (int)this->irq_prio[irq_nbr])
153 && (this->currently_running_prio > (int)this->irq_prio[irq_nbr])) {
154 winner = irq_nbr;
155 winner_prio = this->irq_prio[irq_nbr];
156 }
157 }
158 }
159 return winner;
160 }
161
hw_irq_ctrl_get_current_lock(unsigned int inst)162 uint32_t hw_irq_ctrl_get_current_lock(unsigned int inst)
163 {
164 return nhw_intctrl_st[inst].irqs_locked;
165 }
166
167 /*
168 * Change the overall interrupt controller "interrupt lock"
169 * The interrupt lock is a flag that provisionally disables all interrupts
170 * without affecting their status or their ability to be pended in the meanwhile
171 */
hw_irq_ctrl_change_lock(unsigned int inst,uint32_t new_lock)172 uint32_t hw_irq_ctrl_change_lock(unsigned int inst, uint32_t new_lock)
173 {
174 struct intctrl_status *this = &nhw_intctrl_st[inst];
175 uint32_t previous_lock = this->irqs_locked;
176
177 this->irqs_locked = new_lock;
178
179 if ((previous_lock == true) && (new_lock == false)) {
180 if (irq_status_not_zero(inst)) {
181 nsif_cpun_irq_raised_from_sw(inst);
182 }
183 }
184 return previous_lock;
185 }
186
hw_irq_ctrl_clear_all_enabled_irqs(unsigned int inst)187 void hw_irq_ctrl_clear_all_enabled_irqs(unsigned int inst)
188 {
189 struct intctrl_status *this = &nhw_intctrl_st[inst];
190
191 for (int i=0; i < IRQ_64s; i++) {
192 this->irq_status[i] = 0U;
193 this->irq_premask[i] &= ~this->irq_mask[i];
194 }
195 }
196
197 /*
198 * Un-pend all interrupt from the interrupt controller.
199 * Note: For level interrupts, the interrupts may be re-pended soon after.
200 *
201 * This is an API between the MCU model/IRQ handling side and the IRQ controller
202 * model (NVIC)
203 */
hw_irq_ctrl_clear_all_irqs(unsigned int inst)204 void hw_irq_ctrl_clear_all_irqs(unsigned int inst)
205 {
206 struct intctrl_status *this = &nhw_intctrl_st[inst];
207
208 for (int i=0; i < IRQ_64s; i++) {
209 this->irq_status[i] = 0U;
210 this->irq_premask[i] = 0U;
211 }
212 }
213
214 /*
215 * Disable (mask) one interrupt
216 *
217 * This is an API between the MCU model/IRQ handling side and the IRQ controller
218 * model (NVIC)
219 */
hw_irq_ctrl_disable_irq(unsigned int inst,unsigned int irq)220 void hw_irq_ctrl_disable_irq(unsigned int inst, unsigned int irq)
221 {
222 nhw_intctrl_st[inst].irq_mask[irq/64] &= ~((uint64_t)1<<(irq%64));
223 }
224
225 /*
226 * Check if an interrupt is enabled (not masked) (But not necessarily pended)
227 * return 1 is enabled, 0 is disabled.
228 *
229 * This is an API between the MCU model/IRQ handling side and the IRQ controller
230 * model (NVIC)
231 */
hw_irq_ctrl_is_irq_enabled(unsigned int inst,unsigned int irq)232 int hw_irq_ctrl_is_irq_enabled(unsigned int inst, unsigned int irq)
233 {
234 return (nhw_intctrl_st[inst].irq_mask[irq/64] & ((uint64_t)1 << (irq%64)))?1:0;
235 }
236
237 /*
238 * Un-pend an interrupt from the interrupt controller.
239 * Note: For level interrupts, the interrupt may be repended soon after.
240 *
241 * This is an API between the MCU model/IRQ handling side and the IRQ controller
242 * model (NVIC)
243 */
hw_irq_ctrl_clear_irq(unsigned int inst,unsigned int irq)244 void hw_irq_ctrl_clear_irq(unsigned int inst, unsigned int irq)
245 {
246 struct intctrl_status *this = &nhw_intctrl_st[inst];
247 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
248 uint irq_idx = irq/64;
249
250 this->irq_status[irq_idx] &= ~irq_bit;
251 this->irq_premask[irq_idx] &= ~irq_bit;
252 }
253
254 /*
255 * Reevaluate if an interrupt still has its interrupt line up,
256 * and if it does, re-pend immediately the interrupt
257 *
258 * This is an API between the MCU model/IRQ handling side and the IRQ controller
259 * model (NVIC).
260 * To properly model an NVIC behavior call it only when the MCU is exiting
261 * the interrupt handler for this irq
262 */
hw_irq_ctrl_reeval_level_irq(unsigned int inst,unsigned int irq)263 void hw_irq_ctrl_reeval_level_irq(unsigned int inst, unsigned int irq)
264 {
265 struct intctrl_status *this = &nhw_intctrl_st[inst];
266 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
267 uint irq_idx = irq/64;
268
269 if ((this->irq_lines[irq_idx] & irq_bit) != 0) {
270 this->irq_premask[irq_idx] |= irq_bit;
271
272 if (this->irq_mask[irq_idx] & irq_bit) {
273 this->irq_status[irq_idx] |= irq_bit;
274 }
275 }
276 }
277
278 /**
279 * Enable an interrupt
280 *
281 * This function may only be called from SW threads
282 *
283 * If the enabled interrupt is pending, it will immediately vector to its
284 * interrupt handler and continue (maybe with some swap() before)
285 *
286 * This is an API between the MCU model/IRQ handling side and the IRQ controller
287 * model (NVIC).
288 */
hw_irq_ctrl_enable_irq(unsigned int inst,unsigned int irq)289 void hw_irq_ctrl_enable_irq(unsigned int inst, unsigned int irq)
290 {
291 struct intctrl_status *this = &nhw_intctrl_st[inst];
292 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
293 uint irq_idx = irq/64;
294
295 this->irq_mask[irq_idx] |= irq_bit;
296 if (this->irq_premask[irq_idx] & irq_bit) { /*if the interrupt is pending*/
297 hw_irq_ctrl_raise_im_from_sw(inst, irq);
298 }
299 }
300
hw_irq_ctrl_irq_raise_prefix(unsigned int inst,unsigned int irq)301 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int inst, unsigned int irq)
302 {
303 struct intctrl_status *this = &nhw_intctrl_st[inst];
304
305 if ( irq < NHW_INTCTRL_MAX_INTLINES ) {
306 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
307 uint irq_idx = irq/64;
308
309 this->irq_premask[irq_idx] |= irq_bit;
310
311 if (this->irq_mask[irq_idx] & irq_bit) {
312 this->irq_status[irq_idx] |= irq_bit;
313 }
314 } else if (irq == PHONY_HARD_IRQ) {
315 this->lock_ignore = true;
316 }
317 }
318
319 /**
320 * Set/Raise/Pend an interrupt
321 *
322 * This function is meant to be used by either the SW manual IRQ raising
323 * or by HW which wants the IRQ to be raised in one delta cycle from now
324 *
325 * Note that this is equivalent to a HW peripheral sending a *pulse* interrupt
326 * to the interrupt controller
327 *
328 * This is an API towards the HW models. Embedded SW may not call it.
329 */
hw_irq_ctrl_set_irq(unsigned int inst,unsigned int irq)330 void hw_irq_ctrl_set_irq(unsigned int inst, unsigned int irq)
331 {
332 struct intctrl_status *this = &nhw_intctrl_st[inst];
333
334 hw_irq_ctrl_irq_raise_prefix(inst, irq);
335 if ((this->irqs_locked == false) || (this->lock_ignore)) {
336 /*
337 * Awake CPU in 1 delta
338 * Note that we awake the CPU even if the IRQ is disabled
339 * => we assume the CPU is always idling in a WFE() like
340 * instruction and the CPU is allowed to awake just with the irq
341 * being marked as pending
342 */
343 this->awaking_CPU = true;
344 Timer_irq_ctrl = nsi_hws_get_time();
345 nsi_hws_find_next_event();
346 }
347 }
348
349 /**
350 * Raise an interrupt line from a HW peripheral to the interrupt controller
351 *
352 * This function is meant to be used only from a HW model which wants
353 * to emulate level interrupts.
354 * An IRQ will be raised in one delta cycle from now
355 *
356 * Any call from the hardware models to this function must be eventually
357 * followed by a call to hw_irq_ctrl_lower_level_irq_line(), otherwise
358 * the interrupt controller will keep interrupting the CPU and causing it to
359 * re-enter the interrupt handler
360 *
361 * This is an API towards the HW models. Embedded SW may not call it.
362 */
hw_irq_ctrl_raise_level_irq_line(unsigned int inst,unsigned int irq)363 void hw_irq_ctrl_raise_level_irq_line(unsigned int inst, unsigned int irq)
364 {
365 struct intctrl_status *this = &nhw_intctrl_st[inst];
366
367 if ( irq >= NHW_INTCTRL_MAX_INTLINES ) {
368 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
369 }
370
371 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
372 uint irq_idx = irq/64;
373
374 if ((this->irq_lines[irq_idx] & irq_bit) == 0) {
375 this->irq_lines[irq_idx] |= irq_bit;
376 hw_irq_ctrl_set_irq(inst, irq);
377 }
378 }
379
380 /**
381 * Lower an interrupt line from a HW peripheral to the interrupt controller
382 *
383 * This function is meant to be used only from a HW model which wants
384 * to emulate level interrupts.
385 *
386 * This is an API towards the HW models. Embedded SW may not call it.
387 */
hw_irq_ctrl_lower_level_irq_line(unsigned int inst,unsigned int irq)388 void hw_irq_ctrl_lower_level_irq_line(unsigned int inst, unsigned int irq)
389 {
390 if ( irq >= NHW_INTCTRL_MAX_INTLINES ) {
391 bs_trace_error_line_time("Phony interrupts cannot use this API\n");
392 }
393 uint64_t irq_bit = ((uint64_t)1<<(irq%64));
394 uint irq_idx = irq/64;
395
396 nhw_intctrl_st[inst].irq_lines[irq_idx] &= ~irq_bit;
397 }
398
399
irq_raising_from_hw_now(unsigned int inst)400 static void irq_raising_from_hw_now(unsigned int inst)
401 {
402 struct intctrl_status *this = &nhw_intctrl_st[inst];
403 /*
404 * We always awake the CPU even if the IRQ was masked,
405 * but not if irqs are locked unless this is due to a
406 * PHONY_HARD_IRQ
407 */
408 if ((this->irqs_locked == false) || (this->lock_ignore)) {
409 this->lock_ignore = false;
410 nsif_cpun_irq_raised(inst);
411 }
412 }
413
414 /**
415 * Set/Raise/Pend an interrupt immediately.
416 * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
417 * 1 delta cycle
418 *
419 * Call only from HW threads; Should be used with care
420 *
421 * This is an API towards the HW models. Embedded SW may not call it.
422 */
hw_irq_ctrl_raise_im(unsigned int inst,unsigned int irq)423 void hw_irq_ctrl_raise_im(unsigned int inst, unsigned int irq)
424 {
425 hw_irq_ctrl_irq_raise_prefix(inst, irq);
426 irq_raising_from_hw_now(inst);
427 }
428
429 /**
430 * Like hw_irq_ctrl_raise_im() but for SW threads
431 *
432 * Call only from SW threads; Should be used with care
433 */
hw_irq_ctrl_raise_im_from_sw(unsigned int inst,unsigned int irq)434 void hw_irq_ctrl_raise_im_from_sw(unsigned int inst, unsigned int irq)
435 {
436 hw_irq_ctrl_irq_raise_prefix(inst, irq);
437
438 if (nhw_intctrl_st[inst].irqs_locked == false) {
439 nsif_cpun_irq_raised_from_sw(inst);
440 }
441 }
442
443 /*
444 * Event timer handler for the IRQ controller HW model
445 */
hw_irq_ctrl_timer_triggered(void)446 static void hw_irq_ctrl_timer_triggered(void)
447 {
448 Timer_irq_ctrl = TIME_NEVER;
449 for (int i = 0; i < NHW_INTCTRL_TOTAL_INST; i++) {
450 if (nhw_intctrl_st[i].awaking_CPU) {
451 nhw_intctrl_st[i].awaking_CPU = false;
452 irq_raising_from_hw_now(i);
453 }
454 }
455 nsi_hws_find_next_event();
456 }
457
458 NSI_HW_EVENT(Timer_irq_ctrl, hw_irq_ctrl_timer_triggered, 10 /* Purposely a low value */);
459
460 /*
461 * Get the name of an interrupt given the interrupt controller instance
462 * and the int line number.
463 *
464 * Only for debugging/logging/tracing purposes.
465 */
hw_irq_ctrl_get_name(unsigned int inst,unsigned int irq)466 const char *hw_irq_ctrl_get_name(unsigned int inst, unsigned int irq)
467 {
468 static const char *irqnames[NHW_INTCTRL_TOTAL_INST][NHW_INTCTRL_MAX_INTLINES] = NHW_INT_NAMES;
469
470 if ((inst < NHW_INTCTRL_TOTAL_INST) && (irq < NHW_INTCTRL_MAX_INTLINES)) {
471 return irqnames[inst][irq];
472 } else {
473 return NULL;
474 }
475 }
476
477 /*
478 * NOTE: This is not a Interrupt controller function per se, but a common function
479 * for all peripherals to evaluate if they want to raise or not
480 * their interrupt line
481 *
482 * int_line Previous/Current interrutp line level
483 * new_int_line new interrupt line level
484 * irq_map Interrupt mapping (interrupt controller and line)
485 */
hw_irq_ctrl_toggle_level_irq_line_if(bool * int_line,bool new_int_line,struct nhw_irq_mapping * irq_map)486 void hw_irq_ctrl_toggle_level_irq_line_if(bool *int_line,
487 bool new_int_line,
488 struct nhw_irq_mapping *irq_map)
489 {
490 if (*int_line == false && new_int_line == true) {
491 *int_line = true;
492 hw_irq_ctrl_raise_level_irq_line(irq_map->cntl_inst,
493 irq_map->int_nbr);
494 } else if (*int_line == true && new_int_line == false) {
495 *int_line = false;
496 hw_irq_ctrl_lower_level_irq_line(irq_map->cntl_inst,
497 irq_map->int_nbr);
498 }
499
500 }
501