mirror of
https://github.com/torvalds/linux.git
synced 2024-11-20 02:51:44 +00:00
1579c7b9fe
In order to save power the DDR pins should be put into high impedance when in suspend to RAM. This requires manually requesting self refresh (rather than using the automatic mode implemented by the CCM / ESDCTL), followed by reconfiguring the IOMUXC. Of course the code to do this cannot itself run from DDR so the code is copied to and executed from internal memory. In my tests using a custom i.MX53 board with LPDDR2 RAM this reduced the suspend power consumption from 200mW to 60mW. Signed-off-by: Martin Fuzzey <mfuzzey@parkeon.com> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
140 lines
3.4 KiB
ArmAsm
140 lines
3.4 KiB
ArmAsm
/*
|
|
* Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
|
|
*/
|
|
/*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
#define M4IF_MCR0_OFFSET (0x008C)
|
|
#define M4IF_MCR0_FDVFS (0x1 << 11)
|
|
#define M4IF_MCR0_FDVACK (0x1 << 27)
|
|
|
|
.align 3
|
|
|
|
/*
|
|
* ==================== low level suspend ====================
|
|
*
|
|
* On entry
|
|
* r0: pm_info structure address;
|
|
*
|
|
* suspend ocram space layout:
|
|
* ======================== high address ======================
|
|
* .
|
|
* .
|
|
* .
|
|
* ^
|
|
* ^
|
|
* ^
|
|
* imx53_suspend code
|
|
* PM_INFO structure(imx53_suspend_info)
|
|
* ======================== low address =======================
|
|
*/
|
|
|
|
/* Offsets of members of struct imx53_suspend_info */
|
|
#define SUSPEND_INFO_MX53_M4IF_V_OFFSET 0x0
|
|
#define SUSPEND_INFO_MX53_IOMUXC_V_OFFSET 0x4
|
|
#define SUSPEND_INFO_MX53_IO_COUNT_OFFSET 0x8
|
|
#define SUSPEND_INFO_MX53_IO_STATE_OFFSET 0xc
|
|
|
|
ENTRY(imx53_suspend)
|
|
stmfd sp!, {r4,r5,r6,r7}
|
|
|
|
/* Save pad config */
|
|
ldr r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
|
|
cmp r1, #0
|
|
beq skip_pad_conf_1
|
|
|
|
add r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
|
|
ldr r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
|
|
|
|
1:
|
|
ldr r5, [r2], #12 /* IOMUXC register offset */
|
|
ldr r6, [r3, r5] /* current value */
|
|
str r6, [r2], #4 /* save area */
|
|
subs r1, r1, #1
|
|
bne 1b
|
|
|
|
skip_pad_conf_1:
|
|
/* Set FDVFS bit of M4IF_MCR0 to request DDR to enter self-refresh */
|
|
ldr r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
|
|
ldr r2,[r1, #M4IF_MCR0_OFFSET]
|
|
orr r2, r2, #M4IF_MCR0_FDVFS
|
|
str r2,[r1, #M4IF_MCR0_OFFSET]
|
|
|
|
/* Poll FDVACK bit of M4IF_MCR to wait for DDR to enter self-refresh */
|
|
wait_sr_ack:
|
|
ldr r2,[r1, #M4IF_MCR0_OFFSET]
|
|
ands r2, r2, #M4IF_MCR0_FDVACK
|
|
beq wait_sr_ack
|
|
|
|
/* Set pad config */
|
|
ldr r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
|
|
cmp r1, #0
|
|
beq skip_pad_conf_2
|
|
|
|
add r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
|
|
ldr r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
|
|
|
|
2:
|
|
ldr r5, [r2], #4 /* IOMUXC register offset */
|
|
ldr r6, [r2], #4 /* clear */
|
|
ldr r7, [r3, r5]
|
|
bic r7, r7, r6
|
|
ldr r6, [r2], #8 /* set */
|
|
orr r7, r7, r6
|
|
str r7, [r3, r5]
|
|
subs r1, r1, #1
|
|
bne 2b
|
|
|
|
skip_pad_conf_2:
|
|
/* Zzz, enter stop mode */
|
|
wfi
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
/* Restore pad config */
|
|
ldr r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
|
|
cmp r1, #0
|
|
beq skip_pad_conf_3
|
|
|
|
add r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
|
|
ldr r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
|
|
|
|
3:
|
|
ldr r5, [r2], #12 /* IOMUXC register offset */
|
|
ldr r6, [r2], #4 /* saved value */
|
|
str r6, [r3, r5]
|
|
subs r1, r1, #1
|
|
bne 3b
|
|
|
|
skip_pad_conf_3:
|
|
/* Clear FDVFS bit of M4IF_MCR0 to request DDR to exit self-refresh */
|
|
ldr r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
|
|
ldr r2,[r1, #M4IF_MCR0_OFFSET]
|
|
bic r2, r2, #M4IF_MCR0_FDVFS
|
|
str r2,[r1, #M4IF_MCR0_OFFSET]
|
|
|
|
/* Poll FDVACK bit of M4IF_MCR to wait for DDR to exit self-refresh */
|
|
wait_ar_ack:
|
|
ldr r2,[r1, #M4IF_MCR0_OFFSET]
|
|
ands r2, r2, #M4IF_MCR0_FDVACK
|
|
bne wait_ar_ack
|
|
|
|
/* Restore registers */
|
|
ldmfd sp!, {r4,r5,r6,r7}
|
|
mov pc, lr
|
|
|
|
ENDPROC(imx53_suspend)
|
|
|
|
ENTRY(imx53_suspend_sz)
|
|
.word . - imx53_suspend
|