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