| /* |
| * linux/arch/arm/plat-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 */ |