1176ce1b7SKuninori Morimoto/* SPDX-License-Identifier: GPL-2.0 2176ce1b7SKuninori Morimoto * 3e9edb3feSPaul Mundt * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S 4e9edb3feSPaul Mundt * 5e9edb3feSPaul Mundt * Sleep mode and Standby modes support for SuperH Mobile 6e9edb3feSPaul Mundt * 7e9edb3feSPaul Mundt * Copyright (C) 2009 Magnus Damm 8e9edb3feSPaul Mundt */ 9e9edb3feSPaul Mundt 10e9edb3feSPaul Mundt#include <linux/sys.h> 11e9edb3feSPaul Mundt#include <linux/errno.h> 12e9edb3feSPaul Mundt#include <linux/linkage.h> 13e9edb3feSPaul Mundt#include <asm/asm-offsets.h> 14e9edb3feSPaul Mundt#include <asm/suspend.h> 15e9edb3feSPaul Mundt 16309214afSMagnus Damm/* 17309214afSMagnus Damm * Kernel mode register usage, see entry.S: 18309214afSMagnus Damm * k0 scratch 19309214afSMagnus Damm * k1 scratch 20309214afSMagnus Damm */ 21309214afSMagnus Damm#define k0 r0 22309214afSMagnus Damm#define k1 r1 23309214afSMagnus Damm 24323ef8dbSMagnus Damm/* manage self-refresh and enter standby mode. must be self-contained. 25e9edb3feSPaul Mundt * this code will be copied to on-chip memory and executed from there. 26e9edb3feSPaul Mundt */ 27323ef8dbSMagnus Damm .balign 4 28323ef8dbSMagnus DammENTRY(sh_mobile_sleep_enter_start) 29e9edb3feSPaul Mundt 30323ef8dbSMagnus Damm /* save mode flags */ 31323ef8dbSMagnus Damm mov.l r4, @(SH_SLEEP_MODE, r5) 32309214afSMagnus Damm 33309214afSMagnus Damm /* save original vbr */ 34323ef8dbSMagnus Damm stc vbr, r0 35323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_VBR, r5) 36309214afSMagnus Damm 37309214afSMagnus Damm /* point vbr to our on-chip memory page */ 38309214afSMagnus Damm ldc r5, vbr 39309214afSMagnus Damm 40309214afSMagnus Damm /* save return address */ 41323ef8dbSMagnus Damm sts pr, r0 42323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_SPC, r5) 43309214afSMagnus Damm 44309214afSMagnus Damm /* save sr */ 45323ef8dbSMagnus Damm stc sr, r0 46323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_SR, r5) 47309214afSMagnus Damm 4841bfb7d7SMagnus Damm /* save general purpose registers to stack if needed */ 4941bfb7d7SMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 5041bfb7d7SMagnus Damm tst #SUSP_SH_REGS, r0 5141bfb7d7SMagnus Damm bt skip_regs_save 5241bfb7d7SMagnus Damm 5341bfb7d7SMagnus Damm sts.l pr, @-r15 5441bfb7d7SMagnus Damm mov.l r14, @-r15 5541bfb7d7SMagnus Damm mov.l r13, @-r15 5641bfb7d7SMagnus Damm mov.l r12, @-r15 5741bfb7d7SMagnus Damm mov.l r11, @-r15 5841bfb7d7SMagnus Damm mov.l r10, @-r15 5941bfb7d7SMagnus Damm mov.l r9, @-r15 6041bfb7d7SMagnus Damm mov.l r8, @-r15 6141bfb7d7SMagnus Damm 6241bfb7d7SMagnus Damm /* make sure bank0 is selected, save low registers */ 6341bfb7d7SMagnus Damm mov.l rb_bit, r9 6441bfb7d7SMagnus Damm not r9, r9 6541bfb7d7SMagnus Damm bsr set_sr 6641bfb7d7SMagnus Damm mov #0, r10 6741bfb7d7SMagnus Damm 6841bfb7d7SMagnus Damm bsr save_low_regs 6941bfb7d7SMagnus Damm nop 7041bfb7d7SMagnus Damm 7141bfb7d7SMagnus Damm /* switch to bank 1, save low registers */ 7241bfb7d7SMagnus Damm mov.l rb_bit, r10 7341bfb7d7SMagnus Damm bsr set_sr 7441bfb7d7SMagnus Damm mov #-1, r9 7541bfb7d7SMagnus Damm 7641bfb7d7SMagnus Damm bsr save_low_regs 7741bfb7d7SMagnus Damm nop 7841bfb7d7SMagnus Damm 7941bfb7d7SMagnus Damm /* switch back to bank 0 */ 8041bfb7d7SMagnus Damm mov.l rb_bit, r9 8141bfb7d7SMagnus Damm not r9, r9 8241bfb7d7SMagnus Damm bsr set_sr 8341bfb7d7SMagnus Damm mov #0, r10 8441bfb7d7SMagnus Damm 8541bfb7d7SMagnus Dammskip_regs_save: 8641bfb7d7SMagnus Damm 8741bfb7d7SMagnus Damm /* save sp, also set to internal ram */ 88bb3e0eedSMagnus Damm mov.l r15, @(SH_SLEEP_SP, r5) 8941bfb7d7SMagnus Damm mov r5, r15 90bb3e0eedSMagnus Damm 91323ef8dbSMagnus Damm /* save stbcr */ 92323ef8dbSMagnus Damm bsr save_register 93323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 94309214afSMagnus Damm 9599675a7aSMagnus Damm /* save mmu and cache context if needed */ 9699675a7aSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 9799675a7aSMagnus Damm tst #SUSP_SH_MMU, r0 9899675a7aSMagnus Damm bt skip_mmu_save_disable 9999675a7aSMagnus Damm 10099675a7aSMagnus Damm /* save mmu state */ 10199675a7aSMagnus Damm bsr save_register 10299675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEH, r0 10399675a7aSMagnus Damm 10499675a7aSMagnus Damm bsr save_register 10599675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEL, r0 10699675a7aSMagnus Damm 10799675a7aSMagnus Damm bsr save_register 10899675a7aSMagnus Damm mov #SH_SLEEP_REG_TTB, r0 10999675a7aSMagnus Damm 11099675a7aSMagnus Damm bsr save_register 11199675a7aSMagnus Damm mov #SH_SLEEP_REG_TEA, r0 11299675a7aSMagnus Damm 11399675a7aSMagnus Damm bsr save_register 11499675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 11599675a7aSMagnus Damm 11699675a7aSMagnus Damm bsr save_register 11799675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEA, r0 11899675a7aSMagnus Damm 11999675a7aSMagnus Damm bsr save_register 12099675a7aSMagnus Damm mov #SH_SLEEP_REG_PASCR, r0 12199675a7aSMagnus Damm 12299675a7aSMagnus Damm bsr save_register 12399675a7aSMagnus Damm mov #SH_SLEEP_REG_IRMCR, r0 12499675a7aSMagnus Damm 12599675a7aSMagnus Damm /* invalidate TLBs and disable the MMU */ 12699675a7aSMagnus Damm bsr get_register 12799675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 12899675a7aSMagnus Damm mov #4, r1 12999675a7aSMagnus Damm mov.l r1, @r0 13099675a7aSMagnus Damm icbi @r0 13199675a7aSMagnus Damm 13299675a7aSMagnus Damm /* save cache registers and disable caches */ 13399675a7aSMagnus Damm bsr save_register 13499675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 13599675a7aSMagnus Damm 13699675a7aSMagnus Damm bsr save_register 13799675a7aSMagnus Damm mov #SH_SLEEP_REG_RAMCR, r0 13899675a7aSMagnus Damm 13999675a7aSMagnus Damm bsr get_register 14099675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 14199675a7aSMagnus Damm mov #0, r1 14299675a7aSMagnus Damm mov.l r1, @r0 14399675a7aSMagnus Damm icbi @r0 14499675a7aSMagnus Damm 14599675a7aSMagnus Dammskip_mmu_save_disable: 146323ef8dbSMagnus Damm /* call self-refresh entering code if needed */ 147323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 148e9edb3feSPaul Mundt tst #SUSP_SH_SF, r0 149e9edb3feSPaul Mundt bt skip_set_sf 150237674e0SMagnus Damm 151323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SF_PRE, r5), r0 152323ef8dbSMagnus Damm jsr @r0 153323ef8dbSMagnus Damm nop 154e9edb3feSPaul Mundt 155e9edb3feSPaul Mundtskip_set_sf: 156323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 157e9edb3feSPaul Mundt tst #SUSP_SH_STANDBY, r0 158e9edb3feSPaul Mundt bt test_rstandby 159e9edb3feSPaul Mundt 160e9edb3feSPaul Mundt /* set mode to "software standby mode" */ 161e9edb3feSPaul Mundt bra do_sleep 162e9edb3feSPaul Mundt mov #0x80, r1 163e9edb3feSPaul Mundt 164e9edb3feSPaul Mundttest_rstandby: 165e9edb3feSPaul Mundt tst #SUSP_SH_RSTANDBY, r0 166e9edb3feSPaul Mundt bt test_ustandby 167e9edb3feSPaul Mundt 168bb3e0eedSMagnus Damm /* setup BAR register */ 169bb3e0eedSMagnus Damm bsr get_register 170bb3e0eedSMagnus Damm mov #SH_SLEEP_REG_BAR, r0 171bb3e0eedSMagnus Damm mov.l @(SH_SLEEP_RESUME, r5), r1 172bb3e0eedSMagnus Damm mov.l r1, @r0 173bb3e0eedSMagnus Damm 174e9edb3feSPaul Mundt /* set mode to "r-standby mode" */ 175e9edb3feSPaul Mundt bra do_sleep 176e9edb3feSPaul Mundt mov #0x20, r1 177e9edb3feSPaul Mundt 178e9edb3feSPaul Mundttest_ustandby: 179e9edb3feSPaul Mundt tst #SUSP_SH_USTANDBY, r0 180309214afSMagnus Damm bt force_sleep 181e9edb3feSPaul Mundt 182e9edb3feSPaul Mundt /* set mode to "u-standby mode" */ 183309214afSMagnus Damm bra do_sleep 184e9edb3feSPaul Mundt mov #0x10, r1 185e9edb3feSPaul Mundt 186309214afSMagnus Dammforce_sleep: 187309214afSMagnus Damm 188309214afSMagnus Damm /* set mode to "sleep mode" */ 189309214afSMagnus Damm mov #0x00, r1 190e9edb3feSPaul Mundt 191e9edb3feSPaul Mundtdo_sleep: 192e9edb3feSPaul Mundt /* setup and enter selected standby mode */ 193323ef8dbSMagnus Damm bsr get_register 194323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 195323ef8dbSMagnus Damm mov.l r1, @r0 196309214afSMagnus Dammagain: 197e9edb3feSPaul Mundt sleep 198309214afSMagnus Damm bra again 199309214afSMagnus Damm nop 200309214afSMagnus Damm 201323ef8dbSMagnus Dammsave_register: 202323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 203323ef8dbSMagnus Damm mov.l @(r0, r5), r1 204323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_ADDR, r0 205323ef8dbSMagnus Damm mov.l @r1, r1 206323ef8dbSMagnus Damm add #SH_SLEEP_BASE_DATA, r0 207323ef8dbSMagnus Damm mov.l r1, @(r0, r5) 208323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_DATA, r0 209323ef8dbSMagnus Damm rts 210323ef8dbSMagnus Damm nop 211323ef8dbSMagnus Damm 212323ef8dbSMagnus Dammget_register: 213323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 214323ef8dbSMagnus Damm mov.l @(r0, r5), r0 215323ef8dbSMagnus Damm rts 216323ef8dbSMagnus Damm nop 21741bfb7d7SMagnus Damm 21841bfb7d7SMagnus Dammset_sr: 21941bfb7d7SMagnus Damm stc sr, r8 22041bfb7d7SMagnus Damm and r9, r8 22141bfb7d7SMagnus Damm or r10, r8 22241bfb7d7SMagnus Damm ldc r8, sr 22341bfb7d7SMagnus Damm rts 22441bfb7d7SMagnus Damm nop 22541bfb7d7SMagnus Damm 22641bfb7d7SMagnus Dammsave_low_regs: 22741bfb7d7SMagnus Damm mov.l r7, @-r15 22841bfb7d7SMagnus Damm mov.l r6, @-r15 22941bfb7d7SMagnus Damm mov.l r5, @-r15 23041bfb7d7SMagnus Damm mov.l r4, @-r15 23141bfb7d7SMagnus Damm mov.l r3, @-r15 23241bfb7d7SMagnus Damm mov.l r2, @-r15 23341bfb7d7SMagnus Damm mov.l r1, @-r15 23441bfb7d7SMagnus Damm rts 23541bfb7d7SMagnus Damm mov.l r0, @-r15 23641bfb7d7SMagnus Damm 23741bfb7d7SMagnus Damm .balign 4 23841bfb7d7SMagnus Dammrb_bit: .long 0x20000000 ! RB=1 23941bfb7d7SMagnus Damm 240323ef8dbSMagnus DammENTRY(sh_mobile_sleep_enter_end) 241323ef8dbSMagnus Damm 242323ef8dbSMagnus Damm .balign 4 243323ef8dbSMagnus DammENTRY(sh_mobile_sleep_resume_start) 244323ef8dbSMagnus Damm 245323ef8dbSMagnus Damm /* figure out start address */ 246323ef8dbSMagnus Damm bsr 0f 247323ef8dbSMagnus Damm nop 248323ef8dbSMagnus Damm0: 249323ef8dbSMagnus Damm sts pr, k1 250323ef8dbSMagnus Damm mov.l 1f, k0 251323ef8dbSMagnus Damm and k0, k1 252323ef8dbSMagnus Damm 253323ef8dbSMagnus Damm /* store pointer to data area in VBR */ 254323ef8dbSMagnus Damm ldc k1, vbr 255323ef8dbSMagnus Damm 256323ef8dbSMagnus Damm /* setup sr with saved sr */ 257323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SR, k1), k0 258323ef8dbSMagnus Damm ldc k0, sr 259323ef8dbSMagnus Damm 260323ef8dbSMagnus Damm /* now: user register set! */ 261323ef8dbSMagnus Damm stc vbr, r5 262323ef8dbSMagnus Damm 263309214afSMagnus Damm /* setup spc with return address to c code */ 264323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SPC, r5), r0 265323ef8dbSMagnus Damm ldc r0, spc 266309214afSMagnus Damm 267309214afSMagnus Damm /* restore vbr */ 268323ef8dbSMagnus Damm mov.l @(SH_SLEEP_VBR, r5), r0 269323ef8dbSMagnus Damm ldc r0, vbr 270309214afSMagnus Damm 271309214afSMagnus Damm /* setup ssr with saved sr */ 272323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SR, r5), r0 273323ef8dbSMagnus Damm ldc r0, ssr 274309214afSMagnus Damm 275bb3e0eedSMagnus Damm /* restore sp */ 276bb3e0eedSMagnus Damm mov.l @(SH_SLEEP_SP, r5), r15 277bb3e0eedSMagnus Damm 278323ef8dbSMagnus Damm /* restore sleep mode register */ 279323ef8dbSMagnus Damm bsr restore_register 280323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 281e9edb3feSPaul Mundt 282323ef8dbSMagnus Damm /* call self-refresh resume code if needed */ 283323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 284323ef8dbSMagnus Damm tst #SUSP_SH_SF, r0 285e9edb3feSPaul Mundt bt skip_restore_sf 286e9edb3feSPaul Mundt 287323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SF_POST, r5), r0 288323ef8dbSMagnus Damm jsr @r0 289237674e0SMagnus Damm nop 290237674e0SMagnus Damm 291e9edb3feSPaul Mundtskip_restore_sf: 29299675a7aSMagnus Damm /* restore mmu and cache state if needed */ 29399675a7aSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 29499675a7aSMagnus Damm tst #SUSP_SH_MMU, r0 29599675a7aSMagnus Damm bt skip_restore_mmu 29699675a7aSMagnus Damm 29799675a7aSMagnus Damm /* restore mmu state */ 29899675a7aSMagnus Damm bsr restore_register 29999675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEH, r0 30099675a7aSMagnus Damm 30199675a7aSMagnus Damm bsr restore_register 30299675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEL, r0 30399675a7aSMagnus Damm 30499675a7aSMagnus Damm bsr restore_register 30599675a7aSMagnus Damm mov #SH_SLEEP_REG_TTB, r0 30699675a7aSMagnus Damm 30799675a7aSMagnus Damm bsr restore_register 30899675a7aSMagnus Damm mov #SH_SLEEP_REG_TEA, r0 30999675a7aSMagnus Damm 31099675a7aSMagnus Damm bsr restore_register 31199675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEA, r0 31299675a7aSMagnus Damm 31399675a7aSMagnus Damm bsr restore_register 31499675a7aSMagnus Damm mov #SH_SLEEP_REG_PASCR, r0 31599675a7aSMagnus Damm 31699675a7aSMagnus Damm bsr restore_register 31799675a7aSMagnus Damm mov #SH_SLEEP_REG_IRMCR, r0 31899675a7aSMagnus Damm 31999675a7aSMagnus Damm bsr restore_register 32099675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 32199675a7aSMagnus Damm icbi @r0 32299675a7aSMagnus Damm 32399675a7aSMagnus Damm /* restore cache settings */ 32499675a7aSMagnus Damm bsr restore_register 32599675a7aSMagnus Damm mov #SH_SLEEP_REG_RAMCR, r0 32699675a7aSMagnus Damm icbi @r0 32799675a7aSMagnus Damm 32899675a7aSMagnus Damm bsr restore_register 32999675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 33099675a7aSMagnus Damm icbi @r0 33199675a7aSMagnus Damm 33299675a7aSMagnus Dammskip_restore_mmu: 33341bfb7d7SMagnus Damm 33441bfb7d7SMagnus Damm /* restore general purpose registers if needed */ 33541bfb7d7SMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 33641bfb7d7SMagnus Damm tst #SUSP_SH_REGS, r0 33741bfb7d7SMagnus Damm bt skip_restore_regs 33841bfb7d7SMagnus Damm 33941bfb7d7SMagnus Damm /* switch to bank 1, restore low registers */ 34041bfb7d7SMagnus Damm mov.l _rb_bit, r10 34141bfb7d7SMagnus Damm bsr _set_sr 34241bfb7d7SMagnus Damm mov #-1, r9 34341bfb7d7SMagnus Damm 34441bfb7d7SMagnus Damm bsr restore_low_regs 34541bfb7d7SMagnus Damm nop 34641bfb7d7SMagnus Damm 34741bfb7d7SMagnus Damm /* switch to bank0, restore low registers */ 34841bfb7d7SMagnus Damm mov.l _rb_bit, r9 34941bfb7d7SMagnus Damm not r9, r9 35041bfb7d7SMagnus Damm bsr _set_sr 35141bfb7d7SMagnus Damm mov #0, r10 35241bfb7d7SMagnus Damm 35341bfb7d7SMagnus Damm bsr restore_low_regs 35441bfb7d7SMagnus Damm nop 35541bfb7d7SMagnus Damm 35641bfb7d7SMagnus Damm /* restore the rest of the registers */ 35741bfb7d7SMagnus Damm mov.l @r15+, r8 35841bfb7d7SMagnus Damm mov.l @r15+, r9 35941bfb7d7SMagnus Damm mov.l @r15+, r10 36041bfb7d7SMagnus Damm mov.l @r15+, r11 36141bfb7d7SMagnus Damm mov.l @r15+, r12 36241bfb7d7SMagnus Damm mov.l @r15+, r13 36341bfb7d7SMagnus Damm mov.l @r15+, r14 36441bfb7d7SMagnus Damm lds.l @r15+, pr 36541bfb7d7SMagnus Damm 36641bfb7d7SMagnus Dammskip_restore_regs: 367323ef8dbSMagnus Damm rte 368323ef8dbSMagnus Damm nop 369323ef8dbSMagnus Damm 370323ef8dbSMagnus Dammrestore_register: 371323ef8dbSMagnus Damm add #SH_SLEEP_BASE_DATA, r0 372323ef8dbSMagnus Damm mov.l @(r0, r5), r1 373323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_DATA, r0 374323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 375323ef8dbSMagnus Damm mov.l @(r0, r5), r0 376323ef8dbSMagnus Damm mov.l r1, @r0 377323ef8dbSMagnus Damm rts 378e9edb3feSPaul Mundt nop 379e9edb3feSPaul Mundt 38041bfb7d7SMagnus Damm_set_sr: 38141bfb7d7SMagnus Damm stc sr, r8 38241bfb7d7SMagnus Damm and r9, r8 38341bfb7d7SMagnus Damm or r10, r8 38441bfb7d7SMagnus Damm ldc r8, sr 38541bfb7d7SMagnus Damm rts 38641bfb7d7SMagnus Damm nop 38741bfb7d7SMagnus Damm 38841bfb7d7SMagnus Dammrestore_low_regs: 38941bfb7d7SMagnus Damm mov.l @r15+, r0 39041bfb7d7SMagnus Damm mov.l @r15+, r1 39141bfb7d7SMagnus Damm mov.l @r15+, r2 39241bfb7d7SMagnus Damm mov.l @r15+, r3 39341bfb7d7SMagnus Damm mov.l @r15+, r4 39441bfb7d7SMagnus Damm mov.l @r15+, r5 39541bfb7d7SMagnus Damm mov.l @r15+, r6 39641bfb7d7SMagnus Damm rts 39741bfb7d7SMagnus Damm mov.l @r15+, r7 39841bfb7d7SMagnus Damm 399e9edb3feSPaul Mundt .balign 4 40041bfb7d7SMagnus Damm_rb_bit: .long 0x20000000 ! RB=1 401323ef8dbSMagnus Damm1: .long ~0x7ff 402323ef8dbSMagnus DammENTRY(sh_mobile_sleep_resume_end) 403