aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/suspend-imx6.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/suspend-imx6.S')
-rw-r--r--arch/arm/mach-imx/suspend-imx6.S361
1 files changed, 361 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S
new file mode 100644
index 000000000000..20048ff05739
--- /dev/null
+++ b/arch/arm/mach-imx/suspend-imx6.S
@@ -0,0 +1,361 @@
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)