GNU Linux-libre 4.4.284-gnu1
[releases.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/assembler.h>
14 #include <asm/asm-offsets.h>
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_DDR_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
68 #define MX6Q_MMDC_MPDGCTRL0     0x83c
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         .arm
77
78         .macro  sync_l2_cache
79
80         /* sync L2 cache to drain L2's buffers to DRAM. */
81 #ifdef CONFIG_CACHE_L2X0
82         ldr     r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
83         teq     r11, #0
84         beq     6f
85         mov     r6, #0x0
86         str     r6, [r11, #L2X0_CACHE_SYNC]
87 1:
88         ldr     r6, [r11, #L2X0_CACHE_SYNC]
89         ands    r6, r6, #0x1
90         bne     1b
91 6:
92 #endif
93
94         .endm
95
96         .macro  resume_mmdc
97
98         /* restore MMDC IO */
99         cmp     r5, #0x0
100         ldreq   r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
101         ldrne   r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
102
103         ldr     r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
104         ldr     r7, =PM_INFO_MMDC_IO_VAL_OFFSET
105         add     r7, r7, r0
106 1:
107         ldr     r8, [r7], #0x4
108         ldr     r9, [r7], #0x4
109         str     r9, [r11, r8]
110         subs    r6, r6, #0x1
111         bne     1b
112
113         cmp     r5, #0x0
114         ldreq   r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
115         ldrne   r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
116
117         cmp     r3, #IMX_DDR_TYPE_LPDDR2
118         bne     4f
119
120         /* reset read FIFO, RST_RD_FIFO */
121         ldr     r7, =MX6Q_MMDC_MPDGCTRL0
122         ldr     r6, [r11, r7]
123         orr     r6, r6, #(1 << 31)
124         str     r6, [r11, r7]
125 2:
126         ldr     r6, [r11, r7]
127         ands    r6, r6, #(1 << 31)
128         bne     2b
129
130         /* reset FIFO a second time */
131         ldr     r6, [r11, r7]
132         orr     r6, r6, #(1 << 31)
133         str     r6, [r11, r7]
134 3:
135         ldr     r6, [r11, r7]
136         ands    r6, r6, #(1 << 31)
137         bne     3b
138 4:
139         /* let DDR out of self-refresh */
140         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
141         bic     r7, r7, #(1 << 21)
142         str     r7, [r11, #MX6Q_MMDC_MAPSR]
143 5:
144         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
145         ands    r7, r7, #(1 << 25)
146         bne     5b
147
148         /* enable DDR auto power saving */
149         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
150         bic     r7, r7, #0x1
151         str     r7, [r11, #MX6Q_MMDC_MAPSR]
152
153         .endm
154
155 ENTRY(imx6_suspend)
156         ldr     r1, [r0, #PM_INFO_PBASE_OFFSET]
157         ldr     r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
158         ldr     r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
159         ldr     r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
160
161         /*
162          * counting the resume address in iram
163          * to set it in SRC register.
164          */
165         ldr     r6, =imx6_suspend
166         ldr     r7, =resume
167         sub     r7, r7, r6
168         add     r8, r1, r4
169         add     r9, r8, r7
170
171         /*
172          * make sure TLB contain the addr we want,
173          * as we will access them after MMDC IO floated.
174          */
175
176         ldr     r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
177         ldr     r6, [r11, #0x0]
178         ldr     r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
179         ldr     r6, [r11, #0x0]
180         ldr     r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
181         ldr     r6, [r11, #0x0]
182
183         /* use r11 to store the IO address */
184         ldr     r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
185         /* store physical resume addr and pm_info address. */
186         str     r9, [r11, #MX6Q_SRC_GPR1]
187         str     r1, [r11, #MX6Q_SRC_GPR2]
188
189         /* need to sync L2 cache before DSM. */
190         sync_l2_cache
191
192         ldr     r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
193         /*
194          * put DDR explicitly into self-refresh and
195          * disable automatic power savings.
196          */
197         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
198         orr     r7, r7, #0x1
199         str     r7, [r11, #MX6Q_MMDC_MAPSR]
200
201         /* make the DDR explicitly enter self-refresh. */
202         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
203         orr     r7, r7, #(1 << 21)
204         str     r7, [r11, #MX6Q_MMDC_MAPSR]
205
206 poll_dvfs_set:
207         ldr     r7, [r11, #MX6Q_MMDC_MAPSR]
208         ands    r7, r7, #(1 << 25)
209         beq     poll_dvfs_set
210
211         ldr     r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
212         ldr     r6, =0x0
213         ldr     r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
214         ldr     r8, =PM_INFO_MMDC_IO_VAL_OFFSET
215         add     r8, r8, r0
216         /* LPDDR2's last 3 IOs need special setting */
217         cmp     r3, #IMX_DDR_TYPE_LPDDR2
218         subeq   r7, r7, #0x3
219 set_mmdc_io_lpm:
220         ldr     r9, [r8], #0x8
221         str     r6, [r11, r9]
222         subs    r7, r7, #0x1
223         bne     set_mmdc_io_lpm
224
225         cmp     r3, #IMX_DDR_TYPE_LPDDR2
226         bne     set_mmdc_io_lpm_done
227         ldr     r6, =0x1000
228         ldr     r9, [r8], #0x8
229         str     r6, [r11, r9]
230         ldr     r9, [r8], #0x8
231         str     r6, [r11, r9]
232         ldr     r6, =0x80000
233         ldr     r9, [r8]
234         str     r6, [r11, r9]
235 set_mmdc_io_lpm_done:
236
237         /*
238          * mask all GPC interrupts before
239          * enabling the RBC counters to
240          * avoid the counter starting too
241          * early if an interupt is already
242          * pending.
243          */
244         ldr     r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
245         ldr     r6, [r11, #MX6Q_GPC_IMR1]
246         ldr     r7, [r11, #MX6Q_GPC_IMR2]
247         ldr     r8, [r11, #MX6Q_GPC_IMR3]
248         ldr     r9, [r11, #MX6Q_GPC_IMR4]
249
250         ldr     r10, =0xffffffff
251         str     r10, [r11, #MX6Q_GPC_IMR1]
252         str     r10, [r11, #MX6Q_GPC_IMR2]
253         str     r10, [r11, #MX6Q_GPC_IMR3]
254         str     r10, [r11, #MX6Q_GPC_IMR4]
255
256         /*
257          * enable the RBC bypass counter here
258          * to hold off the interrupts. RBC counter
259          * = 32 (1ms), Minimum RBC delay should be
260          * 400us for the analog LDOs to power down.
261          */
262         ldr     r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
263         ldr     r10, [r11, #MX6Q_CCM_CCR]
264         bic     r10, r10, #(0x3f << 21)
265         orr     r10, r10, #(0x20 << 21)
266         str     r10, [r11, #MX6Q_CCM_CCR]
267
268         /* enable the counter. */
269         ldr     r10, [r11, #MX6Q_CCM_CCR]
270         orr     r10, r10, #(0x1 << 27)
271         str     r10, [r11, #MX6Q_CCM_CCR]
272
273         /* unmask all the GPC interrupts. */
274         ldr     r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
275         str     r6, [r11, #MX6Q_GPC_IMR1]
276         str     r7, [r11, #MX6Q_GPC_IMR2]
277         str     r8, [r11, #MX6Q_GPC_IMR3]
278         str     r9, [r11, #MX6Q_GPC_IMR4]
279
280         /*
281          * now delay for a short while (3usec)
282          * ARM is at 1GHz at this point
283          * so a short loop should be enough.
284          * this delay is required to ensure that
285          * the RBC counter can start counting in
286          * case an interrupt is already pending
287          * or in case an interrupt arrives just
288          * as ARM is about to assert DSM_request.
289          */
290         ldr     r6, =2000
291 rbc_loop:
292         subs    r6, r6, #0x1
293         bne     rbc_loop
294
295         /* Zzz, enter stop mode */
296         wfi
297         nop
298         nop
299         nop
300         nop
301
302         /*
303          * run to here means there is pending
304          * wakeup source, system should auto
305          * resume, we need to restore MMDC IO first
306          */
307         mov     r5, #0x0
308         resume_mmdc
309
310         /* return to suspend finish */
311         ret     lr
312
313 resume:
314         /* invalidate L1 I-cache first */
315         mov     r6, #0x0
316         mcr     p15, 0, r6, c7, c5, 0
317         mcr     p15, 0, r6, c7, c5, 6
318         /* enable the Icache and branch prediction */
319         mov     r6, #0x1800
320         mcr     p15, 0, r6, c1, c0, 0
321         isb
322
323         /* get physical resume address from pm_info. */
324         ldr     lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
325         /* clear core0's entry and parameter */
326         ldr     r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
327         mov     r7, #0x0
328         str     r7, [r11, #MX6Q_SRC_GPR1]
329         str     r7, [r11, #MX6Q_SRC_GPR2]
330
331         ldr     r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
332         mov     r5, #0x1
333         resume_mmdc
334
335         ret     lr
336 ENDPROC(imx6_suspend)