Linux Audio

Check our new training course

Loading...
  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]
 831:
 84	ldr	r6, [r11, #L2X0_CACHE_SYNC]
 85	ands	r6, r6, #0x1
 86	bne	1b
 876:
 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
1021:
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]
1212:
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]
1303:
131	ldr	r6, [r11, r7]
132	ands	r6, r6, #(1 << 31)
133	bne	3b
1344:
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]
1395:
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
151ENTRY(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
202poll_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
215set_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]
231set_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
287rbc_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
309resume:
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
332ENDPROC(imx6_suspend)