1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Copyright 2014 Freescale Semiconductor, Inc.
4 */
5
6#include <linux/linkage.h>
7#include <asm/assembler.h>
8#include <asm/asm-offsets.h>
9#include <asm/hardware/cache-l2x0.h>
10#include "hardware.h"
11
12.arch armv7-a
13
14/*
15 * ==================== low level suspend ====================
16 *
17 * Better to follow below rules to use ARM registers:
18 * r0: pm_info structure address;
19 * r1 ~ r4: for saving pm_info members;
20 * r5 ~ r10: free registers;
21 * r11: io base address.
22 *
23 * suspend ocram space layout:
24 * ======================== high address ======================
25 * .
26 * .
27 * .
28 * ^
29 * ^
30 * ^
31 * imx6_suspend code
32 * PM_INFO structure(imx6_cpu_pm_info)
33 * ======================== low address =======================
34 */
35
36/*
37 * Below offsets are based on struct imx6_cpu_pm_info
38 * which defined in arch/arm/mach-imx/pm-imx6q.c, this
39 * structure contains necessary pm info for low level
40 * suspend related code.
41 */
42#define PM_INFO_PBASE_OFFSET 0x0
43#define PM_INFO_RESUME_ADDR_OFFSET 0x4
44#define PM_INFO_DDR_TYPE_OFFSET 0x8
45#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
46#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
47#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
48#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
49#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
50#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
51#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
52#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
53#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
54#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
55#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
56#define PM_INFO_MX6Q_L2_P_OFFSET 0x38
57#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
58#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
59#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
60
61#define MX6Q_SRC_GPR1 0x20
62#define MX6Q_SRC_GPR2 0x24
63#define MX6Q_MMDC_MAPSR 0x404
64#define MX6Q_MMDC_MPDGCTRL0 0x83c
65#define MX6Q_GPC_IMR1 0x08
66#define MX6Q_GPC_IMR2 0x0c
67#define MX6Q_GPC_IMR3 0x10
68#define MX6Q_GPC_IMR4 0x14
69#define MX6Q_CCM_CCR 0x0
70
71 .align 3
72 .arm
73
74 .macro sync_l2_cache
75
76 /* sync L2 cache to drain L2's buffers to DRAM. */
77#ifdef CONFIG_CACHE_L2X0
78 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
79 teq r11, #0
80 beq 6f
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
876:
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, #IMX_DDR_TYPE_LPDDR2
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_DDR_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 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
177 ldr r6, [r11, #0x0]
178
179 /* use r11 to store the IO address */
180 ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
181 /* store physical resume addr and pm_info address. */
182 str r9, [r11, #MX6Q_SRC_GPR1]
183 str r1, [r11, #MX6Q_SRC_GPR2]
184
185 /* need to sync L2 cache before DSM. */
186 sync_l2_cache
187
188 ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
189 /*
190 * put DDR explicitly into self-refresh and
191 * disable automatic power savings.
192 */
193 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
194 orr r7, r7, #0x1
195 str r7, [r11, #MX6Q_MMDC_MAPSR]
196
197 /* make the DDR explicitly enter self-refresh. */
198 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
199 orr r7, r7, #(1 << 21)
200 str r7, [r11, #MX6Q_MMDC_MAPSR]
201
202poll_dvfs_set:
203 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
204 ands r7, r7, #(1 << 25)
205 beq poll_dvfs_set
206
207 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
208 ldr r6, =0x0
209 ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
210 ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
211 add r8, r8, r0
212 /* LPDDR2's last 3 IOs need special setting */
213 cmp r3, #IMX_DDR_TYPE_LPDDR2
214 subeq r7, r7, #0x3
215set_mmdc_io_lpm:
216 ldr r9, [r8], #0x8
217 str r6, [r11, r9]
218 subs r7, r7, #0x1
219 bne set_mmdc_io_lpm
220
221 cmp r3, #IMX_DDR_TYPE_LPDDR2
222 bne set_mmdc_io_lpm_done
223 ldr r6, =0x1000
224 ldr r9, [r8], #0x8
225 str r6, [r11, r9]
226 ldr r9, [r8], #0x8
227 str r6, [r11, r9]
228 ldr r6, =0x80000
229 ldr r9, [r8]
230 str r6, [r11, r9]
231set_mmdc_io_lpm_done:
232
233 /*
234 * mask all GPC interrupts before
235 * enabling the RBC counters to
236 * avoid the counter starting too
237 * early if an interupt is already
238 * pending.
239 */
240 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
241 ldr r6, [r11, #MX6Q_GPC_IMR1]
242 ldr r7, [r11, #MX6Q_GPC_IMR2]
243 ldr r8, [r11, #MX6Q_GPC_IMR3]
244 ldr r9, [r11, #MX6Q_GPC_IMR4]
245
246 ldr r10, =0xffffffff
247 str r10, [r11, #MX6Q_GPC_IMR1]
248 str r10, [r11, #MX6Q_GPC_IMR2]
249 str r10, [r11, #MX6Q_GPC_IMR3]
250 str r10, [r11, #MX6Q_GPC_IMR4]
251
252 /*
253 * enable the RBC bypass counter here
254 * to hold off the interrupts. RBC counter
255 * = 32 (1ms), Minimum RBC delay should be
256 * 400us for the analog LDOs to power down.
257 */
258 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
259 ldr r10, [r11, #MX6Q_CCM_CCR]
260 bic r10, r10, #(0x3f << 21)
261 orr r10, r10, #(0x20 << 21)
262 str r10, [r11, #MX6Q_CCM_CCR]
263
264 /* enable the counter. */
265 ldr r10, [r11, #MX6Q_CCM_CCR]
266 orr r10, r10, #(0x1 << 27)
267 str r10, [r11, #MX6Q_CCM_CCR]
268
269 /* unmask all the GPC interrupts. */
270 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
271 str r6, [r11, #MX6Q_GPC_IMR1]
272 str r7, [r11, #MX6Q_GPC_IMR2]
273 str r8, [r11, #MX6Q_GPC_IMR3]
274 str r9, [r11, #MX6Q_GPC_IMR4]
275
276 /*
277 * now delay for a short while (3usec)
278 * ARM is at 1GHz at this point
279 * so a short loop should be enough.
280 * this delay is required to ensure that
281 * the RBC counter can start counting in
282 * case an interrupt is already pending
283 * or in case an interrupt arrives just
284 * as ARM is about to assert DSM_request.
285 */
286 ldr r6, =2000
287rbc_loop:
288 subs r6, r6, #0x1
289 bne rbc_loop
290
291 /* Zzz, enter stop mode */
292 wfi
293 nop
294 nop
295 nop
296 nop
297
298 /*
299 * run to here means there is pending
300 * wakeup source, system should auto
301 * resume, we need to restore MMDC IO first
302 */
303 mov r5, #0x0
304 resume_mmdc
305
306 /* return to suspend finish */
307 ret lr
308
309resume:
310 /* invalidate L1 I-cache first */
311 mov r6, #0x0
312 mcr p15, 0, r6, c7, c5, 0
313 mcr p15, 0, r6, c7, c5, 6
314 /* enable the Icache and branch prediction */
315 mov r6, #0x1800
316 mcr p15, 0, r6, c1, c0, 0
317 isb
318
319 /* get physical resume address from pm_info. */
320 ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
321 /* clear core0's entry and parameter */
322 ldr r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
323 mov r7, #0x0
324 str r7, [r11, #MX6Q_SRC_GPR1]
325 str r7, [r11, #MX6Q_SRC_GPR2]
326
327 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
328 mov r5, #0x1
329 resume_mmdc
330
331 ret lr
332ENDPROC(imx6_suspend)
333

source code of linux/arch/arm/mach-imx/suspend-imx6.S