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