1 /*
2  * Copyright (c) 2017 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * HW IRQ controller model
7  */
8 
9 #include <stdint.h>
10 #include <stdbool.h>
11 #include "hw_models_top.h"
12 #include "irq_ctrl.h"
13 #include "irq_handler.h"
14 #include "arch/posix/arch.h" /* for find_lsb_set() */
15 #include "board_soc.h"
16 #include "posix_soc.h"
17 #include "zephyr/types.h"
18 
19 uint64_t irq_ctrl_timer = NEVER;
20 
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[N_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 
hw_irq_ctrl_init(void)48 void hw_irq_ctrl_init(void)
49 {
50 	irq_mask = 0U; /* Let's assume all interrupts are disable at boot */
51 	irq_premask = 0U;
52 	irqs_locked = false;
53 	lock_ignore = false;
54 
55 	for (int i = 0 ; i < N_IRQS; i++) {
56 		irq_prio[i] = 255U;
57 	}
58 }
59 
hw_irq_ctrl_cleanup(void)60 void hw_irq_ctrl_cleanup(void)
61 {
62 	/* Nothing to be done */
63 }
64 
hw_irq_ctrl_set_cur_prio(int new)65 void hw_irq_ctrl_set_cur_prio(int new)
66 {
67 	currently_running_prio = new;
68 }
69 
hw_irq_ctrl_get_cur_prio(void)70 int hw_irq_ctrl_get_cur_prio(void)
71 {
72 	return currently_running_prio;
73 }
74 
hw_irq_ctrl_prio_set(unsigned int irq,unsigned int prio)75 void hw_irq_ctrl_prio_set(unsigned int irq, unsigned int prio)
76 {
77 	irq_prio[irq] = prio;
78 }
79 
hw_irq_ctrl_get_prio(unsigned int irq)80 uint8_t hw_irq_ctrl_get_prio(unsigned int irq)
81 {
82 	return irq_prio[irq];
83 }
84 
85 /**
86  * Get the currently pending highest priority interrupt which has a priority
87  * higher than a possibly currently running interrupt
88  *
89  * If none, return -1
90  */
hw_irq_ctrl_get_highest_prio_irq(void)91 int hw_irq_ctrl_get_highest_prio_irq(void)
92 {
93 	if (irqs_locked) {
94 		return -1;
95 	}
96 
97 	uint64_t irq_status = hw_irq_ctrl_get_irq_status();
98 	int winner = -1;
99 	int winner_prio = 256;
100 
101 	while (irq_status != 0U) {
102 		int irq_nbr = find_lsb_set(irq_status) - 1;
103 
104 		irq_status &= ~((uint64_t) 1 << irq_nbr);
105 		if ((winner_prio > (int)irq_prio[irq_nbr])
106 		   && (currently_running_prio > (int)irq_prio[irq_nbr])) {
107 			winner = irq_nbr;
108 			winner_prio = irq_prio[irq_nbr];
109 		}
110 	}
111 	return winner;
112 }
113 
114 
hw_irq_ctrl_get_current_lock(void)115 uint32_t hw_irq_ctrl_get_current_lock(void)
116 {
117 	return irqs_locked;
118 }
119 
hw_irq_ctrl_change_lock(uint32_t new_lock)120 uint32_t hw_irq_ctrl_change_lock(uint32_t new_lock)
121 {
122 	uint32_t previous_lock = irqs_locked;
123 
124 	irqs_locked = new_lock;
125 
126 	if ((previous_lock == true) && (new_lock == false)) {
127 		if (irq_status != 0U) {
128 			posix_irq_handler_im_from_sw();
129 		}
130 	}
131 	return previous_lock;
132 }
133 
hw_irq_ctrl_get_irq_status(void)134 uint64_t hw_irq_ctrl_get_irq_status(void)
135 {
136 	return irq_status;
137 }
138 
hw_irq_ctrl_clear_all_enabled_irqs(void)139 void hw_irq_ctrl_clear_all_enabled_irqs(void)
140 {
141 	irq_status  = 0U;
142 	irq_premask &= ~irq_mask;
143 }
144 
hw_irq_ctrl_clear_all_irqs(void)145 void hw_irq_ctrl_clear_all_irqs(void)
146 {
147 	irq_status  = 0U;
148 	irq_premask = 0U;
149 }
150 
hw_irq_ctrl_disable_irq(unsigned int irq)151 void hw_irq_ctrl_disable_irq(unsigned int irq)
152 {
153 	irq_mask &= ~((uint64_t)1<<irq);
154 }
155 
hw_irq_ctrl_is_irq_enabled(unsigned int irq)156 int hw_irq_ctrl_is_irq_enabled(unsigned int irq)
157 {
158 	return (irq_mask & ((uint64_t)1 << irq))?1:0;
159 }
160 
hw_irq_ctrl_get_irq_mask(void)161 uint64_t hw_irq_ctrl_get_irq_mask(void)
162 {
163 	return irq_mask;
164 }
165 
hw_irq_ctrl_clear_irq(unsigned int irq)166 void hw_irq_ctrl_clear_irq(unsigned int irq)
167 {
168 	irq_status  &= ~((uint64_t)1<<irq);
169 	irq_premask &= ~((uint64_t)1<<irq);
170 }
171 
172 
173 /**
174  * Enable an interrupt
175  *
176  * This function may only be called from SW threads
177  *
178  * If the enabled interrupt is pending, it will immediately vector to its
179  * interrupt handler and continue (maybe with some swap() before)
180  */
hw_irq_ctrl_enable_irq(unsigned int irq)181 void hw_irq_ctrl_enable_irq(unsigned int irq)
182 {
183 	irq_mask |= ((uint64_t)1<<irq);
184 	if (irq_premask & ((uint64_t)1<<irq)) { /* if IRQ is pending */
185 		hw_irq_ctrl_raise_im_from_sw(irq);
186 	}
187 }
188 
189 
hw_irq_ctrl_irq_raise_prefix(unsigned int irq)190 static inline void hw_irq_ctrl_irq_raise_prefix(unsigned int irq)
191 {
192 	if (irq < N_IRQS) {
193 		irq_premask |= ((uint64_t)1<<irq);
194 
195 		if (irq_mask & (1 << irq)) {
196 			irq_status |= ((uint64_t)1<<irq);
197 		}
198 	} else if (irq == PHONY_HARD_IRQ) {
199 		lock_ignore = true;
200 	}
201 }
202 
203 /**
204  * Set/Raise an interrupt
205  *
206  * This function is meant to be used by either the SW manual IRQ raising
207  * or by HW which wants the IRQ to be raised in one delta cycle from now
208  */
hw_irq_ctrl_set_irq(unsigned int irq)209 void hw_irq_ctrl_set_irq(unsigned int irq)
210 {
211 	hw_irq_ctrl_irq_raise_prefix(irq);
212 	if ((irqs_locked == false) || (lock_ignore)) {
213 		/*
214 		 * Awake CPU in 1 delta
215 		 * Note that we awake the CPU even if the IRQ is disabled
216 		 * => we assume the CPU is always idling in a WFE() like
217 		 * instruction and the CPU is allowed to awake just with the irq
218 		 * being marked as pending
219 		 */
220 		irq_ctrl_timer = hwm_get_time();
221 		hwm_find_next_timer();
222 	}
223 }
224 
225 
226 
irq_raising_from_hw_now(void)227 static void irq_raising_from_hw_now(void)
228 {
229 	/*
230 	 * We always awake the CPU even if the IRQ was masked,
231 	 * but not if irqs are locked unless this is due to a
232 	 * PHONY_HARD_IRQ
233 	 */
234 	if ((irqs_locked == false) || (lock_ignore)) {
235 		lock_ignore = false;
236 		posix_interrupt_raised();
237 	}
238 }
239 
240 /**
241  * Set/Raise an interrupt immediately.
242  * Like hw_irq_ctrl_set_irq() but awake immediately the CPU instead of in
243  * 1 delta cycle
244  *
245  * Call only from HW threads
246  */
hw_irq_ctrl_raise_im(unsigned int irq)247 void hw_irq_ctrl_raise_im(unsigned int irq)
248 {
249 	hw_irq_ctrl_irq_raise_prefix(irq);
250 	irq_raising_from_hw_now();
251 }
252 
253 /**
254  * Like hw_irq_ctrl_raise_im() but for SW threads
255  *
256  * Call only from SW threads
257  */
hw_irq_ctrl_raise_im_from_sw(unsigned int irq)258 void hw_irq_ctrl_raise_im_from_sw(unsigned int irq)
259 {
260 	hw_irq_ctrl_irq_raise_prefix(irq);
261 
262 	if (irqs_locked == false) {
263 		posix_irq_handler_im_from_sw();
264 	}
265 }
266 
hw_irq_ctrl_timer_triggered(void)267 void hw_irq_ctrl_timer_triggered(void)
268 {
269 	irq_ctrl_timer = NEVER;
270 	irq_raising_from_hw_now();
271 }
272