blob: 4d426d10582895b7dd95b54efc8c4c203b621b7e [file] [log] [blame]
/*
* linux/arch/arm/mach-omap/sleep.S
*
* Low-level OMAP1510/1610 sleep/wakeUp support
*
* Initial SA1110 code:
* Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
*
* Adapted for PXA by Nicolas Pitre:
* Copyright (c) 2002 Monta Vista Software, Inc.
*
* Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/arch/io.h>
#include <asm/arch/pm.h>
.text
/*
* Forces OMAP into idle state
*
* omapXXXX_idle_loop_suspend()
*
* Note: This code get's copied to internal SRAM at boot. When the OMAP
* wakes up it continues execution at the point it went to sleep.
*
* Note: Because of slightly different configuration values we have
* processor specific functions here.
*/
#ifdef CONFIG_ARCH_OMAP1510
ENTRY(omap1510_idle_loop_suspend)
stmfd sp!, {r0 - r12, lr} @ save registers on stack
@ load base address of ARM_IDLECT1 and ARM_IDLECT2
mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
@ turn off clock domains
@ get ARM_IDLECT2 into r2
ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
@ request ARM idle
@ get ARM_IDLECT1 into r1
ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
orr r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff
strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
mov r5, #IDLE_WAIT_CYCLES & 0xff
orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00
l_1510: subs r5, r5, #1
bne l_1510
/*
* Let's wait for the next clock tick to wake us up.
*/
mov r0, #0
mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt
/*
* omap1510_idle_loop_suspend()'s resume point.
*
* It will just start executing here, so we'll restore stuff from the
* stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
*/
@ restore ARM_IDLECT1 and ARM_IDLECT2 and return
@ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
ldmfd sp!, {r0 - r12, pc} @ restore regs and return
ENTRY(omap1510_idle_loop_suspend_sz)
.word . - omap1510_idle_loop_suspend
#endif /* CONFIG_ARCH_OMAP1510 */
#if defined(CONFIG_ARCH_OMAP16XX)
ENTRY(omap1610_idle_loop_suspend)
stmfd sp!, {r0 - r12, lr} @ save registers on stack
@ load base address of ARM_IDLECT1 and ARM_IDLECT2
mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
@ turn off clock domains
@ get ARM_IDLECT2 into r2
ldrh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
@ request ARM idle
@ get ARM_IDLECT1 into r1
ldrh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
orr r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff
strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
mov r5, #IDLE_WAIT_CYCLES & 0xff
orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00
l_1610: subs r5, r5, #1
bne l_1610
/*
* Let's wait for the next clock tick to wake us up.
*/
mov r0, #0
mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt
/*
* omap1610_idle_loop_suspend()'s resume point.
*
* It will just start executing here, so we'll restore stuff from the
* stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
*/
@ restore ARM_IDLECT1 and ARM_IDLECT2 and return
@ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
strh r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
strh r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
ldmfd sp!, {r0 - r12, pc} @ restore regs and return
ENTRY(omap1610_idle_loop_suspend_sz)
.word . - omap1610_idle_loop_suspend
#endif /* CONFIG_ARCH_OMAP16XX */
/*
* Forces OMAP into deep sleep state
*
* omapXXXX_cpu_suspend()
*
* The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed
* as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1
* in register r1.
*
* Note: This code get's copied to internal SRAM at boot. When the OMAP
* wakes up it continues execution at the point it went to sleep.
*
* Note: Because of errata work arounds we have processor specific functions
* here. They are mostly the same, but slightly different.
*
*/
#ifdef CONFIG_ARCH_OMAP1510
ENTRY(omap1510_cpu_suspend)
@ save registers on stack
stmfd sp!, {r0 - r12, lr}
@ load base address of Traffic Controller
mov r4, #TCMIF_ASM_BASE & 0xff000000
orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
@ work around errata of OMAP1510 PDE bit for TC shut down
@ clear PDE bit
ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
bic r5, r5, #PDE_BIT & 0xff
str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
@ set PWD_EN bit
and r5, r5, #PWD_EN_BIT & 0xff
str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
@ prepare to put SDRAM into self-refresh manually
ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
orr r5, r5, #SELF_REFRESH_MODE & 0xff000000
orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff
str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
@ prepare to put EMIFS to Sleep
ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff
str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
@ load base address of ARM_IDLECT1 and ARM_IDLECT2
mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
@ turn off clock domains
mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
orr r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
@ request ARM idle
mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff
orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00
strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
mov r5, #IDLE_WAIT_CYCLES & 0xff
orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00
l_1510_2:
subs r5, r5, #1
bne l_1510_2
/*
* Let's wait for the next wake up event to wake us up. r0 can't be
* used here because r0 holds ARM_IDLECT1
*/
mov r2, #0
mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt
/*
* omap1510_cpu_suspend()'s resume point.
*
* It will just start executing here, so we'll restore stuff from the
* stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
*/
strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
@ restore regs and return
ldmfd sp!, {r0 - r12, pc}
ENTRY(omap1510_cpu_suspend_sz)
.word . - omap1510_cpu_suspend
#endif /* CONFIG_ARCH_OMAP1510 */
#if defined(CONFIG_ARCH_OMAP16XX)
ENTRY(omap1610_cpu_suspend)
@ save registers on stack
stmfd sp!, {r0 - r12, lr}
@ load base address of Traffic Controller
mov r4, #TCMIF_ASM_BASE & 0xff000000
orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
@ prepare to put SDRAM into self-refresh manually
ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
orr r5, r5, #SELF_REFRESH_MODE & 0xff000000
orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff
str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
@ prepare to put EMIFS to Sleep
ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff
str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
@ load base address of ARM_IDLECT1 and ARM_IDLECT2
mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
@ turn off clock domains
mov r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
orr r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
@ work around errata of OMAP1610/5912. Enable (!) peripheral
@ clock to let the chip go into deep sleep
ldrh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
orr r5,r5, #EN_PERCK_BIT & 0xff
strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
@ request ARM idle
mov r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff
orr r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00
strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
mov r5, #IDLE_WAIT_CYCLES & 0xff
orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00
l_1610_2:
subs r5, r5, #1
bne l_1610_2
/*
* Let's wait for the next wake up event to wake us up. r0 can't be
* used here because r0 holds ARM_IDLECT1
*/
mov r2, #0
mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt
/*
* omap1610_cpu_suspend()'s resume point.
*
* It will just start executing here, so we'll restore stuff from the
* stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
*/
strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
@ restore regs and return
ldmfd sp!, {r0 - r12, pc}
ENTRY(omap1610_cpu_suspend_sz)
.word . - omap1610_cpu_suspend
#endif /* CONFIG_ARCH_OMAP16XX */