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 wil 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 #define ARCH_ISR_DIRECT_DECLARE(name) \
170 	static inline int name##_body(void); \
171 	_Pragma("GCC diagnostic push") \
172 	_Pragma("GCC diagnostic ignored \"-Wattributes\"") \
173 	__attribute__ ((interrupt ("IRQ"))) void name(void) \
174 	{ \
175 		int check_reschedule; \
176 		ISR_DIRECT_HEADER(); \
177 		check_reschedule = name##_body(); \
178 		ISR_DIRECT_FOOTER(check_reschedule); \
179 	} \
180 	_Pragma("GCC diagnostic pop") \
181 	static inline int name##_body(void)
182 
183 #if defined(CONFIG_DYNAMIC_DIRECT_INTERRUPTS)
184 
185 extern void z_arm_irq_direct_dynamic_dispatch_reschedule(void);
186 extern void z_arm_irq_direct_dynamic_dispatch_no_reschedule(void);
187 
188 /**
189  * @brief Macro to register an ISR Dispatcher (with or without re-scheduling
190  * request) for dynamic direct interrupts.
191  *
192  * This macro registers the ISR dispatcher function for dynamic direct
193  * interrupts for a particular IRQ line, allowing the use of dynamic
194  * direct ISRs in the kernel for that interrupt source.
195  * The dispatcher function is invoked when the hardware
196  * interrupt occurs and then triggers the (software) Interrupt Service Routine
197  * (ISR) that is registered dynamically (i.e. at run-time) into the software
198  * ISR table stored in SRAM. The ISR must be connected with
199  * irq_connect_dynamic() and enabled via irq_enable() before the dynamic direct
200  * interrupt can be serviced. This ISR dispatcher must be configured by the
201  * user to trigger thread re-secheduling upon return, using the @param resch
202  * parameter.
203  *
204  * These ISRs are designed for performance-critical interrupt handling and do
205  * not go through all of the common interrupt handling code.
206  *
207  * With respect to their declaration, dynamic 'direct' interrupts are regular
208  * Zephyr interrupts; their signature must match void isr(void* parameter), as,
209  * unlike regular direct interrupts, they are not placed directly into the
210  * ROM hardware vector table but instead they are installed in the software
211  * ISR table.
212  *
213  * The major differences with regular Zephyr interrupts are the following:
214  * - Similar to direct interrupts, the call into the OS to exit power
215  *   management idle state is optional. Normal interrupts always do this
216  *   before the ISR is run, but with dynamic direct ones when and if it runs
217  *   is controlled by the placement of
218  *   a ISR_DIRECT_PM() macro, or omitted entirely.
219  * - Similar to direct interrupts, scheduling decisions are optional. Unlike
220  *   direct interrupts, the decisions must be made at build time.
221  *   They are controlled by @param resch to this macro.
222  *
223  * @param irq_p IRQ line number.
224  * @param priority_p Interrupt priority.
225  * @param flags_p Architecture-specific IRQ configuration flags.
226  * @param resch Set flag to 'reschedule' to request thread
227  *              re-scheduling upon ISR function. Set flag
228  *              'no_reschedule' to skip thread re-scheduling
229  *
230  * Note: the function is an ARM Cortex-M only API.
231  *
232  * @return Interrupt vector assigned to this interrupt.
233  */
234 #define ARM_IRQ_DIRECT_DYNAMIC_CONNECT(irq_p, priority_p, flags_p, resch) \
235 	IRQ_DIRECT_CONNECT(irq_p, priority_p, \
236 		_CONCAT(z_arm_irq_direct_dynamic_dispatch_, resch), flags_p)
237 
238 #endif /* CONFIG_DYNAMIC_DIRECT_INTERRUPTS */
239 
240 #if defined(CONFIG_ARM_SECURE_FIRMWARE)
241 /* Architecture-specific definition for the target security
242  * state of an NVIC IRQ line.
243  */
244 typedef enum {
245 	IRQ_TARGET_STATE_SECURE = 0,
246 	IRQ_TARGET_STATE_NON_SECURE
247 } irq_target_state_t;
248 
249 #endif /* CONFIG_ARM_SECURE_FIRMWARE */
250 
251 #endif /* _ASMLANGUAGE */
252 
253 #ifdef __cplusplus
254 }
255 #endif
256 
257 #endif /* ZEPHYR_INCLUDE_ARCH_ARM_IRQ_H_ */
258