xref: /openbmc/linux/arch/arm/mach-imx/suspend-imx6.S (revision 455f9726)
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_CPU_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	mov	r6, #0x0
83	str	r6, [r11, #L2X0_CACHE_SYNC]
841:
85	ldr	r6, [r11, #L2X0_CACHE_SYNC]
86	ands	r6, r6, #0x1
87	bne	1b
88#endif
89
90	.endm
91
92	.macro	resume_mmdc
93
94	/* restore MMDC IO */
95	cmp	r5, #0x0
96	ldreq	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
97	ldrne	r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
98
99	ldr	r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
100	ldr	r7, =PM_INFO_MMDC_IO_VAL_OFFSET
101	add	r7, r7, r0
1021:
103	ldr	r8, [r7], #0x4
104	ldr	r9, [r7], #0x4
105	str	r9, [r11, r8]
106	subs	r6, r6, #0x1
107	bne	1b
108
109	cmp	r5, #0x0
110	ldreq	r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
111	ldrne	r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
112
113	cmp 	r3, #MXC_CPU_IMX6SL
114	bne	4f
115
116	/* reset read FIFO, RST_RD_FIFO */
117	ldr	r7, =MX6Q_MMDC_MPDGCTRL0
118	ldr	r6, [r11, r7]
119	orr     r6, r6, #(1 << 31)
120	str	r6, [r11, r7]
1212:
122	ldr	r6, [r11, r7]
123	ands	r6, r6, #(1 << 31)
124	bne	2b
125
126	/* reset FIFO a second time */
127	ldr	r6, [r11, r7]
128	orr     r6, r6, #(1 << 31)
129	str	r6, [r11, r7]
1303:
131	ldr	r6, [r11, r7]
132	ands	r6, r6, #(1 << 31)
133	bne	3b
1344:
135	/* let DDR out of self-refresh */
136	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
137	bic	r7, r7, #(1 << 21)
138	str	r7, [r11, #MX6Q_MMDC_MAPSR]
1395:
140	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
141	ands	r7, r7, #(1 << 25)
142	bne	5b
143
144	/* enable DDR auto power saving */
145	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
146	bic	r7, r7, #0x1
147	str	r7, [r11, #MX6Q_MMDC_MAPSR]
148
149	.endm
150
151ENTRY(imx6_suspend)
152	ldr	r1, [r0, #PM_INFO_PBASE_OFFSET]
153	ldr	r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
154	ldr	r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
155	ldr	r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
156
157	/*
158	 * counting the resume address in iram
159	 * to set it in SRC register.
160	 */
161	ldr	r6, =imx6_suspend
162	ldr	r7, =resume
163	sub	r7, r7, r6
164	add	r8, r1, r4
165	add	r9, r8, r7
166
167	/*
168	 * make sure TLB contain the addr we want,
169	 * as we will access them after MMDC IO floated.
170	 */
171
172	ldr	r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
173	ldr	r6, [r11, #0x0]
174	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_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	/* i.MX6SL's last 3 IOs need special setting */
211	cmp	r3, #MXC_CPU_IMX6SL
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, #MXC_CPU_IMX6SL
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_CPU_TYPE_OFFSET]
326	mov	r5, #0x1
327	resume_mmdc
328
329	ret	lr
330ENDPROC(imx6_suspend)
331
332/*
333 * The following code must assume it is running from physical address
334 * where absolute virtual addresses to the data section have to be
335 * turned into relative ones.
336 */
337
338ENTRY(v7_cpu_resume)
339	bl	v7_invalidate_l1
340#ifdef CONFIG_CACHE_L2X0
341	bl	l2c310_early_resume
342#endif
343	b	cpu_resume
344ENDPROC(v7_cpu_resume)
345