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