1/* SPDX-License-Identifier: GPL-2.0
2 *
3 * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S
4 *
5 * Sleep mode and Standby modes support for SuperH Mobile
6 *
7 * Copyright (C) 2009 Magnus Damm
8 */
9
10#include <linux/sys.h>
11#include <linux/errno.h>
12#include <linux/linkage.h>
13#include <asm/asm-offsets.h>
14#include <asm/suspend.h>
15
16/*
17 * Kernel mode register usage, see entry.S:
18 * k0 scratch
19 * k1 scratch
20 */
21#define k0 r0
22#define k1 r1
23
24/* manage self-refresh and enter standby mode. must be self-contained.
25 * this code will be copied to on-chip memory and executed from there.
26 */
27 .balign 4
28ENTRY(sh_mobile_sleep_enter_start)
29
30 /* save mode flags */
31 mov.l r4, @(SH_SLEEP_MODE, r5)
32
33 /* save original vbr */
34 stc vbr, r0
35 mov.l r0, @(SH_SLEEP_VBR, r5)
36
37 /* point vbr to our on-chip memory page */
38 ldc r5, vbr
39
40 /* save return address */
41 sts pr, r0
42 mov.l r0, @(SH_SLEEP_SPC, r5)
43
44 /* save sr */
45 stc sr, r0
46 mov.l r0, @(SH_SLEEP_SR, r5)
47
48 /* save general purpose registers to stack if needed */
49 mov.l @(SH_SLEEP_MODE, r5), r0
50 tst #SUSP_SH_REGS, r0
51 bt skip_regs_save
52
53 sts.l pr, @-r15
54 mov.l r14, @-r15
55 mov.l r13, @-r15
56 mov.l r12, @-r15
57 mov.l r11, @-r15
58 mov.l r10, @-r15
59 mov.l r9, @-r15
60 mov.l r8, @-r15
61
62 /* make sure bank0 is selected, save low registers */
63 mov.l rb_bit, r9
64 not r9, r9
65 bsr set_sr
66 mov #0, r10
67
68 bsr save_low_regs
69 nop
70
71 /* switch to bank 1, save low registers */
72 mov.l rb_bit, r10
73 bsr set_sr
74 mov #-1, r9
75
76 bsr save_low_regs
77 nop
78
79 /* switch back to bank 0 */
80 mov.l rb_bit, r9
81 not r9, r9
82 bsr set_sr
83 mov #0, r10
84
85skip_regs_save:
86
87 /* save sp, also set to internal ram */
88 mov.l r15, @(SH_SLEEP_SP, r5)
89 mov r5, r15
90
91 /* save stbcr */
92 bsr save_register
93 mov #SH_SLEEP_REG_STBCR, r0
94
95 /* save mmu and cache context if needed */
96 mov.l @(SH_SLEEP_MODE, r5), r0
97 tst #SUSP_SH_MMU, r0
98 bt skip_mmu_save_disable
99
100 /* save mmu state */
101 bsr save_register
102 mov #SH_SLEEP_REG_PTEH, r0
103
104 bsr save_register
105 mov #SH_SLEEP_REG_PTEL, r0
106
107 bsr save_register
108 mov #SH_SLEEP_REG_TTB, r0
109
110 bsr save_register
111 mov #SH_SLEEP_REG_TEA, r0
112
113 bsr save_register
114 mov #SH_SLEEP_REG_MMUCR, r0
115
116 bsr save_register
117 mov #SH_SLEEP_REG_PTEA, r0
118
119 bsr save_register
120 mov #SH_SLEEP_REG_PASCR, r0
121
122 bsr save_register
123 mov #SH_SLEEP_REG_IRMCR, r0
124
125 /* invalidate TLBs and disable the MMU */
126 bsr get_register
127 mov #SH_SLEEP_REG_MMUCR, r0
128 mov #4, r1
129 mov.l r1, @r0
130 icbi @r0
131
132 /* save cache registers and disable caches */
133 bsr save_register
134 mov #SH_SLEEP_REG_CCR, r0
135
136 bsr save_register
137 mov #SH_SLEEP_REG_RAMCR, r0
138
139 bsr get_register
140 mov #SH_SLEEP_REG_CCR, r0
141 mov #0, r1
142 mov.l r1, @r0
143 icbi @r0
144
145skip_mmu_save_disable:
146 /* call self-refresh entering code if needed */
147 mov.l @(SH_SLEEP_MODE, r5), r0
148 tst #SUSP_SH_SF, r0
149 bt skip_set_sf
150
151 mov.l @(SH_SLEEP_SF_PRE, r5), r0
152 jsr @r0
153 nop
154
155skip_set_sf:
156 mov.l @(SH_SLEEP_MODE, r5), r0
157 tst #SUSP_SH_STANDBY, r0
158 bt test_rstandby
159
160 /* set mode to "software standby mode" */
161 bra do_sleep
162 mov #0x80, r1
163
164test_rstandby:
165 tst #SUSP_SH_RSTANDBY, r0
166 bt test_ustandby
167
168 /* setup BAR register */
169 bsr get_register
170 mov #SH_SLEEP_REG_BAR, r0
171 mov.l @(SH_SLEEP_RESUME, r5), r1
172 mov.l r1, @r0
173
174 /* set mode to "r-standby mode" */
175 bra do_sleep
176 mov #0x20, r1
177
178test_ustandby:
179 tst #SUSP_SH_USTANDBY, r0
180 bt force_sleep
181
182 /* set mode to "u-standby mode" */
183 bra do_sleep
184 mov #0x10, r1
185
186force_sleep:
187
188 /* set mode to "sleep mode" */
189 mov #0x00, r1
190
191do_sleep:
192 /* setup and enter selected standby mode */
193 bsr get_register
194 mov #SH_SLEEP_REG_STBCR, r0
195 mov.l r1, @r0
196again:
197 sleep
198 bra again
199 nop
200
201save_register:
202 add #SH_SLEEP_BASE_ADDR, r0
203 mov.l @(r0, r5), r1
204 add #-SH_SLEEP_BASE_ADDR, r0
205 mov.l @r1, r1
206 add #SH_SLEEP_BASE_DATA, r0
207 mov.l r1, @(r0, r5)
208 add #-SH_SLEEP_BASE_DATA, r0
209 rts
210 nop
211
212get_register:
213 add #SH_SLEEP_BASE_ADDR, r0
214 mov.l @(r0, r5), r0
215 rts
216 nop
217
218set_sr:
219 stc sr, r8
220 and r9, r8
221 or r10, r8
222 ldc r8, sr
223 rts
224 nop
225
226save_low_regs:
227 mov.l r7, @-r15
228 mov.l r6, @-r15
229 mov.l r5, @-r15
230 mov.l r4, @-r15
231 mov.l r3, @-r15
232 mov.l r2, @-r15
233 mov.l r1, @-r15
234 rts
235 mov.l r0, @-r15
236
237 .balign 4
238rb_bit: .long 0x20000000 ! RB=1
239
240ENTRY(sh_mobile_sleep_enter_end)
241
242 .balign 4
243ENTRY(sh_mobile_sleep_resume_start)
244
245 /* figure out start address */
246 bsr 0f
247 nop
2480:
249 sts pr, k1
250 mov.l 1f, k0
251 and k0, k1
252
253 /* store pointer to data area in VBR */
254 ldc k1, vbr
255
256 /* setup sr with saved sr */
257 mov.l @(SH_SLEEP_SR, k1), k0
258 ldc k0, sr
259
260 /* now: user register set! */
261 stc vbr, r5
262
263 /* setup spc with return address to c code */
264 mov.l @(SH_SLEEP_SPC, r5), r0
265 ldc r0, spc
266
267 /* restore vbr */
268 mov.l @(SH_SLEEP_VBR, r5), r0
269 ldc r0, vbr
270
271 /* setup ssr with saved sr */
272 mov.l @(SH_SLEEP_SR, r5), r0
273 ldc r0, ssr
274
275 /* restore sp */
276 mov.l @(SH_SLEEP_SP, r5), r15
277
278 /* restore sleep mode register */
279 bsr restore_register
280 mov #SH_SLEEP_REG_STBCR, r0
281
282 /* call self-refresh resume code if needed */
283 mov.l @(SH_SLEEP_MODE, r5), r0
284 tst #SUSP_SH_SF, r0
285 bt skip_restore_sf
286
287 mov.l @(SH_SLEEP_SF_POST, r5), r0
288 jsr @r0
289 nop
290
291skip_restore_sf:
292 /* restore mmu and cache state if needed */
293 mov.l @(SH_SLEEP_MODE, r5), r0
294 tst #SUSP_SH_MMU, r0
295 bt skip_restore_mmu
296
297 /* restore mmu state */
298 bsr restore_register
299 mov #SH_SLEEP_REG_PTEH, r0
300
301 bsr restore_register
302 mov #SH_SLEEP_REG_PTEL, r0
303
304 bsr restore_register
305 mov #SH_SLEEP_REG_TTB, r0
306
307 bsr restore_register
308 mov #SH_SLEEP_REG_TEA, r0
309
310 bsr restore_register
311 mov #SH_SLEEP_REG_PTEA, r0
312
313 bsr restore_register
314 mov #SH_SLEEP_REG_PASCR, r0
315
316 bsr restore_register
317 mov #SH_SLEEP_REG_IRMCR, r0
318
319 bsr restore_register
320 mov #SH_SLEEP_REG_MMUCR, r0
321 icbi @r0
322
323 /* restore cache settings */
324 bsr restore_register
325 mov #SH_SLEEP_REG_RAMCR, r0
326 icbi @r0
327
328 bsr restore_register
329 mov #SH_SLEEP_REG_CCR, r0
330 icbi @r0
331
332skip_restore_mmu:
333
334 /* restore general purpose registers if needed */
335 mov.l @(SH_SLEEP_MODE, r5), r0
336 tst #SUSP_SH_REGS, r0
337 bt skip_restore_regs
338
339 /* switch to bank 1, restore low registers */
340 mov.l _rb_bit, r10
341 bsr _set_sr
342 mov #-1, r9
343
344 bsr restore_low_regs
345 nop
346
347 /* switch to bank0, restore low registers */
348 mov.l _rb_bit, r9
349 not r9, r9
350 bsr _set_sr
351 mov #0, r10
352
353 bsr restore_low_regs
354 nop
355
356 /* restore the rest of the registers */
357 mov.l @r15+, r8
358 mov.l @r15+, r9
359 mov.l @r15+, r10
360 mov.l @r15+, r11
361 mov.l @r15+, r12
362 mov.l @r15+, r13
363 mov.l @r15+, r14
364 lds.l @r15+, pr
365
366skip_restore_regs:
367 rte
368 nop
369
370restore_register:
371 add #SH_SLEEP_BASE_DATA, r0
372 mov.l @(r0, r5), r1
373 add #-SH_SLEEP_BASE_DATA, r0
374 add #SH_SLEEP_BASE_ADDR, r0
375 mov.l @(r0, r5), r0
376 mov.l r1, @r0
377 rts
378 nop
379
380_set_sr:
381 stc sr, r8
382 and r9, r8
383 or r10, r8
384 ldc r8, sr
385 rts
386 nop
387
388restore_low_regs:
389 mov.l @r15+, r0
390 mov.l @r15+, r1
391 mov.l @r15+, r2
392 mov.l @r15+, r3
393 mov.l @r15+, r4
394 mov.l @r15+, r5
395 mov.l @r15+, r6
396 rts
397 mov.l @r15+, r7
398
399 .balign 4
400_rb_bit: .long 0x20000000 ! RB=1
4011: .long ~0x7ff
402ENTRY(sh_mobile_sleep_resume_end)
403

source code of linux/arch/sh/kernel/cpu/shmobile/sleep.S