1 /*
2 * Copyright (c) 2023 Meta
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief Public interface for multi-level interrupts
10 */
11 #ifndef ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_
12 #define ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_
13
14 #ifndef _ASMLANGUAGE
15 #include <zephyr/sys/__assert.h>
16 #include <zephyr/sys/util_macro.h>
17 #include <zephyr/types.h>
18
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22
23 #if defined(CONFIG_MULTI_LEVEL_INTERRUPTS) || defined(__DOXYGEN__)
24
25 typedef union _z_irq {
26 /* Zephyr multilevel-encoded IRQ */
27 uint32_t irq;
28
29 /* Interrupt bits */
30 struct {
31 /* First level interrupt bits */
32 uint32_t l1: CONFIG_1ST_LEVEL_INTERRUPT_BITS;
33 /* Second level interrupt bits */
34 uint32_t l2: CONFIG_2ND_LEVEL_INTERRUPT_BITS;
35 #if defined(CONFIG_3RD_LEVEL_INTERRUPTS) || defined(__DOXYGEN__)
36 /* Third level interrupt bits */
37 uint32_t l3: CONFIG_3RD_LEVEL_INTERRUPT_BITS;
38 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
39 } bits;
40
41 #if defined(CONFIG_3RD_LEVEL_INTERRUPTS) || defined(__DOXYGEN__)
42 /* Third level IRQ's interrupt controller */
43 struct {
44 /* IRQ of the third level interrupt aggregator */
45 uint32_t irq: CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS;
46 } l3_intc;
47 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
48
49 /* Second level IRQ's interrupt controller */
50 struct {
51 /* IRQ of the second level interrupt aggregator */
52 uint32_t irq: CONFIG_1ST_LEVEL_INTERRUPT_BITS;
53 } l2_intc;
54 } _z_irq_t;
55
56 BUILD_ASSERT(sizeof(_z_irq_t) == sizeof(uint32_t), "Size of `_z_irq_t` must equal to `uint32_t`");
57
_z_l1_irq(_z_irq_t irq)58 static inline uint32_t _z_l1_irq(_z_irq_t irq)
59 {
60 return irq.bits.l1;
61 }
62
_z_l2_irq(_z_irq_t irq)63 static inline uint32_t _z_l2_irq(_z_irq_t irq)
64 {
65 return irq.bits.l2 - 1;
66 }
67
68 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
_z_l3_irq(_z_irq_t irq)69 static inline uint32_t _z_l3_irq(_z_irq_t irq)
70 {
71 return irq.bits.l3 - 1;
72 }
73 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
74
_z_irq_get_level(_z_irq_t z_irq)75 static inline unsigned int _z_irq_get_level(_z_irq_t z_irq)
76 {
77 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
78 if (z_irq.bits.l3 != 0) {
79 return 3;
80 }
81 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
82
83 if (z_irq.bits.l2 != 0) {
84 return 2;
85 }
86
87 return 1;
88 }
89
90 /**
91 * @brief Return IRQ level
92 * This routine returns the interrupt level number of the provided interrupt.
93 *
94 * @param irq IRQ number in its zephyr format
95 *
96 * @return 1 if IRQ level 1, 2 if IRQ level 2, 3 if IRQ level 3
97 */
irq_get_level(unsigned int irq)98 static inline unsigned int irq_get_level(unsigned int irq)
99 {
100 _z_irq_t z_irq = {
101 .irq = irq,
102 };
103
104 return _z_irq_get_level(z_irq);
105 }
106
107 /**
108 * @brief Return the 2nd level interrupt number
109 *
110 * This routine returns the second level irq number of the zephyr irq
111 * number passed in
112 *
113 * @param irq IRQ number in its zephyr format
114 *
115 * @return 2nd level IRQ number
116 */
irq_from_level_2(unsigned int irq)117 static inline unsigned int irq_from_level_2(unsigned int irq)
118 {
119 _z_irq_t z_irq = {
120 .irq = irq,
121 };
122
123 return _z_l2_irq(z_irq);
124 }
125
126 /**
127 * @brief Preprocessor macro to convert `irq` from level 1 to level 2 format
128 *
129 * @param irq IRQ number in its zephyr format
130 *
131 * @return 2nd level IRQ number
132 */
133 #define IRQ_TO_L2(irq) ((irq + 1) << CONFIG_1ST_LEVEL_INTERRUPT_BITS)
134
135 /**
136 * @brief Converts irq from level 1 to level 2 format
137 *
138 *
139 * This routine converts the input into the level 2 irq number format
140 *
141 * @note Values >= 0xFF are invalid
142 *
143 * @param irq IRQ number in its zephyr format
144 *
145 * @return 2nd level IRQ number
146 */
irq_to_level_2(unsigned int irq)147 static inline unsigned int irq_to_level_2(unsigned int irq)
148 {
149 _z_irq_t z_irq = {
150 .bits = {
151 .l1 = 0,
152 .l2 = irq + 1,
153 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
154 .l3 = 0,
155 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
156 },
157 };
158
159 return z_irq.irq;
160 }
161
162 /**
163 * @brief Returns the parent IRQ of the level 2 raw IRQ number
164 *
165 *
166 * The parent of a 2nd level interrupt is in the 1st byte
167 *
168 * @param irq IRQ number in its zephyr format
169 *
170 * @return 2nd level IRQ parent
171 */
irq_parent_level_2(unsigned int irq)172 static inline unsigned int irq_parent_level_2(unsigned int irq)
173 {
174 _z_irq_t z_irq = {
175 .irq = irq,
176 };
177
178 return _z_l1_irq(z_irq);
179 }
180
181 #if defined(CONFIG_3RD_LEVEL_INTERRUPTS) || defined(__DOXYGEN__)
182 /**
183 * @brief Return the 3rd level interrupt number
184 *
185 *
186 * This routine returns the third level irq number of the zephyr irq
187 * number passed in
188 *
189 * @param irq IRQ number in its zephyr format
190 *
191 * @return 3rd level IRQ number
192 */
irq_from_level_3(unsigned int irq)193 static inline unsigned int irq_from_level_3(unsigned int irq)
194 {
195 _z_irq_t z_irq = {
196 .irq = irq,
197 };
198
199 return _z_l3_irq(z_irq);
200 }
201
202 /**
203 * @brief Preprocessor macro to convert `irq` from level 1 to level 3 format
204 *
205 * @param irq IRQ number in its zephyr format
206 *
207 * @return 3rd level IRQ number
208 */
209 #define IRQ_TO_L3(irq) \
210 ((irq + 1) << (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS))
211
212 /**
213 * @brief Converts irq from level 1 to level 3 format
214 *
215 *
216 * This routine converts the input into the level 3 irq number format
217 *
218 * @note Values >= 0xFF are invalid
219 *
220 * @param irq IRQ number in its zephyr format
221 *
222 * @return 3rd level IRQ number
223 */
irq_to_level_3(unsigned int irq)224 static inline unsigned int irq_to_level_3(unsigned int irq)
225 {
226 _z_irq_t z_irq = {
227 .bits = {
228 .l1 = 0,
229 .l2 = 0,
230 .l3 = irq + 1,
231 },
232 };
233
234 return z_irq.irq;
235 }
236
237 /**
238 * @brief Returns the parent IRQ of the level 3 raw IRQ number
239 *
240 *
241 * The parent of a 3rd level interrupt is in the 2nd byte
242 *
243 * @param irq IRQ number in its zephyr format
244 *
245 * @return 3rd level IRQ parent
246 */
irq_parent_level_3(unsigned int irq)247 static inline unsigned int irq_parent_level_3(unsigned int irq)
248 {
249 _z_irq_t z_irq = {
250 .irq = irq,
251 };
252
253 return _z_l2_irq(z_irq);
254 }
255 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
256
257 /**
258 * @brief Return the interrupt number for a given level
259 *
260 * @param irq IRQ number in its zephyr format
261 * @param level IRQ level
262 *
263 * @return IRQ number in the level
264 */
irq_from_level(unsigned int irq,unsigned int level)265 static inline unsigned int irq_from_level(unsigned int irq, unsigned int level)
266 {
267 if (level == 1) {
268 return irq;
269 } else if (level == 2) {
270 return irq_from_level_2(irq);
271 }
272 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
273 else if (level == 3) {
274 return irq_from_level_3(irq);
275 }
276 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
277
278 /* level is higher than what's supported */
279 __ASSERT_NO_MSG(false);
280 return irq;
281 }
282
283 /**
284 * @brief Converts irq from level 1 to a given level
285 *
286 * @param irq IRQ number in its zephyr format
287 * @param level IRQ level
288 *
289 * @return Converted IRQ number in the level
290 */
irq_to_level(unsigned int irq,unsigned int level)291 static inline unsigned int irq_to_level(unsigned int irq, unsigned int level)
292 {
293 if (level == 1) {
294 return irq;
295 } else if (level == 2) {
296 return irq_to_level_2(irq);
297 }
298 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
299 else if (level == 3) {
300 return irq_to_level_3(irq);
301 }
302 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
303
304 /* level is higher than what's supported */
305 __ASSERT_NO_MSG(false);
306 return irq;
307 }
308
309 /**
310 * @brief Returns the parent IRQ of the given level raw IRQ number
311 *
312 * @param irq IRQ number in its zephyr format
313 * @param level IRQ level
314 *
315 * @return IRQ parent of the given level
316 */
irq_parent_level(unsigned int irq,unsigned int level)317 static inline unsigned int irq_parent_level(unsigned int irq, unsigned int level)
318 {
319 if (level == 1) {
320 /* doesn't really make sense, but return anyway */
321 return irq;
322 } else if (level == 2) {
323 return irq_parent_level_2(irq);
324 }
325 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
326 else if (level == 3) {
327 return irq_parent_level_3(irq);
328 }
329 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
330
331 /* level is higher than what's supported */
332 __ASSERT_NO_MSG(false);
333 return irq;
334 }
335
336 /**
337 * @brief Returns the parent interrupt controller IRQ of the given IRQ number
338 *
339 * @param irq IRQ number in its zephyr format
340 *
341 * @return IRQ of the interrupt controller
342 */
irq_get_intc_irq(unsigned int irq)343 static inline unsigned int irq_get_intc_irq(unsigned int irq)
344 {
345 const unsigned int level = irq_get_level(irq);
346 _z_irq_t z_irq = {
347 .irq = irq,
348 };
349
350 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
351 __ASSERT_NO_MSG(level <= 3);
352 if (level == 3) {
353 return z_irq.l3_intc.irq;
354 }
355 #else
356 __ASSERT_NO_MSG(level <= 2);
357 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
358
359 if (level == 2) {
360 return z_irq.l2_intc.irq;
361 }
362
363 return irq;
364 }
365
366 /**
367 * @brief Increments the multilevel-encoded @a irq by @a val
368 *
369 * @param irq IRQ number in its zephyr format
370 * @param val Amount to increment
371 *
372 * @return @a irq incremented by @a val
373 */
irq_increment(unsigned int irq,unsigned int val)374 static inline unsigned int irq_increment(unsigned int irq, unsigned int val)
375 {
376 _z_irq_t z_irq = {
377 .irq = irq,
378 };
379
380 if (false) {
381 /* so that it evaluates the next condition */
382 #ifdef CONFIG_3RD_LEVEL_INTERRUPTS
383 } else if (z_irq.bits.l3 != 0) {
384 z_irq.bits.l3 += val;
385 #endif /* CONFIG_3RD_LEVEL_INTERRUPTS */
386 } else if (z_irq.bits.l2 != 0) {
387 z_irq.bits.l2 += val;
388 } else {
389 z_irq.bits.l1 += val;
390 }
391
392 return z_irq.irq;
393 }
394
395 #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
396 #ifdef __cplusplus
397 }
398 #endif
399
400 #endif /* _ASMLANGUAGE */
401 #endif /* ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_ */
402