1 /*
2  * Copyright (c) 2013-2014 Wind River Systems, Inc.
3  * Copyright (c) 2019 Nordic Semiconductor ASA.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief ARM AArch32 public interrupt handling
11  *
12  * ARM AArch32-specific kernel interrupt handling interface. Included by
13  * arm/arch.h.
14  */
15 
16 #ifndef ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_IRQ_H_
17 #define ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_IRQ_H_
18 
19 #include <zephyr/sw_isr_table.h>
20 #include <stdbool.h>
21 
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25 
26 #ifdef _ASMLANGUAGE
27 GTEXT(z_arm_int_exit);
28 GTEXT(arch_irq_enable)
29 GTEXT(arch_irq_disable)
30 GTEXT(arch_irq_is_enabled)
31 #if defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER)
32 GTEXT(z_soc_irq_get_active)
33 GTEXT(z_soc_irq_eoi)
34 #endif /* CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */
35 #else
36 
37 #if !defined(CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER)
38 
39 extern void arch_irq_enable(unsigned int irq);
40 extern void arch_irq_disable(unsigned int irq);
41 extern int arch_irq_is_enabled(unsigned int irq);
42 
43 /* internal routine documented in C file, needed by IRQ_CONNECT() macro */
44 extern void z_arm_irq_priority_set(unsigned int irq, unsigned int prio,
45 				   uint32_t flags);
46 
47 #else
48 
49 /*
50  * When a custom interrupt controller is specified, map the architecture
51  * interrupt control functions to the SoC layer interrupt control functions.
52  */
53 
54 void z_soc_irq_init(void);
55 void z_soc_irq_enable(unsigned int irq);
56 void z_soc_irq_disable(unsigned int irq);
57 int z_soc_irq_is_enabled(unsigned int irq);
58 
59 void z_soc_irq_priority_set(
60 	unsigned int irq, unsigned int prio, unsigned int flags);
61 
62 unsigned int z_soc_irq_get_active(void);
63 void z_soc_irq_eoi(unsigned int irq);
64 
65 #define arch_irq_enable(irq)		z_soc_irq_enable(irq)
66 #define arch_irq_disable(irq)		z_soc_irq_disable(irq)
67 #define arch_irq_is_enabled(irq)	z_soc_irq_is_enabled(irq)
68 
69 #define z_arm_irq_priority_set(irq, prio, flags)	\
70 	z_soc_irq_priority_set(irq, prio, flags)
71 
72 #endif /* !CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER */
73 
74 extern void z_arm_int_exit(void);
75 
76 extern void z_arm_interrupt_init(void);
77 
78 /* macros convert value of its argument to a string */
79 #define DO_TOSTR(s) #s
80 #define TOSTR(s) DO_TOSTR(s)
81 
82 /* concatenate the values of the arguments into one */
83 #define DO_CONCAT(x, y) x ## y
84 #define CONCAT(x, y) DO_CONCAT(x, y)
85 
86 /* Flags for use with IRQ_CONNECT() */
87 /**
88  * Set this interrupt up as a zero-latency IRQ. If CONFIG_ZERO_LATENCY_LEVELS
89  * is 1 it has a fixed hardware priority level (discarding what was supplied
90  * in the interrupt's priority argument). If CONFIG_ZERO_LATENCY_LEVELS is
91  * greater 1 it has the priority level assigned by the argument.
92  * The interrupt wil run even if irq_lock() is active. Be careful!
93  */
94 #define IRQ_ZERO_LATENCY	BIT(0)
95 
96 #ifdef CONFIG_CPU_CORTEX_M
97 
98 #if defined(CONFIG_ZERO_LATENCY_LEVELS)
99 #define ZERO_LATENCY_LEVELS CONFIG_ZERO_LATENCY_LEVELS
100 #else
101 #define ZERO_LATENCY_LEVELS 1
102 #endif
103 
104 #define _CHECK_PRIO(priority_p, flags_p) \
105 	BUILD_ASSERT(((flags_p & IRQ_ZERO_LATENCY) && \
106 		      ((ZERO_LATENCY_LEVELS == 1) || \
107 		       (priority_p < ZERO_LATENCY_LEVELS))) || \
108 		     (priority_p <= IRQ_PRIO_LOWEST), \
109 		     "Invalid interrupt priority. Values must not exceed IRQ_PRIO_LOWEST");
110 #else
111 #define _CHECK_PRIO(priority_p, flags_p)
112 #endif
113 
114 /* All arguments must be computable by the compiler at build time.
115  *
116  * Z_ISR_DECLARE will populate the .intList section with the interrupt's
117  * parameters, which will then be used by gen_irq_tables.py to create
118  * the vector table and the software ISR table. This is all done at
119  * build-time.
120  *
121  * We additionally set the priority in the interrupt controller at
122  * runtime.
123  */
124 #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
125 { \
126 	BUILD_ASSERT(IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) || !(flags_p & IRQ_ZERO_LATENCY), \
127 			"ZLI interrupt registered but feature is disabled"); \
128 	_CHECK_PRIO(priority_p, flags_p) \
129 	Z_ISR_DECLARE(irq_p, 0, isr_p, isr_param_p); \
130 	z_arm_irq_priority_set(irq_p, priority_p, flags_p); \
131 }
132 
133 #define ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
134 { \
135 	BUILD_ASSERT(IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) || !(flags_p & IRQ_ZERO_LATENCY), \
136 			"ZLI interrupt registered but feature is disabled"); \
137 	_CHECK_PRIO(priority_p, flags_p) \
138 	Z_ISR_DECLARE(irq_p, ISR_FLAG_DIRECT, isr_p, NULL); \
139 	z_arm_irq_priority_set(irq_p, priority_p, flags_p); \
140 }
141 
142 #ifdef CONFIG_PM
143 extern void _arch_isr_direct_pm(void);
144 #define ARCH_ISR_DIRECT_PM() _arch_isr_direct_pm()
145 #else
146 #define ARCH_ISR_DIRECT_PM() do { } while (false)
147 #endif
148 
149 #define ARCH_ISR_DIRECT_HEADER() arch_isr_direct_header()
150 #define ARCH_ISR_DIRECT_FOOTER(swap) arch_isr_direct_footer(swap)
151 
152 /* arch/arm/core/exc_exit.S */
153 extern void z_arm_int_exit(void);
154 
155 #ifdef CONFIG_TRACING_ISR
156 extern void sys_trace_isr_enter(void);
157 extern void sys_trace_isr_exit(void);
158 #endif
159 
160 static inline void arch_isr_direct_header(void)
161 {
162 #ifdef CONFIG_TRACING_ISR
163 	sys_trace_isr_enter();
164 #endif
165 }
166 
167 static inline void arch_isr_direct_footer(int maybe_swap)
168 {
169 #ifdef CONFIG_TRACING_ISR
170 	sys_trace_isr_exit();
171 #endif
172 	if (maybe_swap != 0) {
173 		z_arm_int_exit();
174 	}
175 }
176 
177 #define ARCH_ISR_DIRECT_DECLARE(name) \
178 	static inline int name##_body(void); \
179 	_Pragma("GCC diagnostic push") \
180 	_Pragma("GCC diagnostic ignored \"-Wattributes\"") \
181 	__attribute__ ((interrupt ("IRQ"))) void name(void) \
182 	{ \
183 		int check_reschedule; \
184 		ISR_DIRECT_HEADER(); \
185 		check_reschedule = name##_body(); \
186 		ISR_DIRECT_FOOTER(check_reschedule); \
187 	} \
188 	_Pragma("GCC diagnostic pop") \
189 	static inline int name##_body(void)
190 
191 #if defined(CONFIG_DYNAMIC_DIRECT_INTERRUPTS)
192 
193 extern void z_arm_irq_direct_dynamic_dispatch_reschedule(void);
194 extern void z_arm_irq_direct_dynamic_dispatch_no_reschedule(void);
195 
196 /**
197  * @brief Macro to register an ISR Dispatcher (with or without re-scheduling
198  * request) for dynamic direct interrupts.
199  *
200  * This macro registers the ISR dispatcher function for dynamic direct
201  * interrupts for a particular IRQ line, allowing the use of dynamic
202  * direct ISRs in the kernel for that interrupt source.
203  * The dispatcher function is invoked when the hardware
204  * interrupt occurs and then triggers the (software) Interrupt Service Routine
205  * (ISR) that is registered dynamically (i.e. at run-time) into the software
206  * ISR table stored in SRAM. The ISR must be connected with
207  * irq_connect_dynamic() and enabled via irq_enable() before the dynamic direct
208  * interrupt can be serviced. This ISR dispatcher must be configured by the
209  * user to trigger thread re-secheduling upon return, using the @param resch
210  * parameter.
211  *
212  * These ISRs are designed for performance-critical interrupt handling and do
213  * not go through all of the common interrupt handling code.
214  *
215  * With respect to their declaration, dynamic 'direct' interrupts are regular
216  * Zephyr interrupts; their signature must match void isr(void* parameter), as,
217  * unlike regular direct interrupts, they are not placed directly into the
218  * ROM hardware vector table but instead they are installed in the software
219  * ISR table.
220  *
221  * The major differences with regular Zephyr interrupts are the following:
222  * - Similar to direct interrupts, the call into the OS to exit power
223  *   management idle state is optional. Normal interrupts always do this
224  *   before the ISR is run, but with dynamic direct ones when and if it runs
225  *   is controlled by the placement of
226  *   a ISR_DIRECT_PM() macro, or omitted entirely.
227  * - Similar to direct interrupts, scheduling decisions are optional. Unlike
228  *   direct interrupts, the decisions must be made at build time.
229  *   They are controlled by @param resch to this macro.
230  *
231  * @param irq_p IRQ line number.
232  * @param priority_p Interrupt priority.
233  * @param flags_p Architecture-specific IRQ configuration flags.
234  * @param resch Set flag to 'reschedule' to request thread
235  *              re-scheduling upon ISR function. Set flag
236  *              'no_reschedule' to skip thread re-scheduling
237  *
238  * Note: the function is an ARM Cortex-M only API.
239  *
240  * @return Interrupt vector assigned to this interrupt.
241  */
242 #define ARM_IRQ_DIRECT_DYNAMIC_CONNECT(irq_p, priority_p, flags_p, resch) \
243 	IRQ_DIRECT_CONNECT(irq_p, priority_p, \
244 		CONCAT(z_arm_irq_direct_dynamic_dispatch_, resch), flags_p)
245 
246 #endif /* CONFIG_DYNAMIC_DIRECT_INTERRUPTS */
247 
248 #if defined(CONFIG_ARM_SECURE_FIRMWARE)
249 /* Architecture-specific definition for the target security
250  * state of an NVIC IRQ line.
251  */
252 typedef enum {
253 	IRQ_TARGET_STATE_SECURE = 0,
254 	IRQ_TARGET_STATE_NON_SECURE
255 } irq_target_state_t;
256 
257 #endif /* CONFIG_ARM_SECURE_FIRMWARE */
258 
259 #endif /* _ASMLANGUAGE */
260 
261 #ifdef __cplusplus
262 }
263 #endif
264 
265 #endif /* ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_IRQ_H_ */
266