ARM: tegra: support LP1 suspend mode
Subsystems (6)
v3 cover ARM: tegra: support LP1 suspend mode
This series adds the support of LP1 suspend mode for Tegra.
V3:
* setting up CCLK burst policy to PLLX in LP1 resume code for Tegra114 too
* moving the CCLKG burst policy resume code to tegra_cpu_car_ops for Tegra114 V2:
* double confirm each patch can be built with all the combination of Tegra SoCs by bisect
* and make sure the function is still OK with above testing
Verified on Seaboard, Cardhu and Dalmore.
And tested with THUMB2_KERNEL as well.
P.S.
A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the root cause of this is about the default settings of EMC registers that cause the DRAM can't leave self-refresh mode immediately. If you want to test with quick LP1 resume on Dalmore, I can provide another HACK for this.
Joseph Lo (8):
ARM: tegra: add common resume handling code for LP1 resuming
ARM: tegra: config the polarity of the request of sys clock
clk: tegra114: add LP1 suspend/resume support
ARM: tegra: add common LP1 suspend support
ARM: tegra: add LP1 suspend support for Tegra30
ARM: tegra: add LP1 suspend support for Tegra20
ARM: tegra: add LP1 suspend support for Tegra114
ARM: tegra: enable LP1 suspend mode
arch/arm/boot/dts/tegra114-dalmore.dts | 2 +- arch/arm/boot/dts/tegra20-colibri-512.dtsi | 2 +- arch/arm/boot/dts/tegra20-harmony.dts | 2 +- arch/arm/boot/dts/tegra20-paz00.dts | 2 +- arch/arm/boot/dts/tegra20-seaboard.dts | 2 +- arch/arm/boot/dts/tegra20-tamonten.dtsi | 2 +- arch/arm/boot/dts/tegra20-trimslice.dts | 2 +- arch/arm/boot/dts/tegra20-ventana.dts | 2 +- arch/arm/boot/dts/tegra20-whistler.dts | 2 +- arch/arm/boot/dts/tegra30-beaver.dts | 2 +- arch/arm/boot/dts/tegra30-cardhu.dtsi | 2 +- arch/arm/mach-tegra/Makefile | 3 + arch/arm/mach-tegra/iomap.h | 8 + arch/arm/mach-tegra/pm-tegra20.c | 34 ++ arch/arm/mach-tegra/pm-tegra30.c | 34 ++ arch/arm/mach-tegra/pm.c | 138 ++++++- arch/arm/mach-tegra/pm.h | 12 + arch/arm/mach-tegra/pmc.c | 37 ++ arch/arm/mach-tegra/pmc.h | 3 + arch/arm/mach-tegra/reset-handler.S | 13 + arch/arm/mach-tegra/reset.c | 2 + arch/arm/mach-tegra/reset.h | 4 + arch/arm/mach-tegra/sleep-tegra20.S | 296 ++++++++++++++ arch/arm/mach-tegra/sleep-tegra30.S | 607 +++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S | 8 +- arch/arm/mach-tegra/sleep.h | 8 + drivers/clk/tegra/clk-tegra114.c | 12 + 27 files changed, 1221 insertions(+), 20 deletions(-) create mode 100644 arch/arm/mach-tegra/pm-tegra20.c create mode 100644 arch/arm/mach-tegra/pm-tegra30.c
Signature · 1 line
1.8.3.4
PATCH v3 1/8 ARM: tegra: add common resume handling code for LP1 resuming
Add support to the Tegra CPU reset vector to detect whether the CPU is resuming from LP1 suspend state. If it is, branch to the LP1-specific resume code.
When Tegra enters the LP1 suspend state, the SDRAM controller is placed into a self-refresh state. For this reason, we must place the LP1 resume code into IRAM, so that it is accessible before SDRAM access has been re-enabled.
---
V3:
* no change
V2:
* update the commit message
* move the define of IRAM_CODE_AREA to iomap.h
* move the THUMB marco code before the 'bx' instruction
--- arch/arm/mach-tegra/iomap.h | 2 ++ arch/arm/mach-tegra/reset-handler.S | 13 +++++++++++++ arch/arm/mach-tegra/reset.c | 2 ++ 3 files changed, 17 insertions(+)
arch/arm/mach-tegra/iomap.h +2 -0
@@ -24,6 +24,8 @@#define TEGRA_IRAM_BASE 0x40000000#define TEGRA_IRAM_SIZE SZ_256K +#define TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) +#define TEGRA_HOST1X_BASE 0x50000000#define TEGRA_HOST1X_SIZE 0x24000
arch/arm/mach-tegra/reset-handler.S +13 -0
@@ -182,6 +182,19 @@ after_errata: 1: #endif + /* Waking up from LP1? */ + ldr r8, [r12, #RESET_DATA(MASK_LP1)] + tst r8, r11 @ if in_lp1 + beq __is_not_lp1 + cmp r10, #0 + bne __die @ only CPU0 can be here + ldr lr, [r12, #RESET_DATA(STARTUP_LP1)] + cmp lr, #0 + bleq __die @ no LP1 startup handler + THUMB( add lr, lr, #1 ) @ switch to Thumb mode + bx lr +__is_not_lp1: + /* Waking up from LP2? */ ldr r9, [r12, #RESET_DATA(MASK_LP2)] tst r9, r11 @ if in_lp2
arch/arm/mach-tegra/reset.c +2 -0
@@ -81,6 +81,8 @@ void __init tegra_cpu_reset_handler_init(void)#endif #ifdef CONFIG_PM_SLEEP + __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1 ] = +TEGRA_IRAM_CODE_AREA ; __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2 ] =virt_to_phys ((void *)tegra_resume);#endif
Signature · 1 line
1.8.3.4
PATCH v3 2/8 ARM: tegra: config the polarity of the request of sys clock
In reply to Joseph LoWhen suspending to LP1 mode, the SYSCLK will be clock gated. And different board may have different polarity of the request of SYSCLK, this patch configure the polarity from the DT for the board.
---
V3:
* no change
V2:
* s/inverts/polarity/
--- arch/arm/mach-tegra/pmc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
arch/arm/mach-tegra/pmc.c +16 -0
@@ -27,6 +27,8 @@#include "pmc.h" #include "sleep.h" +#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10)/* sys clk polarity */ +#define TEGRA_POWER_SYSCLK_OE (1 << 11)/* system clock enable */ #define TEGRA_POWER_EFFECT_LP0 (1 << 14)/* LP0 when CPU pwr gated */ #define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15)/* CPU pwr req polarity */ #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16)/* CPU pwr req enable */
@@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void) reg =tegra_pmc_readl (PMC_CTRL ); reg |=TEGRA_POWER_CPU_PWRREQ_OE ;tegra_pmc_writel (reg,PMC_CTRL ); + + reg =tegra_pmc_readl (PMC_CTRL ); + +if (!pmc_pm_data.sysclkreq_high) + reg |=TEGRA_POWER_SYSCLK_POLARITY ; +else + reg &= ~TEGRA_POWER_SYSCLK_POLARITY ; + +/* configure the output polarity while the request is tristated */ +tegra_pmc_writel (reg,PMC_CTRL ); + +/* now enable the request */ + reg |=TEGRA_POWER_SYSCLK_OE ; + tegra_pmc_writel(reg, PMC_CTRL); }#endif
Signature · 1 line
1.8.3.4
PATCH v3 3/8 clk: tegra114: add LP1 suspend/resume support
In reply to Joseph LoWhen the system suspends to LP1, the CPU clock source is switched to CLK_M (12MHz Oscillator) during suspend/resume flow. The CPU clock source is controlled by the CCLKG_BURST_POLICY register, and hence this register must be restored during LP1 resume.
---
V3:
* move the CCLKG burst policy resume code to tegra_cpu_car_ops V2:
* update the commit message
--- drivers/clk/tegra/clk-tegra114.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/clk/tegra/clk-tegra114.c +12 -0
@@ -293,6 +293,8 @@#ifdef CONFIG_PM_SLEEP static struct cpu_clk_suspend_context {u32 clk_csite_src; +u32 cclkg_burst; +u32 cclkg_divider; } tegra114_cpu_clk_sctx;#endif
@@ -2155,12 +2157,22 @@ static void tegra114_cpu_clock_suspend(void) tegra114_cpu_clk_sctx.clk_csite_src =readl (clk_base +CLK_SOURCE_CSITE );writel (3 <<30 , clk_base +CLK_SOURCE_CSITE ); + + tegra114_cpu_clk_sctx.cclkg_burst = +readl (clk_base +CCLKG_BURST_POLICY ); + tegra114_cpu_clk_sctx.cclkg_divider = + readl(clk_base + CCLKG_BURST_POLICY + 4); }static void tegra114_cpu_clock_resume (void ) {writel (tegra114_cpu_clk_sctx.clk_csite_src, clk_base +CLK_SOURCE_CSITE ); + +writel (tegra114_cpu_clk_sctx.cclkg_burst, + clk_base +CCLKG_BURST_POLICY ); +writel (tegra114_cpu_clk_sctx.cclkg_divider, + clk_base +CCLKG_BURST_POLICY +4 ); }#endif
Signature · 1 line
1.8.3.4
PATCH v3 4/8 ARM: tegra: add common LP1 suspend support
In reply to Joseph LoThe LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are clock gated and SDRAM in self-refresh mode. That means the low level LP1 suspending and resuming code couldn't be run on DRAM and the CPU must switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator. The LP1 low level handling code need to be moved to IRAM area first. And marking the LP1 mask for indicating the Tegra device is in LP1. The CPU power timer needs to be re-calculated based on 32KHz that was originally based on PCLK.
When resuming from LP1, the LP1 reset handler will resume PLLs and then put DRAM to normal mode. Then jumping to the "tegra_resume" that will restore full context before back to kernel. The "tegra_resume" handler was expected to be found in PMC_SCRATCH41 register.
This is common LP1 procedures for Tegra, so we do these jobs mainly in this patch:
* moving LP1 low level handling code to IRAM
* marking LP1 mask
* copying the physical address of "tegra_resume" to PMC_SCRATCH41
* re-calculate the CPU power timer based on 32KHz
---
V3:
* using a "#define" of IRAM_CODE to replace original static variable V2:
* don't duplicate tegraXX_lp1_iram for different SoC
* unify the suspend func call to cpu_suspend with "tegra_sleep_func"
--- arch/arm/mach-tegra/pm.c | 108 ++++++++++++++++++++++++++++++++++++++++++-- arch/arm/mach-tegra/pm.h | 7 +++ arch/arm/mach-tegra/pmc.c | 21 +++++++++ arch/arm/mach-tegra/pmc.h | 3 ++ arch/arm/mach-tegra/reset.h | 4 ++ 5 files changed, 138 insertions(+), 5 deletions(-)
arch/arm/mach-tegra/pm.c +103 -5
@@ -37,12 +37,19 @@#include "reset.h" #include "flowctrl.h" #include "fuse.h" +#include "pm.h" #include "pmc.h" #include "sleep.h" #ifdef CONFIG_PM_SLEEP +#define IRAM_CODE IO_ADDRESS(TEGRA_IRAM_CODE_AREA)static DEFINE_SPINLOCK (tegra_lp2_lock); +static u32 iram_save_size; +static void *iram_save_addr; +struct tegra_lp1_iram tegra_lp1_iram;void (*tegra_tear_down_cpu)(void ); +void (*tegra_sleep_core_finish)(unsigned long v2p); +static int (*tegra_sleep_func)(unsigned long v2p);static void tegra_tear_down_cpu_init (void ) {
@@ -174,14 +181,75 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode( enum tegra_suspend_mode mode) {/* -* The Tegra devices only support suspending to LP2 currently. +* The Tegra devices support suspending to LP1 or lower currently. */ -if (mode >TEGRA_SUSPEND_LP2 ) -return TEGRA_SUSPEND_LP2 ; +if (mode >TEGRA_SUSPEND_LP1 ) +return TEGRA_SUSPEND_LP1 ;return mode; } +static int tegra_sleep_core (unsigned long v2p) +{ +setup_mm_for_reboot (); +tegra_sleep_core_finish (v2p); + +/* should never here */ +BUG (); + +return 0 ; +} + +/* +* tegra_lp1_iram_hook +* +* Hooking the address of LP1 reset vector and SDRAM self-refresh code in +* SDRAM. These codes not be copied to IRAM in this fuction. We need to +* copy these code to IRAM before LP0/LP1 suspend and restore the content +* of IRAM after resume. +*/ +static bool tegra_lp1_iram_hook (void ) +{ +if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr) +return false; + + iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr; + iram_save_addr =kmalloc (iram_save_size,GFP_KERNEL ); +if (!iram_save_addr) +return false; + +return true; +} + +static bool tegra_sleep_core_init (void ) +{ +if (!tegra_sleep_core_finish) +return false; + +return true; +} + +static void tegra_suspend_enter_lp1 (void ) +{ +tegra_pmc_suspend (); + +/* copy the reset vector & SDRAM shutdown code into IRAM */ +memcpy (iram_save_addr,IRAM_CODE , iram_save_size); +memcpy (IRAM_CODE , tegra_lp1_iram.start_addr, iram_save_size); + + *((u32 *)tegra_cpu_lp1_mask) =1 ; +} + +static void tegra_suspend_exit_lp1 (void ) +{ +tegra_pmc_resume (); + +/* restore IRAM */ +memcpy (IRAM_CODE , iram_save_addr, iram_save_size); + + *(u32 *)tegra_cpu_lp1_mask =0 ; +} +static const char *lp_state[TEGRA_MAX_SUSPEND_MODE ] = { [TEGRA_SUSPEND_NONE ] ="none" , [TEGRA_SUSPEND_LP2 ] ="LP2" ,
@@ -205,6 +273,9 @@ static int tegra_suspend_enter(suspend_state_t state)suspend_cpu_complex ();switch (mode) { +case TEGRA_SUSPEND_LP1 : +tegra_suspend_enter_lp1 (); +break ;case TEGRA_SUSPEND_LP2 :tegra_set_cpu_in_lp2 ();break ;
@@ -212,9 +283,12 @@ static int tegra_suspend_enter(suspend_state_t state)break ; } -cpu_suspend (PHYS_OFFSET -PAGE_OFFSET , &tegra_sleep_cpu); +cpu_suspend (PHYS_OFFSET -PAGE_OFFSET , tegra_sleep_func);switch (mode) { +case TEGRA_SUSPEND_LP1 : +tegra_suspend_exit_lp1 (); +break ;case TEGRA_SUSPEND_LP2 :tegra_clear_cpu_in_lp2 ();break ;
@@ -235,12 +309,36 @@ static const struct platform_suspend_ops tegra_suspend_ops = {void __init tegra_init_suspend (void ) { -if (tegra_pmc_get_suspend_mode () ==TEGRA_SUSPEND_NONE ) +enum tegra_suspend_mode mode =tegra_pmc_get_suspend_mode (); + +if (mode ==TEGRA_SUSPEND_NONE )return ;tegra_tear_down_cpu_init ();tegra_pmc_suspend_init (); +if (mode >=TEGRA_SUSPEND_LP1 ) { +if (!tegra_lp1_iram_hook () || !tegra_sleep_core_init ()) { +pr_err ("%s: unable to allocate memory for SDRAM" +"self-refresh -- LP0/LP1 unavailable\n" , + __func__); +tegra_pmc_set_suspend_mode (TEGRA_SUSPEND_LP2 ); + mode =TEGRA_SUSPEND_LP2 ; + } + } + +/* set up sleep function for cpu_suspend */ +switch (mode) { +case TEGRA_SUSPEND_LP1 : + tegra_sleep_func = tegra_sleep_core; +break ; +case TEGRA_SUSPEND_LP2 : + tegra_sleep_func = tegra_sleep_cpu; +break ; +default : +break ; + } +suspend_set_ops (&tegra_suspend_ops); }#endif
arch/arm/mach-tegra/pm.h +7 -0
@@ -23,6 +23,13 @@#include "pmc.h" +struct tegra_lp1_iram { +void *start_addr; +void *end_addr; +}; +extern struct tegra_lp1_iram tegra_lp1_iram; +extern void (*tegra_sleep_core_finish)(unsigned long v2p); +extern unsigned long l2x0_saved_regs_addr;void save_cpu_arch_register (void );
arch/arm/mach-tegra/pmc.c +21 -0
@@ -196,6 +196,24 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)return pmc_pm_data.suspend_mode; } +void tegra_pmc_set_suspend_mode (enum tegra_suspend_mode mode) +{ +if (mode <TEGRA_SUSPEND_NONE || mode >=TEGRA_MAX_SUSPEND_MODE ) +return ; + + pmc_pm_data.suspend_mode = mode; +} + +void tegra_pmc_suspend (void ) +{ +tegra_pmc_writel (virt_to_phys (tegra_resume),PMC_SCRATCH41 ); +} + +void tegra_pmc_resume (void ) +{ +tegra_pmc_writel (0x0 ,PMC_SCRATCH41 ); +} +void tegra_pmc_pm_set (enum tegra_suspend_mode mode) {u32 reg, csr_reg;
@@ -219,6 +237,9 @@ void tegra_pmc_pm_set(enum tegra_suspend_mode mode) }switch (mode) { +case TEGRA_SUSPEND_LP1 : + rate =32768 ; +break ;case TEGRA_SUSPEND_LP2 : rate =clk_get_rate (tegra_pclk);break ;
arch/arm/mach-tegra/pmc.h +3 -0
@@ -28,6 +28,9 @@ enum tegra_suspend_mode {#ifdef CONFIG_PM_SLEEP enum tegra_suspend_mode tegra_pmc_get_suspend_mode (void ); +void tegra_pmc_set_suspend_mode (enum tegra_suspend_mode mode); +void tegra_pmc_suspend (void ); +void tegra_pmc_resume (void );void tegra_pmc_pm_set (enum tegra_suspend_mode mode);void tegra_pmc_suspend_init (void );#endif
arch/arm/mach-tegra/reset.h +4 -0
@@ -39,6 +39,10 @@ void __tegra_cpu_reset_handler_end(void);void tegra_secondary_startup (void );#ifdef CONFIG_PM_SLEEP +#define tegra_cpu_lp1_mask \ + (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ + ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \ + (u32)__tegra_cpu_reset_handler_start)))#define tegra_cpu_lp2_mask \ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
Signature · 1 line
1.8.3.4
PATCH v3 4/8 ARM: tegra: add common LP1 suspend support
Quoted 22 lines · Joseph Lo
The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are clock gated and SDRAM in self-refresh mode. That means the low level LP1 suspending and resuming code couldn't be run on DRAM and the CPU must switch to the always on clock domain (a.k.a.
CLK_M12MHz oscillator). And the system clock (SCLK) would be switched toCLK_S, a 32KHz oscillator. The LP1 low level handling code need to be moved to IRAM area first. And marking the LP1 mask for indicating the Tegra device is in LP1. The CPU power timer needs to be re-calculated based on 32KHz that was originally based on PCLK.When resuming from LP1, the LP1 reset handler will resume PLLs and then put DRAM to normal mode. Then jumping to the "
tegra_resume" that will restore full context before back to kernel. The "tegra_resume" handler was expected to be found inPMC_SCRATCH41register.This is common LP1 procedures for Tegra, so we do these jobs mainly in this patch:
* moving LP1 low level handling code to IRAM
* marking LP1 mask
* copying the physical address of "tegra_resume" toPMC_SCRATCH41
* re-calculate the CPU power timer based on 32KHz
Quoted 4 lines
Signed-off-by Joseph Lo <[email protected]>---
V3:
* using a "#define" ofIRAM_CODEto replace original static variable
I didn't mean change the variable to a define, but rather simply to use the IO_ADDRESS() define instead of any new define. i.e.:
arch/arm/mach-tegra/pm.c line 45
Quoted 3 lines
arch/arm/mach-tegra/pm.c +1
-0+#define IRAM_CODE IO_ADDRESS(TEGRA_IRAM_CODE_AREA)
... just don't add that, and ...
arch/arm/mach-tegra/pm.c enum tegra_suspend_mode tegra_pm_validate_suspend_mode( · line 238
Quoted 7 lines
arch/arm/mach-tegra/pm.c +7
-0+static void tegra_suspend_enter_lp1 (void ) +{ +tegra_pmc_suspend (); + +/* copy the reset vector & SDRAM shutdown code into IRAM */ +memcpy (iram_save_addr,IRAM_CODE , iram_save_size); +memcpy (IRAM_CODE , tegra_lp1_iram.start_addr, iram_save_size);
... and s/IRAM_CODE/IO_ADDRESS(TEGRA_IRAM_CODE_AREA)/ in that code.
I'll make that fixup when applying these patches.
PATCH v3 4/8 ARM: tegra: add common LP1 suspend support
Quoted 47 lines · Stephen Warren
Quoted 22 lines · Joseph Lo
The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are clock gated and SDRAM in self-refresh mode. That means the low level LP1 suspending and resuming code couldn't be run on DRAM and the CPU must switch to the always on clock domain (a.k.a.
CLK_M12MHz oscillator). And the system clock (SCLK) would be switched toCLK_S, a 32KHz oscillator. The LP1 low level handling code need to be moved to IRAM area first. And marking the LP1 mask for indicating the Tegra device is in LP1. The CPU power timer needs to be re-calculated based on 32KHz that was originally based on PCLK.When resuming from LP1, the LP1 reset handler will resume PLLs and then put DRAM to normal mode. Then jumping to the "
tegra_resume" that will restore full context before back to kernel. The "tegra_resume" handler was expected to be found inPMC_SCRATCH41register.This is common LP1 procedures for Tegra, so we do these jobs mainly in this patch:
* moving LP1 low level handling code to IRAM
* marking LP1 mask
* copying the physical address of "tegra_resume" toPMC_SCRATCH41
* re-calculate the CPU power timer based on 32KHzQuoted 4 lines
Signed-off-by Joseph Lo <[email protected]>---
V3:
* using a "#define" ofIRAM_CODEto replace original static variableI didn't mean change the variable to a define, but rather simply to use the
IO_ADDRESS()define instead of any new define. i.e.:arch/arm/mach-tegra/pm.c line 45
Quoted 3 lines
arch/arm/mach-tegra/pm.c +1
-0+#define IRAM_CODE IO_ADDRESS(TEGRA_IRAM_CODE_AREA)... just don't add that, and ...
arch/arm/mach-tegra/pm.c enum tegra_suspend_mode tegra_pm_validate_suspend_mode( · line 238
Quoted 7 lines
arch/arm/mach-tegra/pm.c +7
-0+static void tegra_suspend_enter_lp1 (void ) +{ +tegra_pmc_suspend (); + +/* copy the reset vector & SDRAM shutdown code into IRAM */ +memcpy (iram_save_addr,IRAM_CODE , iram_save_size); +memcpy (IRAM_CODE , tegra_lp1_iram.start_addr, iram_save_size);... and s/IRAM_CODE/IO_ADDRESS(TEGRA_IRAM_CODE_AREA)/ in that code.
Quoted 1 line
I'll make that fixup when applying these patches.
OK. Thanks.
Joseph
PATCH v3 5/8 ARM: tegra: add LP1 suspend support for Tegra30
In reply to Joseph LoThe 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, CCLK burst policy be set in PLLX. 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: Scott Williams <[email protected]>
---
V3:
* remove the no meaning message "AND MUST BE FIRST" V2:
* remove wait_for_us
* using IS_ENABLE for SoC specific code
* calculating "tegra30_sdram_pad_save" by assemby symbol
--- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/pm-tegra30.c | 34 +++ arch/arm/mach-tegra/pm.c | 18 ++ arch/arm/mach-tegra/pm.h | 3 + arch/arm/mach-tegra/sleep-tegra30.S | 492 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S | 8 +- arch/arm/mach-tegra/sleep.h | 8 + 7 files changed, 560 insertions(+), 4 deletions(-) create mode 100644 arch/arm/mach-tegra/pm-tegra30.c
arch/arm/mach-tegra/Makefile +1 -0
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o endif obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o endif
arch/arm/mach-tegra/pm-tegra30.c new file +34 -0
@@ -0,0 +1,34 @@ +/* +* Copyright (c) 2013, NVIDIA Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. +*/ +#include <linux/kernel.h> + +#include "pm.h" + +#ifdef CONFIG_PM_SLEEP +extern u32 tegra30_iram_start, tegra30_iram_end; +extern void tegra30_sleep_core_finish (unsigned long ); + +void tegra30_lp1_iram_hook (void ) +{ + tegra_lp1_iram.start_addr = &tegra30_iram_start; + tegra_lp1_iram.end_addr = &tegra30_iram_end; +} + +void tegra30_sleep_core_init (void ) +{ + tegra_sleep_core_finish = tegra30_sleep_core_finish; +} +#endif
arch/arm/mach-tegra/pm.c +18 -0
@@ -210,6 +210,15 @@ static int tegra_sleep_core(unsigned long v2p)*/ static bool tegra_lp1_iram_hook (void ) { +switch (tegra_chip_id) { +case TEGRA30 : +if (IS_ENABLED (CONFIG_ARCH_TEGRA_3x_SOC)) +tegra30_lp1_iram_hook (); +break ; +default : +break ; + } +if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr)return false;
@@ -223,6 +232,15 @@ static bool tegra_lp1_iram_hook(void)static bool tegra_sleep_core_init (void ) { +switch (tegra_chip_id) { +case TEGRA30 : +if (IS_ENABLED (CONFIG_ARCH_TEGRA_3x_SOC)) +tegra30_sleep_core_init (); +break ; +default : +break ; + } +if (!tegra_sleep_core_finish)return false;
arch/arm/mach-tegra/pm.h +3 -0
@@ -30,6 +30,9 @@ struct tegra_lp1_iram {extern struct tegra_lp1_iram tegra_lp1_iram;extern void (*tegra_sleep_core_finish)(unsigned long v2p); +void tegra30_lp1_iram_hook (void ); +void tegra30_sleep_core_init (void ); +extern unsigned long l2x0_saved_regs_addr;void save_cpu_arch_register (void );
arch/arm/mach-tegra/sleep-tegra30.S +492 -0
521 lines not on this page · raw message
arch/arm/mach-tegra/sleep.S +4 -4
@@ -134,10 +134,10 @@ ENTRY(tegra_shut_off_mmu) #ifdef CONFIG_CACHE_L2X0 /* Disable L2 cache */ check_cpu_part_num 0xc09, r9, r10 - movweq r4, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000) - movteq r4, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000) - moveq r5, #0 - streq r5, [r4, #L2X0_CTRL] + movweq r2, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000) + movteq r2, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000) + moveq r3, #0 + streq r3, [r2, #L2X0_CTRL] #endif mov pc, r0 ENDPROC(tegra_shut_off_mmu)
arch/arm/mach-tegra/sleep.h +8 -0
@@ -46,6 +46,14 @@#define TEGRA_FLUSH_CACHE_ALL 1#ifdef __ASSEMBLY__ +/* waits until the microsecond counter (base) is > rn */ +.macro wait_until, rn, base, tmp + add \rn, \rn, #1 +1001: ldr \tmp, [\base] + cmp \tmp, \rn + bmi 1001b +.endm + /* returns the offset of the flow controller halt register for a cpu */ .macro cpu_to_halt_reg rd, rcpu cmp \rcpu, #0
Signature · 1 line
1.8.3.4
PATCH v3 6/8 ARM: tegra: add LP1 suspend support for Tegra20
In reply to Joseph LoThe 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
* putting SDRAM into self-refresh
* storing some EMC registers and SCLK burst policy
* switching CPU to CLK_M (12MHz OSC)
* switching SCLK to CLK_S (32KHz OSC)
* tunning off PLLM, PLLP and PLLC
* shutting off the CPU rail
The sequence of LP1 resuming:
* re-enabling PLLM, PLLP, and PLLC
* restoring some EMC registers and SCLK burst policy
* setting up CCLK burst policy to PLLP
* 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, CCLK burst policy be set in PLLP. 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:
Colin Cross <[email protected]>
Gary King <[email protected]>
---
V3:
* switch SCLK burst policy reg to CLK_S after disabling PLLs to align with Tegra30/114
V2:
* using IS_ENABLE for SoC specific code
* calculating "tegra20_sdram_pad_save" by assembly symbol
--- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/pm-tegra20.c | 34 +++++ arch/arm/mach-tegra/pm.c | 8 + arch/arm/mach-tegra/pm.h | 2 + arch/arm/mach-tegra/sleep-tegra20.S | 296 ++++++++++++++++++++++++++++++++++++ 5 files changed, 341 insertions(+) create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
arch/arm/mach-tegra/Makefile +1 -0
@@ -17,6 +17,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o endif
arch/arm/mach-tegra/pm-tegra20.c new file +34 -0
@@ -0,0 +1,34 @@ +/* +* Copyright (c) 2013, NVIDIA Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. +*/ +#include <linux/kernel.h> + +#include "pm.h" + +#ifdef CONFIG_PM_SLEEP +extern u32 tegra20_iram_start, tegra20_iram_end; +extern void tegra20_sleep_core_finish (unsigned long ); + +void tegra20_lp1_iram_hook (void ) +{ + tegra_lp1_iram.start_addr = &tegra20_iram_start; + tegra_lp1_iram.end_addr = &tegra20_iram_end; +} + +void tegra20_sleep_core_init (void ) +{ + tegra_sleep_core_finish = tegra20_sleep_core_finish; +} +#endif
arch/arm/mach-tegra/pm.c +8 -0
@@ -211,6 +211,10 @@ static int tegra_sleep_core(unsigned long v2p)static bool tegra_lp1_iram_hook (void ) {switch (tegra_chip_id) { +case TEGRA20 : +if (IS_ENABLED (CONFIG_ARCH_TEGRA_2x_SOC)) +tegra20_lp1_iram_hook (); +break ;case TEGRA30 :if (IS_ENABLED (CONFIG_ARCH_TEGRA_3x_SOC))tegra30_lp1_iram_hook ();
@@ -233,6 +237,10 @@ static bool tegra_lp1_iram_hook(void)static bool tegra_sleep_core_init (void ) {switch (tegra_chip_id) { +case TEGRA20 : +if (IS_ENABLED (CONFIG_ARCH_TEGRA_2x_SOC)) +tegra20_sleep_core_init (); +break ;case TEGRA30 :if (IS_ENABLED (CONFIG_ARCH_TEGRA_3x_SOC))tegra30_sleep_core_init ();
arch/arm/mach-tegra/pm.h +2 -0
@@ -30,6 +30,8 @@ struct tegra_lp1_iram {extern struct tegra_lp1_iram tegra_lp1_iram;extern void (*tegra_sleep_core_finish)(unsigned long v2p); +void tegra20_lp1_iram_hook (void ); +void tegra20_sleep_core_init (void );void tegra30_lp1_iram_hook (void );void tegra30_sleep_core_init (void );
arch/arm/mach-tegra/sleep-tegra20.S +296 -0
322 lines not on this page · raw message
Signature · 1 line
1.8.3.4
PATCH v3 7/8 ARM: tegra: add LP1 suspend support for Tegra114
In reply to Joseph LoThe 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 <[email protected]>
---
V3:
* setting up CCLK burst policy to PLLX in lp1_resume V2:
* using IS_ENABLE for SoC specific code
* moving the fix of PLL lock func to previous patch
--- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/iomap.h | 6 ++ arch/arm/mach-tegra/pm.c | 8 +- arch/arm/mach-tegra/sleep-tegra30.S | 141 ++++++++++++++++++++++++++++++++---- 4 files changed, 141 insertions(+), 15 deletions(-)
arch/arm/mach-tegra/Makefile +1 -0
@@ -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
arch/arm/mach-tegra/iomap.h +6 -0
@@ -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
arch/arm/mach-tegra/pm.c +6 -2
@@ -216,7 +216,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 :
@@ -242,7 +244,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 :
arch/arm/mach-tegra/sleep-tegra30.S +128 -13
257 lines not on this page · raw message
Signature · 1 line
1.8.3.4
PATCH v3 8/8 ARM: tegra: enable LP1 suspend mode
In reply to Joseph LoEnabling the LP1 suspend mode for Tegra devices.
---
V3:
* fix the "Tested-by" line
V2:
* no change
--- arch/arm/boot/dts/tegra114-dalmore.dts | 2 +- arch/arm/boot/dts/tegra20-colibri-512.dtsi | 2 +- arch/arm/boot/dts/tegra20-harmony.dts | 2 +- arch/arm/boot/dts/tegra20-paz00.dts | 2 +- arch/arm/boot/dts/tegra20-seaboard.dts | 2 +- arch/arm/boot/dts/tegra20-tamonten.dtsi | 2 +- arch/arm/boot/dts/tegra20-trimslice.dts | 2 +- arch/arm/boot/dts/tegra20-ventana.dts | 2 +- arch/arm/boot/dts/tegra20-whistler.dts | 2 +- arch/arm/boot/dts/tegra30-beaver.dts | 2 +- arch/arm/boot/dts/tegra30-cardhu.dtsi | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-)
arch/arm/boot/dts/tegra114-dalmore.dts +1 -1
@@ -1024,7 +1024,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <500>; nvidia,cpu-pwr-off-time = <300>; nvidia,core-pwr-good-time = <641 3845>;
arch/arm/boot/dts/tegra20-colibri-512.dtsi +1 -1
@@ -363,7 +363,7 @@ }; pmc { - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <5000>; nvidia,cpu-pwr-off-time = <5000>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-harmony.dts +1 -1
@@ -417,7 +417,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <5000>; nvidia,cpu-pwr-off-time = <5000>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-paz00.dts +1 -1
@@ -417,7 +417,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <2000>; nvidia,cpu-pwr-off-time = <0>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-seaboard.dts +1 -1
@@ -518,7 +518,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <5000>; nvidia,cpu-pwr-off-time = <5000>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-tamonten.dtsi +1 -1
@@ -459,7 +459,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <5000>; nvidia,cpu-pwr-off-time = <5000>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-trimslice.dts +1 -1
@@ -302,7 +302,7 @@ }; pmc { - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <5000>; nvidia,cpu-pwr-off-time = <5000>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-ventana.dts +1 -1
@@ -494,7 +494,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <2000>; nvidia,cpu-pwr-off-time = <100>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra20-whistler.dts +1 -1
@@ -497,7 +497,7 @@ pmc { nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <2000>; nvidia,cpu-pwr-off-time = <1000>; nvidia,core-pwr-good-time = <0 3845>;
arch/arm/boot/dts/tegra30-beaver.dts +1 -1
@@ -262,7 +262,7 @@ pmc { status = "okay"; nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <2000>; nvidia,cpu-pwr-off-time = <200>; nvidia,core-pwr-good-time = <3845 3845>;
arch/arm/boot/dts/tegra30-cardhu.dtsi +1 -1
@@ -314,7 +314,7 @@ pmc { status = "okay"; nvidia,invert-interrupt; - nvidia,suspend-mode = <2>; + nvidia,suspend-mode = <1>; nvidia,cpu-pwr-good-time = <2000>; nvidia,cpu-pwr-off-time = <200>; nvidia,core-pwr-good-time = <3845 3845>;
Signature · 1 line
1.8.3.4
Stephen Warren
Appliedv3 cover ARM: tegra: support LP1 suspend mode
In reply to Joseph LoQuoted 1 line · Joseph Lo
This series adds the support of LP1 suspend mode for Tegra.
I have applied patches 1-7 to Tegra's for-3.12/soc and path 8 to Tegra's for-3.12/dt branch.