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  * @brief Return IRQ level
26  * This routine returns the interrupt level number of the provided interrupt.
27  *
28  * @param irq IRQ number in its zephyr format
29  *
30  * @return 1 if IRQ level 1, 2 if IRQ level 2, 3 if IRQ level 3
31  */
irq_get_level(unsigned int irq)32 static inline unsigned int irq_get_level(unsigned int irq)
33 {
34 	const uint32_t mask2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS) <<
35 		CONFIG_1ST_LEVEL_INTERRUPT_BITS;
36 	const uint32_t mask3 = BIT_MASK(CONFIG_3RD_LEVEL_INTERRUPT_BITS) <<
37 		(CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS);
38 
39 	if (IS_ENABLED(CONFIG_3RD_LEVEL_INTERRUPTS) && (irq & mask3) != 0) {
40 		return 3;
41 	}
42 
43 	if (IS_ENABLED(CONFIG_2ND_LEVEL_INTERRUPTS) && (irq & mask2) != 0) {
44 		return 2;
45 	}
46 
47 	return 1;
48 }
49 
50 /**
51  * @brief Return the 2nd level interrupt number
52  *
53  * This routine returns the second level irq number of the zephyr irq
54  * number passed in
55  *
56  * @param irq IRQ number in its zephyr format
57  *
58  * @return 2nd level IRQ number
59  */
irq_from_level_2(unsigned int irq)60 static inline unsigned int irq_from_level_2(unsigned int irq)
61 {
62 	if (IS_ENABLED(CONFIG_3RD_LEVEL_INTERRUPTS)) {
63 		return ((irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) &
64 			BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS)) - 1;
65 	} else {
66 		return (irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) - 1;
67 	}
68 }
69 
70 /**
71  * @brief Preprocessor macro to convert `irq` from level 1 to level 2 format
72  *
73  * @param irq IRQ number in its zephyr format
74  *
75  * @return 2nd level IRQ number
76  */
77 #define IRQ_TO_L2(irq) ((irq + 1) << CONFIG_1ST_LEVEL_INTERRUPT_BITS)
78 
79 /**
80  * @brief Converts irq from level 1 to level 2 format
81  *
82  *
83  * This routine converts the input into the level 2 irq number format
84  *
85  * @note Values >= 0xFF are invalid
86  *
87  * @param irq IRQ number in its zephyr format
88  *
89  * @return 2nd level IRQ number
90  */
irq_to_level_2(unsigned int irq)91 static inline unsigned int irq_to_level_2(unsigned int irq)
92 {
93 	return IRQ_TO_L2(irq);
94 }
95 
96 /**
97  * @brief Returns the parent IRQ of the level 2 raw IRQ number
98  *
99  *
100  * The parent of a 2nd level interrupt is in the 1st byte
101  *
102  * @param irq IRQ number in its zephyr format
103  *
104  * @return 2nd level IRQ parent
105  */
irq_parent_level_2(unsigned int irq)106 static inline unsigned int irq_parent_level_2(unsigned int irq)
107 {
108 	return irq & BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
109 }
110 
111 /**
112  * @brief Return the 3rd level interrupt number
113  *
114  *
115  * This routine returns the third level irq number of the zephyr irq
116  * number passed in
117  *
118  * @param irq IRQ number in its zephyr format
119  *
120  * @return 3rd level IRQ number
121  */
irq_from_level_3(unsigned int irq)122 static inline unsigned int irq_from_level_3(unsigned int irq)
123 {
124 	return (irq >> (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS)) - 1;
125 }
126 
127 /**
128  * @brief Preprocessor macro to convert `irq` from level 1 to level 3 format
129  *
130  * @param irq IRQ number in its zephyr format
131  *
132  * @return 3rd level IRQ number
133  */
134 #define IRQ_TO_L3(irq)                                                                             \
135 	((irq + 1) << (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS))
136 
137 /**
138  * @brief Converts irq from level 1 to level 3 format
139  *
140  *
141  * This routine converts the input into the level 3 irq number format
142  *
143  * @note Values >= 0xFF are invalid
144  *
145  * @param irq IRQ number in its zephyr format
146  *
147  * @return 3rd level IRQ number
148  */
irq_to_level_3(unsigned int irq)149 static inline unsigned int irq_to_level_3(unsigned int irq)
150 {
151 	return IRQ_TO_L3(irq);
152 }
153 
154 /**
155  * @brief Returns the parent IRQ of the level 3 raw IRQ number
156  *
157  *
158  * The parent of a 3rd level interrupt is in the 2nd byte
159  *
160  * @param irq IRQ number in its zephyr format
161  *
162  * @return 3rd level IRQ parent
163  */
irq_parent_level_3(unsigned int irq)164 static inline unsigned int irq_parent_level_3(unsigned int irq)
165 {
166 	return (irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) &
167 		BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS);
168 }
169 
170 /**
171  * @brief Return the interrupt number for a given level
172  *
173  * @param irq IRQ number in its zephyr format
174  * @param level IRQ level
175  *
176  * @return IRQ number in the level
177  */
irq_from_level(unsigned int irq,unsigned int level)178 static inline unsigned int irq_from_level(unsigned int irq, unsigned int level)
179 {
180 	if (level == 1) {
181 		return irq;
182 	} else if (level == 2) {
183 		return irq_from_level_2(irq);
184 	} else if (level == 3) {
185 		return irq_from_level_3(irq);
186 	}
187 
188 	/* level is higher than 3 */
189 	__ASSERT_NO_MSG(false);
190 	return irq;
191 }
192 
193 /**
194  * @brief Converts irq from level 1 to a given level
195  *
196  * @param irq IRQ number in its zephyr format
197  * @param level IRQ level
198  *
199  * @return Converted IRQ number in the level
200  */
irq_to_level(unsigned int irq,unsigned int level)201 static inline unsigned int irq_to_level(unsigned int irq, unsigned int level)
202 {
203 	if (level == 1) {
204 		return irq;
205 	} else if (level == 2) {
206 		return irq_to_level_2(irq);
207 	} else if (level == 3) {
208 		return irq_to_level_3(irq);
209 	}
210 
211 	/* level is higher than 3 */
212 	__ASSERT_NO_MSG(false);
213 	return irq;
214 }
215 
216 /**
217  * @brief Returns the parent IRQ of the given level raw IRQ number
218  *
219  * @param irq IRQ number in its zephyr format
220  * @param level IRQ level
221  *
222  * @return IRQ parent of the given level
223  */
irq_parent_level(unsigned int irq,unsigned int level)224 static inline unsigned int irq_parent_level(unsigned int irq, unsigned int level)
225 {
226 	if (level == 1) {
227 		/* doesn't really make sense, but return anyway */
228 		return irq;
229 	} else if (level == 2) {
230 		return irq_parent_level_2(irq);
231 	} else if (level == 3) {
232 		return irq_parent_level_3(irq);
233 	}
234 
235 	/* level is higher than 3 */
236 	__ASSERT_NO_MSG(false);
237 	return irq;
238 }
239 
240 /**
241  * @brief Returns the parent interrupt controller IRQ of the given IRQ number
242  *
243  * @param irq IRQ number in its zephyr format
244  *
245  * @return IRQ of the interrupt controller
246  */
irq_get_intc_irq(unsigned int irq)247 static inline unsigned int irq_get_intc_irq(unsigned int irq)
248 {
249 	const unsigned int level = irq_get_level(irq);
250 
251 	__ASSERT_NO_MSG(level > 1 && level <= 3);
252 
253 	return irq & BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS +
254 			      (level == 3 ? CONFIG_2ND_LEVEL_INTERRUPT_BITS : 0));
255 }
256 
257 #endif /* CONFIG_MULTI_LEVEL_INTERRUPTS */
258 #ifdef __cplusplus
259 }
260 #endif
261 
262 #endif /* _ASMLANGUAGE */
263 #endif /* ZEPHYR_INCLUDE_IRQ_MULTILEVEL_H_ */
264