diff options
author | Scott Williams <scwilliams@nvidia.com> | 2011-07-18 15:20:56 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:46:48 -0800 |
commit | 07e32729adeff79693e0da4f64ade4a64f52b024 (patch) | |
tree | 1dad1a27668df6e24bb91bbb854d472644e2406e /arch/arm/mach-tegra/sleep-t2.S | |
parent | a30f93203f93896205fc7de4956a05b285846c62 (diff) |
ARM: tegra: Split sleep.S for Tegra2
Change-Id: I22bbfe62c6fed753a6852b12246f4a1f2414a96f
Signed-off-by: Scott Williams <scwilliams@nvidia.com>
DW: Split into logical changes
Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com>
Rebase-Id: R2d7985afe7ffafac651d747205e528331f5f993e
Diffstat (limited to 'arch/arm/mach-tegra/sleep-t2.S')
-rw-r--r-- | arch/arm/mach-tegra/sleep-t2.S | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/sleep-t2.S b/arch/arm/mach-tegra/sleep-t2.S new file mode 100644 index 000000000000..df88d81d2ee4 --- /dev/null +++ b/arch/arm/mach-tegra/sleep-t2.S @@ -0,0 +1,516 @@ +/* + * arch/arm/mach-tegra/include/mach/sleep-t2.S + * + * Copyright (c) 2010-2011, NVIDIA Corporation. + * Copyright (c) 2011, Google, Inc. + * + * Author: Colin Cross <ccross@android.com> + * Gary King <gking@nvidia.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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/const.h> +#include <linux/init.h> +#include <linux/linkage.h> + +#include <asm/assembler.h> +#include <asm/cache.h> +#include <asm/domain.h> +#include <asm/memory.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/asm-offsets.h> +#include <asm/glue-cache.h> +#include <asm/glue-proc.h> +#include <asm/system.h> + +#include <mach/iomap.h> +#include <mach/io.h> + +#include "asm_macros.h" +#include "sleep.h" + +#define EMC_CFG 0xc +#define EMC_ADR_CFG 0x10 +#define EMC_REFRESH 0x70 +#define EMC_NOP 0xdc +#define EMC_SELF_REF 0xe0 +#define EMC_REQ_CTRL 0x2b0 +#define EMC_EMC_STATUS 0x2b4 + +#define CLK_RESET_CCLK_BURST 0x20 +#define CLK_RESET_CCLK_DIVIDER 0x24 +#define CLK_RESET_SCLK_BURST 0x28 +#define CLK_RESET_SCLK_DIVIDER 0x2c + +#define CLK_RESET_PLLC_BASE 0x80 +#define CLK_RESET_PLLM_BASE 0x90 +#define CLK_RESET_PLLP_BASE 0xa0 +#define CLK_RESET_PLLP_OUTA 0xa4 +#define CLK_RESET_PLLP_OUTB 0xa8 +#define CLK_RESET_PLLP_MISC 0xac +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 + +#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) +#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS + IO_CPU_VIRT) +#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT) + +/* + * tegra2_sleep_reset(unsigned long v2p) + * + * puts the current cpu in reset + * uses tegra_cpu_save to take the cpu out of coherence + * should never return + */ +ENTRY(tegra2_sleep_reset) + bl tegra_cpu_save + cpu_id r0 + bl tegra2_cpu_reset + mov pc, lr +ENDPROC(tegra2_sleep_reset) + +/* + * tegra2_cpu_reset(int cpu) + * + * r0 is cpu to reset + * + * puts the specified CPU in wait-for-event mode on the flow controller + * and puts the CPU in reset + * can be called on the current cpu or another cpu + * if called on the current cpu, does not return + * + * corrupts r0-r3, r12 + */ +ENTRY(tegra2_cpu_reset) + mov32 r3, TEGRA_PMC_VIRT + add r1, r3, #PMC_SCRATCH41 + mov r12, #CPU_RESETTABLE + str r12, [r1] + + cpu_to_halt_reg r1, r0 + mov32 r3, TEGRA_FLOW_CTRL_VIRT + mov r2, #FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME + str r2, [r3, r1] @ put flow controller in wait event mode + ldr r2, [r3, r1] + isb + dsb + movw r1, 0x1011 + mov r1, r1, lsl r0 + mov32 r3, TEGRA_CLK_RESET_VIRT + str r1, [r3, #0x340] @ put slave CPU in reset + isb + dsb + cpu_id r3 + cmp r3, r0 + beq . + mov pc, lr +ENDPROC(tegra2_cpu_reset) + +/* + * tegra2_cpu_set_resettable_soon(void) + * + * Called to set the "resettable soon" flag in PMC_SCRATCH41 when + * it is expected that the secondary CPU will be idle soon. + */ +ENTRY(tegra2_cpu_set_resettable_soon) + mov32 r3, TEGRA_PMC_VIRT + add r1, r3, #PMC_SCRATCH41 + mov r12, #CPU_RESETTABLE_SOON + str r12, [r1] + mov pc, lr +ENDPROC(tegra2_cpu_set_resettable_soon) + +/* + * tegra2_cpu_is_resettable_soon(void) + * + * Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been + * set because it is expected that the secondary CPU will be idle soon. + */ +ENTRY(tegra2_cpu_is_resettable_soon) + mov32 r3, TEGRA_PMC_VIRT + add r1, r3, #PMC_SCRATCH41 + ldr r12, [r1] + cmp r12, #CPU_RESETTABLE_SOON + moveq r0, #1 + movne r0, #0 + mov pc, lr +ENDPROC(tegra2_cpu_is_resettable_soon) + +/* + * tegra2_sleep_core(unsigned long v2p) + * + * enters suspend in LP0 or LP1 by turning off the mmu and jumping to + * tegra2_tear_down_core in IRAM + */ +ENTRY(tegra2_sleep_core) + mov r3, lr @ set resume address to lr + bl tegra_cpu_save + + mov32 r1, tegra2_tear_down_core + mov32 r2, tegra2_iram_start + sub r1, r1, r2 + mov32 r2, TEGRA_IRAM_CODE_AREA + add r1, r1, r2 + b tegra_turn_off_mmu +ENDPROC(tegra2_sleep_core) + +/* + * tegra2_sleep_wfi(unsigned long v2p) + */ +ENTRY(tegra2_sleep_wfi) + mov r3, lr @ set resume address to lr + mrc p15, 0, r2, c1, c0, 1 @ save actlr before exiting coherency + bl tegra_cpu_save + + mov r11, r2 + + mov32 r3, TEGRA_PMC_VIRT + add r0, r3, #PMC_SCRATCH41 + mov r3, #CPU_RESETTABLE + str r3, [r0] + + bl tegra_cpu_wfi + + /* + * cpu may be reset while in wfi, which will return through + * tegra_secondary_resume to cpu_resume to tegra_cpu_resume + * or interrupt may wake wfi, which will return here + * cpu state is unchanged - MMU is on, cache is on, coherency is off + * + * r11 contains the original actlr + */ + + bl tegra_pen_lock + + mov32 r3, TEGRA_PMC_VIRT + add r0, r3, #PMC_SCRATCH41 + mov r3, #CPU_NOT_RESETTABLE + str r3, [r0] + + bl tegra_pen_unlock + + mcr p15, 0, r11, c1, c0, 1 @ reenable coherency + + @ the cpu was running with coherency disabled, caches may be out of date +#ifdef MULTI_CACHE + mov32 r10, cpu_cache + mov lr, pc + ldr pc, [r10, #CACHE_FLUSH_KERN_ALL] +#else + bl __cpuc_flush_kern_all +#endif + + ldmfd sp!, {r4} + mcr p15, 0, r4, c15, c0, 1 @ write diagnostic register + ldmfd sp!, {lr} + ldmfd sp!, {r4 - r11} + mov pc, lr +ENDPROC(tegra2_sleep_wfi) + +/* + * tegra2_tear_down_cpu + * + * Switches the CPU cluster to PLL-P and enters sleep. + */ +ENTRY(tegra2_tear_down_cpu) + bl tegra_cpu_pllp + b tegra2_enter_sleep +ENDPROC(tegra2_tear_down_cpu) + +/* START OF ROUTINES COPIED TO IRAM */ + .align L1_CACHE_SHIFT + .globl tegra2_iram_start +tegra2_iram_start: + +/* + * tegra2_lp1_reset + * + * reset vector for LP1 restore; copied into IRAM during suspend. + * brings the system back up to a safe starting point (SDRAM out of + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP, + * system clock running on the same PLL that it suspended at), and + * jumps to tegra_lp2_startup to restore PLLX and virtual addressing. + * physical address of tegra_lp2_startup expected to be stored in + * PMC_SCRATCH41 + * + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST. + */ +ENTRY(tegra2_lp1_reset) + /* + * the CPU and system bus are running at 32KHz and executing from + * IRAM when this code is executed; immediately switch to CLKM and + * enable PLLP. + */ + mov32 r0, TEGRA_CLK_RESET_BASE + mov r1, #(1 << 28) + str r1, [r0, #CLK_RESET_SCLK_BURST] + str r1, [r0, #CLK_RESET_CCLK_BURST] + mov r1, #0 + str r1, [r0, #CLK_RESET_SCLK_DIVIDER] + str r1, [r0, #CLK_RESET_CCLK_DIVIDER] + + ldr r1, [r0, #CLK_RESET_PLLM_BASE] + tst r1, #(1 << 30) + orreq r1, r1, #(1 << 30) + streq r1, [r0, #CLK_RESET_PLLM_BASE] + ldr r1, [r0, #CLK_RESET_PLLP_BASE] + tst r1, #(1 << 30) + orreq r1, r1, #(1 << 30) + streq r1, [r0, #CLK_RESET_PLLP_BASE] + ldr r1, [r0, #CLK_RESET_PLLC_BASE] + tst r1, #(1 << 30) + orreq r1, r1, #(1 << 30) + streq r1, [r0, #CLK_RESET_PLLC_BASE] + + adr r2, tegra2_sdram_pad_address + adr r4, tegra2_sdram_pad_save + mov r5, #0 + +padload: + ldr r0, [r2, r5] @ r0 is emc register address + + ldr r1, [r4, r5] + str r1, [r0] @ set emc register to safe vals + + add r5, r5, #4 + ldr r0, tegra2_sdram_pad_size + cmp r0, r5 + bne padload + +padload_done: + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #0xff @ 255uS delay for PLL stabilization + +1: ldr r0, [r7] + cmp r0, r1 + dmb + bmi 1b + + adr r4, tegra2_sclk_save + ldr r4, [r4] + mov32 r0, TEGRA_CLK_RESET_BASE + str r4, [r0, #CLK_RESET_SCLK_BURST] + ldr r4, =((1 << 28) | (4)) @ burst policy is PLLP + str r4, [r0, #CLK_RESET_CCLK_BURST] + + mov32 r0, TEGRA_EMC_BASE + ldr r1, [r0, #EMC_CFG] + bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP + str r1, [r0, #EMC_CFG] + + mov r1, #0 + str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh + mov r1, #1 + str r1, [r0, #EMC_NOP] + str r1, [r0, #EMC_NOP] + str r1, [r0, #EMC_REFRESH] + + ldr r1, [r0, #EMC_ADR_CFG] + tst r1, #(0x3 << 24) + moveq r1, #(0x1 << 8) @ just 1 device + movne r1, #(0x3 << 8) @ 2 devices + +exit_selfrefresh_loop: + ldr r2, [r0, #EMC_EMC_STATUS] + ands r2, r2, r1 + bne exit_selfrefresh_loop + + mov r1, #0 + str r1, [r0, #EMC_REQ_CTRL] + + mov32 r0, TEGRA_PMC_BASE + ldr r0, [r0, #PMC_SCRATCH41] + mov pc, r0 +ENDPROC(tegra2_lp1_reset) + +/* + * tegra2_tear_down_core + * + * copied into and executed from IRAM + * puts memory in self-refresh for LP0 and LP1 + */ +tegra2_tear_down_core: + bl tegra2_sdram_self_refresh + bl tegra2_cpu_clk32k + b tegra2_enter_sleep + +/* + * tegra2_cpu_clk32k + * + * In LP0 and LP1 all plls will be turned off. Switch the CPU and system clock + * to the 32khz clock (clks) + */ +tegra2_cpu_clk32k: + /* start by jumping to clkm to safely disable PLLs, then jump + * to clks */ + mov r0, #(1 << 28) + str r0, [r5, #CLK_RESET_SCLK_BURST] + str r0, [r5, #CLK_RESET_CCLK_BURST] + mov r0, #0 + str r0, [r5, #CLK_RESET_CCLK_DIVIDER] + str r0, [r5, #CLK_RESET_SCLK_DIVIDER] + + /* 2 us delay between changing sclk and disabling PLLs */ + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #3 + +1: ldr r0, [r7] + cmp r0, r1 + dmb + bmi 1b + + /* switch to CLKS */ + mov r0, #0 /* burst policy = 32KHz */ + str r0, [r5, #CLK_RESET_SCLK_BURST] + + /* disable PLLP, PLLM, PLLC in LP0 and LP1 states */ + ldr r0, [r5, #CLK_RESET_PLLM_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLM_BASE] + ldr r0, [r5, #CLK_RESET_PLLP_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLP_BASE] + ldr r0, [r5, #CLK_RESET_PLLC_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLC_BASE] + mov pc, lr + +/* + * tegra2_enter_sleep + * + * uses flow controller to enter sleep state + * executes from IRAM with SDRAM in selfrefresh when target state is LP0 and LP1 + * executes from SDRAM with target state is LP2 + */ +tegra2_enter_sleep: + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + mov32 r4, TEGRA_PMC_BASE + str r1, [r4, #PMC_SCRATCH38] + dsb + mov32 r6, TEGRA_FLOW_CTRL_BASE + + mov r0, #FLOW_CTRL_STOP_UNTIL_IRQ + orr r0, r0, #FLOW_CTRL_IRQ_RESUME | FLOW_CTRL_FIQ_RESUME + cpu_id r1 + cpu_to_halt_reg r1, r1 + str r0, [r6, r1] + dsb + ldr r0, [r6, r1] /* memory barrier */ + +halted: dsb + wfe /* CPU should be power gated here */ + isb + b halted + +/* + * tegra2_sdram_self_refresh + * + * called with MMU off and caches disabled + * puts sdram in self refresh + * must execute from IRAM + */ +tegra2_sdram_self_refresh: + mov32 r1, TEGRA_EMC_BASE + mov r2, #3 + str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests + +emcidle:ldr r2, [r1, #EMC_EMC_STATUS] + tst r2, #4 + beq emcidle + + mov r2, #1 + str r2, [r1, #EMC_SELF_REF] + + ldr r2, [r1, #EMC_ADR_CFG] + tst r2, #(0x3 << 24) + moveq r2, #(0x1 << 8) @ just 1 device + movne r2, #(0x3 << 8) @ 2 devices + +emcself:ldr r3, [r1, #EMC_EMC_STATUS] + and r3, r3, r2 + cmp r3, r2 + bne emcself @ loop until DDR in self-refresh + + adr r2, tegra2_sdram_pad_address + adr r3, tegra2_sdram_pad_safe + adr r4, tegra2_sdram_pad_save + mov r5, #0 + +padsave: + ldr r0, [r2, r5] @ r0 is emc register address + + ldr r1, [r0] + str r1, [r4, r5] @ save emc register + + ldr r1, [r3, r5] + str r1, [r0] @ set emc register to safe vals + + add r5, r5, #4 + ldr r0, tegra2_sdram_pad_size + cmp r0, r5 + bne padsave +padsave_done: + + mov32 r5, TEGRA_CLK_RESET_BASE + ldr r0, [r5, #CLK_RESET_SCLK_BURST] + adr r2, tegra2_sclk_save + str r0, [r2] + dsb + mov pc, lr + +tegra2_sdram_pad_address: + .word TEGRA_APB_MISC_BASE + 0x8c8 /* XM2CFGCPADCTRL */ + .word TEGRA_APB_MISC_BASE + 0x8cc /* XM2CFGDPADCTRL */ + .word TEGRA_APB_MISC_BASE + 0x8d0 /* XM2CLKCFGPADCTRL */ + .word TEGRA_APB_MISC_BASE + 0x8d4 /* XM2COMPPADCTRL */ + .word TEGRA_APB_MISC_BASE + 0x8d8 /* XM2VTTGENPADCTRL */ + .word TEGRA_APB_MISC_BASE + 0x8e4 /* XM2CFGCPADCTRL2 */ + .word TEGRA_APB_MISC_BASE + 0x8e8 /* XM2CFGDPADCTRL2 */ + +tegra2_sdram_pad_size: + .word tegra2_sdram_pad_size - tegra2_sdram_pad_address + +tegra2_sdram_pad_safe: + .word 0x8 + .word 0x8 + .word 0x0 + .word 0x8 + .word 0x5500 + .word 0x08080040 + .word 0x0 + +tegra2_sclk_save: + .word 0x0 + +tegra2_sdram_pad_save: + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + + .ltorg +/* dummy symbol for end of IRAM */ + .align L1_CACHE_SHIFT + .globl tegra2_iram_end +tegra2_iram_end: + b . |