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