1/* 2 * Copyright (c) 2014 Wind River Systems, Inc. 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7/** 8 * @file 9 * @brief Handling of transitions to-and-from fast IRQs (FIRQ) 10 * 11 * This module implements the code for handling entry to and exit from Fast IRQs. 12 * 13 * See isr_wrapper.S for details. 14 */ 15 16#include <kernel_structs.h> 17#include <offsets_short.h> 18#include <toolchain.h> 19#include <linker/sections.h> 20#include <arch/cpu.h> 21#include <swap_macros.h> 22 23GTEXT(_firq_enter) 24GTEXT(_firq_exit) 25 26/** 27 * 28 * @brief Work to be done before handing control to a FIRQ ISR 29 * 30 * The processor switches to a second register bank so registers from the 31 * current bank do not have to be preserved yet. The only issue is the LP_START/ 32 * LP_COUNT/LP_END registers, which are not banked. These can be saved 33 * in available callee saved registers. 34 * 35 * If all FIRQ ISRs are programmed such that there are no use of the LP 36 * registers (ie. no LPcc instruction), and CONFIG_ARC_STACK_CHECKING is 37 * not set, then the kernel can be configured to not save and restore them. 38 * 39 * When entering a FIRQ, interrupts might as well be locked: the processor is 40 * running at its highest priority, and cannot be interrupted by any other 41 * interrupt. An exception, however, can be taken. 42 * 43 * Assumption by _isr_demux: r3 is untouched by _firq_enter. 44 * 45 * @return N/A 46 */ 47 48SECTION_FUNC(TEXT, _firq_enter) 49/* 50 * ATTENTION: 51 * If CONFIG_RGF_NUM_BANKS>1, firq uses a 2nd register bank so GPRs do 52 * not need to be saved. 53 * If CONFIG_RGF_NUM_BANKS==1, firq must use the stack to save registers. 54 * This has already been done by _isr_wrapper. 55 */ 56#ifdef CONFIG_ARC_STACK_CHECKING 57#ifdef CONFIG_ARC_SECURE_FIRMWARE 58 lr r2, [_ARC_V2_SEC_STAT] 59 bclr r2, r2, _ARC_V2_SEC_STAT_SSC_BIT 60 sflag r2 61#else 62 /* disable stack checking */ 63 lr r2, [_ARC_V2_STATUS32] 64 bclr r2, r2, _ARC_V2_STATUS32_SC_BIT 65 kflag r2 66#endif 67#endif 68 69#if CONFIG_RGF_NUM_BANKS != 1 70 /* 71 * Save LP_START/LP_COUNT/LP_END because called handler might use. 72 * Save these in callee saved registers to avoid using memory. 73 * These will be saved by the compiler if it needs to spill them. 74 */ 75 mov r23,lp_count 76 lr r24, [_ARC_V2_LP_START] 77 lr r25, [_ARC_V2_LP_END] 78#endif 79 80 /* check whether irq stack is used */ 81 _check_and_inc_int_nest_counter r0, r1 82 83 bne.d firq_nest 84 mov_s r0, sp 85 86 _get_curr_cpu_irq_stack sp 87#if CONFIG_RGF_NUM_BANKS != 1 88 b firq_nest_1 89firq_nest: 90 /* 91 * because firq and rirq share the same interrupt stack, 92 * switch back to original register bank to get correct sp. 93 * to get better firq latency, an approach is to prepare 94 * separate interrupt stack for firq and do not do thread 95 * switch in firq. 96 */ 97 lr r1, [_ARC_V2_STATUS32] 98 and r1, r1, ~_ARC_V2_STATUS32_RB(7) 99 kflag r1 100 101 /* here use _ARC_V2_USER_SP and ilink to exchange sp 102 * save original value of _ARC_V2_USER_SP and ilink into 103 * the stack of interrupted context first, then restore them later 104 */ 105 push ilink 106 PUSHAX ilink, _ARC_V2_USER_SP 107 108 /* sp here is the sp of interrupted context */ 109 sr sp, [_ARC_V2_USER_SP] 110 /* here, bank 0 sp must go back to the value before push and 111 * PUSHAX as we will switch to bank1, the pop and POPAX later will 112 * change bank1's sp, not bank0's sp 113 */ 114 add sp, sp, 8 115 116 /* switch back to banked reg, only ilink can be used */ 117 lr ilink, [_ARC_V2_STATUS32] 118 or ilink, ilink, _ARC_V2_STATUS32_RB(1) 119 kflag ilink 120 lr sp, [_ARC_V2_USER_SP] 121 122 POPAX ilink, _ARC_V2_USER_SP 123 pop ilink 124firq_nest_1: 125#else 126firq_nest: 127#endif 128 push_s r0 129 j _isr_demux 130 131 132 133/** 134 * 135 * @brief Work to be done exiting a FIRQ 136 * 137 * @return N/A 138 */ 139 140SECTION_FUNC(TEXT, _firq_exit) 141 142#if CONFIG_RGF_NUM_BANKS != 1 143 /* restore lp_count, lp_start, lp_end from r23-r25 */ 144 mov lp_count,r23 145 sr r24, [_ARC_V2_LP_START] 146 sr r25, [_ARC_V2_LP_END] 147#endif 148 _dec_int_nest_counter r0, r1 149 150 _check_nest_int_by_irq_act r0, r1 151 152 jne _firq_no_switch 153 154 /* sp is struct k_thread **old of z_arc_switch_in_isr 155 * which is a wrapper of z_get_next_switch_handle. 156 * r0 contains the 1st thread in ready queue. if 157 * it equals _current(r2) ,then do swap, or no swap. 158 */ 159 _get_next_switch_handle 160 161 cmp r0, r2 162 bne _firq_switch 163 164 /* fall to no switch */ 165 166.align 4 167_firq_no_switch: 168 /* restore interrupted context' sp */ 169 pop sp 170 /* 171 * Keeping this code block close to those that use it allows using brxx 172 * instruction instead of a pair of cmp and bxx 173 */ 174#if CONFIG_RGF_NUM_BANKS == 1 175 _pop_irq_stack_frame 176#endif 177 rtie 178 179.align 4 180_firq_switch: 181 /* restore interrupted context' sp */ 182 pop sp 183 184#if CONFIG_RGF_NUM_BANKS != 1 185/* 186 * save r0, r2 in irq stack for a while, as they will be changed by register 187 * bank switch 188 */ 189 _get_curr_cpu_irq_stack r1 190 st r0, [r1, -4] 191 st r2, [r1, -8] 192 193 /* 194 * We know there is no interrupted interrupt of lower priority at this 195 * point, so when switching back to register bank 0, it will contain the 196 * registers from the interrupted thread. 197 */ 198#if defined(CONFIG_USERSPACE) 199/* when USERSPACE is configured, here need to consider the case where firq comes 200 * out in user mode, according to ARCv2 ISA and nsim, the following micro ops 201 * will be executed: 202 * sp<-reg bank1'sp 203 * switch between sp and _ARC_V2_USER_SP 204 * then: 205 * sp is the sp of kernel stack of interrupted thread 206 * _ARC_V2_USER_SP is reg bank1'sp 207 * the sp of user stack of interrupted thread is reg bank0'sp 208 * if firq comes out in kernel mode, the following micro ops will be executed: 209 * sp<-reg bank'sp 210 * so, sw needs to do necessary handling to set up the correct sp 211 */ 212 lr r0, [_ARC_V2_AUX_IRQ_ACT] 213 bbit0 r0, 31, _firq_from_kernel 214 aex sp, [_ARC_V2_USER_SP] 215 lr r0, [_ARC_V2_STATUS32] 216 and r0, r0, ~_ARC_V2_STATUS32_RB(7) 217 kflag r0 218 aex sp, [_ARC_V2_USER_SP] 219 b _firq_create_irq_stack_frame 220_firq_from_kernel: 221#endif 222 /* chose register bank #0 */ 223 lr r0, [_ARC_V2_STATUS32] 224 and r0, r0, ~_ARC_V2_STATUS32_RB(7) 225 kflag r0 226 227_firq_create_irq_stack_frame: 228 /* we're back on the outgoing thread's stack */ 229 _create_irq_stack_frame 230 231 /* 232 * In a FIRQ, STATUS32 of the outgoing thread is in STATUS32_P0 and the 233 * PC in ILINK: save them in status32/pc respectively. 234 */ 235 236 lr r0, [_ARC_V2_STATUS32_P0] 237 st_s r0, [sp, ___isf_t_status32_OFFSET] 238 239 st ilink, [sp, ___isf_t_pc_OFFSET] /* ilink into pc */ 240/* 241 * load r0, r2 from irq stack 242 */ 243 _get_curr_cpu_irq_stack r1 244 ld r0, [r1, -4] 245 ld r2, [r1, -8] 246#endif 247 /* r2 is old thread */ 248 _irq_store_old_thread_callee_regs 249 250 st _CAUSE_FIRQ, [r2, _thread_offset_to_relinquish_cause] 251 252 /* mov new thread (r0) to r2 */ 253 254 mov r2, r0 255 _load_new_thread_callee_regs 256 257 breq r3, _CAUSE_RIRQ, _firq_switch_from_rirq 258 nop_s 259 breq r3, _CAUSE_FIRQ, _firq_switch_from_firq 260 nop_s 261 262 /* fall through */ 263 264.align 4 265_firq_switch_from_coop: 266 267 _set_misc_regs_irq_switch_from_coop 268 269 /* pc into ilink */ 270 pop_s r0 271 mov ilink, r0 272 273 pop_s r0 /* status32 into r0 */ 274 sr r0, [_ARC_V2_STATUS32_P0] 275 276#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING 277 push_s blink 278 279 bl z_thread_mark_switched_in 280 281 pop_s blink 282#endif 283 rtie 284 285.align 4 286_firq_switch_from_rirq: 287_firq_switch_from_firq: 288 289 _set_misc_regs_irq_switch_from_irq 290 291 _pop_irq_stack_frame 292 293 ld ilink, [sp, -4] /* status32 into ilink */ 294 sr ilink, [_ARC_V2_STATUS32_P0] 295 ld ilink, [sp, -8] /* pc into ilink */ 296 297#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING 298 push_s blink 299 300 bl z_thread_mark_switched_in 301 302 pop_s blink 303#endif 304 /* LP registers are already restored, just switch back to bank 0 */ 305 rtie 306