1e9edb3feSPaul Mundt/* 2e9edb3feSPaul Mundt * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S 3e9edb3feSPaul Mundt * 4e9edb3feSPaul Mundt * Sleep mode and Standby modes support for SuperH Mobile 5e9edb3feSPaul Mundt * 6e9edb3feSPaul Mundt * Copyright (C) 2009 Magnus Damm 7e9edb3feSPaul Mundt * 8e9edb3feSPaul Mundt * This file is subject to the terms and conditions of the GNU General Public 9e9edb3feSPaul Mundt * License. See the file "COPYING" in the main directory of this archive 10e9edb3feSPaul Mundt * for more details. 11e9edb3feSPaul Mundt */ 12e9edb3feSPaul Mundt 13e9edb3feSPaul Mundt#include <linux/sys.h> 14e9edb3feSPaul Mundt#include <linux/errno.h> 15e9edb3feSPaul Mundt#include <linux/linkage.h> 16e9edb3feSPaul Mundt#include <asm/asm-offsets.h> 17e9edb3feSPaul Mundt#include <asm/suspend.h> 18e9edb3feSPaul Mundt 19309214afSMagnus Damm/* 20309214afSMagnus Damm * Kernel mode register usage, see entry.S: 21309214afSMagnus Damm * k0 scratch 22309214afSMagnus Damm * k1 scratch 23309214afSMagnus Damm */ 24309214afSMagnus Damm#define k0 r0 25309214afSMagnus Damm#define k1 r1 26309214afSMagnus Damm 27323ef8dbSMagnus Damm/* manage self-refresh and enter standby mode. must be self-contained. 28e9edb3feSPaul Mundt * this code will be copied to on-chip memory and executed from there. 29e9edb3feSPaul Mundt */ 30323ef8dbSMagnus Damm .balign 4 31323ef8dbSMagnus DammENTRY(sh_mobile_sleep_enter_start) 32e9edb3feSPaul Mundt 33323ef8dbSMagnus Damm /* save mode flags */ 34323ef8dbSMagnus Damm mov.l r4, @(SH_SLEEP_MODE, r5) 35309214afSMagnus Damm 36309214afSMagnus Damm /* save original vbr */ 37323ef8dbSMagnus Damm stc vbr, r0 38323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_VBR, r5) 39309214afSMagnus Damm 40309214afSMagnus Damm /* point vbr to our on-chip memory page */ 41309214afSMagnus Damm ldc r5, vbr 42309214afSMagnus Damm 43309214afSMagnus Damm /* save return address */ 44323ef8dbSMagnus Damm sts pr, r0 45323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_SPC, r5) 46309214afSMagnus Damm 47309214afSMagnus Damm /* save sr */ 48323ef8dbSMagnus Damm stc sr, r0 49323ef8dbSMagnus Damm mov.l r0, @(SH_SLEEP_SR, r5) 50309214afSMagnus Damm 5141bfb7d7SMagnus Damm /* save general purpose registers to stack if needed */ 5241bfb7d7SMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 5341bfb7d7SMagnus Damm tst #SUSP_SH_REGS, r0 5441bfb7d7SMagnus Damm bt skip_regs_save 5541bfb7d7SMagnus Damm 5641bfb7d7SMagnus Damm sts.l pr, @-r15 5741bfb7d7SMagnus Damm mov.l r14, @-r15 5841bfb7d7SMagnus Damm mov.l r13, @-r15 5941bfb7d7SMagnus Damm mov.l r12, @-r15 6041bfb7d7SMagnus Damm mov.l r11, @-r15 6141bfb7d7SMagnus Damm mov.l r10, @-r15 6241bfb7d7SMagnus Damm mov.l r9, @-r15 6341bfb7d7SMagnus Damm mov.l r8, @-r15 6441bfb7d7SMagnus Damm 6541bfb7d7SMagnus Damm /* make sure bank0 is selected, save low registers */ 6641bfb7d7SMagnus Damm mov.l rb_bit, r9 6741bfb7d7SMagnus Damm not r9, r9 6841bfb7d7SMagnus Damm bsr set_sr 6941bfb7d7SMagnus Damm mov #0, r10 7041bfb7d7SMagnus Damm 7141bfb7d7SMagnus Damm bsr save_low_regs 7241bfb7d7SMagnus Damm nop 7341bfb7d7SMagnus Damm 7441bfb7d7SMagnus Damm /* switch to bank 1, save low registers */ 7541bfb7d7SMagnus Damm mov.l rb_bit, r10 7641bfb7d7SMagnus Damm bsr set_sr 7741bfb7d7SMagnus Damm mov #-1, r9 7841bfb7d7SMagnus Damm 7941bfb7d7SMagnus Damm bsr save_low_regs 8041bfb7d7SMagnus Damm nop 8141bfb7d7SMagnus Damm 8241bfb7d7SMagnus Damm /* switch back to bank 0 */ 8341bfb7d7SMagnus Damm mov.l rb_bit, r9 8441bfb7d7SMagnus Damm not r9, r9 8541bfb7d7SMagnus Damm bsr set_sr 8641bfb7d7SMagnus Damm mov #0, r10 8741bfb7d7SMagnus Damm 8841bfb7d7SMagnus Dammskip_regs_save: 8941bfb7d7SMagnus Damm 9041bfb7d7SMagnus Damm /* save sp, also set to internal ram */ 91bb3e0eedSMagnus Damm mov.l r15, @(SH_SLEEP_SP, r5) 9241bfb7d7SMagnus Damm mov r5, r15 93bb3e0eedSMagnus Damm 94323ef8dbSMagnus Damm /* save stbcr */ 95323ef8dbSMagnus Damm bsr save_register 96323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 97309214afSMagnus Damm 9899675a7aSMagnus Damm /* save mmu and cache context if needed */ 9999675a7aSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 10099675a7aSMagnus Damm tst #SUSP_SH_MMU, r0 10199675a7aSMagnus Damm bt skip_mmu_save_disable 10299675a7aSMagnus Damm 10399675a7aSMagnus Damm /* save mmu state */ 10499675a7aSMagnus Damm bsr save_register 10599675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEH, r0 10699675a7aSMagnus Damm 10799675a7aSMagnus Damm bsr save_register 10899675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEL, r0 10999675a7aSMagnus Damm 11099675a7aSMagnus Damm bsr save_register 11199675a7aSMagnus Damm mov #SH_SLEEP_REG_TTB, r0 11299675a7aSMagnus Damm 11399675a7aSMagnus Damm bsr save_register 11499675a7aSMagnus Damm mov #SH_SLEEP_REG_TEA, r0 11599675a7aSMagnus Damm 11699675a7aSMagnus Damm bsr save_register 11799675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 11899675a7aSMagnus Damm 11999675a7aSMagnus Damm bsr save_register 12099675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEA, r0 12199675a7aSMagnus Damm 12299675a7aSMagnus Damm bsr save_register 12399675a7aSMagnus Damm mov #SH_SLEEP_REG_PASCR, r0 12499675a7aSMagnus Damm 12599675a7aSMagnus Damm bsr save_register 12699675a7aSMagnus Damm mov #SH_SLEEP_REG_IRMCR, r0 12799675a7aSMagnus Damm 12899675a7aSMagnus Damm /* invalidate TLBs and disable the MMU */ 12999675a7aSMagnus Damm bsr get_register 13099675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 13199675a7aSMagnus Damm mov #4, r1 13299675a7aSMagnus Damm mov.l r1, @r0 13399675a7aSMagnus Damm icbi @r0 13499675a7aSMagnus Damm 13599675a7aSMagnus Damm /* save cache registers and disable caches */ 13699675a7aSMagnus Damm bsr save_register 13799675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 13899675a7aSMagnus Damm 13999675a7aSMagnus Damm bsr save_register 14099675a7aSMagnus Damm mov #SH_SLEEP_REG_RAMCR, r0 14199675a7aSMagnus Damm 14299675a7aSMagnus Damm bsr get_register 14399675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 14499675a7aSMagnus Damm mov #0, r1 14599675a7aSMagnus Damm mov.l r1, @r0 14699675a7aSMagnus Damm icbi @r0 14799675a7aSMagnus Damm 14899675a7aSMagnus Dammskip_mmu_save_disable: 149323ef8dbSMagnus Damm /* call self-refresh entering code if needed */ 150323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 151e9edb3feSPaul Mundt tst #SUSP_SH_SF, r0 152e9edb3feSPaul Mundt bt skip_set_sf 153237674e0SMagnus Damm 154323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SF_PRE, r5), r0 155323ef8dbSMagnus Damm jsr @r0 156323ef8dbSMagnus Damm nop 157e9edb3feSPaul Mundt 158e9edb3feSPaul Mundtskip_set_sf: 159323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 160e9edb3feSPaul Mundt tst #SUSP_SH_STANDBY, r0 161e9edb3feSPaul Mundt bt test_rstandby 162e9edb3feSPaul Mundt 163e9edb3feSPaul Mundt /* set mode to "software standby mode" */ 164e9edb3feSPaul Mundt bra do_sleep 165e9edb3feSPaul Mundt mov #0x80, r1 166e9edb3feSPaul Mundt 167e9edb3feSPaul Mundttest_rstandby: 168e9edb3feSPaul Mundt tst #SUSP_SH_RSTANDBY, r0 169e9edb3feSPaul Mundt bt test_ustandby 170e9edb3feSPaul Mundt 171bb3e0eedSMagnus Damm /* setup BAR register */ 172bb3e0eedSMagnus Damm bsr get_register 173bb3e0eedSMagnus Damm mov #SH_SLEEP_REG_BAR, r0 174bb3e0eedSMagnus Damm mov.l @(SH_SLEEP_RESUME, r5), r1 175bb3e0eedSMagnus Damm mov.l r1, @r0 176bb3e0eedSMagnus Damm 177e9edb3feSPaul Mundt /* set mode to "r-standby mode" */ 178e9edb3feSPaul Mundt bra do_sleep 179e9edb3feSPaul Mundt mov #0x20, r1 180e9edb3feSPaul Mundt 181e9edb3feSPaul Mundttest_ustandby: 182e9edb3feSPaul Mundt tst #SUSP_SH_USTANDBY, r0 183309214afSMagnus Damm bt force_sleep 184e9edb3feSPaul Mundt 185e9edb3feSPaul Mundt /* set mode to "u-standby mode" */ 186309214afSMagnus Damm bra do_sleep 187e9edb3feSPaul Mundt mov #0x10, r1 188e9edb3feSPaul Mundt 189309214afSMagnus Dammforce_sleep: 190309214afSMagnus Damm 191309214afSMagnus Damm /* set mode to "sleep mode" */ 192309214afSMagnus Damm mov #0x00, r1 193e9edb3feSPaul Mundt 194e9edb3feSPaul Mundtdo_sleep: 195e9edb3feSPaul Mundt /* setup and enter selected standby mode */ 196323ef8dbSMagnus Damm bsr get_register 197323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 198323ef8dbSMagnus Damm mov.l r1, @r0 199309214afSMagnus Dammagain: 200e9edb3feSPaul Mundt sleep 201309214afSMagnus Damm bra again 202309214afSMagnus Damm nop 203309214afSMagnus Damm 204323ef8dbSMagnus Dammsave_register: 205323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 206323ef8dbSMagnus Damm mov.l @(r0, r5), r1 207323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_ADDR, r0 208323ef8dbSMagnus Damm mov.l @r1, r1 209323ef8dbSMagnus Damm add #SH_SLEEP_BASE_DATA, r0 210323ef8dbSMagnus Damm mov.l r1, @(r0, r5) 211323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_DATA, r0 212323ef8dbSMagnus Damm rts 213323ef8dbSMagnus Damm nop 214323ef8dbSMagnus Damm 215323ef8dbSMagnus Dammget_register: 216323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 217323ef8dbSMagnus Damm mov.l @(r0, r5), r0 218323ef8dbSMagnus Damm rts 219323ef8dbSMagnus Damm nop 22041bfb7d7SMagnus Damm 22141bfb7d7SMagnus Dammset_sr: 22241bfb7d7SMagnus Damm stc sr, r8 22341bfb7d7SMagnus Damm and r9, r8 22441bfb7d7SMagnus Damm or r10, r8 22541bfb7d7SMagnus Damm ldc r8, sr 22641bfb7d7SMagnus Damm rts 22741bfb7d7SMagnus Damm nop 22841bfb7d7SMagnus Damm 22941bfb7d7SMagnus Dammsave_low_regs: 23041bfb7d7SMagnus Damm mov.l r7, @-r15 23141bfb7d7SMagnus Damm mov.l r6, @-r15 23241bfb7d7SMagnus Damm mov.l r5, @-r15 23341bfb7d7SMagnus Damm mov.l r4, @-r15 23441bfb7d7SMagnus Damm mov.l r3, @-r15 23541bfb7d7SMagnus Damm mov.l r2, @-r15 23641bfb7d7SMagnus Damm mov.l r1, @-r15 23741bfb7d7SMagnus Damm rts 23841bfb7d7SMagnus Damm mov.l r0, @-r15 23941bfb7d7SMagnus Damm 24041bfb7d7SMagnus Damm .balign 4 24141bfb7d7SMagnus Dammrb_bit: .long 0x20000000 ! RB=1 24241bfb7d7SMagnus Damm 243323ef8dbSMagnus DammENTRY(sh_mobile_sleep_enter_end) 244323ef8dbSMagnus Damm 245323ef8dbSMagnus Damm .balign 4 246323ef8dbSMagnus DammENTRY(sh_mobile_sleep_resume_start) 247323ef8dbSMagnus Damm 248323ef8dbSMagnus Damm /* figure out start address */ 249323ef8dbSMagnus Damm bsr 0f 250323ef8dbSMagnus Damm nop 251323ef8dbSMagnus Damm0: 252323ef8dbSMagnus Damm sts pr, k1 253323ef8dbSMagnus Damm mov.l 1f, k0 254323ef8dbSMagnus Damm and k0, k1 255323ef8dbSMagnus Damm 256323ef8dbSMagnus Damm /* store pointer to data area in VBR */ 257323ef8dbSMagnus Damm ldc k1, vbr 258323ef8dbSMagnus Damm 259323ef8dbSMagnus Damm /* setup sr with saved sr */ 260323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SR, k1), k0 261323ef8dbSMagnus Damm ldc k0, sr 262323ef8dbSMagnus Damm 263323ef8dbSMagnus Damm /* now: user register set! */ 264323ef8dbSMagnus Damm stc vbr, r5 265323ef8dbSMagnus Damm 266309214afSMagnus Damm /* setup spc with return address to c code */ 267323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SPC, r5), r0 268323ef8dbSMagnus Damm ldc r0, spc 269309214afSMagnus Damm 270309214afSMagnus Damm /* restore vbr */ 271323ef8dbSMagnus Damm mov.l @(SH_SLEEP_VBR, r5), r0 272323ef8dbSMagnus Damm ldc r0, vbr 273309214afSMagnus Damm 274309214afSMagnus Damm /* setup ssr with saved sr */ 275323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SR, r5), r0 276323ef8dbSMagnus Damm ldc r0, ssr 277309214afSMagnus Damm 278bb3e0eedSMagnus Damm /* restore sp */ 279bb3e0eedSMagnus Damm mov.l @(SH_SLEEP_SP, r5), r15 280bb3e0eedSMagnus Damm 281323ef8dbSMagnus Damm /* restore sleep mode register */ 282323ef8dbSMagnus Damm bsr restore_register 283323ef8dbSMagnus Damm mov #SH_SLEEP_REG_STBCR, r0 284e9edb3feSPaul Mundt 285323ef8dbSMagnus Damm /* call self-refresh resume code if needed */ 286323ef8dbSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 287323ef8dbSMagnus Damm tst #SUSP_SH_SF, r0 288e9edb3feSPaul Mundt bt skip_restore_sf 289e9edb3feSPaul Mundt 290323ef8dbSMagnus Damm mov.l @(SH_SLEEP_SF_POST, r5), r0 291323ef8dbSMagnus Damm jsr @r0 292237674e0SMagnus Damm nop 293237674e0SMagnus Damm 294e9edb3feSPaul Mundtskip_restore_sf: 29599675a7aSMagnus Damm /* restore mmu and cache state if needed */ 29699675a7aSMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 29799675a7aSMagnus Damm tst #SUSP_SH_MMU, r0 29899675a7aSMagnus Damm bt skip_restore_mmu 29999675a7aSMagnus Damm 30099675a7aSMagnus Damm /* restore mmu state */ 30199675a7aSMagnus Damm bsr restore_register 30299675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEH, r0 30399675a7aSMagnus Damm 30499675a7aSMagnus Damm bsr restore_register 30599675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEL, r0 30699675a7aSMagnus Damm 30799675a7aSMagnus Damm bsr restore_register 30899675a7aSMagnus Damm mov #SH_SLEEP_REG_TTB, r0 30999675a7aSMagnus Damm 31099675a7aSMagnus Damm bsr restore_register 31199675a7aSMagnus Damm mov #SH_SLEEP_REG_TEA, r0 31299675a7aSMagnus Damm 31399675a7aSMagnus Damm bsr restore_register 31499675a7aSMagnus Damm mov #SH_SLEEP_REG_PTEA, r0 31599675a7aSMagnus Damm 31699675a7aSMagnus Damm bsr restore_register 31799675a7aSMagnus Damm mov #SH_SLEEP_REG_PASCR, r0 31899675a7aSMagnus Damm 31999675a7aSMagnus Damm bsr restore_register 32099675a7aSMagnus Damm mov #SH_SLEEP_REG_IRMCR, r0 32199675a7aSMagnus Damm 32299675a7aSMagnus Damm bsr restore_register 32399675a7aSMagnus Damm mov #SH_SLEEP_REG_MMUCR, r0 32499675a7aSMagnus Damm icbi @r0 32599675a7aSMagnus Damm 32699675a7aSMagnus Damm /* restore cache settings */ 32799675a7aSMagnus Damm bsr restore_register 32899675a7aSMagnus Damm mov #SH_SLEEP_REG_RAMCR, r0 32999675a7aSMagnus Damm icbi @r0 33099675a7aSMagnus Damm 33199675a7aSMagnus Damm bsr restore_register 33299675a7aSMagnus Damm mov #SH_SLEEP_REG_CCR, r0 33399675a7aSMagnus Damm icbi @r0 33499675a7aSMagnus Damm 33599675a7aSMagnus Dammskip_restore_mmu: 33641bfb7d7SMagnus Damm 33741bfb7d7SMagnus Damm /* restore general purpose registers if needed */ 33841bfb7d7SMagnus Damm mov.l @(SH_SLEEP_MODE, r5), r0 33941bfb7d7SMagnus Damm tst #SUSP_SH_REGS, r0 34041bfb7d7SMagnus Damm bt skip_restore_regs 34141bfb7d7SMagnus Damm 34241bfb7d7SMagnus Damm /* switch to bank 1, restore low registers */ 34341bfb7d7SMagnus Damm mov.l _rb_bit, r10 34441bfb7d7SMagnus Damm bsr _set_sr 34541bfb7d7SMagnus Damm mov #-1, r9 34641bfb7d7SMagnus Damm 34741bfb7d7SMagnus Damm bsr restore_low_regs 34841bfb7d7SMagnus Damm nop 34941bfb7d7SMagnus Damm 35041bfb7d7SMagnus Damm /* switch to bank0, restore low registers */ 35141bfb7d7SMagnus Damm mov.l _rb_bit, r9 35241bfb7d7SMagnus Damm not r9, r9 35341bfb7d7SMagnus Damm bsr _set_sr 35441bfb7d7SMagnus Damm mov #0, r10 35541bfb7d7SMagnus Damm 35641bfb7d7SMagnus Damm bsr restore_low_regs 35741bfb7d7SMagnus Damm nop 35841bfb7d7SMagnus Damm 35941bfb7d7SMagnus Damm /* restore the rest of the registers */ 36041bfb7d7SMagnus Damm mov.l @r15+, r8 36141bfb7d7SMagnus Damm mov.l @r15+, r9 36241bfb7d7SMagnus Damm mov.l @r15+, r10 36341bfb7d7SMagnus Damm mov.l @r15+, r11 36441bfb7d7SMagnus Damm mov.l @r15+, r12 36541bfb7d7SMagnus Damm mov.l @r15+, r13 36641bfb7d7SMagnus Damm mov.l @r15+, r14 36741bfb7d7SMagnus Damm lds.l @r15+, pr 36841bfb7d7SMagnus Damm 36941bfb7d7SMagnus Dammskip_restore_regs: 370323ef8dbSMagnus Damm rte 371323ef8dbSMagnus Damm nop 372323ef8dbSMagnus Damm 373323ef8dbSMagnus Dammrestore_register: 374323ef8dbSMagnus Damm add #SH_SLEEP_BASE_DATA, r0 375323ef8dbSMagnus Damm mov.l @(r0, r5), r1 376323ef8dbSMagnus Damm add #-SH_SLEEP_BASE_DATA, r0 377323ef8dbSMagnus Damm add #SH_SLEEP_BASE_ADDR, r0 378323ef8dbSMagnus Damm mov.l @(r0, r5), r0 379323ef8dbSMagnus Damm mov.l r1, @r0 380323ef8dbSMagnus Damm rts 381e9edb3feSPaul Mundt nop 382e9edb3feSPaul Mundt 38341bfb7d7SMagnus Damm_set_sr: 38441bfb7d7SMagnus Damm stc sr, r8 38541bfb7d7SMagnus Damm and r9, r8 38641bfb7d7SMagnus Damm or r10, r8 38741bfb7d7SMagnus Damm ldc r8, sr 38841bfb7d7SMagnus Damm rts 38941bfb7d7SMagnus Damm nop 39041bfb7d7SMagnus Damm 39141bfb7d7SMagnus Dammrestore_low_regs: 39241bfb7d7SMagnus Damm mov.l @r15+, r0 39341bfb7d7SMagnus Damm mov.l @r15+, r1 39441bfb7d7SMagnus Damm mov.l @r15+, r2 39541bfb7d7SMagnus Damm mov.l @r15+, r3 39641bfb7d7SMagnus Damm mov.l @r15+, r4 39741bfb7d7SMagnus Damm mov.l @r15+, r5 39841bfb7d7SMagnus Damm mov.l @r15+, r6 39941bfb7d7SMagnus Damm rts 40041bfb7d7SMagnus Damm mov.l @r15+, r7 40141bfb7d7SMagnus Damm 402e9edb3feSPaul Mundt .balign 4 40341bfb7d7SMagnus Damm_rb_bit: .long 0x20000000 ! RB=1 404323ef8dbSMagnus Damm1: .long ~0x7ff 405323ef8dbSMagnus DammENTRY(sh_mobile_sleep_resume_end) 406