Commit 323ef8db authored by Magnus Damm's avatar Magnus Damm Committed by Paul Mundt

sh: Rework SuperH Mobile sleep mode code

Rework the SuperH Mobile sleep code from including
board specific code to allowing each board to provide
pre/post code snippets. These snippets should contain
sdram management code to enter and leave self-refresh.
Signed-off-by: default avatarMagnus Damm <damm@opensource.se>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent eb0cd9e8
...@@ -34,6 +34,33 @@ extern struct atomic_notifier_head sh_mobile_post_sleep_notifier_list; ...@@ -34,6 +34,33 @@ extern struct atomic_notifier_head sh_mobile_post_sleep_notifier_list;
void sh_mobile_register_self_refresh(unsigned long flags, void sh_mobile_register_self_refresh(unsigned long flags,
void *pre_start, void *pre_end, void *pre_start, void *pre_end,
void *post_start, void *post_end); void *post_start, void *post_end);
/* register structure for address/data information */
struct sh_sleep_regs {
unsigned long stbcr;
};
/* data area for low-level sleep code */
struct sh_sleep_data {
/* current sleep mode (SUSP_SH_...) */
unsigned long mode;
/* addresses of board specific self-refresh snippets */
unsigned long sf_pre;
unsigned long sf_post;
/* register state saved and restored by the assembly code */
unsigned long vbr;
unsigned long spc;
unsigned long sr;
/* structure for keeping register addresses */
struct sh_sleep_regs addr;
/* structure for saving/restoring register state */
struct sh_sleep_regs data;
};
#endif #endif
/* flags passed to assembly suspend code */ /* flags passed to assembly suspend code */
......
...@@ -34,5 +34,15 @@ int main(void) ...@@ -34,5 +34,15 @@ int main(void)
DEFINE(PBE_NEXT, offsetof(struct pbe, next)); DEFINE(PBE_NEXT, offsetof(struct pbe, next));
DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs)); DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs));
#endif #endif
DEFINE(SH_SLEEP_MODE, offsetof(struct sh_sleep_data, mode));
DEFINE(SH_SLEEP_SF_PRE, offsetof(struct sh_sleep_data, sf_pre));
DEFINE(SH_SLEEP_SF_POST, offsetof(struct sh_sleep_data, sf_post));
DEFINE(SH_SLEEP_VBR, offsetof(struct sh_sleep_data, vbr));
DEFINE(SH_SLEEP_SPC, offsetof(struct sh_sleep_data, spc));
DEFINE(SH_SLEEP_SR, offsetof(struct sh_sleep_data, sr));
DEFINE(SH_SLEEP_BASE_ADDR, offsetof(struct sh_sleep_data, addr));
DEFINE(SH_SLEEP_BASE_DATA, offsetof(struct sh_sleep_data, data));
DEFINE(SH_SLEEP_REG_STBCR, offsetof(struct sh_sleep_regs, stbcr));
return 0; return 0;
} }
...@@ -42,13 +42,14 @@ ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list); ...@@ -42,13 +42,14 @@ ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list);
#define ILRAM_BASE 0xe5200000 #define ILRAM_BASE 0xe5200000
extern const unsigned char sh_mobile_standby[];
extern const unsigned int sh_mobile_standby_size;
void sh_mobile_call_standby(unsigned long mode) void sh_mobile_call_standby(unsigned long mode)
{ {
void *onchip_mem = (void *)ILRAM_BASE; void *onchip_mem = (void *)ILRAM_BASE;
void (*standby_onchip_mem)(unsigned long, unsigned long) = onchip_mem; struct sh_sleep_data *sdp = onchip_mem;
void (*standby_onchip_mem)(unsigned long, unsigned long);
/* code located directly after data structure */
standby_onchip_mem = (void *)(sdp + 1);
atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list, atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list,
mode, NULL); mode, NULL);
...@@ -60,10 +61,48 @@ void sh_mobile_call_standby(unsigned long mode) ...@@ -60,10 +61,48 @@ void sh_mobile_call_standby(unsigned long mode)
mode, NULL); mode, NULL);
} }
extern char sh_mobile_sleep_enter_start;
extern char sh_mobile_sleep_enter_end;
extern char sh_mobile_sleep_resume_start;
extern char sh_mobile_sleep_resume_end;
void sh_mobile_register_self_refresh(unsigned long flags, void sh_mobile_register_self_refresh(unsigned long flags,
void *pre_start, void *pre_end, void *pre_start, void *pre_end,
void *post_start, void *post_end) void *post_start, void *post_end)
{ {
void *onchip_mem = (void *)ILRAM_BASE;
void *vp;
struct sh_sleep_data *sdp;
int n;
/* part 0: data area */
sdp = onchip_mem;
sdp->addr.stbcr = 0xa4150020; /* STBCR */
vp = sdp + 1;
/* part 1: common code to enter sleep mode */
n = &sh_mobile_sleep_enter_end - &sh_mobile_sleep_enter_start;
memcpy(vp, &sh_mobile_sleep_enter_start, n);
vp += roundup(n, 4);
/* part 2: board specific code to enter self-refresh mode */
n = pre_end - pre_start;
memcpy(vp, pre_start, n);
sdp->sf_pre = (unsigned long)vp;
vp += roundup(n, 4);
/* part 3: board specific code to resume from self-refresh mode */
n = post_end - post_start;
memcpy(vp, post_start, n);
sdp->sf_post = (unsigned long)vp;
vp += roundup(n, 4);
/* part 4: common code to resume from sleep mode */
WARN_ON(vp > (onchip_mem + 0x600));
vp = onchip_mem + 0x600; /* located at interrupt vector */
n = &sh_mobile_sleep_resume_end - &sh_mobile_sleep_resume_start;
memcpy(vp, &sh_mobile_sleep_resume_start, n);
} }
static int sh_pm_enter(suspend_state_t state) static int sh_pm_enter(suspend_state_t state)
...@@ -83,13 +122,6 @@ static struct platform_suspend_ops sh_pm_ops = { ...@@ -83,13 +122,6 @@ static struct platform_suspend_ops sh_pm_ops = {
static int __init sh_pm_init(void) static int __init sh_pm_init(void)
{ {
void *onchip_mem = (void *)ILRAM_BASE;
/* Copy the assembly snippet to the otherwise ununsed ILRAM */
memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size);
wmb();
ctrl_barrier();
suspend_set_ops(&sh_pm_ops); suspend_set_ops(&sh_pm_ops);
sh_mobile_setup_cpuidle(); sh_mobile_setup_cpuidle();
return 0; return 0;
......
...@@ -20,79 +20,49 @@ ...@@ -20,79 +20,49 @@
* Kernel mode register usage, see entry.S: * Kernel mode register usage, see entry.S:
* k0 scratch * k0 scratch
* k1 scratch * k1 scratch
* k4 scratch
*/ */
#define k0 r0 #define k0 r0
#define k1 r1 #define k1 r1
#define k4 r4
/* manage self-refresh and enter standby mode. /* manage self-refresh and enter standby mode. must be self-contained.
* this code will be copied to on-chip memory and executed from there. * this code will be copied to on-chip memory and executed from there.
*/ */
.balign 4
ENTRY(sh_mobile_sleep_enter_start)
.balign 4096,0,4096 /* save mode flags */
ENTRY(sh_mobile_standby) mov.l r4, @(SH_SLEEP_MODE, r5)
/* save original vbr */ /* save original vbr */
stc vbr, r1 stc vbr, r0
mova saved_vbr, r0 mov.l r0, @(SH_SLEEP_VBR, r5)
mov.l r1, @r0
/* point vbr to our on-chip memory page */ /* point vbr to our on-chip memory page */
ldc r5, vbr ldc r5, vbr
/* save return address */ /* save return address */
mova saved_spc, r0 sts pr, r0
sts pr, r5 mov.l r0, @(SH_SLEEP_SPC, r5)
mov.l r5, @r0
/* save sr */ /* save sr */
mova saved_sr, r0 stc sr, r0
stc sr, r5 mov.l r0, @(SH_SLEEP_SR, r5)
mov.l r5, @r0
/* save mode flags */ /* save stbcr */
mova saved_mode, r0 bsr save_register
mov.l r4, @r0 mov #SH_SLEEP_REG_STBCR, r0
/* put mode flags in r0 */
mov r4, r0
/* call self-refresh entering code if needed */
mov.l @(SH_SLEEP_MODE, r5), r0
tst #SUSP_SH_SF, r0 tst #SUSP_SH_SF, r0
bt skip_set_sf bt skip_set_sf
#ifdef CONFIG_CPU_SUBTYPE_SH7724
/* DBSC: put memory in self-refresh mode */ mov.l @(SH_SLEEP_SF_PRE, r5), r0
mov.l dben_reg, r4 jsr @r0
mov.l dben_data0, r1 nop
mov.l r1, @r4
mov.l dbrfpdn0_reg, r4
mov.l dbrfpdn0_data0, r1
mov.l r1, @r4
mov.l dbcmdcnt_reg, r4
mov.l dbcmdcnt_data0, r1
mov.l r1, @r4
mov.l dbcmdcnt_reg, r4
mov.l dbcmdcnt_data1, r1
mov.l r1, @r4
mov.l dbrfpdn0_reg, r4
mov.l dbrfpdn0_data1, r1
mov.l r1, @r4
#else
/* SBSC: disable power down and put in self-refresh mode */
mov.l 1f, r4
mov.l 2f, r1
mov.l @r4, r2
or r1, r2
mov.l 3f, r3
and r3, r2
mov.l r2, @r4
#endif
skip_set_sf: skip_set_sf:
mov.l @(SH_SLEEP_MODE, r5), r0
tst #SUSP_SH_STANDBY, r0 tst #SUSP_SH_STANDBY, r0
bt test_rstandby bt test_rstandby
...@@ -123,124 +93,92 @@ force_sleep: ...@@ -123,124 +93,92 @@ force_sleep:
do_sleep: do_sleep:
/* setup and enter selected standby mode */ /* setup and enter selected standby mode */
mov.l 5f, r4 bsr get_register
mov.l r1, @r4 mov #SH_SLEEP_REG_STBCR, r0
mov.l r1, @r0
again: again:
sleep sleep
bra again bra again
nop nop
restore_jump_vbr: 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
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 */ /* setup spc with return address to c code */
mov.l saved_spc, k0 mov.l @(SH_SLEEP_SPC, r5), r0
ldc k0, spc ldc r0, spc
/* restore vbr */ /* restore vbr */
mov.l saved_vbr, k0 mov.l @(SH_SLEEP_VBR, r5), r0
ldc k0, vbr ldc r0, vbr
/* setup ssr with saved sr */ /* setup ssr with saved sr */
mov.l saved_sr, k0 mov.l @(SH_SLEEP_SR, r5), r0
ldc k0, ssr ldc r0, ssr
/* get mode flags */
mov.l saved_mode, k0
done_sleep: /* restore sleep mode register */
/* reset standby mode to sleep mode */ bsr restore_register
mov.l 5f, k4 mov #SH_SLEEP_REG_STBCR, r0
mov #0x00, k1
mov.l k1, @k4
tst #SUSP_SH_SF, k0 /* call self-refresh resume code if needed */
mov.l @(SH_SLEEP_MODE, r5), r0
tst #SUSP_SH_SF, r0
bt skip_restore_sf bt skip_restore_sf
#ifdef CONFIG_CPU_SUBTYPE_SH7724 mov.l @(SH_SLEEP_SF_POST, r5), r0
/* DBSC: put memory in auto-refresh mode */ jsr @r0
mov.l dbrfpdn0_reg, k4 nop
mov.l dbrfpdn0_data0, k1
mov.l k1, @k4
nop /* sleep 140 ns */
nop
nop
nop
mov.l dbcmdcnt_reg, k4
mov.l dbcmdcnt_data0, k1
mov.l k1, @k4
mov.l dbcmdcnt_reg, k4
mov.l dbcmdcnt_data1, k1
mov.l k1, @k4
mov.l dben_reg, k4
mov.l dben_data1, k1
mov.l k1, @k4
mov.l dbrfpdn0_reg, k4
mov.l dbrfpdn0_data2, k1
mov.l k1, @k4
#else
/* SBSC: set auto-refresh mode */
mov.l 1f, k4
mov.l @k4, k0
mov.l 4f, k1
and k1, k0
mov.l k0, @k4
mov.l 6f, k4
mov.l 8f, k0
mov.l @k4, k1
mov #-1, k4
add k4, k1
or k1, k0
mov.l 7f, k1
mov.l k0, @k1
#endif
skip_restore_sf: skip_restore_sf:
/* jump to vbr vector */ rte
mov.l saved_vbr, k0
mov.l offset_vbr, k4
add k4, k0
jmp @k0
nop nop
.balign 4 restore_register:
saved_mode: .long 0 add #SH_SLEEP_BASE_DATA, r0
saved_spc: .long 0 mov.l @(r0, r5), r1
saved_sr: .long 0 add #-SH_SLEEP_BASE_DATA, r0
saved_vbr: .long 0 add #SH_SLEEP_BASE_ADDR, r0
offset_vbr: .long 0x600 mov.l @(r0, r5), r0
#ifdef CONFIG_CPU_SUBTYPE_SH7724 mov.l r1, @r0
dben_reg: .long 0xfd000010 /* DBEN */ rts
dben_data0: .long 0
dben_data1: .long 1
dbrfpdn0_reg: .long 0xfd000040 /* DBRFPDN0 */
dbrfpdn0_data0: .long 0
dbrfpdn0_data1: .long 1
dbrfpdn0_data2: .long 0x00010000
dbcmdcnt_reg: .long 0xfd000014 /* DBCMDCNT */
dbcmdcnt_data0: .long 2
dbcmdcnt_data1: .long 4
#else
1: .long 0xfe400008 /* SDCR0 */
2: .long 0x00000400
3: .long 0xffff7fff
4: .long 0xfffffbff
#endif
5: .long 0xa4150020 /* STBCR */
6: .long 0xfe40001c /* RTCOR */
7: .long 0xfe400018 /* RTCNT */
8: .long 0xa55a0000
/* interrupt vector @ 0x600 */
.balign 0x400,0,0x400
.long 0xdeadbeef
.balign 0x200,0,0x200
bra restore_jump_vbr
nop nop
sh_mobile_standby_end:
ENTRY(sh_mobile_standby_size) .balign 4
.long sh_mobile_standby_end - sh_mobile_standby 1: .long ~0x7ff
ENTRY(sh_mobile_sleep_resume_end)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment