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 | |
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')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle-t2.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/hotplug.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-t2.S | 516 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.S | 476 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 62 |
7 files changed, 594 insertions, 483 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 381f2180f062..d9c2a46b76a8 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -15,7 +15,10 @@ obj-y += delay.o obj-y += powergate.o obj-y += pm.o obj-$(CONFIG_PM_SLEEP) += pm-irq.o -obj-$(CONFIG_PM_SLEEP) += sleep.o +ifeq ($(CONFIG_PM_SLEEP),y) +obj-y += sleep.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-t2.o +endif obj-y += fuse.o obj-y += kfuse.o obj-$(CONFIG_TEGRA_LEGACY_AUDIO) += tegra_i2s_audio.o diff --git a/arch/arm/mach-tegra/cpuidle-t2.c b/arch/arm/mach-tegra/cpuidle-t2.c index badeeccf98fa..d8523661016d 100644 --- a/arch/arm/mach-tegra/cpuidle-t2.c +++ b/arch/arm/mach-tegra/cpuidle-t2.c @@ -74,7 +74,7 @@ static int tegra2_reset_sleeping_cpu(int cpu) tegra_pen_lock(); if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE) - tegra_cpu_reset(cpu); + tegra2_cpu_reset(cpu); else ret = -EINVAL; @@ -140,7 +140,7 @@ static int tegra2_idle_lp2_last(struct cpuidle_device *dev, { int i; - while (tegra_cpu_is_resettable_soon()) + while (tegra2_cpu_is_resettable_soon()) cpu_relax(); if (tegra2_reset_other_cpus(dev->cpu)) @@ -176,7 +176,7 @@ void tegra2_idle_lp2(struct cpuidle_device *dev, } } } else - tegra_sleep_wfi(PLAT_PHYS_OFFSET - PAGE_OFFSET); + tegra2_sleep_wfi(PLAT_PHYS_OFFSET - PAGE_OFFSET); cpu_pm_exit(); tegra_clear_cpu_in_lp2(dev->cpu); diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index 5e8277853986..cde2659451ae 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -51,7 +51,9 @@ int platform_cpu_kill(unsigned int cpu) void platform_cpu_die(unsigned int cpu) { - tegra_sleep_reset(); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + tegra2_sleep_reset(); +#endif /* * tegra_cpu_suspend can return through tegra_cpu_resume, but that diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index b8cd50194660..f86382f0bc0a 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -448,7 +448,7 @@ bool tegra_set_cpu_in_lp2(int cpu) last_cpu = true; #ifdef CONFIG_ARCH_TEGRA_2x_SOC else - tegra_cpu_set_resettable_soon(); + tegra2_cpu_set_resettable_soon(); #endif spin_unlock(&tegra_lp2_lock); @@ -527,7 +527,7 @@ static int tegra_common_suspend(void) /* copy the reset vector and SDRAM shutdown code into IRAM */ memcpy(iram_save, iram_code, iram_save_size); - memcpy(iram_code, &tegra_iram_start, iram_save_size); + memcpy(iram_code, tegra_iram_start(), iram_save_size); return 0; } @@ -604,7 +604,7 @@ static void tegra_pm_set(enum tegra_suspend_mode mode) * tegra_lp1_reset in IRAM, which resumes the CPU to * the address in scratch 41 to tegra_resume */ - writel(&tegra_lp1_reset - &tegra_iram_start + + writel(tegra_lp1_reset() - tegra_iram_start() + TEGRA_IRAM_CODE_AREA, evp_reset); __raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41); break; @@ -804,7 +804,7 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat) plat->suspend_mode = TEGRA_SUSPEND_LP1; } - iram_save_size = &tegra_iram_end - &tegra_iram_start; + iram_save_size = tegra_iram_end() - tegra_iram_start(); iram_save = kmalloc(iram_save_size, GFP_KERNEL); if (!iram_save) { 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 . diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S index 11e38f2aaa34..dd3fcc3571fa 100644 --- a/arch/arm/mach-tegra/sleep.S +++ b/arch/arm/mach-tegra/sleep.S @@ -1,4 +1,6 @@ /* + * arch/arm/mach-tegra/sleep.S + * * Copyright (c) 2010-2011, NVIDIA Corporation. * Copyright (c) 2011, Google, Inc. * @@ -41,40 +43,11 @@ #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 FLOW_CTRL_WAITEVENT (2 << 29) -#define FLOW_CTRL_STOP_UNTIL_IRQ (4 << 29) -#define FLOW_CTRL_JTAG_RESUME (1 << 28) -#define FLOW_CTRL_IRQ_RESUME (1 << 10) -#define FLOW_CTRL_FIQ_RESUME (1 << 8) - -#define FLOW_CTRL_CSR_INTR_FLAG (1<<15) -#define FLOW_CTRL_CSR_EVENT_FLAG (1<<14) #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_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT) #define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT) /* @@ -131,24 +104,6 @@ ENTRY(tegra_pen_unlock) mov pc, lr ENDPROC(tegra_pen_unlock) -ENTRY(tegra_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(tegra_cpu_set_resettable_soon) - -ENTRY(tegra_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(tegra_cpu_is_resettable_soon) - /* * tegra_cpu_save * @@ -219,108 +174,26 @@ ENTRY(tegra_cpu_wfi) mov pc, lr ENDPROC(tegra_cpu_wfi) -/* - * tegra_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(tegra_sleep_reset) - bl tegra_cpu_save - cpu_id r0 - bl tegra_cpu_reset - mov pc, lr -ENDPROC(tegra_sleep_reset) - -/* - * tegra_sleep_core(unsigned long v2p) - * - * enters suspend in LP0 or LP1 by turning off the mmu and jumping to - * tegra_tear_down_core in IRAM - */ -ENTRY(tegra_sleep_core) - mov r3, lr @ set resume address to lr - bl tegra_cpu_save - - mov32 r1, tegra_tear_down_core - mov32 r2, tegra_iram_start - sub r1, r1, r2 - mov32 r2, TEGRA_IRAM_CODE_AREA - add r1, r1, r2 - b tegra_turn_off_mmu -ENDPROC(tegra_sleep_core) - .word /* * tegra_sleep_cpu(unsigned long v2p) * * enters suspend in LP2 by turning off the mmu and jumping to - * tegra_tear_down_cpu + * tegra?_tear_down_cpu */ ENTRY(tegra_sleep_cpu) mov r3, lr @ set resume address to lr bl tegra_cpu_save - mov32 r1, tegra_tear_down_cpu +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + mov32 r1, tegra2_tear_down_cpu +#endif add r1, r1, r0 b tegra_turn_off_mmu ENDPROC(tegra_sleep_cpu) /* - * tegra_sleep_wfi(unsigned long v2p) - */ -ENTRY(tegra_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 - mov r0, #0 - mcr p15, 0, r0, c8, c3, 0 @ invalidate TLB - mcr p15, 0, r0, c7, c5, 6 @ flush BTAC -#ifdef MULTI_CACHE - mov32 r10, cpu_cache - mov lr, pc - ldr pc, [r10, #CACHE_FLUSH_KERN_ALL] -#else - bl __cpuc_flush_kern_all -#endif - - b tegra_cpu_sleep_abort -ENDPROC(tegra_sleep_wfi) - -/* * tegra_cpu_resume * * reloads the volatile CPU state from the context area @@ -335,9 +208,8 @@ tegra_cpu_resume: dsb isb - bl cpu_init + bl cpu_init -tegra_cpu_sleep_abort: ldmfd sp!, {r4} mcr p15, 0, r4, c15, c0, 1 @ write diagnostic register ldmfd sp!, {lr} @@ -345,49 +217,12 @@ tegra_cpu_sleep_abort: mov pc, lr /* - * tegra_cpu_reset - * - * 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(tegra_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(tegra_cpu_reset) - -/* * tegra_turn_off_mmu * * r0 = v2p * r1 = physical address to jump to with mmu off */ -tegra_turn_off_mmu: +ENTRY(tegra_turn_off_mmu) /* * change page table pointer to tegra_pgd_phys, so that IRAM * and MMU shut-off will be mapped virtual == physical @@ -413,6 +248,7 @@ tegra_turn_off_mmu: add r3, r3, r0 mov r0, r1 mov pc, r3 +ENDPROC(tegra_turn_off_mmu) tegra_pgd_phys_address: .word tegra_pgd_phys @@ -434,305 +270,19 @@ tegra_shut_off_mmu: isb mov pc, r0 - .ltorg - /* * tegra_cpu_clk32k * * In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp */ - tegra_cpu_pllp: +ENTRY(tegra_cpu_pllp) /* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */ mov32 r5, TEGRA_CLK_RESET_BASE - mov r0, #(2 << 28) /* burst policy = run mode */ - orr r0, r0, #(4 << 4) /* use PLLP in run mode burst */ - str r0, [r5, #CLK_RESET_CCLK_BURST] - mov r0, #0 - str r0, [r5, #CLK_RESET_CCLK_DIVIDER] - mov pc, lr - -tegra_tear_down_cpu: - bl tegra_cpu_pllp - b tegra_enter_sleep - -/* START OF ROUTINES COPIED TO IRAM */ - .align L1_CACHE_SHIFT - .globl tegra_iram_start -tegra_iram_start: - -/* - * tegra_tear_down_core - * - * copied into and executed from IRAM - * puts memory in self-refresh for LP0 and LP1 - */ -tegra_tear_down_core: - bl tegra_sdram_self_refresh - bl tegra_cpu_clk32k - b tegra_enter_sleep - -/* - * tegra_cpu_clk32k - * - * In LP0 and LP1 all plls will be turned off. Switch the CPU and system clock - * to the 32khz clock (clks) - */ -tegra_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] + mov r0, #(2 << 28) @ burst policy = run mode + orr r0, r0, #(4 << 4) @ use PLLP in run mode 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 - -/* - * tegra_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 - */ -tegra_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 - -/* - * tegra_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 - */ -ENTRY(tegra_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, tegra_sdram_pad_address - adr r4, tegra_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, tegra_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, tegra_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(tegra_lp1_reset) - -/* - * tegra_sdram_self_refresh - * - * called with MMU off and caches disabled - * puts sdram in self refresh - * must execute from IRAM - */ -tegra_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, tegra_sdram_pad_address - adr r3, tegra_sdram_pad_safe - adr r4, tegra_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, tegra_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, tegra_sclk_save - str r0, [r2] - dsb mov pc, lr +ENDPROC(tegra_cpu_pllp) -tegra_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 */ - -tegra_sdram_pad_size: - .word tegra_sdram_pad_size - tegra_sdram_pad_address - -tegra_sdram_pad_safe: - .word 0x8 - .word 0x8 - .word 0x0 - .word 0x8 - .word 0x5500 - .word 0x08080040 - .word 0x0 - -tegra_sclk_save: - .word 0x0 - -tegra_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 tegra_iram_end -tegra_iram_end: - b . diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index d95763ea53d4..34cffd17ff0b 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -3,7 +3,7 @@ * * Declarations for power state transition code * - * Copyright (c) 2010, NVIDIA Corporation. + * Copyright (c) 2010-2011, NVIDIA Corporation. * * 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 @@ -39,23 +39,63 @@ #define CPU_RESETTABLE_SOON 1 #define CPU_NOT_RESETTABLE 0 +#define FLOW_CTRL_WAITEVENT (2 << 29) +#define FLOW_CTRL_STOP_UNTIL_IRQ (4 << 29) +#define FLOW_CTRL_JTAG_RESUME (1 << 28) +#define FLOW_CTRL_IRQ_RESUME (1 << 10) +#define FLOW_CTRL_FIQ_RESUME (1 << 8) + +#define FLOW_CTRL_CSR_INTR_FLAG (1<<15) +#define FLOW_CTRL_CSR_EVENT_FLAG (1<<14) + +#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT) + #ifndef __ASSEMBLY__ -/* assembly routines implemented in sleep.S */ void tegra_pen_lock(void); void tegra_pen_unlock(void); void tegra_cpu_wfi(void); -void tegra_cpu_reset(int cpu); -void tegra_cpu_set_resettable_soon(void); -int tegra_cpu_is_resettable_soon(void); -extern void tegra_lp1_reset; -extern void tegra_iram_start; -extern void tegra_iram_end; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +extern void tegra2_iram_start; +extern void tegra2_iram_end; +extern void tegra2_lp1_reset; +int tegra2_cpu_is_resettable_soon(void); +void tegra2_cpu_reset(int cpu); +void tegra2_cpu_set_resettable_soon(void); +void tegra2_sleep_core(unsigned long v2p); +void tegra2_sleep_reset(void); +void tegra2_sleep_wfi(unsigned long v2p); +#endif + +static inline void *tegra_iram_start(void) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + return &tegra2_iram_start; +#endif +} + +static inline void *tegra_iram_end(void) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + return &tegra2_iram_end; +#endif +} + +static inline void *tegra_lp1_reset(void) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + return &tegra2_lp1_reset; +#endif +} + +static inline void tegra_sleep_core(unsigned long v2p) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + tegra2_sleep_core(v2p); +#endif +} -void tegra_sleep_reset(void); -void tegra_sleep_wfi(unsigned long v2p); void tegra_sleep_cpu(unsigned long v2p); -void tegra_sleep_core(unsigned long v2p); void tegra_resume(void); void tegra_secondary_resume(void); |