1 /*
2  * Copyright (c) 2014, Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Software-managed ISR table
10  *
11  * Data types for a software-managed ISR table, with a parameter per-ISR.
12  */
13 
14 #ifndef ZEPHYR_INCLUDE_SW_ISR_TABLE_H_
15 #define ZEPHYR_INCLUDE_SW_ISR_TABLE_H_
16 
17 #if !defined(_ASMLANGUAGE)
18 #include <zephyr/device.h>
19 #include <zephyr/sys/iterable_sections.h>
20 #include <zephyr/types.h>
21 #include <zephyr/toolchain.h>
22 #include <zephyr/sys/util.h>
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 /* Default vector for the IRQ vector table */
29 void _isr_wrapper(void);
30 
31 /* Spurious interrupt handler. Throws an error if called */
32 void z_irq_spurious(const void *unused);
33 
34 /*
35  * Note the order: arg first, then ISR. This allows a table entry to be
36  * loaded arg -> r0, isr -> r3 in _isr_wrapper with one ldmia instruction,
37  * on ARM Cortex-M (Thumb2).
38  */
39 struct _isr_table_entry {
40 	const void *arg;
41 	void (*isr)(const void *);
42 };
43 
44 /* The software ISR table itself, an array of these structures indexed by the
45  * irq line
46  */
47 extern struct _isr_table_entry _sw_isr_table[];
48 
49 struct _irq_parent_entry {
50 	const struct device *dev;
51 	unsigned int level;
52 	unsigned int irq;
53 	unsigned int offset;
54 };
55 
56 /**
57  * @cond INTERNAL_HIDDEN
58  */
59 
60 /* Mapping between aggregator level to order */
61 #define Z_STR_L2 2ND
62 #define Z_STR_L3 3RD
63 /**
64  * @brief Get the Software ISR table offset Kconfig for the given aggregator level
65  *
66  * @param l Aggregator level, must be 2 or 3
67  *
68  * @return `CONFIG_2ND_LVL_ISR_TBL_OFFSET` if second level aggregator,
69  * `CONFIG_3RD_LVL_ISR_TBL_OFFSET` if third level aggregator
70  */
71 #define Z_SW_ISR_TBL_KCONFIG_BY_ALVL(l) CONCAT(CONFIG_, CONCAT(Z_STR_L, l), _LVL_ISR_TBL_OFFSET)
72 
73 /**
74  * INTERNAL_HIDDEN @endcond
75  */
76 
77 /**
78  * @brief Get an interrupt controller node's level base ISR table offset.
79  *
80  * @param node_id node identifier of the interrupt controller
81  *
82  * @return `CONFIG_2ND_LVL_ISR_TBL_OFFSET` if node_id is a second level aggregator,
83  * `CONFIG_3RD_LVL_ISR_TBL_OFFSET` if it is a third level aggregator
84  */
85 #define INTC_BASE_ISR_TBL_OFFSET(node_id)                                                          \
86 	Z_SW_ISR_TBL_KCONFIG_BY_ALVL(DT_INTC_GET_AGGREGATOR_LEVEL(node_id))
87 
88 /**
89  * @brief Get the SW ISR table offset for an instance of interrupt controller
90  *
91  * @param inst DT_DRV_COMPAT interrupt controller driver instance number
92  *
93  * @return Software ISR table offset of the interrupt controller
94  */
95 #define INTC_INST_ISR_TBL_OFFSET(inst)                                                             \
96 	(INTC_BASE_ISR_TBL_OFFSET(DT_DRV_INST(inst)) + (inst * CONFIG_MAX_IRQ_PER_AGGREGATOR))
97 
98 /**
99  * @brief Get the SW ISR table offset for a child interrupt controller
100  *
101  * @details This macro is a alternative form of the `INTC_INST_ISR_TBL_OFFSET`. This is used by
102  * pseudo interrupt controller devices that are child of a main interrupt controller device.
103  *
104  * @param node_id node identifier of the child interrupt controller
105  *
106  * @return Software ISR table offset of the child
107  */
108 #define INTC_CHILD_ISR_TBL_OFFSET(node_id)                                                         \
109 	(INTC_BASE_ISR_TBL_OFFSET(node_id) +                                                       \
110 	 (DT_NODE_CHILD_IDX(node_id) * CONFIG_MAX_IRQ_PER_AGGREGATOR))
111 
112 /**
113  * @brief Register an  interrupt controller with the software ISR table
114  *
115  * @param _name Name of the interrupt controller (must be unique)
116  * @param _dev Pointer to the interrupt controller device instance
117  * @param _irq Interrupt controller IRQ number
118  * @param _offset Software ISR table offset of the interrupt controller
119  * @param _level Interrupt controller aggregator level
120  */
121 #define IRQ_PARENT_ENTRY_DEFINE(_name, _dev, _irq, _offset, _level)                                \
122 	static const STRUCT_SECTION_ITERABLE_ALTERNATE(intc_table, _irq_parent_entry, _name) = {   \
123 		.dev = _dev,                                                                       \
124 		.level = _level,                                                                   \
125 		.irq = _irq,                                                                       \
126 		.offset = _offset,                                                                 \
127 	}
128 
129 /*
130  * Data structure created in a special binary .intlist section for each
131  * configured interrupt. gen_irq_tables.py pulls this out of the binary and
132  * uses it to create the IRQ vector table and the _sw_isr_table.
133  *
134  * More discussion in include/linker/intlist.ld
135  *
136  * This is a version used when CONFIG_ISR_TABLES_LOCAL_DECLARATION is disabled.
137  * See _isr_list_sname used otherwise.
138  */
139 struct _isr_list {
140 	/** IRQ line number */
141 	int32_t irq;
142 	/** Flags for this IRQ, see ISR_FLAG_* definitions */
143 	int32_t flags;
144 	/** ISR to call */
145 	void *func;
146 	/** Parameter for non-direct IRQs */
147 	const void *param;
148 };
149 
150 /*
151  * Data structure created in a special binary .intlist section for each
152  * configured interrupt. gen_isr_tables.py pulls this out of the binary and
153  * uses it to create linker script chunks that would place interrupt table entries
154  * in the right place in the memory.
155  *
156  * This is a version used when CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled.
157  * See _isr_list used otherwise.
158  */
159 struct _isr_list_sname {
160 	/** IRQ line number */
161 	int32_t irq;
162 	/** Flags for this IRQ, see ISR_FLAG_* definitions */
163 	int32_t flags;
164 	/** The section name */
165 	const char sname[];
166 };
167 
168 #ifdef CONFIG_SHARED_INTERRUPTS
169 struct z_shared_isr_table_entry {
170 	struct _isr_table_entry clients[CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS];
171 	size_t client_num;
172 };
173 
174 void z_shared_isr(const void *data);
175 
176 extern struct z_shared_isr_table_entry z_shared_sw_isr_table[];
177 #endif /* CONFIG_SHARED_INTERRUPTS */
178 
179 /** This interrupt gets put directly in the vector table */
180 #define ISR_FLAG_DIRECT BIT(0)
181 
182 #define _MK_ISR_NAME(x, y) __MK_ISR_NAME(x, y)
183 #define __MK_ISR_NAME(x, y) __isr_ ## x ## _irq_ ## y
184 
185 
186 #if defined(CONFIG_ISR_TABLES_LOCAL_DECLARATION)
187 
188 #define _MK_ISR_ELEMENT_NAME(func, id) __MK_ISR_ELEMENT_NAME(func, id)
189 #define __MK_ISR_ELEMENT_NAME(func, id) __isr_table_entry_ ## func ## _irq_ ## id
190 
191 #define _MK_IRQ_ELEMENT_NAME(func, id) __MK_ISR_ELEMENT_NAME(func, id)
192 #define __MK_IRQ_ELEMENT_NAME(func, id) __irq_table_entry_ ## func ## _irq_ ## id
193 
194 #define _MK_ISR_SECTION_NAME(prefix, file, counter) \
195 	"." Z_STRINGIFY(prefix)"."file"." Z_STRINGIFY(counter)
196 
197 #define _MK_ISR_ELEMENT_SECTION(counter) _MK_ISR_SECTION_NAME(irq, __FILE__, counter)
198 #define _MK_IRQ_ELEMENT_SECTION(counter) _MK_ISR_SECTION_NAME(isr, __FILE__, counter)
199 
200 /* Separated macro to create ISR table entry only.
201  * Used by Z_ISR_DECLARE and ISR tables generation script.
202  */
203 #define _Z_ISR_TABLE_ENTRY(irq, func, param, sect) \
204 	static Z_DECL_ALIGN(struct _isr_table_entry)                                      \
205 		__attribute__((section(sect)))                                            \
206 		__used _MK_ISR_ELEMENT_NAME(func, __COUNTER__) = {                        \
207 			.arg = (const void *)(param),                                     \
208 			.isr = (void (*)(const void *))(void *)(func)                     \
209 	}
210 
211 #define Z_ISR_DECLARE_C(irq, flags, func, param, counter) \
212 	_Z_ISR_DECLARE_C(irq, flags, func, param, counter)
213 
214 #define _Z_ISR_DECLARE_C(irq, flags, func, param, counter)                                \
215 	_Z_ISR_TABLE_ENTRY(irq, func, param, _MK_ISR_ELEMENT_SECTION(counter));           \
216 	static struct _isr_list_sname Z_GENERIC_SECTION(.intList)                         \
217 		__used _MK_ISR_NAME(func, counter) =                                      \
218 		{irq, flags, _MK_ISR_ELEMENT_SECTION(counter)}
219 
220 /* Create an entry for _isr_table to be then placed by the linker.
221  * An instance of struct _isr_list which gets put in the .intList
222  * section is created with the name of the section where _isr_table entry is placed to be then
223  * used by isr generation script to create linker script chunk.
224  */
225 #define Z_ISR_DECLARE(irq, flags, func, param)                                            \
226 	BUILD_ASSERT(((flags) & ISR_FLAG_DIRECT) == 0, "Use Z_ISR_DECLARE_DIRECT macro"); \
227 	Z_ISR_DECLARE_C(irq, flags, func, param, __COUNTER__)
228 
229 
230 /* Separated macro to create ISR Direct table entry only.
231  * Used by Z_ISR_DECLARE_DIRECT and ISR tables generation script.
232  */
233 #define _Z_ISR_DIRECT_TABLE_ENTRY(irq, func, sect)                                                 \
234 	COND_CODE_1(CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS, (                                     \
235 			static Z_DECL_ALIGN(uintptr_t)                                             \
236 			__attribute__((section(sect)))                                             \
237 			__used _MK_IRQ_ELEMENT_NAME(func, __COUNTER__) = ((uintptr_t)(func));      \
238 		), (                                                                               \
239 			static void __attribute__((section(sect))) __attribute__((naked))          \
240 			__used _MK_IRQ_ELEMENT_NAME(func, __COUNTER__)(void) {                     \
241 				__asm(ARCH_IRQ_VECTOR_JUMP_CODE(func));                            \
242 			}                                                                          \
243 		))
244 
245 #define Z_ISR_DECLARE_DIRECT_C(irq, flags, func, counter) \
246 	_Z_ISR_DECLARE_DIRECT_C(irq, flags, func, counter)
247 
248 #define _Z_ISR_DECLARE_DIRECT_C(irq, flags, func, counter)                                         \
249 	_Z_ISR_DIRECT_TABLE_ENTRY(irq, func, _MK_IRQ_ELEMENT_SECTION(counter));                    \
250 	static struct _isr_list_sname Z_GENERIC_SECTION(.intList)                                  \
251 		__used _MK_ISR_NAME(func, counter) = {                                             \
252 			irq,                                                                       \
253 			ISR_FLAG_DIRECT | (flags),                                                 \
254 			_MK_IRQ_ELEMENT_SECTION(counter)}
255 
256 /* Create an entry to irq table and place it in specific section which name is then placed
257  * in an instance of struct _isr_list to be then used by the isr generation script to create
258  * the linker script chunks.
259  */
260 #define Z_ISR_DECLARE_DIRECT(irq, flags, func)                                                     \
261 	BUILD_ASSERT(IS_ENABLED(CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS) ||                        \
262 		IS_ENABLED(CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE),                                  \
263 		"CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set");                         \
264 	Z_ISR_DECLARE_DIRECT_C(irq, flags, func, __COUNTER__)
265 
266 
267 #else /* defined(CONFIG_ISR_TABLES_LOCAL_DECLARATION) */
268 
269 /* Create an instance of struct _isr_list which gets put in the .intList
270  * section. This gets consumed by gen_isr_tables.py which creates the vector
271  * and/or SW ISR tables.
272  */
273 #define Z_ISR_DECLARE(irq, flags, func, param) \
274 	static Z_DECL_ALIGN(struct _isr_list) Z_GENERIC_SECTION(.intList) \
275 		__used _MK_ISR_NAME(func, __COUNTER__) = \
276 			{irq, flags, (void *)&func, (const void *)param}
277 
278 /* The version of the Z_ISR_DECLARE that should be used for direct ISR declaration.
279  * It is here for the API match the version with CONFIG_ISR_TABLES_LOCAL_DECLARATION enabled.
280  */
281 #define Z_ISR_DECLARE_DIRECT(irq, flags, func) \
282 	Z_ISR_DECLARE(irq, ISR_FLAG_DIRECT | (flags), func, NULL)
283 
284 #endif
285 
286 #define IRQ_TABLE_SIZE (CONFIG_NUM_IRQS - CONFIG_GEN_IRQ_START_VECTOR)
287 
288 #ifdef CONFIG_DYNAMIC_INTERRUPTS
289 void z_isr_install(unsigned int irq, void (*routine)(const void *),
290 		   const void *param);
291 
292 #ifdef CONFIG_SHARED_INTERRUPTS
293 int z_isr_uninstall(unsigned int irq, void (*routine)(const void *),
294 		    const void *param);
295 #endif /* CONFIG_SHARED_INTERRUPTS */
296 #endif
297 
298 #ifdef __cplusplus
299 }
300 #endif
301 
302 #endif /* _ASMLANGUAGE */
303 
304 #endif /* ZEPHYR_INCLUDE_SW_ISR_TABLE_H_ */
305