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