diff options
author | Magnus Damm <damm@opensource.se> | 2009-10-30 00:24:07 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-10-30 01:36:52 -0400 |
commit | 323ef8dba67fb7b9c709457bd0374d88cfb8f25f (patch) | |
tree | f8d4ae7d78837f13970fecdba061fc0df818ae11 /arch/sh/kernel | |
parent | eb0cd9e88c6a6561055b32a17d44d8918aecc3c7 (diff) |
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: Magnus Damm <damm@opensource.se>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/asm-offsets.c | 10 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/pm.c | 54 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/shmobile/sleep.S | 244 |
3 files changed, 144 insertions, 164 deletions
diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c index d218e808294e..9bdeff962e1c 100644 --- a/arch/sh/kernel/asm-offsets.c +++ b/arch/sh/kernel/asm-offsets.c | |||
@@ -34,5 +34,15 @@ int main(void) | |||
34 | DEFINE(PBE_NEXT, offsetof(struct pbe, next)); | 34 | DEFINE(PBE_NEXT, offsetof(struct pbe, next)); |
35 | DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs)); | 35 | DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs)); |
36 | #endif | 36 | #endif |
37 | |||
38 | DEFINE(SH_SLEEP_MODE, offsetof(struct sh_sleep_data, mode)); | ||
39 | DEFINE(SH_SLEEP_SF_PRE, offsetof(struct sh_sleep_data, sf_pre)); | ||
40 | DEFINE(SH_SLEEP_SF_POST, offsetof(struct sh_sleep_data, sf_post)); | ||
41 | DEFINE(SH_SLEEP_VBR, offsetof(struct sh_sleep_data, vbr)); | ||
42 | DEFINE(SH_SLEEP_SPC, offsetof(struct sh_sleep_data, spc)); | ||
43 | DEFINE(SH_SLEEP_SR, offsetof(struct sh_sleep_data, sr)); | ||
44 | DEFINE(SH_SLEEP_BASE_ADDR, offsetof(struct sh_sleep_data, addr)); | ||
45 | DEFINE(SH_SLEEP_BASE_DATA, offsetof(struct sh_sleep_data, data)); | ||
46 | DEFINE(SH_SLEEP_REG_STBCR, offsetof(struct sh_sleep_regs, stbcr)); | ||
37 | return 0; | 47 | return 0; |
38 | } | 48 | } |
diff --git a/arch/sh/kernel/cpu/shmobile/pm.c b/arch/sh/kernel/cpu/shmobile/pm.c index b424747e4252..cb3d28f2968c 100644 --- a/arch/sh/kernel/cpu/shmobile/pm.c +++ b/arch/sh/kernel/cpu/shmobile/pm.c | |||
@@ -42,13 +42,14 @@ ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list); | |||
42 | 42 | ||
43 | #define ILRAM_BASE 0xe5200000 | 43 | #define ILRAM_BASE 0xe5200000 |
44 | 44 | ||
45 | extern const unsigned char sh_mobile_standby[]; | ||
46 | extern const unsigned int sh_mobile_standby_size; | ||
47 | |||
48 | void sh_mobile_call_standby(unsigned long mode) | 45 | void sh_mobile_call_standby(unsigned long mode) |
49 | { | 46 | { |
50 | void *onchip_mem = (void *)ILRAM_BASE; | 47 | void *onchip_mem = (void *)ILRAM_BASE; |
51 | void (*standby_onchip_mem)(unsigned long, unsigned long) = onchip_mem; | 48 | struct sh_sleep_data *sdp = onchip_mem; |
49 | void (*standby_onchip_mem)(unsigned long, unsigned long); | ||
50 | |||
51 | /* code located directly after data structure */ | ||
52 | standby_onchip_mem = (void *)(sdp + 1); | ||
52 | 53 | ||
53 | atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list, | 54 | atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list, |
54 | mode, NULL); | 55 | mode, NULL); |
@@ -60,10 +61,48 @@ void sh_mobile_call_standby(unsigned long mode) | |||
60 | mode, NULL); | 61 | mode, NULL); |
61 | } | 62 | } |
62 | 63 | ||
64 | extern char sh_mobile_sleep_enter_start; | ||
65 | extern char sh_mobile_sleep_enter_end; | ||
66 | |||
67 | extern char sh_mobile_sleep_resume_start; | ||
68 | extern char sh_mobile_sleep_resume_end; | ||
69 | |||
63 | void sh_mobile_register_self_refresh(unsigned long flags, | 70 | void sh_mobile_register_self_refresh(unsigned long flags, |
64 | void *pre_start, void *pre_end, | 71 | void *pre_start, void *pre_end, |
65 | void *post_start, void *post_end) | 72 | void *post_start, void *post_end) |
66 | { | 73 | { |
74 | void *onchip_mem = (void *)ILRAM_BASE; | ||
75 | void *vp; | ||
76 | struct sh_sleep_data *sdp; | ||
77 | int n; | ||
78 | |||
79 | /* part 0: data area */ | ||
80 | sdp = onchip_mem; | ||
81 | sdp->addr.stbcr = 0xa4150020; /* STBCR */ | ||
82 | vp = sdp + 1; | ||
83 | |||
84 | /* part 1: common code to enter sleep mode */ | ||
85 | n = &sh_mobile_sleep_enter_end - &sh_mobile_sleep_enter_start; | ||
86 | memcpy(vp, &sh_mobile_sleep_enter_start, n); | ||
87 | vp += roundup(n, 4); | ||
88 | |||
89 | /* part 2: board specific code to enter self-refresh mode */ | ||
90 | n = pre_end - pre_start; | ||
91 | memcpy(vp, pre_start, n); | ||
92 | sdp->sf_pre = (unsigned long)vp; | ||
93 | vp += roundup(n, 4); | ||
94 | |||
95 | /* part 3: board specific code to resume from self-refresh mode */ | ||
96 | n = post_end - post_start; | ||
97 | memcpy(vp, post_start, n); | ||
98 | sdp->sf_post = (unsigned long)vp; | ||
99 | vp += roundup(n, 4); | ||
100 | |||
101 | /* part 4: common code to resume from sleep mode */ | ||
102 | WARN_ON(vp > (onchip_mem + 0x600)); | ||
103 | vp = onchip_mem + 0x600; /* located at interrupt vector */ | ||
104 | n = &sh_mobile_sleep_resume_end - &sh_mobile_sleep_resume_start; | ||
105 | memcpy(vp, &sh_mobile_sleep_resume_start, n); | ||
67 | } | 106 | } |
68 | 107 | ||
69 | static int sh_pm_enter(suspend_state_t state) | 108 | static int sh_pm_enter(suspend_state_t state) |
@@ -83,13 +122,6 @@ static struct platform_suspend_ops sh_pm_ops = { | |||
83 | 122 | ||
84 | static int __init sh_pm_init(void) | 123 | static int __init sh_pm_init(void) |
85 | { | 124 | { |
86 | void *onchip_mem = (void *)ILRAM_BASE; | ||
87 | |||
88 | /* Copy the assembly snippet to the otherwise ununsed ILRAM */ | ||
89 | memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size); | ||
90 | wmb(); | ||
91 | ctrl_barrier(); | ||
92 | |||
93 | suspend_set_ops(&sh_pm_ops); | 125 | suspend_set_ops(&sh_pm_ops); |
94 | sh_mobile_setup_cpuidle(); | 126 | sh_mobile_setup_cpuidle(); |
95 | return 0; | 127 | return 0; |
diff --git a/arch/sh/kernel/cpu/shmobile/sleep.S b/arch/sh/kernel/cpu/shmobile/sleep.S index a439e6c7824f..d3221d9b88be 100644 --- a/arch/sh/kernel/cpu/shmobile/sleep.S +++ b/arch/sh/kernel/cpu/shmobile/sleep.S | |||
@@ -20,79 +20,49 @@ | |||
20 | * Kernel mode register usage, see entry.S: | 20 | * Kernel mode register usage, see entry.S: |
21 | * k0 scratch | 21 | * k0 scratch |
22 | * k1 scratch | 22 | * k1 scratch |
23 | * k4 scratch | ||
24 | */ | 23 | */ |
25 | #define k0 r0 | 24 | #define k0 r0 |
26 | #define k1 r1 | 25 | #define k1 r1 |
27 | #define k4 r4 | ||
28 | 26 | ||
29 | /* manage self-refresh and enter standby mode. | 27 | /* manage self-refresh and enter standby mode. must be self-contained. |
30 | * this code will be copied to on-chip memory and executed from there. | 28 | * this code will be copied to on-chip memory and executed from there. |
31 | */ | 29 | */ |
30 | .balign 4 | ||
31 | ENTRY(sh_mobile_sleep_enter_start) | ||
32 | 32 | ||
33 | .balign 4096,0,4096 | 33 | /* save mode flags */ |
34 | ENTRY(sh_mobile_standby) | 34 | mov.l r4, @(SH_SLEEP_MODE, r5) |
35 | 35 | ||
36 | /* save original vbr */ | 36 | /* save original vbr */ |
37 | stc vbr, r1 | 37 | stc vbr, r0 |
38 | mova saved_vbr, r0 | 38 | mov.l r0, @(SH_SLEEP_VBR, r5) |
39 | mov.l r1, @r0 | ||
40 | 39 | ||
41 | /* point vbr to our on-chip memory page */ | 40 | /* point vbr to our on-chip memory page */ |
42 | ldc r5, vbr | 41 | ldc r5, vbr |
43 | 42 | ||
44 | /* save return address */ | 43 | /* save return address */ |
45 | mova saved_spc, r0 | 44 | sts pr, r0 |
46 | sts pr, r5 | 45 | mov.l r0, @(SH_SLEEP_SPC, r5) |
47 | mov.l r5, @r0 | ||
48 | 46 | ||
49 | /* save sr */ | 47 | /* save sr */ |
50 | mova saved_sr, r0 | 48 | stc sr, r0 |
51 | stc sr, r5 | 49 | mov.l r0, @(SH_SLEEP_SR, r5) |
52 | mov.l r5, @r0 | ||
53 | 50 | ||
54 | /* save mode flags */ | 51 | /* save stbcr */ |
55 | mova saved_mode, r0 | 52 | bsr save_register |
56 | mov.l r4, @r0 | 53 | mov #SH_SLEEP_REG_STBCR, r0 |
57 | |||
58 | /* put mode flags in r0 */ | ||
59 | mov r4, r0 | ||
60 | 54 | ||
55 | /* call self-refresh entering code if needed */ | ||
56 | mov.l @(SH_SLEEP_MODE, r5), r0 | ||
61 | tst #SUSP_SH_SF, r0 | 57 | tst #SUSP_SH_SF, r0 |
62 | bt skip_set_sf | 58 | bt skip_set_sf |
63 | #ifdef CONFIG_CPU_SUBTYPE_SH7724 | 59 | |
64 | /* DBSC: put memory in self-refresh mode */ | 60 | mov.l @(SH_SLEEP_SF_PRE, r5), r0 |
65 | mov.l dben_reg, r4 | 61 | jsr @r0 |
66 | mov.l dben_data0, r1 | 62 | nop |
67 | mov.l r1, @r4 | ||
68 | |||
69 | mov.l dbrfpdn0_reg, r4 | ||
70 | mov.l dbrfpdn0_data0, r1 | ||
71 | mov.l r1, @r4 | ||
72 | |||
73 | mov.l dbcmdcnt_reg, r4 | ||
74 | mov.l dbcmdcnt_data0, r1 | ||
75 | mov.l r1, @r4 | ||
76 | |||
77 | mov.l dbcmdcnt_reg, r4 | ||
78 | mov.l dbcmdcnt_data1, r1 | ||
79 | mov.l r1, @r4 | ||
80 | |||
81 | mov.l dbrfpdn0_reg, r4 | ||
82 | mov.l dbrfpdn0_data1, r1 | ||
83 | mov.l r1, @r4 | ||
84 | #else | ||
85 | /* SBSC: disable power down and put in self-refresh mode */ | ||
86 | mov.l 1f, r4 | ||
87 | mov.l 2f, r1 | ||
88 | mov.l @r4, r2 | ||
89 | or r1, r2 | ||
90 | mov.l 3f, r3 | ||
91 | and r3, r2 | ||
92 | mov.l r2, @r4 | ||
93 | #endif | ||
94 | 63 | ||
95 | skip_set_sf: | 64 | skip_set_sf: |
65 | mov.l @(SH_SLEEP_MODE, r5), r0 | ||
96 | tst #SUSP_SH_STANDBY, r0 | 66 | tst #SUSP_SH_STANDBY, r0 |
97 | bt test_rstandby | 67 | bt test_rstandby |
98 | 68 | ||
@@ -123,124 +93,92 @@ force_sleep: | |||
123 | 93 | ||
124 | do_sleep: | 94 | do_sleep: |
125 | /* setup and enter selected standby mode */ | 95 | /* setup and enter selected standby mode */ |
126 | mov.l 5f, r4 | 96 | bsr get_register |
127 | mov.l r1, @r4 | 97 | mov #SH_SLEEP_REG_STBCR, r0 |
98 | mov.l r1, @r0 | ||
128 | again: | 99 | again: |
129 | sleep | 100 | sleep |
130 | bra again | 101 | bra again |
131 | nop | 102 | nop |
132 | 103 | ||
133 | restore_jump_vbr: | 104 | save_register: |
105 | add #SH_SLEEP_BASE_ADDR, r0 | ||
106 | mov.l @(r0, r5), r1 | ||
107 | add #-SH_SLEEP_BASE_ADDR, r0 | ||
108 | mov.l @r1, r1 | ||
109 | add #SH_SLEEP_BASE_DATA, r0 | ||
110 | mov.l r1, @(r0, r5) | ||
111 | add #-SH_SLEEP_BASE_DATA, r0 | ||
112 | rts | ||
113 | nop | ||
114 | |||
115 | get_register: | ||
116 | add #SH_SLEEP_BASE_ADDR, r0 | ||
117 | mov.l @(r0, r5), r0 | ||
118 | rts | ||
119 | nop | ||
120 | ENTRY(sh_mobile_sleep_enter_end) | ||
121 | |||
122 | .balign 4 | ||
123 | ENTRY(sh_mobile_sleep_resume_start) | ||
124 | |||
125 | /* figure out start address */ | ||
126 | bsr 0f | ||
127 | nop | ||
128 | 0: | ||
129 | sts pr, k1 | ||
130 | mov.l 1f, k0 | ||
131 | and k0, k1 | ||
132 | |||
133 | /* store pointer to data area in VBR */ | ||
134 | ldc k1, vbr | ||
135 | |||
136 | /* setup sr with saved sr */ | ||
137 | mov.l @(SH_SLEEP_SR, k1), k0 | ||
138 | ldc k0, sr | ||
139 | |||
140 | /* now: user register set! */ | ||
141 | stc vbr, r5 | ||
142 | |||
134 | /* setup spc with return address to c code */ | 143 | /* setup spc with return address to c code */ |
135 | mov.l saved_spc, k0 | 144 | mov.l @(SH_SLEEP_SPC, r5), r0 |
136 | ldc k0, spc | 145 | ldc r0, spc |
137 | 146 | ||
138 | /* restore vbr */ | 147 | /* restore vbr */ |
139 | mov.l saved_vbr, k0 | 148 | mov.l @(SH_SLEEP_VBR, r5), r0 |
140 | ldc k0, vbr | 149 | ldc r0, vbr |
141 | 150 | ||
142 | /* setup ssr with saved sr */ | 151 | /* setup ssr with saved sr */ |
143 | mov.l saved_sr, k0 | 152 | mov.l @(SH_SLEEP_SR, r5), r0 |
144 | ldc k0, ssr | 153 | ldc r0, ssr |
145 | |||
146 | /* get mode flags */ | ||
147 | mov.l saved_mode, k0 | ||
148 | 154 | ||
149 | done_sleep: | 155 | /* restore sleep mode register */ |
150 | /* reset standby mode to sleep mode */ | 156 | bsr restore_register |
151 | mov.l 5f, k4 | 157 | mov #SH_SLEEP_REG_STBCR, r0 |
152 | mov #0x00, k1 | ||
153 | mov.l k1, @k4 | ||
154 | 158 | ||
155 | tst #SUSP_SH_SF, k0 | 159 | /* call self-refresh resume code if needed */ |
160 | mov.l @(SH_SLEEP_MODE, r5), r0 | ||
161 | tst #SUSP_SH_SF, r0 | ||
156 | bt skip_restore_sf | 162 | bt skip_restore_sf |
157 | 163 | ||
158 | #ifdef CONFIG_CPU_SUBTYPE_SH7724 | 164 | mov.l @(SH_SLEEP_SF_POST, r5), r0 |
159 | /* DBSC: put memory in auto-refresh mode */ | 165 | jsr @r0 |
160 | mov.l dbrfpdn0_reg, k4 | 166 | nop |
161 | mov.l dbrfpdn0_data0, k1 | 167 | |
162 | mov.l k1, @k4 | ||
163 | |||
164 | nop /* sleep 140 ns */ | ||
165 | nop | ||
166 | nop | ||
167 | nop | ||
168 | |||
169 | mov.l dbcmdcnt_reg, k4 | ||
170 | mov.l dbcmdcnt_data0, k1 | ||
171 | mov.l k1, @k4 | ||
172 | |||
173 | mov.l dbcmdcnt_reg, k4 | ||
174 | mov.l dbcmdcnt_data1, k1 | ||
175 | mov.l k1, @k4 | ||
176 | |||
177 | mov.l dben_reg, k4 | ||
178 | mov.l dben_data1, k1 | ||
179 | mov.l k1, @k4 | ||
180 | |||
181 | mov.l dbrfpdn0_reg, k4 | ||
182 | mov.l dbrfpdn0_data2, k1 | ||
183 | mov.l k1, @k4 | ||
184 | #else | ||
185 | /* SBSC: set auto-refresh mode */ | ||
186 | mov.l 1f, k4 | ||
187 | mov.l @k4, k0 | ||
188 | mov.l 4f, k1 | ||
189 | and k1, k0 | ||
190 | mov.l k0, @k4 | ||
191 | mov.l 6f, k4 | ||
192 | mov.l 8f, k0 | ||
193 | mov.l @k4, k1 | ||
194 | mov #-1, k4 | ||
195 | add k4, k1 | ||
196 | or k1, k0 | ||
197 | mov.l 7f, k1 | ||
198 | mov.l k0, @k1 | ||
199 | #endif | ||
200 | skip_restore_sf: | 168 | skip_restore_sf: |
201 | /* jump to vbr vector */ | 169 | rte |
202 | mov.l saved_vbr, k0 | ||
203 | mov.l offset_vbr, k4 | ||
204 | add k4, k0 | ||
205 | jmp @k0 | ||
206 | nop | 170 | nop |
207 | 171 | ||
208 | .balign 4 | 172 | restore_register: |
209 | saved_mode: .long 0 | 173 | add #SH_SLEEP_BASE_DATA, r0 |
210 | saved_spc: .long 0 | 174 | mov.l @(r0, r5), r1 |
211 | saved_sr: .long 0 | 175 | add #-SH_SLEEP_BASE_DATA, r0 |
212 | saved_vbr: .long 0 | 176 | add #SH_SLEEP_BASE_ADDR, r0 |
213 | offset_vbr: .long 0x600 | 177 | mov.l @(r0, r5), r0 |
214 | #ifdef CONFIG_CPU_SUBTYPE_SH7724 | 178 | mov.l r1, @r0 |
215 | dben_reg: .long 0xfd000010 /* DBEN */ | 179 | rts |
216 | dben_data0: .long 0 | ||
217 | dben_data1: .long 1 | ||
218 | dbrfpdn0_reg: .long 0xfd000040 /* DBRFPDN0 */ | ||
219 | dbrfpdn0_data0: .long 0 | ||
220 | dbrfpdn0_data1: .long 1 | ||
221 | dbrfpdn0_data2: .long 0x00010000 | ||
222 | dbcmdcnt_reg: .long 0xfd000014 /* DBCMDCNT */ | ||
223 | dbcmdcnt_data0: .long 2 | ||
224 | dbcmdcnt_data1: .long 4 | ||
225 | #else | ||
226 | 1: .long 0xfe400008 /* SDCR0 */ | ||
227 | 2: .long 0x00000400 | ||
228 | 3: .long 0xffff7fff | ||
229 | 4: .long 0xfffffbff | ||
230 | #endif | ||
231 | 5: .long 0xa4150020 /* STBCR */ | ||
232 | 6: .long 0xfe40001c /* RTCOR */ | ||
233 | 7: .long 0xfe400018 /* RTCNT */ | ||
234 | 8: .long 0xa55a0000 | ||
235 | |||
236 | |||
237 | /* interrupt vector @ 0x600 */ | ||
238 | .balign 0x400,0,0x400 | ||
239 | .long 0xdeadbeef | ||
240 | .balign 0x200,0,0x200 | ||
241 | bra restore_jump_vbr | ||
242 | nop | 180 | nop |
243 | sh_mobile_standby_end: | ||
244 | 181 | ||
245 | ENTRY(sh_mobile_standby_size) | 182 | .balign 4 |
246 | .long sh_mobile_standby_end - sh_mobile_standby | 183 | 1: .long ~0x7ff |
184 | ENTRY(sh_mobile_sleep_resume_end) | ||