/* * Copyright (c) 2019-2020 Cobham Gaisler AB * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #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