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