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 "irq_ctrl.h"
17 #include "time_machine_if.h"
18 #include "NRF_HW_model_top.h"
19 
20 bs_time_t Timer_irq_ctrl = TIME_NEVER;
21 
22 static uint64_t irq_status;  /*pending interrupts*/
23 static uint64_t irq_premask; /*interrupts before the mask*/
24 
25 /*
26  * Mask of which interrupts will actually cause the cpu to vector into its
27  * irq handler
28  * If an interrupt is masked in this way, it will be pending in the premask in
29  * case it is enabled later before clearing it.
30  * If the irq_mask enables and interrupt pending in irq_premask, it will cause
31  * the controller to raise the interrupt immediately
32  */
33 static uint64_t irq_mask;
34 
35 /*
36  * Interrupts lock/disable. When set, interrupts are registered
37  * (in the irq_status) but do not awake the cpu. if when unlocked,
38  * irq_status != 0 an interrupt will be raised immediately
39  */
40 static bool irqs_locked;
41 static bool lock_ignore; /*For the hard fake IRQ, temporarily ignore lock*/
42 
43 static uint8_t irq_prio[NRF_HW_NBR_IRQs]; /*Priority of each interrupt*/
44 /*note that prio = 0 == highest, prio=255 == lowest*/
45 
46 static int currently_running_prio = 256; /*255 is the lowest prio interrupt*/
47 
48 /*
49  * These functions are provided by the board
50  */
51 extern void posix_interrupt_raised(void);
52 extern void posix_irq_handler_im_from_sw(void);
53 
54 
hw_irq_ctrl_init(void)55 void hw_irq_ctrl_init(void)
56 {
57 	irq_mask = 0; /*Let's assume all interrupts are disable at boot*/
58 	irq_premask = 0;
59 	irqs_locked = false;
60 	lock_ignore = false;
61 
62 	for (int i = 0 ; i < NRF_HW_NBR_IRQs; i++) {
63 		irq_prio[i] = 255;
64 	}
65 }
66 
hw_irq_ctrl_cleanup(void)67 void hw_irq_ctrl_cleanup(void)
68 {
69 	/*Nothing to be done*/
70 }
71 
hw_irq_ctrl_set_cur_prio(int new)72 void hw_irq_ctrl_set_cur_prio(int new)
73 {
74 	currently_running_prio = new;
75 }
76 
hw_irq_ctrl_get_cur_prio(void)77 int hw_irq_ctrl_get_cur_prio(void)
78 {
79 	return currently_running_prio;
80 }
81 
hw_irq_ctrl_prio_set(unsigned int irq,unsigned int prio)82 void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio)
83 {
84 	irq_prio[irq] = prio;
85 }
86 
hw_irq_ctrl_get_prio(unsigned int irq)87 uint8_t hw_irq_ctrl_get_prio(unsigned int irq)
88 {
89 	return irq_prio[irq];
90 }
91 
92 /**
93  * Get the currently pending highest priority interrupt which has a priority
94  * higher than a possibly currently running interrupt
95  *
96  * If none, return -1
97  */
hw_irq_ctrl_get_highest_prio_irq(void)98 int hw_irq_ctrl_get_highest_prio_irq(void)
99 {
100 	if (irqs_locked) {
101 		return -1;
102 	}
103 
104 	uint64_t irq_status = hw_irq_ctrl_get_irq_status();
105 	int winner = -1;
106 	int winner_prio = 256;
107 
108 	while (irq_status != 0) {
109 		int irq_nbr = __builtin_ffs(irq_status) - 1;
110 
111 		irq_status &= ~((uint64_t) 1 << irq_nbr);
112 		if ((winner_prio > (int)irq_prio[irq_nbr])
113 		   && (currently_running_prio > (int)irq_prio[irq_nbr])) {
114 			winner = irq_nbr;
115 			winner_prio = irq_prio[irq_nbr];
116 		}
117 	}
118 	return winner;
119 }
120 
hw_irq_ctrl_get_current_lock(void)121 uint32_t hw_irq_ctrl_get_current_lock(void)
122 {
123 	return irqs_locked;
124 }
125 
hw_irq_ctrl_change_lock(uint32_t new_lock)126 uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock)
127 {
128 	uint32_t previous_lock = irqs_locked;
129 
130 	irqs_locked = new_lock;
131 
132 	if ((previous_lock == true) && (new_lock == false)) {
133 		if (irq_status != 0) {
134 			posix_irq_handler_im_from_sw();
135 		}
136 	}
137 	return previous_lock;
138 }
139 
hw_irq_ctrl_get_irq_status(void)140 uint64_t hw_irq_ctrl_get_irq_status(void)
141 {
142 	return irq_status;
143 }
144 
hw_irq_ctrl_clear_all_enabled_irqs(void)145 void hw_irq_ctrl_clear_all_enabled_irqs(void)
146 {
147 	irq_status  = 0;
148 	irq_premask &= ~irq_mask;
149 }
150 
hw_irq_ctrl_clear_all_irqs(void)151 void hw_irq_ctrl_clear_all_irqs(void)
152 {
153 	irq_status  = 0;
154 	irq_premask = 0;
155 }
156 
hw_irq_ctrl_disable_irq(unsigned int irq)157 void hw_irq_ctrl_disable_irq(unsigned int irq)
158 {
159 	irq_mask &= ~((uint64_t)1<<irq);
160 }
161 
hw_irq_ctrl_is_irq_enabled(unsigned int irq)162 int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
163 {
164 	return (irq_mask & ((uint64_t)1 << irq))?1:0;
165 }
166 
hw_irq_ctrl_get_irq_mask(void)167 uint64_t hw_irq_ctrl_get_irq_mask(void)
168 {
169 	return irq_mask;
170 }
171 
hw_irq_ctrl_clear_irq(unsigned int irq)172 void hw_irq_ctrl_clear_irq(unsigned int irq)
173 {
174 	irq_status  &= ~((uint64_t)1<<irq);
175 	irq_premask &= ~((uint64_t)1<<irq);
176 }
177 
178 /**
179  * Enable an interrupt
180  *
181  * This function may only be called from SW threads
182  *
183  * If the enabled interrupt is pending, it will immediately vector to its
184  * interrupt handler and continue (maybe with some swap() before)
185  */
hw_irq_ctrl_enable_irq(unsigned int irq)186 void hw_irq_ctrl_enable_irq(unsigned int irq)
187 {
188 	irq_mask |= ((uint64_t)1<<irq);
189 	if (irq_premask & ((uint64_t)1<<irq)) { /*if the interrupt is pending*/
190 		hw_irq_ctrl_raise_im_from_sw(irq);
191 	}
192 }
193 
hw_irq_ctrl_irq_raise_prefix(unsigned int irq)194 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq)
195 {
196   if ( irq < NRF_HW_NBR_IRQs ) {
197 		irq_premask |= ((uint64_t)1<<irq);
198 
199 		if (irq_mask & (1 << irq)) {
200 			irq_status |= ((uint64_t)1<<irq);
201 		}
202 	} else if (irq == PHONY_HARD_IRQ) {
203 		lock_ignore = true;
204 	}
205 }
206 
207 /**
208  * Set/Raise an interrupt
209  *
210  * This function is meant to be used by either the SW manual IRQ raising
211  * or by HW which wants the IRQ to be raised in one delta cycle from now
212  */
hw_irq_ctrl_set_irq(unsigned int irq)213 void hw_irq_ctrl_set_irq(unsigned int irq)
214 {
215 	hw_irq_ctrl_irq_raise_prefix(irq);
216 	if ((irqs_locked == false) || (lock_ignore)) {
217 		/*
218 		 * Awake CPU in 1 delta
219 		 * Note that we awake the CPU even if the IRQ is disabled
220 		 * => we assume the CPU is always idling in a WFE() like
221 		 * instruction and the CPU is allowed to awake just with the irq
222 		 * being marked as pending
223 		 */
224 	    Timer_irq_ctrl = tm_get_hw_time();
225 	    nrf_hw_find_next_timer_to_trigger();
226 	}
227 }
228 
irq_raising_from_hw_now(void)229 static void irq_raising_from_hw_now(void)
230 {
231 	/*
232 	 * We always awake the CPU even if the IRQ was masked,
233 	 * but not if irqs are locked unless this is due to a
234 	 * PHONY_HARD_IRQ
235 	 */
236 	if ((irqs_locked == false) || (lock_ignore)) {
237 		lock_ignore = false;
238 		posix_interrupt_raised();
239 	}
240 }
241 
242 /**
243  * Set/Raise an interrupt inmediately.
244  * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
245  * 1 delta cycle
246  *
247  * Call only from HW threads
248  */
hw_irq_ctrl_raise_im(unsigned int irq)249 void hw_irq_ctrl_raise_im(unsigned int irq)
250 {
251 	hw_irq_ctrl_irq_raise_prefix(irq);
252 	irq_raising_from_hw_now();
253 }
254 
255 /**
256  * Like hw_irq_ctrl_raise_im() but for SW threads
257  *
258  * Call only from SW threads
259  */
hw_irq_ctrl_raise_im_from_sw(unsigned int irq)260 void hw_irq_ctrl_raise_im_from_sw(unsigned int irq)
261 {
262 	hw_irq_ctrl_irq_raise_prefix(irq);
263 
264 	if (irqs_locked == false) {
265 		posix_irq_handler_im_from_sw();
266 	}
267 }
268 
hw_irq_ctrl_timer_triggered(void)269 void hw_irq_ctrl_timer_triggered(void)
270 {
271   Timer_irq_ctrl = TIME_NEVER;
272   irq_raising_from_hw_now();
273   nrf_hw_find_next_timer_to_trigger();
274 }
275