Merge tag 'v3.15-rc1' into perf/urgent
[sfrench/cifs-2.6.git] / arch / arm / mach-imx / suspend-imx6.S
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]
83 1:
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
101 1:
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]
120 2:
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]
129 3:
130         ldr     r6, [r11, r7]
131         ands    r6, r6, #(1 << 31)
132         bne     3b
133 4:
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]
138 5:
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
150 ENTRY(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
199 poll_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
212 set_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]
228 set_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
284 rbc_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
306 resume:
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
329 ENDPROC(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
349 l2x0_saved_regs_offset:
350         .word   l2x0_saved_regs - .
351
352 #else
353         .macro  pl310_resume
354         .endm
355 #endif
356
357 ENTRY(v7_cpu_resume)
358         bl      v7_invalidate_l1
359         pl310_resume
360         b       cpu_resume
361 ENDPROC(v7_cpu_resume)