ARM: tegra: add LP1 suspend support for Tegra114

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* storing some EMC registers, DPD (deep power down) status, clk source of
  mselect and SCLK burst policy
* putting SDRAM into self-refresh
* switching CPU to CLK_M (12MHz OSC)
* tunning off PLLM, PLLP, PLLA, PLLC and PLLX
* switching SCLK to CLK_S (32KHz OSC)
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, PLLA, PLLC and PLLX
* restoring the clk source of mselect and SCLK burst policy
* setting up CCLK burst policy to PLLX
* restoring DPD status and some EMC registers
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored. Then jumping to "tegra_resume" that was expected to be stored
in PMC_SCRATCH41 to restore CPU context and back to kernel.

Based on the work by: Bo Yan <byan@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
This commit is contained in:
Joseph Lo 2013-08-12 17:40:06 +08:00 committed by Stephen Warren
parent 731a927438
commit e9f624499c
4 changed files with 141 additions and 15 deletions

View File

@ -33,6 +33,7 @@ obj-$(CONFIG_TEGRA_PCI) += pcie.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += sleep-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += pm-tegra30.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o
endif

View File

@ -239,6 +239,12 @@
#define TEGRA_KFUSE_BASE 0x7000FC00
#define TEGRA_KFUSE_SIZE SZ_1K
#define TEGRA_EMC0_BASE 0x7001A000
#define TEGRA_EMC0_SIZE SZ_2K
#define TEGRA_EMC1_BASE 0x7001A800
#define TEGRA_EMC1_SIZE SZ_2K
#define TEGRA_CSITE_BASE 0x70040000
#define TEGRA_CSITE_SIZE SZ_256K

View File

@ -215,7 +215,9 @@ static bool tegra_lp1_iram_hook(void)
tegra20_lp1_iram_hook();
break;
case TEGRA30:
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC))
case TEGRA114:
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) ||
IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC))
tegra30_lp1_iram_hook();
break;
default:
@ -241,7 +243,9 @@ static bool tegra_sleep_core_init(void)
tegra20_sleep_core_init();
break;
case TEGRA30:
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC))
case TEGRA114:
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) ||
IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC))
tegra30_sleep_core_init();
break;
default:

View File

@ -65,6 +65,10 @@
#define CLK_RESET_PLLA_MISC 0xbc
#define CLK_RESET_PLLX_BASE 0xe0
#define CLK_RESET_PLLX_MISC 0xe4
#define CLK_RESET_PLLX_MISC3 0x518
#define CLK_RESET_PLLX_MISC3_IDDQ 3
#define CLK_RESET_PLLM_MISC_IDDQ 5
#define CLK_RESET_PLLC_MISC_IDDQ 26
#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4
@ -114,6 +118,18 @@
beq 1b
.endm
.macro pll_iddq_exit, rd, car, iddq, iddq_bit
ldr \rd, [\car, #\iddq]
bic \rd, \rd, #(1<<\iddq_bit)
str \rd, [\car, #\iddq]
.endm
.macro pll_iddq_entry, rd, car, iddq, iddq_bit
ldr \rd, [\car, #\iddq]
orr \rd, \rd, #(1<<\iddq_bit)
str \rd, [\car, #\iddq]
.endm
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
/*
* tegra30_hotplug_shutdown(void)
@ -315,6 +331,32 @@ ENTRY(tegra30_lp1_reset)
str r1, [r0, #CLK_RESET_CCLK_DIVIDER]
str r1, [r0, #CLK_RESET_SCLK_DIVIDER]
tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
cmp r10, #TEGRA30
beq _no_pll_iddq_exit
pll_iddq_exit r1, r0, CLK_RESET_PLLM_MISC, CLK_RESET_PLLM_MISC_IDDQ
pll_iddq_exit r1, r0, CLK_RESET_PLLC_MISC, CLK_RESET_PLLC_MISC_IDDQ
pll_iddq_exit r1, r0, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
mov32 r7, TEGRA_TMRUS_BASE
ldr r1, [r7]
add r1, r1, #2
wait_until r1, r7, r3
/* enable PLLM via PMC */
mov32 r2, TEGRA_PMC_BASE
ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
orr r1, r1, #(1 << 12)
str r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
pll_enable r1, r0, CLK_RESET_PLLM_BASE, 0
pll_enable r1, r0, CLK_RESET_PLLC_BASE, 0
pll_enable r1, r0, CLK_RESET_PLLX_BASE, 0
b _pll_m_c_x_done
_no_pll_iddq_exit:
/* enable PLLM via PMC */
mov32 r2, TEGRA_PMC_BASE
ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
@ -322,11 +364,13 @@ ENTRY(tegra30_lp1_reset)
str r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
_pll_m_c_x_done:
pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
pll_locked r1, r0, CLK_RESET_PLLM_BASE
pll_locked r1, r0, CLK_RESET_PLLP_BASE
pll_locked r1, r0, CLK_RESET_PLLA_BASE
@ -346,7 +390,11 @@ ENTRY(tegra30_lp1_reset)
ldr r4, [r5, #0x1C] @ restore SCLK_BURST
str r4, [r0, #CLK_RESET_SCLK_BURST]
mov32 r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
cmp r10, #TEGRA30
movweq r4, #:lower16:((1 << 28) | (0x8)) @ burst policy is PLLX
movteq r4, #:upper16:((1 << 28) | (0x8))
movwne r4, #:lower16:((1 << 28) | (0xe))
movtne r4, #:upper16:((1 << 28) | (0xe))
str r4, [r0, #CLK_RESET_CCLK_BURST]
/* Restore pad power state to normal */
@ -356,8 +404,13 @@ ENTRY(tegra30_lp1_reset)
orr r1, r1, #(1 << 30)
str r1, [r2, #PMC_IO_DPD_REQ] @ DPD_OFF
mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base
cmp r10, #TEGRA30
movweq r0, #:lower16:TEGRA_EMC_BASE @ r0 reserved for emc base
movteq r0, #:upper16:TEGRA_EMC_BASE
movwne r0, #:lower16:TEGRA_EMC0_BASE
movtne r0, #:upper16:TEGRA_EMC0_BASE
exit_self_refresh:
ldr r1, [r5, #0xC] @ restore EMC_XM2VTTGENPADCTRL
str r1, [r0, #EMC_XM2VTTGENPADCTRL]
ldr r1, [r5, #0x10] @ restore EMC_XM2VTTGENPADCTRL2
@ -372,8 +425,14 @@ ENTRY(tegra30_lp1_reset)
emc_timing_update r1, r0
cmp r10, #TEGRA114
movweq r1, #:lower16:TEGRA_EMC1_BASE
movteq r1, #:upper16:TEGRA_EMC1_BASE
cmpeq r0, r1
ldr r1, [r0, #EMC_AUTO_CAL_CONFIG]
orr r1, r1, #(1 << 31) @ set AUTO_CAL_ACTIVE
orreq r1, r1, #(1 << 27) @ set slave mode for channel 1
str r1, [r0, #EMC_AUTO_CAL_CONFIG]
emc_wait_auto_cal_onetime:
@ -388,9 +447,10 @@ emc_wait_auto_cal_onetime:
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]
cmp r10, #TEGRA30
streq r1, [r0, #EMC_NOP]
streq r1, [r0, #EMC_NOP]
streq r1, [r0, #EMC_REFRESH]
emc_device_mask r1, r0
@ -452,6 +512,16 @@ zcal_done:
ldr r1, [r5, #0x0] @ restore EMC_CFG
str r1, [r0, #EMC_CFG]
/* Tegra114 had dual EMC channel, now config the other one */
cmp r10, #TEGRA114
bne __no_dual_emc_chanl
mov32 r1, TEGRA_EMC1_BASE
cmp r0, r1
movne r0, r1
addne r5, r5, #0x20
bne exit_self_refresh
__no_dual_emc_chanl:
mov32 r0, TEGRA_PMC_BASE
ldr r0, [r0, #PMC_SCRATCH41]
mov pc, r0 @ jump to tegra_resume
@ -468,12 +538,30 @@ tegra30_sdram_pad_address:
.word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18
.word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c
tegra114_sdram_pad_address:
.word TEGRA_EMC0_BASE + EMC_CFG @0x0
.word TEGRA_EMC0_BASE + EMC_ZCAL_INTERVAL @0x4
.word TEGRA_EMC0_BASE + EMC_AUTO_CAL_INTERVAL @0x8
.word TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL @0xc
.word TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL2 @0x10
.word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14
.word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18
.word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c
.word TEGRA_EMC1_BASE + EMC_CFG @0x20
.word TEGRA_EMC1_BASE + EMC_ZCAL_INTERVAL @0x24
.word TEGRA_EMC1_BASE + EMC_AUTO_CAL_INTERVAL @0x28
.word TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL @0x2c
.word TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL2 @0x30
tegra30_sdram_pad_size:
.word tegra30_sdram_pad_size - tegra30_sdram_pad_address
.word tegra114_sdram_pad_address - tegra30_sdram_pad_address
tegra114_sdram_pad_size:
.word tegra30_sdram_pad_size - tegra114_sdram_pad_address
.type tegra30_sdram_pad_save, %object
tegra30_sdram_pad_save:
.rept (tegra30_sdram_pad_size - tegra30_sdram_pad_address) / 4
.rept (tegra30_sdram_pad_size - tegra114_sdram_pad_address) / 4
.long 0
.endr
@ -497,6 +585,7 @@ tegra30_tear_down_core:
* r5 = TEGRA_CLK_RESET_BASE
* r6 = TEGRA_FLOW_CTRL_BASE
* r7 = TEGRA_TMRUS_BASE
* r10= SoC ID
*/
tegra30_switch_cpu_to_clk32k:
/*
@ -543,6 +632,11 @@ tegra30_switch_cpu_to_clk32k:
bic r0, r0, #(1 << 30)
str r0, [r5, #CLK_RESET_PLLX_BASE]
cmp r10, #TEGRA30
beq _no_pll_in_iddq
pll_iddq_entry r1, r5, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
_no_pll_in_iddq:
/* switch to CLKS */
mov r0, #0 /* brust policy = 32KHz */
str r0, [r5, #CLK_RESET_SCLK_BURST]
@ -594,14 +688,19 @@ halted:
* r5 = TEGRA_CLK_RESET_BASE
* r6 = TEGRA_FLOW_CTRL_BASE
* r7 = TEGRA_TMRUS_BASE
* r10= SoC ID
*/
tegra30_sdram_self_refresh:
adr r2, tegra30_sdram_pad_address
adr r8, tegra30_sdram_pad_save
tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
cmp r10, #TEGRA30
adreq r2, tegra30_sdram_pad_address
ldreq r3, tegra30_sdram_pad_size
adrne r2, tegra114_sdram_pad_address
ldrne r3, tegra114_sdram_pad_size
mov r9, #0
ldr r3, tegra30_sdram_pad_size
padsave:
ldr r0, [r2, r9] @ r0 is the addr in the pad_address
@ -615,13 +714,18 @@ padsave_done:
dsb
mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base addr
cmp r10, #TEGRA30
ldreq r0, =TEGRA_EMC_BASE @ r0 reserved for emc base addr
ldrne r0, =TEGRA_EMC0_BASE
enter_self_refresh:
cmp r10, #TEGRA30
mov r1, #0
str r1, [r0, #EMC_ZCAL_INTERVAL]
str r1, [r0, #EMC_AUTO_CAL_INTERVAL]
ldr r1, [r0, #EMC_CFG]
bic r1, r1, #(1 << 28)
bicne r1, r1, #(1 << 29)
str r1, [r0, #EMC_CFG] @ disable DYN_SELF_REF
emc_timing_update r1, r0
@ -660,11 +764,22 @@ emcself:
and r1, r1, r2
str r1, [r0, #EMC_XM2VTTGENPADCTRL]
ldr r1, [r0, #EMC_XM2VTTGENPADCTRL2]
orr r1, r1, #7 @ set E_NO_VTTGEN
cmp r10, #TEGRA30
orreq r1, r1, #7 @ set E_NO_VTTGEN
orrne r1, r1, #0x3f
str r1, [r0, #EMC_XM2VTTGENPADCTRL2]
emc_timing_update r1, r0
/* Tegra114 had dual EMC channel, now config the other one */
cmp r10, #TEGRA114
bne no_dual_emc_chanl
mov32 r1, TEGRA_EMC1_BASE
cmp r0, r1
movne r0, r1
bne enter_self_refresh
no_dual_emc_chanl:
ldr r1, [r4, #PMC_CTRL]
tst r1, #PMC_CTRL_SIDE_EFFECT_LP0
bne pmc_io_dpd_skip