Commit | Line | Data |
---|---|---|
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> | |
c356bdb4 | 13 | #include <asm/asm-offsets.h> |
df595746 AH |
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 | |
64b08681 | 67 | #define MX6Q_MMDC_MPDGCTRL0 0x83c |
df595746 AH |
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 | ||
64b08681 AH |
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: | |
df595746 AH |
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] | |
64b08681 | 138 | 5: |
df595746 AH |
139 | ldr r7, [r11, #MX6Q_MMDC_MAPSR] |
140 | ands r7, r7, #(1 << 25) | |
64b08681 | 141 | bne 5b |
df595746 AH |
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 | |
64b08681 AH |
209 | /* i.MX6SL's last 3 IOs need special setting */ |
210 | cmp r3, #MXC_CPU_IMX6SL | |
211 | subeq r7, r7, #0x3 | |
df595746 AH |
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 | ||
64b08681 AH |
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 | ||
df595746 AH |
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 | ||
64b08681 | 324 | ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET] |
df595746 AH |
325 | mov r5, #0x1 |
326 | resume_mmdc | |
327 | ||
328 | mov pc, lr | |
329 | ENDPROC(imx6_suspend) | |
c356bdb4 SG |
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) |