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