xref: /openbmc/linux/arch/arm/mach-imx/suspend-imx6.S (revision f21e49be)
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Copyright 2014 Freescale Semiconductor, Inc.
4 */
5
6#include <linux/linkage.h>
7#include <asm/assembler.h>
8#include <asm/asm-offsets.h>
9#include <asm/hardware/cache-l2x0.h>
10#include "hardware.h"
11
12/*
13 * ==================== low level suspend ====================
14 *
15 * Better to follow below rules to use ARM registers:
16 * r0: pm_info structure address;
17 * r1 ~ r4: for saving pm_info members;
18 * r5 ~ r10: free registers;
19 * r11: io base address.
20 *
21 * suspend ocram space layout:
22 * ======================== high address ======================
23 *                              .
24 *                              .
25 *                              .
26 *                              ^
27 *                              ^
28 *                              ^
29 *                      imx6_suspend code
30 *              PM_INFO structure(imx6_cpu_pm_info)
31 * ======================== low address =======================
32 */
33
34/*
35 * Below offsets are based on struct imx6_cpu_pm_info
36 * which defined in arch/arm/mach-imx/pm-imx6q.c, this
37 * structure contains necessary pm info for low level
38 * suspend related code.
39 */
40#define PM_INFO_PBASE_OFFSET			0x0
41#define PM_INFO_RESUME_ADDR_OFFSET		0x4
42#define PM_INFO_DDR_TYPE_OFFSET			0x8
43#define PM_INFO_PM_INFO_SIZE_OFFSET		0xC
44#define PM_INFO_MX6Q_MMDC_P_OFFSET		0x10
45#define PM_INFO_MX6Q_MMDC_V_OFFSET		0x14
46#define PM_INFO_MX6Q_SRC_P_OFFSET		0x18
47#define PM_INFO_MX6Q_SRC_V_OFFSET		0x1C
48#define PM_INFO_MX6Q_IOMUXC_P_OFFSET		0x20
49#define PM_INFO_MX6Q_IOMUXC_V_OFFSET		0x24
50#define PM_INFO_MX6Q_CCM_P_OFFSET		0x28
51#define PM_INFO_MX6Q_CCM_V_OFFSET		0x2C
52#define PM_INFO_MX6Q_GPC_P_OFFSET		0x30
53#define PM_INFO_MX6Q_GPC_V_OFFSET		0x34
54#define PM_INFO_MX6Q_L2_P_OFFSET		0x38
55#define PM_INFO_MX6Q_L2_V_OFFSET		0x3C
56#define PM_INFO_MMDC_IO_NUM_OFFSET		0x40
57#define PM_INFO_MMDC_IO_VAL_OFFSET		0x44
58
59#define MX6Q_SRC_GPR1	0x20
60#define MX6Q_SRC_GPR2	0x24
61#define MX6Q_MMDC_MAPSR	0x404
62#define MX6Q_MMDC_MPDGCTRL0	0x83c
63#define MX6Q_GPC_IMR1	0x08
64#define MX6Q_GPC_IMR2	0x0c
65#define MX6Q_GPC_IMR3	0x10
66#define MX6Q_GPC_IMR4	0x14
67#define MX6Q_CCM_CCR	0x0
68
69	.align 3
70	.arm
71
72	.macro  sync_l2_cache
73
74	/* sync L2 cache to drain L2's buffers to DRAM. */
75#ifdef CONFIG_CACHE_L2X0
76	ldr	r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
77	teq	r11, #0
78	beq	6f
79	mov	r6, #0x0
80	str	r6, [r11, #L2X0_CACHE_SYNC]
811:
82	ldr	r6, [r11, #L2X0_CACHE_SYNC]
83	ands	r6, r6, #0x1
84	bne	1b
856:
86#endif
87
88	.endm
89
90	.macro	resume_mmdc
91
92	/* restore MMDC IO */
93	cmp	r5, #0x0
94	ldreq	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
95	ldrne	r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
96
97	ldr	r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
98	ldr	r7, =PM_INFO_MMDC_IO_VAL_OFFSET
99	add	r7, r7, r0
1001:
101	ldr	r8, [r7], #0x4
102	ldr	r9, [r7], #0x4
103	str	r9, [r11, r8]
104	subs	r6, r6, #0x1
105	bne	1b
106
107	cmp	r5, #0x0
108	ldreq	r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
109	ldrne	r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
110
111	cmp	r3, #IMX_DDR_TYPE_LPDDR2
112	bne	4f
113
114	/* reset read FIFO, RST_RD_FIFO */
115	ldr	r7, =MX6Q_MMDC_MPDGCTRL0
116	ldr	r6, [r11, r7]
117	orr     r6, r6, #(1 << 31)
118	str	r6, [r11, r7]
1192:
120	ldr	r6, [r11, r7]
121	ands	r6, r6, #(1 << 31)
122	bne	2b
123
124	/* reset FIFO a second time */
125	ldr	r6, [r11, r7]
126	orr     r6, r6, #(1 << 31)
127	str	r6, [r11, r7]
1283:
129	ldr	r6, [r11, r7]
130	ands	r6, r6, #(1 << 31)
131	bne	3b
1324:
133	/* let DDR out of self-refresh */
134	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
135	bic	r7, r7, #(1 << 21)
136	str	r7, [r11, #MX6Q_MMDC_MAPSR]
1375:
138	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
139	ands	r7, r7, #(1 << 25)
140	bne	5b
141
142	/* enable DDR auto power saving */
143	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
144	bic	r7, r7, #0x1
145	str	r7, [r11, #MX6Q_MMDC_MAPSR]
146
147	.endm
148
149ENTRY(imx6_suspend)
150	ldr	r1, [r0, #PM_INFO_PBASE_OFFSET]
151	ldr	r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
152	ldr	r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
153	ldr	r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
154
155	/*
156	 * counting the resume address in iram
157	 * to set it in SRC register.
158	 */
159	ldr	r6, =imx6_suspend
160	ldr	r7, =resume
161	sub	r7, r7, r6
162	add	r8, r1, r4
163	add	r9, r8, r7
164
165	/*
166	 * make sure TLB contain the addr we want,
167	 * as we will access them after MMDC IO floated.
168	 */
169
170	ldr	r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
171	ldr	r6, [r11, #0x0]
172	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
173	ldr	r6, [r11, #0x0]
174	ldr	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
175	ldr	r6, [r11, #0x0]
176
177	/* use r11 to store the IO address */
178	ldr	r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
179	/* store physical resume addr and pm_info address. */
180	str	r9, [r11, #MX6Q_SRC_GPR1]
181	str	r1, [r11, #MX6Q_SRC_GPR2]
182
183	/* need to sync L2 cache before DSM. */
184	sync_l2_cache
185
186	ldr	r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
187	/*
188	 * put DDR explicitly into self-refresh and
189	 * disable automatic power savings.
190	 */
191	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
192	orr	r7, r7, #0x1
193	str	r7, [r11, #MX6Q_MMDC_MAPSR]
194
195	/* make the DDR explicitly enter self-refresh. */
196	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
197	orr	r7, r7, #(1 << 21)
198	str	r7, [r11, #MX6Q_MMDC_MAPSR]
199
200poll_dvfs_set:
201	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
202	ands	r7, r7, #(1 << 25)
203	beq	poll_dvfs_set
204
205	ldr	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
206	ldr	r6, =0x0
207	ldr	r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
208	ldr	r8, =PM_INFO_MMDC_IO_VAL_OFFSET
209	add	r8, r8, r0
210	/* LPDDR2's last 3 IOs need special setting */
211	cmp	r3, #IMX_DDR_TYPE_LPDDR2
212	subeq	r7, r7, #0x3
213set_mmdc_io_lpm:
214	ldr	r9, [r8], #0x8
215	str	r6, [r11, r9]
216	subs	r7, r7, #0x1
217	bne	set_mmdc_io_lpm
218
219	cmp 	r3, #IMX_DDR_TYPE_LPDDR2
220	bne	set_mmdc_io_lpm_done
221	ldr	r6, =0x1000
222	ldr	r9, [r8], #0x8
223	str	r6, [r11, r9]
224	ldr	r9, [r8], #0x8
225	str	r6, [r11, r9]
226	ldr	r6, =0x80000
227	ldr	r9, [r8]
228	str	r6, [r11, r9]
229set_mmdc_io_lpm_done:
230
231	/*
232	 * mask all GPC interrupts before
233	 * enabling the RBC counters to
234	 * avoid the counter starting too
235	 * early if an interupt is already
236	 * pending.
237	 */
238	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
239	ldr	r6, [r11, #MX6Q_GPC_IMR1]
240	ldr	r7, [r11, #MX6Q_GPC_IMR2]
241	ldr	r8, [r11, #MX6Q_GPC_IMR3]
242	ldr	r9, [r11, #MX6Q_GPC_IMR4]
243
244	ldr	r10, =0xffffffff
245	str	r10, [r11, #MX6Q_GPC_IMR1]
246	str	r10, [r11, #MX6Q_GPC_IMR2]
247	str	r10, [r11, #MX6Q_GPC_IMR3]
248	str	r10, [r11, #MX6Q_GPC_IMR4]
249
250	/*
251	 * enable the RBC bypass counter here
252	 * to hold off the interrupts. RBC counter
253	 * = 32 (1ms), Minimum RBC delay should be
254	 * 400us for the analog LDOs to power down.
255	 */
256	ldr	r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
257	ldr	r10, [r11, #MX6Q_CCM_CCR]
258	bic	r10, r10, #(0x3f << 21)
259	orr	r10, r10, #(0x20 << 21)
260	str	r10, [r11, #MX6Q_CCM_CCR]
261
262	/* enable the counter. */
263	ldr	r10, [r11, #MX6Q_CCM_CCR]
264	orr	r10, r10, #(0x1 << 27)
265	str	r10, [r11, #MX6Q_CCM_CCR]
266
267	/* unmask all the GPC interrupts. */
268	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
269	str	r6, [r11, #MX6Q_GPC_IMR1]
270	str	r7, [r11, #MX6Q_GPC_IMR2]
271	str	r8, [r11, #MX6Q_GPC_IMR3]
272	str	r9, [r11, #MX6Q_GPC_IMR4]
273
274	/*
275	 * now delay for a short while (3usec)
276	 * ARM is at 1GHz at this point
277	 * so a short loop should be enough.
278	 * this delay is required to ensure that
279	 * the RBC counter can start counting in
280	 * case an interrupt is already pending
281	 * or in case an interrupt arrives just
282	 * as ARM is about to assert DSM_request.
283	 */
284	ldr	r6, =2000
285rbc_loop:
286	subs	r6, r6, #0x1
287	bne	rbc_loop
288
289	/* Zzz, enter stop mode */
290	wfi
291	nop
292	nop
293	nop
294	nop
295
296	/*
297	 * run to here means there is pending
298	 * wakeup source, system should auto
299	 * resume, we need to restore MMDC IO first
300	 */
301	mov	r5, #0x0
302	resume_mmdc
303
304	/* return to suspend finish */
305	ret	lr
306
307resume:
308	/* invalidate L1 I-cache first */
309	mov     r6, #0x0
310	mcr     p15, 0, r6, c7, c5, 0
311	mcr     p15, 0, r6, c7, c5, 6
312	/* enable the Icache and branch prediction */
313	mov     r6, #0x1800
314	mcr     p15, 0, r6, c1, c0, 0
315	isb
316
317	/* get physical resume address from pm_info. */
318	ldr	lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
319	/* clear core0's entry and parameter */
320	ldr	r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
321	mov	r7, #0x0
322	str	r7, [r11, #MX6Q_SRC_GPR1]
323	str	r7, [r11, #MX6Q_SRC_GPR2]
324
325	ldr	r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
326	mov	r5, #0x1
327	resume_mmdc
328
329	ret	lr
330ENDPROC(imx6_suspend)
331