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