blob: dd1aa7a3eb272509ce88cf2b8c374c45522fd0d4 [file] [log] [blame]
// core-save.S -- core state save/restore routines (used by PSO)
// $Id: //depot/rel/Eaglenest/Xtensa/OS/xtos/core-save.S#1 $
// Copyright (c) 2012-2013 Tensilica Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/specreg.h>
#include <xtensa/cacheasm.h>
#include <xtensa/cacheattrasm.h>
#include <xtensa/xdm-regs.h>
#include <xtensa/config/specreg.h>
#include <xtensa/xtruntime-core-state.h>
#include "xtos-internal.h"
.text
// (Place this alternate entry symbol *outside* the _xtos_core_save()
// function, to avoid confusing debugging / profiling / etc.)
.align 4
.global _xtos_core_save_entry
.type _xtos_core_save_entry,@function
_xtos_core_save_entry:
j .Lcore_save
.size _xtos_core_save_entry, . - _xtos_core_save_entry
// int _xtos_core_save(unsigned flags, XtosCoreState *savearea, void *code)
//
// Generic processor state save routine.
//
// On entry (after ENTRY if windowed):
// a0 = return PC
// a2 = flags argument
// a3 = ptr to save area
// a4 = ptr to code to jump to after save (just return if 0)
// Returns:
// 0 when first returning from this call (if a4 == 0)
// non-zero (passed from restore call) when returning from restore
// (if a4 != 0, return behavior if any depends on code at a4)
//
.align 4
.global _xtos_core_save
.type _xtos_core_save,@function
_xtos_core_save:
abi_entry
.Lcore_save:
s32i a0, a3, CS_SA_areg + 0*4 // save a0 (clobbered below)
s32i a1, a3, CS_SA_areg + 1*4 // save a1
s32i a2, a3, CS_SA_areg + 2*4 // save a2 (flags arg, for debugging only)
s32i a4, a3, CS_SA_areg + 4*4 // save a4 (code to jump to after saving)
#ifdef __XTENSA_CALL0_ABI__
// Callee-saved regs:
s32i a12, a3, CS_SA_areg + 12*4 // save a12
s32i a13, a3, CS_SA_areg + 13*4 // save a13
s32i a14, a3, CS_SA_areg + 14*4 // save a14
s32i a15, a3, CS_SA_areg + 15*4 // save a15
#else
call4 xthal_window_spill // spill live caller windows to stack
#endif
j .Ls1
.align 16
.Ls1:
#if XCHAL_HAVE_INTERRUPTS
rsil a4, 15 // disable interrupts before clobbering a0
#elif XCHAL_HAVE_EXCEPTIONS
rsr a4, ps
#endif
#if XCHAL_HAVE_CCOUNT
rsr a5, CCOUNT // save CCOUNT restore value
#endif
#if XCHAL_HAVE_INTERRUPTS
rsr a6, INTERRUPT // save pending interrupts
s32i a6, a3, CS_SA_interrupt
#endif
#if XCHAL_HAVE_CCOUNT
s32i a5, a3, CS_SA_ccount
#endif
call0 _xtos_core_save_common // save and shutoff -- returns after wakeup
// a2 now contains return value.
// a3 still points to save area.
// Interrupts still disabled.
// Restore WINDOWSTART to single window. Leave WINDOWBASE wherever it is.
//rsr a6, WINDOWBASE
//movi a5, 1
//ssl a6
//sll a5, a5
//wsr a5, WINDOWSTART
//rsync
l32i a0, a3, CS_SA_areg + 0*4 // restore a0
l32i a1, a3, CS_SA_areg + 1*4 // restore a1
#ifdef __XTENSA_CALL0_ABI__
// Callee-saved regs:
l32i a12, a3, CS_SA_areg + 12*4 // restore a12
l32i a13, a3, CS_SA_areg + 13*4 // restore a13
l32i a14, a3, CS_SA_areg + 14*4 // restore a14
l32i a15, a3, CS_SA_areg + 15*4 // restore a15
#endif
#if XCHAL_HAVE_EXCEPTIONS
// Now that we've restored windowed state (a0,a1), we can restore interrupts.
l32i a4, a3, CS_SA_ps // restore ps
wsr a4, ps
rsync
#endif
abi_return
// Generic processor state save routine, callable from assembly-level
// (Does not assume valid stack, saves all ARs, no window-spill etc.)
//
// On entry:
// a0 = return PC
// a2 = flags argument
// a3 = ptr to save area
// a4 = ptr to code to jump to after save (just return if 0)
// All other registers are saved.
// Returns:
// 0 when first returning from this call (if a4 == 0)
// non-zero (passed from restore call) when returning from restore
// (if a4 != 0, return behavior if any depends on code at a4)
//
.align 4
.global _xtos_core_save_nw
_xtos_core_save_nw:
s32i a0, a3, CS_SA_areg + 0*4 // save a0 (clobbered below)
s32i a1, a3, CS_SA_areg + 1*4 // save a1
s32i a2, a3, CS_SA_areg + 2*4 // save a2 (flags arg, for debugging only)
s32i a4, a3, CS_SA_areg + 4*4 // save a4 (code to jump to after saving)
s32i a5, a3, CS_SA_areg + 5*4 // save a5
s32i a6, a3, CS_SA_areg + 6*4 // save a6
s32i a7, a3, CS_SA_areg + 7*4 // save a7
j .Ls2
.align 16
.Ls2:
#if XCHAL_HAVE_INTERRUPTS
rsil a4, 15 // disable interrupts before rotating etc
#elif XCHAL_HAVE_EXCEPTIONS
rsr a4, ps
#endif
#if XCHAL_HAVE_CCOUNT
rsr a5, CCOUNT // save CCOUNT restore value
#endif
#if XCHAL_HAVE_INTERRUPTS
rsr a6, INTERRUPT // save pending interrupts
s32i a6, a3, CS_SA_interrupt
#endif
#if XCHAL_HAVE_CCOUNT
s32i a5, a3, CS_SA_ccount
#endif
#if XCHAL_HAVE_WINDOWED
movi a5, XCHAL_NUM_AREGS / 8 - 1 // number of 8-reg chunks to save (a0-a7 already done)
#endif
1: s32i a8, a3, CS_SA_areg + 8*4 // save a8
s32i a9, a3, CS_SA_areg + 9*4 // save a9
s32i a10,a3, CS_SA_areg + 10*4 // save a10
s32i a11,a3, CS_SA_areg + 11*4 // save a11
s32i a12,a3, CS_SA_areg + 12*4 // save a12
s32i a13,a3, CS_SA_areg + 13*4 // save a13
s32i a14,a3, CS_SA_areg + 14*4 // save a14
s32i a15,a3, CS_SA_areg + 15*4 // save a15
#if XCHAL_HAVE_WINDOWED
addi a11, a3, 8*4 // next frame (a11 will become a3, a13 become a5)
addi a13, a5, -1
rotw 2
bnez a5, 1b // loop until all frames done
rotw 2 // back to starting windowbase
#endif
movi a1, 0 // not to save any regs from stack
call0 _xtos_core_save_common
// a2 now contains return value.
// a3 still points to save area.
// Interrupts still disabled.
#if XCHAL_HAVE_WINDOWED
rotw -2
movi a5, XCHAL_NUM_AREGS / 8 - 1 // 8-reg chunks to restore (a0-a7 already done)
addi a3, a11, XCHAL_NUM_AREGS * 4
1: rotw -2
addi a3, a11, -8*4
addi a5, a13, -1
#endif
l32i a8, a3, CS_SA_areg + 8*4 // restore a8
l32i a9, a3, CS_SA_areg + 9*4 // restore a9
l32i a10,a3, CS_SA_areg + 10*4 // restore a10
l32i a11,a3, CS_SA_areg + 11*4 // restore a11
l32i a12,a3, CS_SA_areg + 12*4 // restore a12
l32i a13,a3, CS_SA_areg + 13*4 // restore a13
l32i a14,a3, CS_SA_areg + 14*4 // restore a14
l32i a15,a3, CS_SA_areg + 15*4 // restore a15
#if XCHAL_HAVE_WINDOWED
bnez a5, 1b // loop until all frames done
// We're now back to starting windowbase, and original a3.
#endif
l32i a0, a3, CS_SA_areg + 0*4 // restore a0
l32i a1, a3, CS_SA_areg + 1*4 // restore a1
// Don't clobber return value, so don't restore a2.
l32i a4, a3, CS_SA_areg + 4*4 // restore a4
l32i a5, a3, CS_SA_areg + 5*4 // restore a5
l32i a6, a3, CS_SA_areg + 6*4 // restore a6
#if XCHAL_HAVE_EXCEPTIONS
// Now that we've restored windowed state (a0,a1,done rotating), we can restore interrupts.
l32i a7, a3, CS_SA_ps // restore ps
wsr a7, ps
rsync
#endif
l32i a7, a3, CS_SA_areg + 7*4 // restore a7
ret
// Common state save / shut-off code.
//
// a0 = return PC within caller shut-off routine
// a1 = stack if != 0
// a2 = flags argument
// a3 = pointer to _xtos_pso_savearea
// a4 = PS to save/restore
// PS.INTLEVEL = 15 (interrupts disabled, except NMI)
// a5-a15 (and other ARs) are available.
// NOTE: CCOUNT and INTERRUPT have already been saved in save area.
//
.align 4
//.global _xtos_core_save_common
_xtos_core_save_common:
#if XCHAL_HAVE_EXCEPTIONS
s32i a4, a3, CS_SA_ps // save PS
#endif
#if XCHAL_HAVE_WINDOWED
// The following discussion is valid if we have a stack:
// At this point, all non-live register windows have been spilled to the
// stack. However, we cannot leave any spilled registers in our stack frame
// or our caller's stack frame, since these frames could change after we
// return and before restore() is called. So all spilled registers in the
// current and previous stack frames must be saved to the save area. This
// means a max of 16 registers: 4 base save registers for our caller, upto
// 8 extra save registers for our caller, and 4 base save registers for the
// next function up from our caller. The stack looks like this:
//
// ------------------------------- <---- stack ptr of function (i - 2)
// Base save area i - 3
// -------------------------------
// Extra save area i - 1
// (0-8 registers depending on call type)
// -------------------------------
// Locals i - 1
// ------------------------------- <---- stack ptr of function (i - 1)
// Base save area i - 2 (our caller)
//
// ------------------------------- <---- Our stack ptr (a1)
// Base save area i - 1
// -------------------------------
//
// We don't have any extra save area or locals in our frame. See the
// Xtensa Programmer's Guide for more details of the stack layout.
//
// NOTE that we are not counting the call0 to _xtos_core_save_common() since
// that does not result in any register window rotation nor stack ptr change.
s32i a1, a3, CS_SA_caller_regs_saved // save flag
beqz a1, .Lendcr // skip if no stack
// Save our caller's a0-a3 from the base save area (a1-16)
addi a4, a1, -16
l32i a5, a4, 0
l32i a6, a4, 4
s32i a5, a3, CS_SA_caller_regs // caller a0
s32i a6, a3, CS_SA_caller_regs + 4 // caller a1
l32i a5, a4, 8
l32i a6, a4, 12
s32i a5, a3, CS_SA_caller_regs + 8 // caller a2
s32i a6, a3, CS_SA_caller_regs + 12 // caller a3
// Save our callers caller's a0-a3 from its base save area (a1+0)
l32i a5, a1, 0
l32i a6, a1, 4
s32i a5, a3, CS_SA_caller_regs + 16 // caller caller a0
s32i a6, a3, CS_SA_caller_regs + 20 // caller caller a1
l32i a5, a1, 8
l32i a6, a1, 12
s32i a5, a3, CS_SA_caller_regs + 24 // caller caller a2
s32i a6, a3, CS_SA_caller_regs + 28 // caller caller a3
// Now save 0-8 registers for our caller from its ext save area
// NOTE we can't use a0 directly because we are one level down
l32i a4, a3, CS_SA_areg // pull in the return address
extui a4, a4, 30, 2 // Top 2 bits of ret addr
blti a4, 2, .Lendcr // No regs to save
l32i a5, a1, 4 // a5 <- caller caller a1
slli a4, a4, 4
sub a4, a5, a4 // a4 <- bottom of extra save area
addi a5, a5, -16 // a5 <- top of extra save area
addi a6, a3, CS_SA_caller_regs + 32 // location to start saving to
.Lcrloop:
l32i a7, a4, 0 // Save in groups of 4 registers
l32i a8, a4, 4
s32i a7, a6, 0
s32i a8, a6, 4
l32i a7, a4, 8
l32i a8, a4, 12
s32i a7, a6, 8
s32i a8, a6, 12
addi a4, a4, 16
addi a6, a6, 16
blt a4, a5, .Lcrloop
.Lendcr:
#endif
// We want to save the CCOUNT value as soon as feasible after disabling
// interrupts, so that the counter does not run past any CCOMPARE value
// and miss a timer interrupt. The callers of this function have saved
// the values of CCOUNT and INTERRUPT immediately after disabling interrupts.
#if XCHAL_HAVE_CCOUNT
.macro save_timer num
rsr a5, CCOMPARE_0 + \num
s32i a5, a3, CS_SA_ccompare + 4*\num
.endm
iterate 0, XCHAL_NUM_TIMERS-1, save_timer
#endif
s32i a0, a3, CS_SA_restore_label // where to return to, to return from function
#if XCHAL_HAVE_INTERRUPTS || XCHAL_HAVE_EXCEPTIONS
rsr a5, EPC1
s32i a5, a3, CS_SA_epc1
rsr a5, EXCSAVE1
s32i a5, a3, CS_SA_excsave1
# ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR
rsr a5, DEPC
s32i a5, a3, CS_SA_depc
# endif
#endif
#if XCHAL_HAVE_WINDOWED
rsr a5, WINDOWBASE
s32i a5, a3, CS_SA_windowbase // save windowbase
rsr a5, WINDOWSTART
s32i a5, a3, CS_SA_windowstart // save windowstart
#endif
rsr a5, sar
s32i a5, a3, CS_SA_sar // save sar
#if XCHAL_HAVE_PSO_CDM
// Save PWRCTL, and update according to flags argument.
movi a4, XDM_MISC_PWRCTL
movi a6, PWRCTL_MEM_WAKEUP
rer a7, a4 // get pwrctl
s32i a7, a3, CS_SA_pwrctl // save pwrctl
// Avoid setting power-control bits if not already set, i.e. clear them only.
bbci.l a2, XTOS_COREF_PSO_SHIFT, 1f // if not shutting off, don't touch power bits
// Set PWRCTL MEM_WAKEUP bit according to flags (whether to let mem power off).
or a5, a7, a6 // set...
xor a5, a5, a6 // ... and clear MEM_WAKEUP bit to write
and a6, a2, a6 // isolate MEM_WAKEUP bit from flags
or a5, a5, a6 // set MEM_WAKEUP bit to write from flags
// Clear PWRCTL DEBUG_WAKEUP bit if cleared in flags (if letting debug power off).
movi a6, ~PWRCTL_DEBUG_WAKEUP
or a6, a2, a6 // isolate DEBUG_WAKEUP bit from flags
and a6, a5, a6 // clear it if was clear in flags
// Update PWRCTL
wer a6, a4 // write new pwrctl
//extw // let the new pwrctl value settle
1:
#endif
.macro save_level num
rsr a5, EPC_2 + \num
s32i a5, a3, CS_SA_epc + 4*\num
rsr a5, EPS_2 + \num
s32i a5, a3, CS_SA_eps + 4*\num
rsr a5, EXCSAVE_2 + \num
s32i a5, a3, CS_SA_excsave + 4*\num
.endm
iterate 0, XCHAL_NUM_INTLEVELS+XCHAL_HAVE_NMI-2, save_level
#if XCHAL_HAVE_LOOPS
rsr a5, LBEG
s32i a5, a3, CS_SA_lbeg
rsr a5, LEND
s32i a5, a3, CS_SA_lend
rsr a5, LCOUNT
s32i a5, a3, CS_SA_lcount
#endif
#if XCHAL_HAVE_ABSOLUTE_LITERALS
rsr a5, LITBASE
s32i a5, a3, CS_SA_litbase
#endif
#if XCHAL_HAVE_VECBASE
rsr a5, VECBASE
s32i a5, a3, CS_SA_vecbase
#endif
#if XCHAL_HAVE_S32C1I && (XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RC_2009_0) /* have ATOMCTL ? */
rsr a5, ATOMCTL
s32i a5, a3, CS_SA_atomctl
#endif
#if XCHAL_HAVE_PREFETCH
movi a5, 0 // disable prefetch during shutoff
xsr a5, PREFCTL
s32i a5, a3, CS_SA_prefctl
#endif
#if XCHAL_USE_MEMCTL
rsr a5, MEMCTL
s32i a5, a3, CS_SA_memctl
#endif
#if XCHAL_HAVE_INTERRUPTS
rsr a5, INTENABLE
s32i a5, a3, CS_SA_intenable
#endif
#if XCHAL_HAVE_DEBUG
// NOTE: restore of debug state is conditional,
// as the power-down and wakeup code might be actively debugged.
rsr a5, ICOUNT
s32i a5, a3, CS_SA_icount
rsr a5, ICOUNTLEVEL
s32i a5, a3, CS_SA_icountlevel
rsr a5, DEBUGCAUSE
s32i a5, a3, CS_SA_debugcause // (won't get restored?)
//rsr a5, DDR
//s32i a5, a3, CS_SA_ddr
# if XCHAL_NUM_IBREAK
rsr a5, IBREAKENABLE
s32i a5, a3, CS_SA_ibreakenable
# endif
.macro save_ibreak num
rsr a5, IBREAKA + \num
s32i a5, a3, CS_SA_ibreaka + 4*\num
.endm
iterate 0, XCHAL_NUM_IBREAK-1, save_ibreak
.macro save_dbreak num
rsr a5, DBREAKC + \num
s32i a5, a3, CS_SA_dbreakc + 4*\num
rsr a5, DBREAKA + \num
s32i a5, a3, CS_SA_dbreaka + 4*\num
.endm
iterate 0, XCHAL_NUM_DBREAK-1, save_dbreak
#endif
.macro save_misc num
rsr a5, MISC_REG_0 + \num
s32i a5, a3, CS_SA_misc + 4*\num
.endm
iterate 0, XCHAL_NUM_MISC_REGS-1, save_misc
#if XCHAL_HAVE_MEM_ECC_PARITY
rsr a5, MEPC
s32i a5, a3, CS_SA_mepc
rsr a5, MEPS
s32i a5, a3, CS_SA_meps
rsr a5, MESAVE
s32i a5, a3, CS_SA_mesave
rsr a5, MESR
s32i a5, a3, CS_SA_mesr
rsr a5, MECR
s32i a5, a3, CS_SA_mecr
rsr a5, MEVADDR
s32i a5, a3, CS_SA_mevaddr
#endif
/* TIE state */
addi a4, a3, CS_SA_ncp
xchal_ncp_store a4, a5,a6,a7,a8 // save non-coprocessor state
#if XCHAL_HAVE_CP
rsr a5, CPENABLE
s32i a5, a3, CS_SA_cpenable
movi a6, -1
wsr a6, CPENABLE // enable all coprocessors
rsync
xchal_cp0_store a4, a5,a6,a7,a8 continue=1
xchal_cp1_store a4, a5,a6,a7,a8 continue=1
xchal_cp2_store a4, a5,a6,a7,a8 continue=1
xchal_cp3_store a4, a5,a6,a7,a8 continue=1
xchal_cp4_store a4, a5,a6,a7,a8 continue=1
xchal_cp5_store a4, a5,a6,a7,a8 continue=1
xchal_cp6_store a4, a5,a6,a7,a8 continue=1
xchal_cp7_store a4, a5,a6,a7,a8 continue=1
//xchal_cp8_store a4, a5,a6,a7,a8 continue=1
//xchal_cp9_store a4, a5,a6,a7,a8 continue=1
//xchal_cp10_store a4, a5,a6,a7,a8 continue=1
//xchal_cp11_store a4, a5,a6,a7,a8 continue=1
//xchal_cp12_store a4, a5,a6,a7,a8 continue=1
//xchal_cp13_store a4, a5,a6,a7,a8 continue=1
//xchal_cp14_store a4, a5,a6,a7,a8 continue=1
//xchal_cp15_store a4, a5,a6,a7,a8 continue=1
#endif
/* TLB state (for known MMU types only, not internal custom) */
#if XCHAL_HAVE_MIMIC_CACHEATTR || XCHAL_HAVE_XLT_CACHEATTR
addi a4, a3, CS_SA_tlbs // where to start storing TLB entry info
movi a5, 0x20000000
movi a6, 0
1: rdtlb1 a7, a6 // read DTLB entry PPN + CA
s32i a7, a4, 0
ritlb1 a7, a6 // read ITLB entry PPN + CA
s32i a7, a4, 4
addi a4, a4, 8
add a6, a6, a5
bnez a6, 1b
#elif XCHAL_HAVE_PTP_MMU
// Declare a table of TLB entries to save/restore.
// Each entry is a 32-bit index to use directly with [rw][di]tlb[01].
// Indices assume ITLBCFG == DTLBCFG == 0.
// Bit 4 means not-for-dtlb, and bit 5 means not-for-itlb
// (these bits aren't used by these instructions, so okay to use for this).
.section .rodata, "a"
.global _xtos_pso_tlbmap
.global _xtos_pso_tlbmap_end
.type _xtos_pso_tlbmap, @object
_xtos_pso_tlbmap:
.long 0x0C0C0C0C, ARF_ENTRIES // *TLB way 0, 4/8 entries of 4KB
.long 0x0C0C0C0C, ARF_ENTRIES // *TLB way 1, 4/8 entries of 4KB
.long 0x0C0C0C0C, ARF_ENTRIES // *TLB way 2, 4/8 entries of 4KB
.long 0x0C0C0C0C, ARF_ENTRIES // *TLB way 3, 4/8 entries of 4KB
.long 0x1A181614, 4 // *TLB way 4, 4 entries of 1MB/4MB/16MB/64MB
# if XCHAL_HAVE_SPANNING_WAY /* MMU v3 */
.long 0x1C1B1C1B, 4 // *TLB way 5, 4 entries of 128MB/256MB
.long 0x1B1D1B1D, 8 // *TLB way 6, 8 entries of 512MB/128MB
# endif
.long 0x0C0C0C0C, 0x8001 // DTLB way 7, 1 entry of 4KB
.long 0x0C0C0C0C, 0x8001 // DTLB way 8, 1 entry of 4KB
.long 0x0C0C0C0C, 0x8001 // DTLB way 9, 1 entry of 4KB
_xtos_pso_tlbmap_end:
.size _xtos_pso_tlbmap, . - _xtos_pso_tlbmap
.text
addi a4, a3, CS_SA_tlbs // where to start storing TLB entry info
movi a10, _xtos_pso_tlbmap
movi a11, _xtos_pso_tlbmap_end
rsr a14, dtlbcfg // page size index (0..3) for each DTLB way
rsr a15, itlbcfg // page size index (0..3) for each ITLB way
s32i a14, a3, CS_SA_dtlbcfg
s32i a15, a3, CS_SA_itlbcfg
rsr a5, ptevaddr
s32i a5, a3, CS_SA_ptevaddr
rsr a5, rasid
s32i a5, a3, CS_SA_rasid
// Loop from last way to first (less register pressure that way).
.Loop_tlbmap:
addi a11, a11, -8 // next way
l32i a8, a11, 0 // map of four (page size log2) per index for this way
// DTLB page size:
extui a12, a14, 0, 4 // page size index for this DTLB way
srli a14, a14, 4 // (for next way)
ssa8l a12 // prepare to shift right by 8*a12
srl a12, a8 // page size log2 for this DTLB way
ssl a12 // prepare to shift left by a12
movi a12, 1 // (to compute 1 << (page size log2))
sll a12, a12 // page size for this DTLB way
// Save all entries of this DTLB way:
l32i a9, a11, 4 // number of entries for this way
sub a5, a11, a10 // way number * 8
srli a5, a5, 3 // way number
extui a9, a9, 0, 8
1: rdtlb0 a6, a5 // read DTLB entry VPN + ASID ...
rdtlb1 a7, a5 // read DTLB entry PPN + CA ...
add a5, a5, a12 // next entry of this DTLB way
s32i a6, a4, 0 // save entry ...
s32i a7, a4, 4
addi a4, a4, 8
addi a9, a9, -1
bnez a9, 1b
// ITLB page size:
extui a12, a15, 0, 4 // page size index for this ITLB way
srli a15, a15, 4 // (for next way)
ssa8l a12 // prepare to shift right by 8*a12
srl a12, a8 // page size log2 for this ITLB way
ssl a12 // prepare to shift left by a12
movi a12, 1 // (to compute 1 << (page size log2))
sll a12, a12 // page size for this ITLB way
// Save all entries of this ITLB way:
l32i a9, a11, 4 // number of entries for this way
sub a5, a11, a10 // way number * 8
srli a5, a5, 3 // way number
bbsi.l a9, 15, 2f // skip ITLB if is a DTLB-only way
extui a9, a9, 0, 8
1: ritlb0 a6, a5 // read ITLB entry VPN + ASID ...
ritlb1 a7, a5 // read ITLB entry PPN + CA ...
add a5, a5, a12 // next entry of this ITLB way
s32i a6, a4, 0 // save entry ...
s32i a7, a4, 4
addi a4, a4, 8
addi a9, a9, -1
bnez a9, 1b
2:
bne a11, a10, .Loop_tlbmap // loop for next TLB way
// Done saving TLBs.
#endif
// With data cache coherency enabled, need a full data cache
// writeback and invalidate, then disable coherency, before shut-off.
// Otherwise, if we'll let dcache power off, writeback its contents.
//
// We make sure the signature only gets written after everything
// else is written back (if we writeback), and only gets written
// back if the rest gets written back.
movi a6, CORE_STATE_SIGNATURE
#if XCHAL_DCACHE_IS_WRITEBACK
# if XCHAL_HAVE_PSO_CDM && XCHAL_DCACHE_IS_COHERENT && XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RE_2012_0
rsr a4, MEMCTL
bbci.l a2, XTOS_COREF_PSO_SHIFT, 1f // if not shutting off, leave snoops as is
bbci.l a4, MEMCTL_SNOOP_EN_SHIFT, 1f // snoops (coherence) enabled?
dcache_writeback_inv_all a4, a5, 0 // yes: writeback-invalidate
memw // wait for writeback to complete
s32i a6, a3, CS_SA_signature
dhwbi a3, CS_SA_signature
// Now that dcache is empty, make sure snoops are off during shut-off.
addi a4, a4, -MEMCTL_SNOOP_EN
wsr a4, MEMCTL
j 9f
1:
# endif
bbsi.l a2, PWRCTL_MEM_WAKEUP_SHIFT, 7f // letting caches power off?
dcache_writeback_all a4, a5, 0 // yes: writeback
memw // wait for writeback to complete
j 8f
// The signature and the cache/TLB state must be written out to
// main memory even though the caches stay on, because on restart
// we will come up with caches bypassed and need to read the state
// back before the cache/TLB is set up.
7:
mov a4, a3
movi a5, CS_SA_ncp
dcache_writeback_region a4, a5, a7
memw
8:
s32i a6, a3, CS_SA_signature
dhwb a3, CS_SA_signature // needed even if caches stay on
#else
s32i a6, a3, CS_SA_signature
#endif
9: l32i a4, a3, CS_SA_areg + 4*4 // restore a4 (code to jump to after saving)
memw // wait for signature to be in memory
beqz a4, 1f // code to jump to?
jx a4 // yes, jump to it
1: l32i a0, a3, CS_SA_restore_label // no, return: restore return PC
movi a2, 0 // return 0
ret
.size _xtos_core_save, . - _xtos_core_save