ARM: mvebu: Netgear RN102: Use Hardware BCH ECC
[cascardo/linux.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_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
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
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]
84 1:
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
102 1:
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, #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]
121 2:
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]
130 3:
131         ldr     r6, [r11, r7]
132         ands    r6, r6, #(1 << 31)
133         bne     3b
134 4:
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]
139 5:
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
151 ENTRY(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
200 poll_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
210         /* i.MX6SL's last 3 IOs need special setting */
211         cmp     r3, #MXC_CPU_IMX6SL
212         subeq   r7, r7, #0x3
213 set_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
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]
229 set_mmdc_io_lpm_done:
230
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
285 rbc_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 */
305         ret     lr
306
307 resume:
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
325         ldr     r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
326         mov     r5, #0x1
327         resume_mmdc
328
329         ret     lr
330 ENDPROC(imx6_suspend)
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
338 ENTRY(v7_cpu_resume)
339         bl      v7_invalidate_l1
340 #ifdef CONFIG_CACHE_L2X0
341         bl      l2c310_early_resume
342 #endif
343         b       cpu_resume
344 ENDPROC(v7_cpu_resume)