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