You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
7.2 KiB
298 lines
7.2 KiB
/* |
|
* Copyright (c) 2014 Wind River Systems, Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/** |
|
* @file |
|
* @brief Handling of transitions to-and-from fast IRQs (FIRQ) |
|
* |
|
* This module implements the code for handling entry to and exit from Fast IRQs. |
|
* |
|
* See isr_wrapper.S for details. |
|
*/ |
|
|
|
#include <zephyr/kernel_structs.h> |
|
#include <offsets_short.h> |
|
#include <zephyr/toolchain.h> |
|
#include <zephyr/linker/sections.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include <swap_macros.h> |
|
|
|
GTEXT(_firq_enter) |
|
GTEXT(_firq_exit) |
|
|
|
/** |
|
* @brief Work to be done before handing control to a FIRQ ISR |
|
* |
|
* The processor switches to a second register bank so registers from the |
|
* current bank do not have to be preserved yet. The only issue is the LP_START/ |
|
* LP_COUNT/LP_END registers, which are not banked. These can be saved |
|
* in available callee saved registers. |
|
* |
|
* If all FIRQ ISRs are programmed such that there are no use of the LP |
|
* registers (ie. no LPcc instruction), and CONFIG_ARC_STACK_CHECKING is |
|
* not set, then the kernel can be configured to not save and restore them. |
|
* |
|
* When entering a FIRQ, interrupts might as well be locked: the processor is |
|
* running at its highest priority, and cannot be interrupted by any other |
|
* interrupt. An exception, however, can be taken. |
|
* |
|
* Assumption by _isr_demux: r3 is untouched by _firq_enter. |
|
*/ |
|
|
|
SECTION_FUNC(TEXT, _firq_enter) |
|
/* |
|
* ATTENTION: |
|
* If CONFIG_RGF_NUM_BANKS>1, firq uses a 2nd register bank so GPRs do |
|
* not need to be saved. |
|
* If CONFIG_RGF_NUM_BANKS==1, firq must use the stack to save registers. |
|
* This has already been done by _isr_wrapper. |
|
*/ |
|
#ifdef CONFIG_ARC_STACK_CHECKING |
|
#ifdef CONFIG_ARC_SECURE_FIRMWARE |
|
lr r2, [_ARC_V2_SEC_STAT] |
|
bclr r2, r2, _ARC_V2_SEC_STAT_SSC_BIT |
|
sflag r2 |
|
#else |
|
/* disable stack checking */ |
|
lr r2, [_ARC_V2_STATUS32] |
|
bclr r2, r2, _ARC_V2_STATUS32_SC_BIT |
|
kflag r2 |
|
#endif |
|
#endif |
|
|
|
#if CONFIG_RGF_NUM_BANKS != 1 |
|
/* |
|
* Save LP_START/LP_COUNT/LP_END because called handler might use. |
|
* Save these in callee saved registers to avoid using memory. |
|
* These will be saved by the compiler if it needs to spill them. |
|
*/ |
|
mov r23,lp_count |
|
lr r24, [_ARC_V2_LP_START] |
|
lr r25, [_ARC_V2_LP_END] |
|
#endif |
|
|
|
/* check whether irq stack is used */ |
|
_check_and_inc_int_nest_counter r0, r1 |
|
|
|
bne.d firq_nest |
|
mov_s r0, sp |
|
|
|
_get_curr_cpu_irq_stack sp |
|
#if CONFIG_RGF_NUM_BANKS != 1 |
|
b firq_nest_1 |
|
firq_nest: |
|
/* |
|
* because firq and rirq share the same interrupt stack, |
|
* switch back to original register bank to get correct sp. |
|
* to get better firq latency, an approach is to prepare |
|
* separate interrupt stack for firq and do not do thread |
|
* switch in firq. |
|
*/ |
|
lr r1, [_ARC_V2_STATUS32] |
|
and r1, r1, ~_ARC_V2_STATUS32_RB(7) |
|
kflag r1 |
|
|
|
/* here use _ARC_V2_USER_SP and ilink to exchange sp |
|
* save original value of _ARC_V2_USER_SP and ilink into |
|
* the stack of interrupted context first, then restore them later |
|
*/ |
|
push ilink |
|
PUSHAX ilink, _ARC_V2_USER_SP |
|
|
|
/* sp here is the sp of interrupted context */ |
|
sr sp, [_ARC_V2_USER_SP] |
|
/* here, bank 0 sp must go back to the value before push and |
|
* PUSHAX as we will switch to bank1, the pop and POPAX later will |
|
* change bank1's sp, not bank0's sp |
|
*/ |
|
add sp, sp, 8 |
|
|
|
/* switch back to banked reg, only ilink can be used */ |
|
lr ilink, [_ARC_V2_STATUS32] |
|
or ilink, ilink, _ARC_V2_STATUS32_RB(1) |
|
kflag ilink |
|
lr sp, [_ARC_V2_USER_SP] |
|
|
|
POPAX ilink, _ARC_V2_USER_SP |
|
pop ilink |
|
firq_nest_1: |
|
#else |
|
firq_nest: |
|
#endif |
|
push_s r0 |
|
j _isr_demux |
|
|
|
|
|
|
|
/** |
|
* @brief Work to be done exiting a FIRQ |
|
*/ |
|
|
|
SECTION_FUNC(TEXT, _firq_exit) |
|
|
|
#if CONFIG_RGF_NUM_BANKS != 1 |
|
/* restore lp_count, lp_start, lp_end from r23-r25 */ |
|
mov lp_count,r23 |
|
sr r24, [_ARC_V2_LP_START] |
|
sr r25, [_ARC_V2_LP_END] |
|
#endif |
|
_dec_int_nest_counter r0, r1 |
|
|
|
_check_nest_int_by_irq_act r0, r1 |
|
|
|
jne _firq_no_switch |
|
|
|
/* sp is struct k_thread **old of z_arc_switch_in_isr which is a wrapper of |
|
* z_get_next_switch_handle. r0 contains the 1st thread in ready queue. If it isn't NULL, |
|
* then do switch to this thread. |
|
*/ |
|
_get_next_switch_handle |
|
|
|
CMPR r0, 0 |
|
bne _firq_switch |
|
|
|
/* fall to no switch */ |
|
|
|
.align 4 |
|
_firq_no_switch: |
|
/* restore interrupted context' sp */ |
|
pop sp |
|
/* |
|
* Keeping this code block close to those that use it allows using brxx |
|
* instruction instead of a pair of cmp and bxx |
|
*/ |
|
#if CONFIG_RGF_NUM_BANKS == 1 |
|
_pop_irq_stack_frame |
|
#endif |
|
rtie |
|
|
|
.align 4 |
|
_firq_switch: |
|
/* restore interrupted context' sp */ |
|
pop sp |
|
|
|
#if CONFIG_RGF_NUM_BANKS != 1 |
|
/* |
|
* save r0, r2 in irq stack for a while, as they will be changed by register |
|
* bank switch |
|
*/ |
|
_get_curr_cpu_irq_stack r1 |
|
st r0, [r1, -4] |
|
st r2, [r1, -8] |
|
|
|
/* |
|
* We know there is no interrupted interrupt of lower priority at this |
|
* point, so when switching back to register bank 0, it will contain the |
|
* registers from the interrupted thread. |
|
*/ |
|
#if defined(CONFIG_USERSPACE) |
|
/* when USERSPACE is configured, here need to consider the case where firq comes |
|
* out in user mode, according to ARCv2 ISA and nsim, the following micro ops |
|
* will be executed: |
|
* sp<-reg bank1'sp |
|
* switch between sp and _ARC_V2_USER_SP |
|
* then: |
|
* sp is the sp of kernel stack of interrupted thread |
|
* _ARC_V2_USER_SP is reg bank1'sp |
|
* the sp of user stack of interrupted thread is reg bank0'sp |
|
* if firq comes out in kernel mode, the following micro ops will be executed: |
|
* sp<-reg bank'sp |
|
* so, sw needs to do necessary handling to set up the correct sp |
|
*/ |
|
lr r0, [_ARC_V2_AUX_IRQ_ACT] |
|
bbit0 r0, 31, _firq_from_kernel |
|
aex sp, [_ARC_V2_USER_SP] |
|
lr r0, [_ARC_V2_STATUS32] |
|
and r0, r0, ~_ARC_V2_STATUS32_RB(7) |
|
kflag r0 |
|
aex sp, [_ARC_V2_USER_SP] |
|
b _firq_create_irq_stack_frame |
|
_firq_from_kernel: |
|
#endif |
|
/* chose register bank #0 */ |
|
lr r0, [_ARC_V2_STATUS32] |
|
and r0, r0, ~_ARC_V2_STATUS32_RB(7) |
|
kflag r0 |
|
|
|
_firq_create_irq_stack_frame: |
|
/* we're back on the outgoing thread's stack */ |
|
_create_irq_stack_frame |
|
|
|
/* |
|
* In a FIRQ, STATUS32 of the outgoing thread is in STATUS32_P0 and the |
|
* PC in ILINK: save them in status32/pc respectively. |
|
*/ |
|
|
|
lr r0, [_ARC_V2_STATUS32_P0] |
|
st_s r0, [sp, ___isf_t_status32_OFFSET] |
|
|
|
st ilink, [sp, ___isf_t_pc_OFFSET] /* ilink into pc */ |
|
/* |
|
* load r0, r2 from irq stack |
|
*/ |
|
_get_curr_cpu_irq_stack r1 |
|
ld r0, [r1, -4] |
|
ld r2, [r1, -8] |
|
#endif |
|
/* r2 is old thread */ |
|
st _CAUSE_FIRQ, [r2, _thread_offset_to_relinquish_cause] |
|
|
|
_irq_store_old_thread_callee_regs |
|
|
|
/* mov new thread (r0) to r2 */ |
|
|
|
mov r2, r0 |
|
_load_new_thread_callee_regs |
|
|
|
breq r3, _CAUSE_RIRQ, _firq_switch_from_rirq |
|
nop_s |
|
breq r3, _CAUSE_FIRQ, _firq_switch_from_firq |
|
nop_s |
|
|
|
/* fall through */ |
|
|
|
.align 4 |
|
_firq_switch_from_coop: |
|
|
|
_set_misc_regs_irq_switch_from_coop |
|
|
|
/* pc into ilink */ |
|
pop_s r0 |
|
mov ilink, r0 |
|
|
|
pop_s r0 /* status32 into r0 */ |
|
sr r0, [_ARC_V2_STATUS32_P0] |
|
|
|
#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING |
|
push_s blink |
|
|
|
bl z_thread_mark_switched_in |
|
|
|
pop_s blink |
|
#endif |
|
rtie |
|
|
|
.align 4 |
|
_firq_switch_from_rirq: |
|
_firq_switch_from_firq: |
|
|
|
_set_misc_regs_irq_switch_from_irq |
|
|
|
_pop_irq_stack_frame |
|
|
|
ld ilink, [sp, -4] /* status32 into ilink */ |
|
sr ilink, [_ARC_V2_STATUS32_P0] |
|
ld ilink, [sp, -8] /* pc into ilink */ |
|
|
|
#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING |
|
push_s blink |
|
|
|
bl z_thread_mark_switched_in |
|
|
|
pop_s blink |
|
#endif |
|
/* LP registers are already restored, just switch back to bank 0 */ |
|
rtie
|
|
|