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