1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Copyright 2014 Freescale Semiconductor, Inc.
4  */
5 
6 #include <linux/linkage.h>
7 #include <asm/assembler.h>
8 #include <asm/asm-offsets.h>
9 #include <asm/hardware/cache-l2x0.h>
10 #include "hardware.h"
11 
12 .arch armv7-a
13 
14 /*
15  * ==================== low level suspend ====================
16  *
17  * Better to follow below rules to use ARM registers:
18  * r0: pm_info structure address;
19  * r1 ~ r4: for saving pm_info members;
20  * r5 ~ r10: free registers;
21  * r11: io base address.
22  *
23  * suspend ocram space layout:
24  * ======================== high address ======================
25  *                              .
26  *                              .
27  *                              .
28  *                              ^
29  *                              ^
30  *                              ^
31  *                      imx6_suspend code
32  *              PM_INFO structure(imx6_cpu_pm_info)
33  * ======================== low address =======================
34  */
35 
36 /*
37  * Below offsets are based on struct imx6_cpu_pm_info
38  * which defined in arch/arm/mach-imx/pm-imx6q.c, this
39  * structure contains necessary pm info for low level
40  * suspend related code.
41  */
42 #define PM_INFO_PBASE_OFFSET			0x0
43 #define PM_INFO_RESUME_ADDR_OFFSET		0x4
44 #define PM_INFO_DDR_TYPE_OFFSET			0x8
45 #define PM_INFO_PM_INFO_SIZE_OFFSET		0xC
46 #define PM_INFO_MX6Q_MMDC_P_OFFSET		0x10
47 #define PM_INFO_MX6Q_MMDC_V_OFFSET		0x14
48 #define PM_INFO_MX6Q_SRC_P_OFFSET		0x18
49 #define PM_INFO_MX6Q_SRC_V_OFFSET		0x1C
50 #define PM_INFO_MX6Q_IOMUXC_P_OFFSET		0x20
51 #define PM_INFO_MX6Q_IOMUXC_V_OFFSET		0x24
52 #define PM_INFO_MX6Q_CCM_P_OFFSET		0x28
53 #define PM_INFO_MX6Q_CCM_V_OFFSET		0x2C
54 #define PM_INFO_MX6Q_GPC_P_OFFSET		0x30
55 #define PM_INFO_MX6Q_GPC_V_OFFSET		0x34
56 #define PM_INFO_MX6Q_L2_P_OFFSET		0x38
57 #define PM_INFO_MX6Q_L2_V_OFFSET		0x3C
58 #define PM_INFO_MMDC_IO_NUM_OFFSET		0x40
59 #define PM_INFO_MMDC_IO_VAL_OFFSET		0x44
60 
61 #define MX6Q_SRC_GPR1	0x20
62 #define MX6Q_SRC_GPR2	0x24
63 #define MX6Q_MMDC_MAPSR	0x404
64 #define MX6Q_MMDC_MPDGCTRL0	0x83c
65 #define MX6Q_GPC_IMR1	0x08
66 #define MX6Q_GPC_IMR2	0x0c
67 #define MX6Q_GPC_IMR3	0x10
68 #define MX6Q_GPC_IMR4	0x14
69 #define MX6Q_CCM_CCR	0x0
70 
71 	.align 3
72 	.arm
73 
74 	.macro  sync_l2_cache
75 
76 	/* sync L2 cache to drain L2's buffers to DRAM. */
77 #ifdef CONFIG_CACHE_L2X0
78 	ldr	r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
79 	teq	r11, #0
80 	beq	6f
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 6:
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, #IMX_DDR_TYPE_LPDDR2
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_DDR_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 	ldr	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
177 	ldr	r6, [r11, #0x0]
178 
179 	/* use r11 to store the IO address */
180 	ldr	r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
181 	/* store physical resume addr and pm_info address. */
182 	str	r9, [r11, #MX6Q_SRC_GPR1]
183 	str	r1, [r11, #MX6Q_SRC_GPR2]
184 
185 	/* need to sync L2 cache before DSM. */
186 	sync_l2_cache
187 
188 	ldr	r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
189 	/*
190 	 * put DDR explicitly into self-refresh and
191 	 * disable automatic power savings.
192 	 */
193 	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
194 	orr	r7, r7, #0x1
195 	str	r7, [r11, #MX6Q_MMDC_MAPSR]
196 
197 	/* make the DDR explicitly enter self-refresh. */
198 	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
199 	orr	r7, r7, #(1 << 21)
200 	str	r7, [r11, #MX6Q_MMDC_MAPSR]
201 
202 poll_dvfs_set:
203 	ldr	r7, [r11, #MX6Q_MMDC_MAPSR]
204 	ands	r7, r7, #(1 << 25)
205 	beq	poll_dvfs_set
206 
207 	ldr	r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
208 	ldr	r6, =0x0
209 	ldr	r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
210 	ldr	r8, =PM_INFO_MMDC_IO_VAL_OFFSET
211 	add	r8, r8, r0
212 	/* LPDDR2's last 3 IOs need special setting */
213 	cmp	r3, #IMX_DDR_TYPE_LPDDR2
214 	subeq	r7, r7, #0x3
215 set_mmdc_io_lpm:
216 	ldr	r9, [r8], #0x8
217 	str	r6, [r11, r9]
218 	subs	r7, r7, #0x1
219 	bne	set_mmdc_io_lpm
220 
221 	cmp 	r3, #IMX_DDR_TYPE_LPDDR2
222 	bne	set_mmdc_io_lpm_done
223 	ldr	r6, =0x1000
224 	ldr	r9, [r8], #0x8
225 	str	r6, [r11, r9]
226 	ldr	r9, [r8], #0x8
227 	str	r6, [r11, r9]
228 	ldr	r6, =0x80000
229 	ldr	r9, [r8]
230 	str	r6, [r11, r9]
231 set_mmdc_io_lpm_done:
232 
233 	/*
234 	 * mask all GPC interrupts before
235 	 * enabling the RBC counters to
236 	 * avoid the counter starting too
237 	 * early if an interupt is already
238 	 * pending.
239 	 */
240 	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
241 	ldr	r6, [r11, #MX6Q_GPC_IMR1]
242 	ldr	r7, [r11, #MX6Q_GPC_IMR2]
243 	ldr	r8, [r11, #MX6Q_GPC_IMR3]
244 	ldr	r9, [r11, #MX6Q_GPC_IMR4]
245 
246 	ldr	r10, =0xffffffff
247 	str	r10, [r11, #MX6Q_GPC_IMR1]
248 	str	r10, [r11, #MX6Q_GPC_IMR2]
249 	str	r10, [r11, #MX6Q_GPC_IMR3]
250 	str	r10, [r11, #MX6Q_GPC_IMR4]
251 
252 	/*
253 	 * enable the RBC bypass counter here
254 	 * to hold off the interrupts. RBC counter
255 	 * = 32 (1ms), Minimum RBC delay should be
256 	 * 400us for the analog LDOs to power down.
257 	 */
258 	ldr	r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
259 	ldr	r10, [r11, #MX6Q_CCM_CCR]
260 	bic	r10, r10, #(0x3f << 21)
261 	orr	r10, r10, #(0x20 << 21)
262 	str	r10, [r11, #MX6Q_CCM_CCR]
263 
264 	/* enable the counter. */
265 	ldr	r10, [r11, #MX6Q_CCM_CCR]
266 	orr	r10, r10, #(0x1 << 27)
267 	str	r10, [r11, #MX6Q_CCM_CCR]
268 
269 	/* unmask all the GPC interrupts. */
270 	ldr	r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
271 	str	r6, [r11, #MX6Q_GPC_IMR1]
272 	str	r7, [r11, #MX6Q_GPC_IMR2]
273 	str	r8, [r11, #MX6Q_GPC_IMR3]
274 	str	r9, [r11, #MX6Q_GPC_IMR4]
275 
276 	/*
277 	 * now delay for a short while (3usec)
278 	 * ARM is at 1GHz at this point
279 	 * so a short loop should be enough.
280 	 * this delay is required to ensure that
281 	 * the RBC counter can start counting in
282 	 * case an interrupt is already pending
283 	 * or in case an interrupt arrives just
284 	 * as ARM is about to assert DSM_request.
285 	 */
286 	ldr	r6, =2000
287 rbc_loop:
288 	subs	r6, r6, #0x1
289 	bne	rbc_loop
290 
291 	/* Zzz, enter stop mode */
292 	wfi
293 	nop
294 	nop
295 	nop
296 	nop
297 
298 	/*
299 	 * run to here means there is pending
300 	 * wakeup source, system should auto
301 	 * resume, we need to restore MMDC IO first
302 	 */
303 	mov	r5, #0x0
304 	resume_mmdc
305 
306 	/* return to suspend finish */
307 	ret	lr
308 
309 resume:
310 	/* invalidate L1 I-cache first */
311 	mov     r6, #0x0
312 	mcr     p15, 0, r6, c7, c5, 0
313 	mcr     p15, 0, r6, c7, c5, 6
314 	/* enable the Icache and branch prediction */
315 	mov     r6, #0x1800
316 	mcr     p15, 0, r6, c1, c0, 0
317 	isb
318 
319 	/* get physical resume address from pm_info. */
320 	ldr	lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
321 	/* clear core0's entry and parameter */
322 	ldr	r11, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
323 	mov	r7, #0x0
324 	str	r7, [r11, #MX6Q_SRC_GPR1]
325 	str	r7, [r11, #MX6Q_SRC_GPR2]
326 
327 	ldr	r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
328 	mov	r5, #0x1
329 	resume_mmdc
330 
331 	ret	lr
332 ENDPROC(imx6_suspend)
333