forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
6.7 KiB
402 lines
6.7 KiB
/* SPDX-License-Identifier: GPL-2.0 |
|
* |
|
* arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S |
|
* |
|
* Sleep mode and Standby modes support for SuperH Mobile |
|
* |
|
* Copyright (C) 2009 Magnus Damm |
|
*/ |
|
|
|
#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)
|
|
|