zephyr/arch/sparc/core/interrupt_trap.S

336 lines
7.7 KiB
ArmAsm

/*
* Copyright (c) 2019-2020 Cobham Gaisler AB
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/toolchain.h>
#include <zephyr/linker/sections.h>
#include <offsets_short.h>
#include <zephyr/arch/sparc/sparc.h>
#include "stack_offsets.h"
GTEXT(__sparc_trap_interrupt)
GTEXT(__sparc_trap_irq_offload)
/*
* Interrupt trap handler
*
* - IU state is saved and restored
*
* On entry:
* %l0: psr (set by trap code)
* %l1: pc
* %l2: npc
* %l3: SPARC interrupt request level (bp_IRL)
* %fp: %sp of current register window at trap time
*
* This module also implements the IRQ offload support. The handling is the
* same as for asynchronous maskable interrupts, with the following exceptions:
* - Do not re-execute the causing (ta) instruction at trap exit.
* - A dedicated interrupt request level (0x8d) is used.
* - z_sparc_enter_irq() knows how to interpret this interrupt request level.
*/
SECTION_SUBSEC_FUNC(TEXT, __sparc_trap_interrupt, __sparc_trap_irq_offload)
/* Preparation in the case of synchronous IRQ offload. */
mov %l2, %l1
add %l2, 4, %l2
set 0x8d, %l3
__sparc_trap_interrupt:
/* %g2, %g3 are used at manual window overflow so save temporarily */
mov %g2, %l4
mov %g3, %l5
/* We may have trapped into the invalid window. If so, make it valid. */
rd %wim, %g2
srl %g2, %l0, %g3
cmp %g3, 1
bne .Lwodone
nop
/* Do the window overflow. */
sll %g2, (CONFIG_SPARC_NWIN-1), %g3
srl %g2, 1, %g2
or %g2, %g3, %g2
/* Enter window to save. */
save
/* Install new wim calculated above. */
mov %g2, %wim
nop
nop
nop
/* Put registers on the dedicated save area of the ABI stack frame. */
std %l0, [%sp + 0x00]
std %l2, [%sp + 0x08]
std %l4, [%sp + 0x10]
std %l6, [%sp + 0x18]
std %i0, [%sp + 0x20]
std %i2, [%sp + 0x28]
std %i4, [%sp + 0x30]
std %i6, [%sp + 0x38]
/* Leave saved window. */
restore
.Lwodone:
/*
* %l4: %g2 at trap time
* %l5: %g3 at trap time
*
* Save the state of the interrupted task including global registers on
* the task stack.
*
* IMPORTANT: Globals are saved here as well on the task stack, since a
* context switch might happen before the context of this interrupted
* task is restored.
*/
/* Allocate stack for isr context. */
sub %fp, ISF_SIZE, %sp
/*
* %fp: %sp of interrupted task
* %sp: %sp of interrupted task - ISF_SIZE.
* (fits what we store here)
*
* Save the interrupted context.
*/
std %l0, [%sp + ISF_PSR_OFFSET] /* psr pc */
st %l2, [%sp + ISF_NPC_OFFSET] /* npc */
st %g1, [%sp + ISF_G1_OFFSET] /* g1 */
std %l4, [%sp + ISF_G2_OFFSET] /* g2 g3 */
st %g4, [%sp + ISF_G4_OFFSET] /* g4 */
rd %y, %g1
st %g1, [%sp + ISF_Y_OFFSET] /* y */
/* %l5: reference to _kernel */
set _kernel, %l5
/* Switch to interrupt stack. */
mov %sp, %fp
ld [%l5 + _kernel_offset_to_irq_stack], %sp
/* Allocate a full C stack frame */
sub %sp, STACK_FRAME_SIZE, %sp
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: irq stack - 96. An ABI frame
*/
/* Enable traps, raise PIL to mask all maskable interrupts. */
or %l0, PSR_PIL, %l6
#if defined(CONFIG_FPU)
/*
* We now check if the interrupted context was using the FPU. The
* result is stored in register l5 which will either get the value 0
* (FPU not used) or PSR_EF (FPU used).
*
* If the FPU was used by the interrupted context, then we do two
* things:
* 1. Store FSR to memory. This has the side-effect of completing all
* pending FPU operations.
* 2. Disable FPU. Floating point instructions in the ISR will trap.
*
* The FPU is be enabled again if needed after the ISR has returned.
*/
set PSR_EF, %l5
andcc %l0, %l5, %l5
bne,a 1f
st %fsr, [%sp]
1:
andn %l6, %l5, %l6
#endif
wr %l6, PSR_ET, %psr
nop
nop
nop
#ifdef CONFIG_SCHED_THREAD_USAGE
call z_sched_usage_stop
nop
#endif
#ifdef CONFIG_TRACING_ISR
call sys_trace_isr_enter
nop
#endif
/* SPARC interrupt request level is the first argument */
call z_sparc_enter_irq
mov %l3, %o0
#ifdef CONFIG_TRACING_ISR
call sys_trace_isr_exit
nop
#endif
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: irq stack - 96. An ABI frame
*/
#ifdef CONFIG_PREEMPT_ENABLED
/* allocate stack for calling C function and for its output value */
sub %fp, (96+8), %sp
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: %sp of interrupted task - ISF_SIZE - STACK_FRAME_SIZE - 8.
*/
call z_arch_get_next_switch_handle
add %sp, 96, %o0
/* we get old thread as "return value" on stack */
ld [%sp + 96], %o1
/*
* o0: new thread
* o1: old thread
*/
cmp %o0, %o1
beq .Lno_reschedule
/* z_sparc_context_switch() is a leaf function not using stack. */
add %sp, (96+8-64), %sp
#if defined(CONFIG_FPU_SHARING)
/* IF PSR_EF at trap time then store the FP context. */
cmp %l5, 0
be .Lno_fp_context
nop
/*
* PSR_EF was 1 at trap time so save the FP registers on stack.
* - Set PSR_EF so we can access the FP registers.
* - Allocate space for the FP registers above the save area used for
* the z_sparc_context_switch() call.
*/
wr %l6, %l5, %psr
nop
nop
nop
sub %sp, 34 * 4, %sp
std %f0, [%sp + 64 + 0x00]
std %f2, [%sp + 64 + 0x08]
std %f4, [%sp + 64 + 0x10]
std %f6, [%sp + 64 + 0x18]
std %f8, [%sp + 64 + 0x20]
std %f10, [%sp + 64 + 0x28]
std %f12, [%sp + 64 + 0x30]
std %f14, [%sp + 64 + 0x38]
std %f16, [%sp + 64 + 0x40]
std %f18, [%sp + 64 + 0x48]
std %f20, [%sp + 64 + 0x50]
std %f22, [%sp + 64 + 0x58]
std %f24, [%sp + 64 + 0x60]
std %f26, [%sp + 64 + 0x68]
std %f28, [%sp + 64 + 0x70]
std %f30, [%sp + 64 + 0x78]
call z_sparc_context_switch
st %fsr, [%sp + 64 + 0x80]
ldd [%sp + 64 + 0x00], %f0
ldd [%sp + 64 + 0x08], %f2
ldd [%sp + 64 + 0x10], %f4
ldd [%sp + 64 + 0x18], %f6
ldd [%sp + 64 + 0x20], %f8
ldd [%sp + 64 + 0x28], %f10
ldd [%sp + 64 + 0x30], %f12
ldd [%sp + 64 + 0x38], %f14
ldd [%sp + 64 + 0x40], %f16
ldd [%sp + 64 + 0x48], %f18
ldd [%sp + 64 + 0x50], %f20
ldd [%sp + 64 + 0x58], %f22
ldd [%sp + 64 + 0x60], %f24
ldd [%sp + 64 + 0x68], %f26
ldd [%sp + 64 + 0x70], %f28
ldd [%sp + 64 + 0x78], %f30
ld [%sp + 64 + 0x80], %fsr
ba .Lno_reschedule
add %sp, 34 * 4, %sp
.Lno_fp_context:
#endif /* CONFIG_FPU_SHARING */
call z_sparc_context_switch
nop
.Lno_reschedule:
#endif /* CONFIG_PREEMPT_ENABLED */
/* Restore the interrupted context. */
ld [%fp + ISF_Y_OFFSET], %g1
wr %g1, 0, %y
ldd [%fp + ISF_PSR_OFFSET], %l0 /* psr, pc */
ld [%fp + ISF_NPC_OFFSET], %l2 /* npc */
/* NOTE: %g1 will be restored later */
/* %g1 is used to access the stack frame later */
mov %fp, %g1
ldd [%fp + ISF_G2_OFFSET], %g2
ld [%fp + ISF_G4_OFFSET], %g4
add %fp, ISF_SIZE, %fp
/*
* Install the PSR we got from the interrupt context. Current PSR.CWP
* is preserved. Keep PSR.ET=0 until we do "rett".
*/
rd %psr, %l3
and %l3, PSR_CWP, %l3
andn %l0, (PSR_CWP | PSR_ET), %l0
or %l3, %l0, %l0
mov %l0, %psr
nop
nop
nop
/* Calculate %l6 := (cwp+1) % NWIN */
rd %wim, %l3
set (CONFIG_SPARC_NWIN), %l7
add %l0, 1, %l6
and %l6, PSR_CWP, %l6
cmp %l6, %l7
bge,a .Lwrapok
mov 0, %l6
.Lwrapok:
/* Determine if we must prepare the return window. */
/* %l5 := %wim >> (cwp+1) */
srl %l3, %l6, %l5
/* %l5 is 1 if (cwp+1) is an invalid window */
cmp %l5, 1
bne .Lwudone
sub %l7, 1, %l7 /* %l7 := NWIN - 1 */
/* Do the window underflow. */
sll %l3, 1, %l4
srl %l3, %l7, %l5
wr %l4, %l5, %wim
nop
nop
nop
restore
ldd [%g1 + 0x00], %l0
ldd [%g1 + 0x08], %l2
ldd [%g1 + 0x10], %l4
ldd [%g1 + 0x18], %l6
ldd [%g1 + 0x20], %i0
ldd [%g1 + 0x28], %i2
ldd [%g1 + 0x30], %i4
ldd [%g1 + 0x38], %i6
save
.Lwudone:
/*
* Restore %psr since we may have trashed condition codes. PSR.ET is
* still 0.
*/
wr %l0, %psr
nop
nop
nop
/* restore g1 */
ld [%g1 + ISF_G1_OFFSET], %g1
jmp %l1
rett %l2