| /* | |
| * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S | |
| * | |
| * Sleep mode and Standby modes support for SuperH Mobile | |
| * | |
| * Copyright (C) 2009 Magnus Damm | |
| * | |
| * This file is subject to the terms and conditions of the GNU General Public | |
| * License. See the file "COPYING" in the main directory of this archive | |
| * for more details. | |
| */ | |
| #include <linux/sys.h> | |
| #include <linux/errno.h> | |
| #include <linux/linkage.h> | |
| #include <asm/asm-offsets.h> | |
| #include <asm/suspend.h> | |
| /* | |
| * Kernel mode register usage, see entry.S: | |
| * k0 scratch | |
| * k1 scratch | |
| */ | |
| #define k0 r0 | |
| #define k1 r1 | |
| /* manage self-refresh and enter standby mode. must be self-contained. | |
| * this code will be copied to on-chip memory and executed from there. | |
| */ | |
| .balign 4 | |
| ENTRY(sh_mobile_sleep_enter_start) | |
| /* save mode flags */ | |
| mov.l r4, @(SH_SLEEP_MODE, r5) | |
| /* save original vbr */ | |
| stc vbr, r0 | |
| mov.l r0, @(SH_SLEEP_VBR, r5) | |
| /* point vbr to our on-chip memory page */ | |
| ldc r5, vbr | |
| /* save return address */ | |
| sts pr, r0 | |
| mov.l r0, @(SH_SLEEP_SPC, r5) | |
| /* save sr */ | |
| stc sr, r0 | |
| mov.l r0, @(SH_SLEEP_SR, r5) | |
| /* save general purpose registers to stack if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_REGS, r0 | |
| bt skip_regs_save | |
| sts.l pr, @-r15 | |
| mov.l r14, @-r15 | |
| mov.l r13, @-r15 | |
| mov.l r12, @-r15 | |
| mov.l r11, @-r15 | |
| mov.l r10, @-r15 | |
| mov.l r9, @-r15 | |
| mov.l r8, @-r15 | |
| /* make sure bank0 is selected, save low registers */ | |
| mov.l rb_bit, r9 | |
| not r9, r9 | |
| bsr set_sr | |
| mov #0, r10 | |
| bsr save_low_regs | |
| nop | |
| /* switch to bank 1, save low registers */ | |
| mov.l rb_bit, r10 | |
| bsr set_sr | |
| mov #-1, r9 | |
| bsr save_low_regs | |
| nop | |
| /* switch back to bank 0 */ | |
| mov.l rb_bit, r9 | |
| not r9, r9 | |
| bsr set_sr | |
| mov #0, r10 | |
| skip_regs_save: | |
| /* save sp, also set to internal ram */ | |
| mov.l r15, @(SH_SLEEP_SP, r5) | |
| mov r5, r15 | |
| /* save stbcr */ | |
| bsr save_register | |
| mov #SH_SLEEP_REG_STBCR, r0 | |
| /* save mmu and cache context if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_MMU, r0 | |
| bt skip_mmu_save_disable | |
| /* save mmu state */ | |
| bsr save_register | |
| mov #SH_SLEEP_REG_PTEH, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_PTEL, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_TTB, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_TEA, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_MMUCR, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_PTEA, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_PASCR, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_IRMCR, r0 | |
| /* invalidate TLBs and disable the MMU */ | |
| bsr get_register | |
| mov #SH_SLEEP_REG_MMUCR, r0 | |
| mov #4, r1 | |
| mov.l r1, @r0 | |
| icbi @r0 | |
| /* save cache registers and disable caches */ | |
| bsr save_register | |
| mov #SH_SLEEP_REG_CCR, r0 | |
| bsr save_register | |
| mov #SH_SLEEP_REG_RAMCR, r0 | |
| bsr get_register | |
| mov #SH_SLEEP_REG_CCR, r0 | |
| mov #0, r1 | |
| mov.l r1, @r0 | |
| icbi @r0 | |
| skip_mmu_save_disable: | |
| /* call self-refresh entering code if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_SF, r0 | |
| bt skip_set_sf | |
| mov.l @(SH_SLEEP_SF_PRE, r5), r0 | |
| jsr @r0 | |
| nop | |
| skip_set_sf: | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_STANDBY, r0 | |
| bt test_rstandby | |
| /* set mode to "software standby mode" */ | |
| bra do_sleep | |
| mov #0x80, r1 | |
| test_rstandby: | |
| tst #SUSP_SH_RSTANDBY, r0 | |
| bt test_ustandby | |
| /* setup BAR register */ | |
| bsr get_register | |
| mov #SH_SLEEP_REG_BAR, r0 | |
| mov.l @(SH_SLEEP_RESUME, r5), r1 | |
| mov.l r1, @r0 | |
| /* set mode to "r-standby mode" */ | |
| bra do_sleep | |
| mov #0x20, r1 | |
| test_ustandby: | |
| tst #SUSP_SH_USTANDBY, r0 | |
| bt force_sleep | |
| /* set mode to "u-standby mode" */ | |
| bra do_sleep | |
| mov #0x10, r1 | |
| force_sleep: | |
| /* set mode to "sleep mode" */ | |
| mov #0x00, r1 | |
| do_sleep: | |
| /* setup and enter selected standby mode */ | |
| bsr get_register | |
| mov #SH_SLEEP_REG_STBCR, r0 | |
| mov.l r1, @r0 | |
| again: | |
| sleep | |
| bra again | |
| nop | |
| save_register: | |
| add #SH_SLEEP_BASE_ADDR, r0 | |
| mov.l @(r0, r5), r1 | |
| add #-SH_SLEEP_BASE_ADDR, r0 | |
| mov.l @r1, r1 | |
| add #SH_SLEEP_BASE_DATA, r0 | |
| mov.l r1, @(r0, r5) | |
| add #-SH_SLEEP_BASE_DATA, r0 | |
| rts | |
| nop | |
| get_register: | |
| add #SH_SLEEP_BASE_ADDR, r0 | |
| mov.l @(r0, r5), r0 | |
| rts | |
| nop | |
| set_sr: | |
| stc sr, r8 | |
| and r9, r8 | |
| or r10, r8 | |
| ldc r8, sr | |
| rts | |
| nop | |
| save_low_regs: | |
| mov.l r7, @-r15 | |
| mov.l r6, @-r15 | |
| mov.l r5, @-r15 | |
| mov.l r4, @-r15 | |
| mov.l r3, @-r15 | |
| mov.l r2, @-r15 | |
| mov.l r1, @-r15 | |
| rts | |
| mov.l r0, @-r15 | |
| .balign 4 | |
| rb_bit: .long 0x20000000 ! RB=1 | |
| ENTRY(sh_mobile_sleep_enter_end) | |
| .balign 4 | |
| ENTRY(sh_mobile_sleep_resume_start) | |
| /* figure out start address */ | |
| bsr 0f | |
| nop | |
| 0: | |
| sts pr, k1 | |
| mov.l 1f, k0 | |
| and k0, k1 | |
| /* store pointer to data area in VBR */ | |
| ldc k1, vbr | |
| /* setup sr with saved sr */ | |
| mov.l @(SH_SLEEP_SR, k1), k0 | |
| ldc k0, sr | |
| /* now: user register set! */ | |
| stc vbr, r5 | |
| /* setup spc with return address to c code */ | |
| mov.l @(SH_SLEEP_SPC, r5), r0 | |
| ldc r0, spc | |
| /* restore vbr */ | |
| mov.l @(SH_SLEEP_VBR, r5), r0 | |
| ldc r0, vbr | |
| /* setup ssr with saved sr */ | |
| mov.l @(SH_SLEEP_SR, r5), r0 | |
| ldc r0, ssr | |
| /* restore sp */ | |
| mov.l @(SH_SLEEP_SP, r5), r15 | |
| /* restore sleep mode register */ | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_STBCR, r0 | |
| /* call self-refresh resume code if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_SF, r0 | |
| bt skip_restore_sf | |
| mov.l @(SH_SLEEP_SF_POST, r5), r0 | |
| jsr @r0 | |
| nop | |
| skip_restore_sf: | |
| /* restore mmu and cache state if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_MMU, r0 | |
| bt skip_restore_mmu | |
| /* restore mmu state */ | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_PTEH, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_PTEL, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_TTB, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_TEA, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_PTEA, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_PASCR, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_IRMCR, r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_MMUCR, r0 | |
| icbi @r0 | |
| /* restore cache settings */ | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_RAMCR, r0 | |
| icbi @r0 | |
| bsr restore_register | |
| mov #SH_SLEEP_REG_CCR, r0 | |
| icbi @r0 | |
| skip_restore_mmu: | |
| /* restore general purpose registers if needed */ | |
| mov.l @(SH_SLEEP_MODE, r5), r0 | |
| tst #SUSP_SH_REGS, r0 | |
| bt skip_restore_regs | |
| /* switch to bank 1, restore low registers */ | |
| mov.l _rb_bit, r10 | |
| bsr _set_sr | |
| mov #-1, r9 | |
| bsr restore_low_regs | |
| nop | |
| /* switch to bank0, restore low registers */ | |
| mov.l _rb_bit, r9 | |
| not r9, r9 | |
| bsr _set_sr | |
| mov #0, r10 | |
| bsr restore_low_regs | |
| nop | |
| /* restore the rest of the registers */ | |
| mov.l @r15+, r8 | |
| mov.l @r15+, r9 | |
| mov.l @r15+, r10 | |
| mov.l @r15+, r11 | |
| mov.l @r15+, r12 | |
| mov.l @r15+, r13 | |
| mov.l @r15+, r14 | |
| lds.l @r15+, pr | |
| skip_restore_regs: | |
| rte | |
| nop | |
| restore_register: | |
| add #SH_SLEEP_BASE_DATA, r0 | |
| mov.l @(r0, r5), r1 | |
| add #-SH_SLEEP_BASE_DATA, r0 | |
| add #SH_SLEEP_BASE_ADDR, r0 | |
| mov.l @(r0, r5), r0 | |
| mov.l r1, @r0 | |
| rts | |
| nop | |
| _set_sr: | |
| stc sr, r8 | |
| and r9, r8 | |
| or r10, r8 | |
| ldc r8, sr | |
| rts | |
| nop | |
| restore_low_regs: | |
| mov.l @r15+, r0 | |
| mov.l @r15+, r1 | |
| mov.l @r15+, r2 | |
| mov.l @r15+, r3 | |
| mov.l @r15+, r4 | |
| mov.l @r15+, r5 | |
| mov.l @r15+, r6 | |
| rts | |
| mov.l @r15+, r7 | |
| .balign 4 | |
| _rb_bit: .long 0x20000000 ! RB=1 | |
| 1: .long ~0x7ff | |
| ENTRY(sh_mobile_sleep_resume_end) |