1/*
2 * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
3 */
4/*
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
8 *
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
11 */
12
13#include <linux/linkage.h>
14
15#define M4IF_MCR0_OFFSET			(0x008C)
16#define M4IF_MCR0_FDVFS				(0x1 << 11)
17#define M4IF_MCR0_FDVACK			(0x1 << 27)
18
19	.align 3
20
21/*
22 * ==================== low level suspend ====================
23 *
24 * On entry
25 * r0: pm_info structure address;
26 *
27 * suspend ocram space layout:
28 * ======================== high address ======================
29 *                              .
30 *                              .
31 *                              .
32 *                              ^
33 *                              ^
34 *                              ^
35 *                      imx53_suspend code
36 *              PM_INFO structure(imx53_suspend_info)
37 * ======================== low address =======================
38 */
39
40/* Offsets of members of struct imx53_suspend_info */
41#define SUSPEND_INFO_MX53_M4IF_V_OFFSET		0x0
42#define SUSPEND_INFO_MX53_IOMUXC_V_OFFSET	0x4
43#define SUSPEND_INFO_MX53_IO_COUNT_OFFSET	0x8
44#define SUSPEND_INFO_MX53_IO_STATE_OFFSET	0xc
45
46ENTRY(imx53_suspend)
47	stmfd	sp!, {r4,r5,r6,r7}
48
49	/* Save pad config */
50	ldr	r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
51	cmp	r1, #0
52	beq	skip_pad_conf_1
53
54	add	r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
55	ldr	r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
56
571:
58	ldr	r5, [r2], #12	/* IOMUXC register offset */
59	ldr	r6, [r3, r5]	/* current value */
60	str	r6, [r2], #4	/* save area */
61	subs	r1, r1, #1
62	bne	1b
63
64skip_pad_conf_1:
65	/* Set FDVFS bit of M4IF_MCR0 to request DDR to enter self-refresh */
66	ldr	r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
67	ldr	r2,[r1, #M4IF_MCR0_OFFSET]
68	orr	r2, r2, #M4IF_MCR0_FDVFS
69	str	r2,[r1, #M4IF_MCR0_OFFSET]
70
71	/* Poll FDVACK bit of M4IF_MCR to wait for DDR to enter self-refresh */
72wait_sr_ack:
73	ldr	r2,[r1, #M4IF_MCR0_OFFSET]
74	ands	r2, r2, #M4IF_MCR0_FDVACK
75	beq	wait_sr_ack
76
77	/* Set pad config */
78	ldr	r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
79	cmp	r1, #0
80	beq	skip_pad_conf_2
81
82	add	r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
83	ldr	r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
84
852:
86	ldr	r5, [r2], #4	/* IOMUXC register offset */
87	ldr	r6, [r2], #4	/* clear */
88	ldr	r7, [r3, r5]
89	bic	r7, r7, r6
90	ldr	r6, [r2], #8	/* set */
91	orr	r7, r7, r6
92	str	r7, [r3, r5]
93	subs	r1, r1, #1
94	bne	2b
95
96skip_pad_conf_2:
97	/* Zzz, enter stop mode */
98	wfi
99	nop
100	nop
101	nop
102	nop
103
104	/* Restore pad config */
105	ldr	r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
106	cmp	r1, #0
107	beq	skip_pad_conf_3
108
109	add	r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
110	ldr	r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
111
1123:
113	ldr	r5, [r2], #12	/* IOMUXC register offset */
114	ldr	r6, [r2], #4	/* saved value */
115	str	r6, [r3, r5]
116	subs	r1, r1, #1
117	bne	3b
118
119skip_pad_conf_3:
120	/* Clear FDVFS bit of M4IF_MCR0 to request DDR to exit self-refresh */
121	ldr	r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
122	ldr	r2,[r1, #M4IF_MCR0_OFFSET]
123	bic	r2, r2, #M4IF_MCR0_FDVFS
124	str	r2,[r1, #M4IF_MCR0_OFFSET]
125
126	/* Poll FDVACK bit of M4IF_MCR to wait for DDR to exit self-refresh */
127wait_ar_ack:
128	ldr	r2,[r1, #M4IF_MCR0_OFFSET]
129	ands	r2, r2, #M4IF_MCR0_FDVACK
130	bne	wait_ar_ack
131
132	/* Restore registers */
133	ldmfd	sp!, {r4,r5,r6,r7}
134	mov	pc, lr
135
136ENDPROC(imx53_suspend)
137
138ENTRY(imx53_suspend_sz)
139        .word   . - imx53_suspend
140