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