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