summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/board-nvodm.c18
-rw-r--r--arch/arm/mach-tegra/cortex_a9_save.S206
-rw-r--r--arch/arm/mach-tegra/power.h9
-rw-r--r--arch/arm/mach-tegra/suspend.c192
4 files changed, 385 insertions, 40 deletions
diff --git a/arch/arm/mach-tegra/board-nvodm.c b/arch/arm/mach-tegra/board-nvodm.c
index 19416f7da720..8d6a1b6b5b36 100644
--- a/arch/arm/mach-tegra/board-nvodm.c
+++ b/arch/arm/mach-tegra/board-nvodm.c
@@ -1320,10 +1320,26 @@ static void __init tegra_setup_suspend(void)
if (!w || !nr_wake)
goto do_register;
+ plat->wake_enb = 0;
+ plat->wake_low = 0;
+ plat->wake_high = 0;
+ plat->wake_any = 0;
+
while (nr_wake--) {
unsigned int pad = w->WakeupPadNumber;
if (pad < ARRAY_SIZE(wakepad_irq) && w->enable)
- enable_irq_wake(wakepad_irq[w->WakeupPadNumber]);
+ enable_irq_wake(wakepad_irq[pad]);
+
+ if (w->enable) {
+ plat->wake_enb |= (1 << pad);
+
+ if (w->Polarity == NvOdmWakeupPadPolarity_Low)
+ plat->wake_low |= (1 << pad);
+ else if (w->Polarity == NvOdmWakeupPadPolarity_High)
+ plat->wake_high |= (1 << pad);
+ else if (w->Polarity == NvOdmWakeupPadPolarity_AnyEdge)
+ plat->wake_any |= (1 << pad);
+ }
w++;
}
diff --git a/arch/arm/mach-tegra/cortex_a9_save.S b/arch/arm/mach-tegra/cortex_a9_save.S
index 4456f68c67d8..6c80e8e28b79 100644
--- a/arch/arm/mach-tegra/cortex_a9_save.S
+++ b/arch/arm/mach-tegra/cortex_a9_save.S
@@ -42,9 +42,18 @@
#define CONTEXT_SIZE_WORDS_SHIFT 7
#define CONTEXT_SIZE_WORDS (1<<CONTEXT_SIZE_WORDS_SHIFT)
+#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 PMC_CTRL 0x0
#define PMC_CTRL_BFI_SHIFT 8
#define PMC_CTRL_BFI_WIDTH 9
+#define PMC_SCRATCH1 0x54
#define PMC_SCRATCH38 0x134
#define CLK_RESET_CCLK_BURST 0x20
@@ -146,6 +155,14 @@
orr \pa, \pa, \tmp
.endm
+.macro emc_device_mask, rd, base
+ ldr \rd, [\base, #EMC_ADR_CFG]
+ tst \rd, #(0x3<<24)
+ moveq \rd, #(0x1<<8) @ just 1 device
+ movne \rd, #(0x3<<8) @ 2 devices
+.endm
+
+
/*
* __cortex_a9_save(unsigned int mode)
*
@@ -481,7 +498,10 @@ __tear_down_master:
* cause the cache state to get invalidated (although LP1 & LP2
* preserve the data in the L2, the control words (L2X0_CTRL,
* L2X0_AUX_CTRL, etc.) need to be cleaned to L3 so that they
- * will be visible on reboot */
+ * will be visible on reboot. skip this for LP0, since the L2 cache
+ * will be shutdown before we reach this point */
+ tst sp, #TEGRA_POWER_EFFECT_LP0
+ bne __l2_clean_done
mov32 r0, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
add r3, r8, #(CONTEXT_SIZE_WORDS*4)
bic r8, r8, #0x1f
@@ -498,6 +518,7 @@ __tear_down_master:
13: ldr r1, [r0, #L2X0_CACHE_SYNC]
tst r1, #1
bne 13b
+__l2_clean_done:
#endif
tst sp, #TEGRA_POWER_SDRAM_SELFREFRESH
@@ -532,7 +553,11 @@ __tear_down_master:
isb
mcr p15, 0, r3, c2, c0, 0 @ TTB 0
isb
- mov32 r3, (IO_IRAM_PHYS) @ __tear_down_master_sdram, in IRAM
+ mov32 r2, __tegra_lp1_reset
+ mov32 r3, __tear_down_master_sdram
+ sub r2, r3, r2
+ mov32 r3, (TEGRA_IRAM_CODE_AREA)
+ add r3, r2, r3
movne pc, r3
ENDPROC(__tear_down_master)
.type __tear_down_master_data, %object
@@ -541,17 +566,164 @@ __tear_down_master_data:
.size __tear_down_master_data, . - __tear_down_master_data
/* START OF ROUTINES COPIED TO IRAM */
- .align 5
+/*
+ * __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_SCRATCH1
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_lp1_reset)
+__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]
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+
+ /* since the optimized settings are still in SDRAM, there is
+ * no need to store them back into the IRAM-local __lp1_pad_area */
+ add r2, pc, #__lp1_pad_area-(.+8)
+padload:ldmia r2!, {r3-r4}
+ cmp r3, #0
+ beq padload_done
+ str r4, [r3]
+ b padload
+padload_done:
+ ldr r2, [r7]
+ add r2, r2, #0x4 @ 4uS delay for DRAM pad restoration
+ wait_until r2, r7, r3
+ add r1, r1, #0xff @ 255uS delay for PLL stabilization
+ wait_until r1, r7, r3
+
+ str r4, [r0, #CLK_RESET_SCLK_BURST]
+ mov32 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]
+
+ emc_device_mask r1, r0
+
+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_SCRATCH1]
+ mov pc, r0
+ENDPROC(__tegra_lp1_reset)
+
+/*
+ * __tear_down_master_sdram
+ *
+ * disables MMU, data cache, and puts SDRAM into self-refresh.
+ * must execute from IRAM.
+ */
+ .align L1_CACHE_SHIFT
__tear_down_master_sdram:
mrc p15, 0, r3, c1, c0, 0
- bic r3, r3, #(1<<0)
- mcr p15, 0, r3, c1, c0, 0 @ disable MMU to prevent page misses
- /* FIXME: save off DDR pad settings, put SDRAM in self refresh,
- * jump to tear_down_master_pll_cpu */
- b .
+ bic r3, r3, #((1<<0) | (1<<2)) @ disable MMU and data-cache
+ mcr p15, 0, r3, c1, c0, 0 @ to avoid page faults & races
+ dsb
+ isb
+
+ 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]
+
+ emc_device_mask r2, r1
+
+emcself:ldr r3, [r1, #EMC_EMC_STATUS]
+ and r3, r3, r2
+ cmp r3, r2
+ bne emcself @ loop until DDR in self-refresh
+
+ add r2, pc, #__lp1_pad_area-(.+8)
+
+padsave:ldm r2, {r0-r1}
+ cmp r0, #0
+ beq padsave_done
+ ldr r3, [r0]
+ str r1, [r0]
+ str r3, [r2, #4]
+ add r2, r2, #8
+ b padsave
+padsave_done:
+
+ ldr r0, [r5, #CLK_RESET_SCLK_BURST]
+ str r0, [r2, #4]
+ dsb
+ b __tear_down_master_pll_cpu
ENDPROC(__tear_down_master_sdram)
- .align 5
+ .align L1_CACHE_SHIFT
+ .type __lp1_pad_area, %object
+__lp1_pad_area:
+ .word TEGRA_APB_MISC_BASE + 0x8c8 /* XM2CFGCPADCTRL */
+ .word 0x8
+ .word TEGRA_APB_MISC_BASE + 0x8cc /* XM2CFGDPADCTRL */
+ .word 0x8
+ .word TEGRA_APB_MISC_BASE + 0x8d0 /* XM2CLKCFGPADCTRL */
+ .word 0x0
+ .word TEGRA_APB_MISC_BASE + 0x8d4 /* XM2COMPPADCTRL */
+ .word 0x8
+ .word TEGRA_APB_MISC_BASE + 0x8d8 /* XM2VTTGENPADCTRL */
+ .word 0x5500
+ .word TEGRA_APB_MISC_BASE + 0x8e4 /* XM2CFGCPADCTRL2 */
+ .word 0x08080040
+ .word TEGRA_APB_MISC_BASE + 0x8e8 /* XM2CFGDPADCTRL2 */
+ .word 0x0
+ .word 0x0 /* end of list */
+ .word 0x0 /* sclk_burst_policy */
+ .size __lp1_pad_area, . - __lp1_pad_area
+
+ .align L1_CACHE_SHIFT
__tear_down_master_pll_cpu:
ldr r0, [r4, #PMC_CTRL]
bfi r0, sp, #PMC_CTRL_BFI_SHIFT, #PMC_CTRL_BFI_WIDTH
@@ -575,9 +747,6 @@ __tear_down_master_pll_cpu:
mov r0, #0
str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
str r0, [r5, #CLK_RESET_SCLK_DIVIDER]
- mov r0, #0 /* burst policy = 32KHz */
- str r0, [r5, #CLK_RESET_SCLK_BURST]
- str r0, [r5, #CLK_RESET_SCLK_DIVIDER]
/* 2 us delay between changing sclk and disabling PLLs */
wait_for_us r1, r7, r9
@@ -585,8 +754,8 @@ __tear_down_master_pll_cpu:
wait_until r1, r7, r9
/* switch to CLKS */
+ mov r0, #0 /* burst policy = 32KHz */
str r0, [r5, #CLK_RESET_SCLK_BURST]
- str r0, [r5, #CLK_RESET_CCLK_BURST]
/* disable PLLP, PLLM, PLLC in LP0 and LP1 states */
ldr r0, [r5, #CLK_RESET_PLLM_BASE]
@@ -641,8 +810,9 @@ __put_cpu_in_reset:
b .
ENDPROC(__put_cpu_in_reset)
-
-/*
- */
-
-/* END OF ROUTINES COPIED TO IRAM */
+/* dummy symbol for end of IRAM */
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_iram_end)
+__tegra_iram_end:
+ b .
+ENDPROC(__tegra_iram_end)
diff --git a/arch/arm/mach-tegra/power.h b/arch/arm/mach-tegra/power.h
index 967ccf9adc8c..78f0c30990fd 100644
--- a/arch/arm/mach-tegra/power.h
+++ b/arch/arm/mach-tegra/power.h
@@ -23,6 +23,8 @@
#ifndef __MACH_TEGRA_POWER_H
#define __MACH_TEGRA_POWER_H
+#include <asm/page.h>
+
#define TEGRA_POWER_SDRAM_SELFREFRESH 0x400 /* SDRAM is in self-refresh */
#define TEGRA_POWER_PWRREQ_POLARITY 0x1 /* core power request polarity */
@@ -36,6 +38,9 @@
#define TEGRA_POWER_PMC_SHIFT 8
#define TEGRA_POWER_PMC_MASK 0x1ff
+/* layout of IRAM used for LP1 save & restore */
+#define TEGRA_IRAM_CODE_AREA TEGRA_IRAM_BASE + SZ_4K
+#define TEGRA_IRAM_CODE_SIZE SZ_4K
#ifndef __ASSEMBLY__
void tegra_lp2_set_trigger(unsigned long cycles);
@@ -47,6 +52,10 @@ struct tegra_suspend_platform_data {
unsigned long cpu_off_timer; /* CPU power off time us, LP2/LP1 */
unsigned long core_timer; /* core power good time in ticks, LP0 */
unsigned long core_off_timer; /* core power off time ticks, LP0 */
+ unsigned long wake_enb; /* mask of enabled wake pads */
+ unsigned long wake_high; /* high-level-triggered wake pads */
+ unsigned long wake_low; /* low-level-triggered wake pads */
+ unsigned long wake_any; /* any-edge-triggered wake pads */
bool dram_suspend; /* platform supports DRAM self-refresh */
bool core_off; /* platform supports core voltage off */
bool corereq_high; /* Core power request active-high */
diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c
index 9cdd805709d8..e99dda3a3b65 100644
--- a/arch/arm/mach-tegra/suspend.c
+++ b/arch/arm/mach-tegra/suspend.c
@@ -71,6 +71,11 @@ static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE)+0x100;
static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
#define PMC_CTRL 0x0
+#define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
+#define PMC_WAKE_MASK 0xc
+#define PMC_WAKE_LEVEL 0x10
+
+#define PMC_SW_WAKE_STATUS 0x18
#define PMC_COREPWRGOOD_TIMER 0x3c
#define PMC_SCRATCH1 0x54
#define PMC_CPUPWRGOOD_TIMER 0xc8
@@ -257,6 +262,131 @@ unsigned int tegra_suspend_lp2(unsigned int us)
}
#ifdef CONFIG_PM
+
+/* ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain */
+static void pmc_32kwritel(u32 val, unsigned long offs)
+{
+ writel(val, pmc + offs);
+ wmb();
+ udelay(130);
+}
+
+static void tegra_setup_wakepads(bool lp0_ok)
+{
+ u32 temp, status, lvl;
+
+ /* wakeup by interrupt, nothing to do here */
+ if (!lp0_ok)
+ return;
+
+ pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
+ temp = readl(pmc + PMC_CTRL);
+ temp |= PMC_CTRL_LATCH_WAKEUPS;
+ pmc_32kwritel(0, PMC_CTRL);
+ temp &= ~PMC_CTRL_LATCH_WAKEUPS;
+ pmc_32kwritel(0, PMC_CTRL);
+
+ status = readl(pmc + PMC_SW_WAKE_STATUS);
+ lvl = readl(pmc + PMC_WAKE_LEVEL);
+
+ /* flip the wakeup trigger for any-edge triggered pads
+ * which are currently asserting as wakeups */
+ status &= pdata->wake_any;
+ lvl &= ~pdata->wake_low;
+ lvl |= pdata->wake_high;
+ lvl ^= status;
+
+ writel(lvl, PMC_WAKE_LEVEL);
+ writel(pdata->wake_enb, PMC_WAKE_MASK);
+}
+
+extern void __tegra_lp1_reset(void);
+extern void __tegra_iram_end(void);
+
+static u8 *iram_save = NULL;
+static unsigned int iram_save_size = 0;
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+
+static void tegra_suspend_dram(bool lp0_ok)
+{
+ static unsigned long cpu_timer_32k = 0;
+ unsigned int lp2_timer;
+ unsigned int mode = TEGRA_POWER_SDRAM_SELFREFRESH;
+ unsigned long orig;
+
+ orig = readl(evp_reset);
+ /* copy the reset vector and SDRAM shutdown code into IRAM */
+ memcpy(iram_save, iram_code, iram_save_size);
+ memcpy(iram_code, (void *)__tegra_lp1_reset, iram_save_size);
+
+ if (!cpu_timer_32k) {
+ unsigned long long temp = 32768ull*pdata->cpu_timer + 999999;
+ do_div(temp, 1000000ul);
+ cpu_timer_32k = temp;
+ }
+
+ if (!lp0_ok) {
+ writel(TEGRA_IRAM_CODE_AREA, evp_reset);
+ NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP1);
+ lp2_timer = readl(pmc + PMC_CPUPWRGOOD_TIMER);
+ writel(cpu_timer_32k, pmc + PMC_CPUPWRGOOD_TIMER);
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ if (pdata->core_off)
+ mode |= TEGRA_POWER_SYSCLK_OE;
+ } else {
+ NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP0);
+ set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer);
+ mode |= TEGRA_POWER_SYSCLK_OE;
+ mode |= TEGRA_POWER_PWRREQ_OE;
+ mode |= TEGRA_POWER_EFFECT_LP0;
+ }
+
+ if (!pdata->corereq_high)
+ mode |= TEGRA_POWER_PWRREQ_POLARITY;
+ if (!pdata->sysclkreq_high)
+ mode |= TEGRA_POWER_SYSCLK_POLARITY;
+
+ /* for platforms where the core & CPU power requests are combined as
+ * a single request to the PMU, transition from the current state to
+ * the desired state by temporarily enabling both requests (if the
+ * previous state does not match the new state)
+ */
+
+ if (!pdata->separate_req) {
+ u32 reg = readl(pmc + PMC_CTRL);
+ reg |= ((mode & TEGRA_POWER_PMC_MASK) << TEGRA_POWER_PMC_SHIFT);
+ pmc_32kwritel(reg, PMC_CTRL);
+ }
+
+ tegra_setup_wakepads(lp0_ok);
+ suspend_cpu_complex();
+ flush_cache_all();
+ outer_shutdown();
+
+ __cortex_a9_save(mode);
+ restore_cpu_complex();
+
+ writel(orig, evp_reset);
+ outer_restart();
+
+ /* restore to CPU power request, assuming that the next transition
+ * will be into LP2 */
+ if (lp0_ok && !pdata->separate_req) {
+ u32 reg = readl(pmc + PMC_CTRL);
+ mode &= TEGRA_POWER_PMC_MASK;
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ reg |= (mode << TEGRA_POWER_PMC_SHIFT);
+ pmc_32kwritel(reg, PMC_CTRL);
+ reg &= ~(TEGRA_POWER_PWRREQ_OE << TEGRA_POWER_PMC_SHIFT);
+ writel(reg, pmc + PMC_CTRL);
+ } else if (!lp0_ok)
+ writel(lp2_timer, pmc + PMC_CPUPWRGOOD_TIMER);
+
+ memcpy(iram_code, iram_save, iram_save_size);
+ wmb();
+}
+
static int tegra_suspend_prepare_late(void)
{
#ifdef CONFIG_TEGRA_NVRM
@@ -397,14 +527,17 @@ static int tegra_suspend_enter(suspend_state_t state)
int irq;
local_irq_save(flags);
- tegra_irq_suspend();
- tegra_dma_suspend();
- tegra_pinmux_suspend();
- tegra_gpio_suspend();
- tegra_clk_suspend();
- mc_data[0] = readl(mc + MC_SECURITY_START);
- mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+ if (pdata->core_off) {
+ tegra_irq_suspend();
+ tegra_dma_suspend();
+ tegra_pinmux_suspend();
+ tegra_gpio_suspend();
+ tegra_clk_suspend();
+
+ mc_data[0] = readl(mc + MC_SECURITY_START);
+ mc_data[1] = readl(mc + MC_SECURITY_SIZE);
+ }
for_each_irq_desc(irq, desc) {
if ((desc->status & IRQ_WAKEUP) &&
@@ -413,9 +546,12 @@ static int tegra_suspend_enter(suspend_state_t state)
}
}
- /* lie about the power state so that the RM restarts DVFS */
- NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP1);
- tegra_suspend_lp2(0);
+ if (!pdata->dram_suspend || !iram_save) {
+ /* lie about the power state so that the RM restarts DVFS */
+ NvRmPrivPowerSetState(s_hRmGlobal, NvRmPowerState_LP1);
+ tegra_suspend_lp2(0);
+ } else
+ tegra_suspend_dram(pdata->core_off);
for_each_irq_desc(irq, desc) {
if ((desc->status & IRQ_WAKEUP) &&
@@ -424,14 +560,16 @@ static int tegra_suspend_enter(suspend_state_t state)
}
}
- writel(mc_data[0], mc + MC_SECURITY_START);
- writel(mc_data[1], mc + MC_SECURITY_SIZE);
-
- tegra_clk_resume();
- tegra_gpio_resume();
- tegra_pinmux_resume();
- tegra_dma_resume();
- tegra_irq_resume();
+ if (pdata->core_off) {
+ writel(mc_data[0], mc + MC_SECURITY_START);
+ writel(mc_data[1], mc + MC_SECURITY_SIZE);
+
+ tegra_clk_resume();
+ tegra_gpio_resume();
+ tegra_pinmux_resume();
+ tegra_dma_resume();
+ tegra_irq_resume();
+ }
local_irq_restore(flags);
@@ -446,12 +584,24 @@ static struct platform_suspend_ops tegra_suspend_ops = {
};
#endif
+extern void __init lp0_suspend_init(void);
+
void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
{
tegra_pclk = clk_get_sys(NULL, "pclk");
BUG_ON(!tegra_pclk);
pdata = plat;
+ iram_save_size = (unsigned long)__tegra_iram_end;
+ iram_save_size -= (unsigned long)__tegra_lp1_reset;
+
+ iram_save = kmalloc(iram_save_size, GFP_KERNEL);
+ if (!iram_save) {
+ pr_err("%s: unable to allocate memory for SDRAM self-refresh "
+ "LP0/LP1 unavailable\n", __func__);
+ }
+ writel(virt_to_phys(tegra_lp2_startup), pmc + PMC_SCRATCH1);
+
if (pdata->core_off) {
u32 reg = 0, mode;
@@ -470,11 +620,11 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
/* configure output inverters while the request is tristated */
reg |= (mode << TEGRA_POWER_PMC_SHIFT);
- writel(reg, pmc + PMC_CTRL);
- wmb();
- udelay(2000); /* 32KHz domain delay */
+ pmc_32kwritel(reg, PMC_CTRL);
reg |= (TEGRA_POWER_SYSCLK_OE << TEGRA_POWER_PMC_SHIFT);
writel(reg, pmc + PMC_CTRL);
+
+ lp0_suspend_init();
}
#ifdef CONFIG_PM
suspend_set_ops(&tegra_suspend_ops);