Skip to content

Commit

Permalink
ARM: tegra: add LP1 suspend support for Tegra30
Browse files Browse the repository at this point in the history
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, 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 <scwilliams@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
  • Loading branch information
Joseph Lo authored and Stephen Warren committed Aug 12, 2013
1 parent 95872f4 commit e7a932b
Show file tree
Hide file tree
Showing 7 changed files with 560 additions and 4 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-tegra/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions arch/arm/mach-tegra/pm-tegra30.c
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions arch/arm/mach-tegra/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,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;

Expand All @@ -222,6 +231,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;

Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mach-tegra/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit e7a932b

Please sign in to comment.