/* SPDX-License-Identifier: GPL-2.01*2* arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S3*4* Sleep mode and Standby modes support for SuperH Mobile5*6* Copyright (C) 2009 Magnus Damm7*/89#include <linux/sys.h>10#include <linux/errno.h>11#include <linux/linkage.h>12#include <asm/asm-offsets.h>13#include <asm/suspend.h>1415/*16* Kernel mode register usage, see entry.S:17* k0 scratch18* k1 scratch19*/20#define k0 r021#define k1 r12223/* manage self-refresh and enter standby mode. must be self-contained.24* this code will be copied to on-chip memory and executed from there.25*/26.balign 427ENTRY(sh_mobile_sleep_enter_start)2829/* save mode flags */30mov.l r4, @(SH_SLEEP_MODE, r5)3132/* save original vbr */33stc vbr, r034mov.l r0, @(SH_SLEEP_VBR, r5)3536/* point vbr to our on-chip memory page */37ldc r5, vbr3839/* save return address */40sts pr, r041mov.l r0, @(SH_SLEEP_SPC, r5)4243/* save sr */44stc sr, r045mov.l r0, @(SH_SLEEP_SR, r5)4647/* save general purpose registers to stack if needed */48mov.l @(SH_SLEEP_MODE, r5), r049tst #SUSP_SH_REGS, r050bt skip_regs_save5152sts.l pr, @-r1553mov.l r14, @-r1554mov.l r13, @-r1555mov.l r12, @-r1556mov.l r11, @-r1557mov.l r10, @-r1558mov.l r9, @-r1559mov.l r8, @-r156061/* make sure bank0 is selected, save low registers */62mov.l rb_bit, r963not r9, r964bsr set_sr65mov #0, r106667bsr save_low_regs68nop6970/* switch to bank 1, save low registers */71mov.l rb_bit, r1072bsr set_sr73mov #-1, r97475bsr save_low_regs76nop7778/* switch back to bank 0 */79mov.l rb_bit, r980not r9, r981bsr set_sr82mov #0, r108384skip_regs_save:8586/* save sp, also set to internal ram */87mov.l r15, @(SH_SLEEP_SP, r5)88mov r5, r158990/* save stbcr */91bsr save_register92mov #SH_SLEEP_REG_STBCR, r09394/* save mmu and cache context if needed */95mov.l @(SH_SLEEP_MODE, r5), r096tst #SUSP_SH_MMU, r097bt skip_mmu_save_disable9899/* save mmu state */100bsr save_register101mov #SH_SLEEP_REG_PTEH, r0102103bsr save_register104mov #SH_SLEEP_REG_PTEL, r0105106bsr save_register107mov #SH_SLEEP_REG_TTB, r0108109bsr save_register110mov #SH_SLEEP_REG_TEA, r0111112bsr save_register113mov #SH_SLEEP_REG_MMUCR, r0114115bsr save_register116mov #SH_SLEEP_REG_PTEA, r0117118bsr save_register119mov #SH_SLEEP_REG_PASCR, r0120121bsr save_register122mov #SH_SLEEP_REG_IRMCR, r0123124/* invalidate TLBs and disable the MMU */125bsr get_register126mov #SH_SLEEP_REG_MMUCR, r0127mov #4, r1128mov.l r1, @r0129icbi @r0130131/* save cache registers and disable caches */132bsr save_register133mov #SH_SLEEP_REG_CCR, r0134135bsr save_register136mov #SH_SLEEP_REG_RAMCR, r0137138bsr get_register139mov #SH_SLEEP_REG_CCR, r0140mov #0, r1141mov.l r1, @r0142icbi @r0143144skip_mmu_save_disable:145/* call self-refresh entering code if needed */146mov.l @(SH_SLEEP_MODE, r5), r0147tst #SUSP_SH_SF, r0148bt skip_set_sf149150mov.l @(SH_SLEEP_SF_PRE, r5), r0151jsr @r0152nop153154skip_set_sf:155mov.l @(SH_SLEEP_MODE, r5), r0156tst #SUSP_SH_STANDBY, r0157bt test_rstandby158159/* set mode to "software standby mode" */160bra do_sleep161mov #0x80, r1162163test_rstandby:164tst #SUSP_SH_RSTANDBY, r0165bt test_ustandby166167/* setup BAR register */168bsr get_register169mov #SH_SLEEP_REG_BAR, r0170mov.l @(SH_SLEEP_RESUME, r5), r1171mov.l r1, @r0172173/* set mode to "r-standby mode" */174bra do_sleep175mov #0x20, r1176177test_ustandby:178tst #SUSP_SH_USTANDBY, r0179bt force_sleep180181/* set mode to "u-standby mode" */182bra do_sleep183mov #0x10, r1184185force_sleep:186187/* set mode to "sleep mode" */188mov #0x00, r1189190do_sleep:191/* setup and enter selected standby mode */192bsr get_register193mov #SH_SLEEP_REG_STBCR, r0194mov.l r1, @r0195again:196sleep197bra again198nop199200save_register:201add #SH_SLEEP_BASE_ADDR, r0202mov.l @(r0, r5), r1203add #-SH_SLEEP_BASE_ADDR, r0204mov.l @r1, r1205add #SH_SLEEP_BASE_DATA, r0206mov.l r1, @(r0, r5)207add #-SH_SLEEP_BASE_DATA, r0208rts209nop210211get_register:212add #SH_SLEEP_BASE_ADDR, r0213mov.l @(r0, r5), r0214rts215nop216217set_sr:218stc sr, r8219and r9, r8220or r10, r8221ldc r8, sr222rts223nop224225save_low_regs:226mov.l r7, @-r15227mov.l r6, @-r15228mov.l r5, @-r15229mov.l r4, @-r15230mov.l r3, @-r15231mov.l r2, @-r15232mov.l r1, @-r15233rts234mov.l r0, @-r15235236.balign 4237rb_bit: .long 0x20000000 ! RB=1238239ENTRY(sh_mobile_sleep_enter_end)240241.balign 4242ENTRY(sh_mobile_sleep_resume_start)243244/* figure out start address */245bsr 0f246nop2470:248sts pr, k1249mov.l 1f, k0250and k0, k1251252/* store pointer to data area in VBR */253ldc k1, vbr254255/* setup sr with saved sr */256mov.l @(SH_SLEEP_SR, k1), k0257ldc k0, sr258259/* now: user register set! */260stc vbr, r5261262/* setup spc with return address to c code */263mov.l @(SH_SLEEP_SPC, r5), r0264ldc r0, spc265266/* restore vbr */267mov.l @(SH_SLEEP_VBR, r5), r0268ldc r0, vbr269270/* setup ssr with saved sr */271mov.l @(SH_SLEEP_SR, r5), r0272ldc r0, ssr273274/* restore sp */275mov.l @(SH_SLEEP_SP, r5), r15276277/* restore sleep mode register */278bsr restore_register279mov #SH_SLEEP_REG_STBCR, r0280281/* call self-refresh resume code if needed */282mov.l @(SH_SLEEP_MODE, r5), r0283tst #SUSP_SH_SF, r0284bt skip_restore_sf285286mov.l @(SH_SLEEP_SF_POST, r5), r0287jsr @r0288nop289290skip_restore_sf:291/* restore mmu and cache state if needed */292mov.l @(SH_SLEEP_MODE, r5), r0293tst #SUSP_SH_MMU, r0294bt skip_restore_mmu295296/* restore mmu state */297bsr restore_register298mov #SH_SLEEP_REG_PTEH, r0299300bsr restore_register301mov #SH_SLEEP_REG_PTEL, r0302303bsr restore_register304mov #SH_SLEEP_REG_TTB, r0305306bsr restore_register307mov #SH_SLEEP_REG_TEA, r0308309bsr restore_register310mov #SH_SLEEP_REG_PTEA, r0311312bsr restore_register313mov #SH_SLEEP_REG_PASCR, r0314315bsr restore_register316mov #SH_SLEEP_REG_IRMCR, r0317318bsr restore_register319mov #SH_SLEEP_REG_MMUCR, r0320icbi @r0321322/* restore cache settings */323bsr restore_register324mov #SH_SLEEP_REG_RAMCR, r0325icbi @r0326327bsr restore_register328mov #SH_SLEEP_REG_CCR, r0329icbi @r0330331skip_restore_mmu:332333/* restore general purpose registers if needed */334mov.l @(SH_SLEEP_MODE, r5), r0335tst #SUSP_SH_REGS, r0336bt skip_restore_regs337338/* switch to bank 1, restore low registers */339mov.l _rb_bit, r10340bsr _set_sr341mov #-1, r9342343bsr restore_low_regs344nop345346/* switch to bank0, restore low registers */347mov.l _rb_bit, r9348not r9, r9349bsr _set_sr350mov #0, r10351352bsr restore_low_regs353nop354355/* restore the rest of the registers */356mov.l @r15+, r8357mov.l @r15+, r9358mov.l @r15+, r10359mov.l @r15+, r11360mov.l @r15+, r12361mov.l @r15+, r13362mov.l @r15+, r14363lds.l @r15+, pr364365skip_restore_regs:366rte367nop368369restore_register:370add #SH_SLEEP_BASE_DATA, r0371mov.l @(r0, r5), r1372add #-SH_SLEEP_BASE_DATA, r0373add #SH_SLEEP_BASE_ADDR, r0374mov.l @(r0, r5), r0375mov.l r1, @r0376rts377nop378379_set_sr:380stc sr, r8381and r9, r8382or r10, r8383ldc r8, sr384rts385nop386387restore_low_regs:388mov.l @r15+, r0389mov.l @r15+, r1390mov.l @r15+, r2391mov.l @r15+, r3392mov.l @r15+, r4393mov.l @r15+, r5394mov.l @r15+, r6395rts396mov.l @r15+, r7397398.balign 4399_rb_bit: .long 0x20000000 ! RB=14001: .long ~0x7ff401ENTRY(sh_mobile_sleep_resume_end)402403404