1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Copyright 2014 Freescale Semiconductor, Inc.
6 #include <linux/linkage.h>
7 #include <asm/assembler.h>
8 #include <asm/asm-offsets.h>
9 #include <asm/hardware/cache-l2x0.h>
13 * ==================== low level suspend ====================
15 * Better to follow below rules to use ARM registers:
16 * r0: pm_info structure address;
17 * r1 ~ r4: for saving pm_info members;
18 * r5 ~ r10: free registers;
19 * r11: io base address.
21 * suspend ocram space layout:
22 * ======================== high address ======================
30 * PM_INFO structure(imx6_cpu_pm_info)
31 * ======================== low address =======================
35 * Below offsets are based on struct imx6_cpu_pm_info
36 * which defined in arch/arm/mach-imx/pm-imx6q.c, this
37 * structure contains necessary pm info for low level
38 * suspend related code.
40 #define PM_INFO_PBASE_OFFSET 0x0
41 #define PM_INFO_RESUME_ADDR_OFFSET 0x4
42 #define PM_INFO_DDR_TYPE_OFFSET 0x8
43 #define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
44 #define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
45 #define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
46 #define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
47 #define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
48 #define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
49 #define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
50 #define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
51 #define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
52 #define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
53 #define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
54 #define PM_INFO_MX6Q_L2_P_OFFSET 0x38
55 #define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
56 #define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
57 #define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
59 #define MX6Q_SRC_GPR1 0x20
60 #define MX6Q_SRC_GPR2 0x24
61 #define MX6Q_MMDC_MAPSR 0x404
62 #define MX6Q_MMDC_MPDGCTRL0 0x83c
63 #define MX6Q_GPC_IMR1 0x08
64 #define MX6Q_GPC_IMR2 0x0c
65 #define MX6Q_GPC_IMR3 0x10
66 #define MX6Q_GPC_IMR4 0x14
67 #define MX6Q_CCM_CCR 0x0
74 /* sync L2 cache to drain L2's buffers to DRAM. */
75 #ifdef CONFIG_CACHE_L2X0
76 ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
80 str r6, [r11, #L2X0_CACHE_SYNC]
82 ldr r6, [r11, #L2X0_CACHE_SYNC]
94 ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
95 ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
97 ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
98 ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
108 ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
109 ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
111 cmp r3, #IMX_DDR_TYPE_LPDDR2
114 /* reset read FIFO, RST_RD_FIFO */
115 ldr r7, =MX6Q_MMDC_MPDGCTRL0
117 orr r6, r6, #(1 << 31)
121 ands r6, r6, #(1 << 31)
124 /* reset FIFO a second time */
126 orr r6, r6, #(1 << 31)
130 ands r6, r6, #(1 << 31)
133 /* let DDR out of self-refresh */
134 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
135 bic r7, r7, #(1 << 21)
136 str r7, [r11, #MX6Q_MMDC_MAPSR]
138 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
139 ands r7, r7, #(1 << 25)
142 /* enable DDR auto power saving */
143 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
145 str r7, [r11, #MX6Q_MMDC_MAPSR]
150 ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
151 ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
152 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
153 ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
156 * counting the resume address in iram
157 * to set it in SRC register.
159 ldr r6, =imx6_suspend
166 * make sure TLB contain the addr we want,
167 * as we will access them after MMDC IO floated.
170 ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
172 ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
174 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
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]
183 /* need to sync L2 cache before DSM. */
186 ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
188 * put DDR explicitly into self-refresh and
189 * disable automatic power savings.
191 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
193 str r7, [r11, #MX6Q_MMDC_MAPSR]
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]
201 ldr r7, [r11, #MX6Q_MMDC_MAPSR]
202 ands r7, r7, #(1 << 25)
205 ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
207 ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
208 ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
210 /* LPDDR2's last 3 IOs need special setting */
211 cmp r3, #IMX_DDR_TYPE_LPDDR2
219 cmp r3, #IMX_DDR_TYPE_LPDDR2
220 bne set_mmdc_io_lpm_done
229 set_mmdc_io_lpm_done:
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
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]
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]
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.
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]
262 /* enable the counter. */
263 ldr r10, [r11, #MX6Q_CCM_CCR]
264 orr r10, r10, #(0x1 << 27)
265 str r10, [r11, #MX6Q_CCM_CCR]
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]
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.
289 /* Zzz, enter stop mode */
297 * run to here means there is pending
298 * wakeup source, system should auto
299 * resume, we need to restore MMDC IO first
304 /* return to suspend finish */
308 /* invalidate L1 I-cache first */
310 mcr p15, 0, r6, c7, c5, 0
311 mcr p15, 0, r6, c7, c5, 6
312 /* enable the Icache and branch prediction */
314 mcr p15, 0, r6, c1, c0, 0
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]
322 str r7, [r11, #MX6Q_SRC_GPR1]
323 str r7, [r11, #MX6Q_SRC_GPR2]
325 ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
330 ENDPROC(imx6_suspend)