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