336 lines
7.7 KiB
ArmAsm
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
|