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