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