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