xref: /linux/arch/powerpc/kernel/swsusp_32.S (revision b2d0f5d5dc53532e6f07bc546a476a55ebdfe0f3)
1/* SPDX-License-Identifier: GPL-2.0 */
2#include <linux/threads.h>
3#include <asm/processor.h>
4#include <asm/page.h>
5#include <asm/cputable.h>
6#include <asm/thread_info.h>
7#include <asm/ppc_asm.h>
8#include <asm/asm-offsets.h>
9#include <asm/mmu.h>
10
11/*
12 * Structure for storing CPU registers on the save area.
13 */
14#define SL_SP		0
15#define SL_PC		4
16#define SL_MSR		8
17#define SL_SDR1		0xc
18#define SL_SPRG0	0x10	/* 4 sprg's */
19#define SL_DBAT0	0x20
20#define SL_IBAT0	0x28
21#define SL_DBAT1	0x30
22#define SL_IBAT1	0x38
23#define SL_DBAT2	0x40
24#define SL_IBAT2	0x48
25#define SL_DBAT3	0x50
26#define SL_IBAT3	0x58
27#define SL_TB		0x60
28#define SL_R2		0x68
29#define SL_CR		0x6c
30#define SL_LR		0x70
31#define SL_R12		0x74	/* r12 to r31 */
32#define SL_SIZE		(SL_R12 + 80)
33
34	.section .data
35	.align	5
36
37_GLOBAL(swsusp_save_area)
38	.space	SL_SIZE
39
40
41	.section .text
42	.align	5
43
44_GLOBAL(swsusp_arch_suspend)
45
46	lis	r11,swsusp_save_area@h
47	ori	r11,r11,swsusp_save_area@l
48
49	mflr	r0
50	stw	r0,SL_LR(r11)
51	mfcr	r0
52	stw	r0,SL_CR(r11)
53	stw	r1,SL_SP(r11)
54	stw	r2,SL_R2(r11)
55	stmw	r12,SL_R12(r11)
56
57	/* Save MSR & SDR1 */
58	mfmsr	r4
59	stw	r4,SL_MSR(r11)
60	mfsdr1	r4
61	stw	r4,SL_SDR1(r11)
62
63	/* Get a stable timebase and save it */
641:	mftbu	r4
65	stw	r4,SL_TB(r11)
66	mftb	r5
67	stw	r5,SL_TB+4(r11)
68	mftbu	r3
69	cmpw	r3,r4
70	bne	1b
71
72	/* Save SPRGs */
73	mfsprg	r4,0
74	stw	r4,SL_SPRG0(r11)
75	mfsprg	r4,1
76	stw	r4,SL_SPRG0+4(r11)
77	mfsprg	r4,2
78	stw	r4,SL_SPRG0+8(r11)
79	mfsprg	r4,3
80	stw	r4,SL_SPRG0+12(r11)
81
82	/* Save BATs */
83	mfdbatu	r4,0
84	stw	r4,SL_DBAT0(r11)
85	mfdbatl	r4,0
86	stw	r4,SL_DBAT0+4(r11)
87	mfdbatu	r4,1
88	stw	r4,SL_DBAT1(r11)
89	mfdbatl	r4,1
90	stw	r4,SL_DBAT1+4(r11)
91	mfdbatu	r4,2
92	stw	r4,SL_DBAT2(r11)
93	mfdbatl	r4,2
94	stw	r4,SL_DBAT2+4(r11)
95	mfdbatu	r4,3
96	stw	r4,SL_DBAT3(r11)
97	mfdbatl	r4,3
98	stw	r4,SL_DBAT3+4(r11)
99	mfibatu	r4,0
100	stw	r4,SL_IBAT0(r11)
101	mfibatl	r4,0
102	stw	r4,SL_IBAT0+4(r11)
103	mfibatu	r4,1
104	stw	r4,SL_IBAT1(r11)
105	mfibatl	r4,1
106	stw	r4,SL_IBAT1+4(r11)
107	mfibatu	r4,2
108	stw	r4,SL_IBAT2(r11)
109	mfibatl	r4,2
110	stw	r4,SL_IBAT2+4(r11)
111	mfibatu	r4,3
112	stw	r4,SL_IBAT3(r11)
113	mfibatl	r4,3
114	stw	r4,SL_IBAT3+4(r11)
115
116#if  0
117	/* Backup various CPU config stuffs */
118	bl	__save_cpu_setup
119#endif
120	/* Call the low level suspend stuff (we should probably have made
121	 * a stackframe...
122	 */
123	bl	swsusp_save
124
125	/* Restore LR from the save area */
126	lis	r11,swsusp_save_area@h
127	ori	r11,r11,swsusp_save_area@l
128	lwz	r0,SL_LR(r11)
129	mtlr	r0
130
131	blr
132
133
134/* Resume code */
135_GLOBAL(swsusp_arch_resume)
136
137#ifdef CONFIG_ALTIVEC
138	/* Stop pending alitvec streams and memory accesses */
139BEGIN_FTR_SECTION
140	DSSALL
141END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
142#endif
143 	sync
144
145	/* Disable MSR:DR to make sure we don't take a TLB or
146	 * hash miss during the copy, as our hash table will
147	 * for a while be unusable. For .text, we assume we are
148	 * covered by a BAT. This works only for non-G5 at this
149	 * point. G5 will need a better approach, possibly using
150	 * a small temporary hash table filled with large mappings,
151	 * disabling the MMU completely isn't a good option for
152	 * performance reasons.
153	 * (Note that 750's may have the same performance issue as
154	 * the G5 in this case, we should investigate using moving
155	 * BATs for these CPUs)
156	 */
157	mfmsr	r0
158	sync
159	rlwinm	r0,r0,0,28,26		/* clear MSR_DR */
160	mtmsr	r0
161	sync
162	isync
163
164	/* Load ptr the list of pages to copy in r3 */
165	lis	r11,(restore_pblist - KERNELBASE)@h
166	ori	r11,r11,restore_pblist@l
167	lwz	r10,0(r11)
168
169	/* Copy the pages. This is a very basic implementation, to
170	 * be replaced by something more cache efficient */
1711:
172	tophys(r3,r10)
173	li	r0,256
174	mtctr	r0
175	lwz	r11,pbe_address(r3)	/* source */
176	tophys(r5,r11)
177	lwz	r10,pbe_orig_address(r3)	/* destination */
178	tophys(r6,r10)
1792:
180	lwz	r8,0(r5)
181	lwz	r9,4(r5)
182	lwz	r10,8(r5)
183	lwz	r11,12(r5)
184	addi	r5,r5,16
185	stw	r8,0(r6)
186	stw	r9,4(r6)
187	stw	r10,8(r6)
188	stw	r11,12(r6)
189	addi	r6,r6,16
190	bdnz	2b
191	lwz		r10,pbe_next(r3)
192	cmpwi	0,r10,0
193	bne	1b
194
195	/* Do a very simple cache flush/inval of the L1 to ensure
196	 * coherency of the icache
197	 */
198	lis	r3,0x0002
199	mtctr	r3
200	li	r3, 0
2011:
202	lwz	r0,0(r3)
203	addi	r3,r3,0x0020
204	bdnz	1b
205	isync
206	sync
207
208	/* Now flush those cache lines */
209	lis	r3,0x0002
210	mtctr	r3
211	li	r3, 0
2121:
213	dcbf	0,r3
214	addi	r3,r3,0x0020
215	bdnz	1b
216	sync
217
218	/* Ok, we are now running with the kernel data of the old
219	 * kernel fully restored. We can get to the save area
220	 * easily now. As for the rest of the code, it assumes the
221	 * loader kernel and the booted one are exactly identical
222	 */
223	lis	r11,swsusp_save_area@h
224	ori	r11,r11,swsusp_save_area@l
225	tophys(r11,r11)
226
227#if 0
228	/* Restore various CPU config stuffs */
229	bl	__restore_cpu_setup
230#endif
231	/* Restore the BATs, and SDR1.  Then we can turn on the MMU.
232	 * This is a bit hairy as we are running out of those BATs,
233	 * but first, our code is probably in the icache, and we are
234	 * writing the same value to the BAT, so that should be fine,
235	 * though a better solution will have to be found long-term
236	 */
237	lwz	r4,SL_SDR1(r11)
238	mtsdr1	r4
239	lwz	r4,SL_SPRG0(r11)
240	mtsprg	0,r4
241	lwz	r4,SL_SPRG0+4(r11)
242	mtsprg	1,r4
243	lwz	r4,SL_SPRG0+8(r11)
244	mtsprg	2,r4
245	lwz	r4,SL_SPRG0+12(r11)
246	mtsprg	3,r4
247
248#if 0
249	lwz	r4,SL_DBAT0(r11)
250	mtdbatu	0,r4
251	lwz	r4,SL_DBAT0+4(r11)
252	mtdbatl	0,r4
253	lwz	r4,SL_DBAT1(r11)
254	mtdbatu	1,r4
255	lwz	r4,SL_DBAT1+4(r11)
256	mtdbatl	1,r4
257	lwz	r4,SL_DBAT2(r11)
258	mtdbatu	2,r4
259	lwz	r4,SL_DBAT2+4(r11)
260	mtdbatl	2,r4
261	lwz	r4,SL_DBAT3(r11)
262	mtdbatu	3,r4
263	lwz	r4,SL_DBAT3+4(r11)
264	mtdbatl	3,r4
265	lwz	r4,SL_IBAT0(r11)
266	mtibatu	0,r4
267	lwz	r4,SL_IBAT0+4(r11)
268	mtibatl	0,r4
269	lwz	r4,SL_IBAT1(r11)
270	mtibatu	1,r4
271	lwz	r4,SL_IBAT1+4(r11)
272	mtibatl	1,r4
273	lwz	r4,SL_IBAT2(r11)
274	mtibatu	2,r4
275	lwz	r4,SL_IBAT2+4(r11)
276	mtibatl	2,r4
277	lwz	r4,SL_IBAT3(r11)
278	mtibatu	3,r4
279	lwz	r4,SL_IBAT3+4(r11)
280	mtibatl	3,r4
281#endif
282
283BEGIN_MMU_FTR_SECTION
284	li	r4,0
285	mtspr	SPRN_DBAT4U,r4
286	mtspr	SPRN_DBAT4L,r4
287	mtspr	SPRN_DBAT5U,r4
288	mtspr	SPRN_DBAT5L,r4
289	mtspr	SPRN_DBAT6U,r4
290	mtspr	SPRN_DBAT6L,r4
291	mtspr	SPRN_DBAT7U,r4
292	mtspr	SPRN_DBAT7L,r4
293	mtspr	SPRN_IBAT4U,r4
294	mtspr	SPRN_IBAT4L,r4
295	mtspr	SPRN_IBAT5U,r4
296	mtspr	SPRN_IBAT5L,r4
297	mtspr	SPRN_IBAT6U,r4
298	mtspr	SPRN_IBAT6L,r4
299	mtspr	SPRN_IBAT7U,r4
300	mtspr	SPRN_IBAT7L,r4
301END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
302
303	/* Flush all TLBs */
304	lis	r4,0x1000
3051:	addic.	r4,r4,-0x1000
306	tlbie	r4
307	bgt	1b
308	sync
309
310	/* restore the MSR and turn on the MMU */
311	lwz	r3,SL_MSR(r11)
312	bl	turn_on_mmu
313	tovirt(r11,r11)
314
315	/* Restore TB */
316	li	r3,0
317	mttbl	r3
318	lwz	r3,SL_TB(r11)
319	lwz	r4,SL_TB+4(r11)
320	mttbu	r3
321	mttbl	r4
322
323	/* Kick decrementer */
324	li	r0,1
325	mtdec	r0
326
327	/* Restore the callee-saved registers and return */
328	lwz	r0,SL_CR(r11)
329	mtcr	r0
330	lwz	r2,SL_R2(r11)
331	lmw	r12,SL_R12(r11)
332	lwz	r1,SL_SP(r11)
333	lwz	r0,SL_LR(r11)
334	mtlr	r0
335
336	// XXX Note: we don't really need to call swsusp_resume
337
338	li	r3,0
339	blr
340
341/* FIXME:This construct is actually not useful since we don't shut
342 * down the instruction MMU, we could just flip back MSR-DR on.
343 */
344turn_on_mmu:
345	mflr	r4
346	mtsrr0	r4
347	mtsrr1	r3
348	sync
349	isync
350	rfi
351
352