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_IRQ_H_
17 #define ZEPHYR_INCLUDE_ARCH_ARM_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 /* Flags for use with IRQ_CONNECT() */
79 /**
80  * Set this interrupt up as a zero-latency IRQ. If CONFIG_ZERO_LATENCY_LEVELS
81  * is 1 it has a fixed hardware priority level (discarding what was supplied
82  * in the interrupt's priority argument). If CONFIG_ZERO_LATENCY_LEVELS is
83  * greater 1 it has the priority level assigned by the argument.
84  * The interrupt will run even if irq_lock() is active. Be careful!
85  */
86 #define IRQ_ZERO_LATENCY	BIT(0)
87 
88 #ifdef CONFIG_CPU_CORTEX_M
89 
90 #if defined(CONFIG_ZERO_LATENCY_LEVELS)
91 #define ZERO_LATENCY_LEVELS CONFIG_ZERO_LATENCY_LEVELS
92 #else
93 #define ZERO_LATENCY_LEVELS 1
94 #endif
95 
96 #define _CHECK_PRIO(priority_p, flags_p) \
97 	BUILD_ASSERT(((flags_p & IRQ_ZERO_LATENCY) && \
98 		      ((ZERO_LATENCY_LEVELS == 1) || \
99 		       (priority_p < ZERO_LATENCY_LEVELS))) || \
100 		     (priority_p <= IRQ_PRIO_LOWEST), \
101 		     "Invalid interrupt priority. Values must not exceed IRQ_PRIO_LOWEST");
102 #else
103 #define _CHECK_PRIO(priority_p, flags_p)
104 #endif
105 
106 /* All arguments must be computable by the compiler at build time.
107  *
108  * Z_ISR_DECLARE will populate the .intList section with the interrupt's
109  * parameters, which will then be used by gen_irq_tables.py to create
110  * the vector table and the software ISR table. This is all done at
111  * build-time.
112  *
113  * We additionally set the priority in the interrupt controller at
114  * runtime.
115  */
116 #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
117 { \
118 	BUILD_ASSERT(IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) || !(flags_p & IRQ_ZERO_LATENCY), \
119 			"ZLI interrupt registered but feature is disabled"); \
120 	_CHECK_PRIO(priority_p, flags_p) \
121 	Z_ISR_DECLARE(irq_p, 0, isr_p, isr_param_p); \
122 	z_arm_irq_priority_set(irq_p, priority_p, flags_p); \
123 }
124 
125 #define ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
126 { \
127 	BUILD_ASSERT(IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) || !(flags_p & IRQ_ZERO_LATENCY), \
128 			"ZLI interrupt registered but feature is disabled"); \
129 	_CHECK_PRIO(priority_p, flags_p) \
130 	Z_ISR_DECLARE_DIRECT(irq_p, ISR_FLAG_DIRECT, isr_p); \
131 	z_arm_irq_priority_set(irq_p, priority_p, flags_p); \
132 }
133 
134 #ifdef CONFIG_PM
135 extern void _arch_isr_direct_pm(void);
136 #define ARCH_ISR_DIRECT_PM() _arch_isr_direct_pm()
137 #else
138 #define ARCH_ISR_DIRECT_PM() do { } while (false)
139 #endif
140 
141 #define ARCH_ISR_DIRECT_HEADER() arch_isr_direct_header()
142 #define ARCH_ISR_DIRECT_FOOTER(swap) arch_isr_direct_footer(swap)
143 
144 /* arch/arm/core/exc_exit.S */
145 extern void z_arm_int_exit(void);
146 
147 #ifdef CONFIG_TRACING_ISR
148 extern void sys_trace_isr_enter(void);
149 extern void sys_trace_isr_exit(void);
150 #endif
151 
152 static inline void arch_isr_direct_header(void)
153 {
154 #ifdef CONFIG_TRACING_ISR
155 	sys_trace_isr_enter();
156 #endif
157 }
158 
159 static inline void arch_isr_direct_footer(int maybe_swap)
160 {
161 #ifdef CONFIG_TRACING_ISR
162 	sys_trace_isr_exit();
163 #endif
164 	if (maybe_swap != 0) {
165 		z_arm_int_exit();
166 	}
167 }
168 
169 #if defined(__clang__)
170 #define ARCH_ISR_DIAG_OFF \
171 	_Pragma("clang diagnostic push") \
172 	_Pragma("clang diagnostic ignored \"-Wextra\"")
173 #define ARCH_ISR_DIAG_ON _Pragma("clang diagnostic pop")
174 #elif defined(__GNUC__)
175 #define ARCH_ISR_DIAG_OFF \
176 	_Pragma("GCC diagnostic push") \
177 	_Pragma("GCC diagnostic ignored \"-Wattributes\"")
178 #define ARCH_ISR_DIAG_ON _Pragma("GCC diagnostic pop")
179 #else
180 #define ARCH_ISR_DIAG_OFF
181 #define ARCH_ISR_DIAG_ON
182 #endif
183 
184 #define ARCH_ISR_DIRECT_DECLARE(name) \
185 	static inline int name##_body(void); \
186 	ARCH_ISR_DIAG_OFF \
187 	__attribute__ ((interrupt ("IRQ"))) void name(void) \
188 	{ \
189 		int check_reschedule; \
190 		ISR_DIRECT_HEADER(); \
191 		check_reschedule = name##_body(); \
192 		ISR_DIRECT_FOOTER(check_reschedule); \
193 	} \
194 	ARCH_ISR_DIAG_ON \
195 	static inline int name##_body(void)
196 
197 #if defined(CONFIG_DYNAMIC_DIRECT_INTERRUPTS)
198 
199 extern void z_arm_irq_direct_dynamic_dispatch_reschedule(void);
200 extern void z_arm_irq_direct_dynamic_dispatch_no_reschedule(void);
201 
202 /**
203  * @brief Macro to register an ISR Dispatcher (with or without re-scheduling
204  * request) for dynamic direct interrupts.
205  *
206  * This macro registers the ISR dispatcher function for dynamic direct
207  * interrupts for a particular IRQ line, allowing the use of dynamic
208  * direct ISRs in the kernel for that interrupt source.
209  * The dispatcher function is invoked when the hardware
210  * interrupt occurs and then triggers the (software) Interrupt Service Routine
211  * (ISR) that is registered dynamically (i.e. at run-time) into the software
212  * ISR table stored in SRAM. The ISR must be connected with
213  * irq_connect_dynamic() and enabled via irq_enable() before the dynamic direct
214  * interrupt can be serviced. This ISR dispatcher must be configured by the
215  * user to trigger thread re-secheduling upon return, using the @param resch
216  * parameter.
217  *
218  * These ISRs are designed for performance-critical interrupt handling and do
219  * not go through all of the common interrupt handling code.
220  *
221  * With respect to their declaration, dynamic 'direct' interrupts are regular
222  * Zephyr interrupts; their signature must match void isr(void* parameter), as,
223  * unlike regular direct interrupts, they are not placed directly into the
224  * ROM hardware vector table but instead they are installed in the software
225  * ISR table.
226  *
227  * The major differences with regular Zephyr interrupts are the following:
228  * - Similar to direct interrupts, the call into the OS to exit power
229  *   management idle state is optional. Normal interrupts always do this
230  *   before the ISR is run, but with dynamic direct ones when and if it runs
231  *   is controlled by the placement of
232  *   a ISR_DIRECT_PM() macro, or omitted entirely.
233  * - Similar to direct interrupts, scheduling decisions are optional. Unlike
234  *   direct interrupts, the decisions must be made at build time.
235  *   They are controlled by @param resch to this macro.
236  *
237  * @param irq_p IRQ line number.
238  * @param priority_p Interrupt priority.
239  * @param flags_p Architecture-specific IRQ configuration flags.
240  * @param resch Set flag to 'reschedule' to request thread
241  *              re-scheduling upon ISR function. Set flag
242  *              'no_reschedule' to skip thread re-scheduling
243  *
244  * Note: the function is an ARM Cortex-M only API.
245  *
246  * @return Interrupt vector assigned to this interrupt.
247  */
248 #define ARM_IRQ_DIRECT_DYNAMIC_CONNECT(irq_p, priority_p, flags_p, resch) \
249 	IRQ_DIRECT_CONNECT(irq_p, priority_p, \
250 		_CONCAT(z_arm_irq_direct_dynamic_dispatch_, resch), flags_p)
251 
252 #endif /* CONFIG_DYNAMIC_DIRECT_INTERRUPTS */
253 
254 #if defined(CONFIG_ARM_SECURE_FIRMWARE)
255 /* Architecture-specific definition for the target security
256  * state of an NVIC IRQ line.
257  */
258 typedef enum {
259 	IRQ_TARGET_STATE_SECURE = 0,
260 	IRQ_TARGET_STATE_NON_SECURE
261 } irq_target_state_t;
262 
263 #endif /* CONFIG_ARM_SECURE_FIRMWARE */
264 
265 #endif /* _ASMLANGUAGE */
266 
267 #ifdef __cplusplus
268 }
269 #endif
270 
271 #endif /* ZEPHYR_INCLUDE_ARCH_ARM_IRQ_H_ */
272