#include <linux/cpu_pm.h>
#include <linux/clk/tegra.h>
#include <linux/export.h>
+#include <linux/vmalloc.h>
+#include <linux/memblock.h>
+#include <linux/console.h>
#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/idmap.h>
#include <asm/localtimer.h>
#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <mach/irqs.h>
#include "clock.h"
#include "cpuidle.h"
#include "flowctrl.h"
+#include "fuse.h"
+#include "gic.h"
#include "iomap.h"
#include "pm.h"
#include "pm-irq.h"
+#include "reset.h"
#include "sleep.h"
-#include "fuse.h"
+#include "timer.h"
+#include "reset.h"
struct suspend_context {
/*
u32 mc[3];
u8 uart[5];
+
+ struct tegra_twd_context twd;
};
+#ifdef CONFIG_PM_SLEEP
+#if USE_TEGRA_CPU_SUSPEND
+void *tegra_cpu_context; /* non-cacheable page for CPU context */
+#endif
+phys_addr_t tegra_pgd_phys; /* pgd used by hotplug & LP2 bootup */
+static pgd_t *tegra_pgd;
+static DEFINE_SPINLOCK(tegra_lp2_lock);
+static cpumask_t tegra_in_lp2;
+static cpumask_t *iram_cpu_lp2_mask;
+static unsigned long *iram_cpu_lp1_mask;
static u8 *iram_save;
static unsigned long iram_save_size;
-
-struct suspend_context tegra_sctx;
-
static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
-static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
-#ifdef CONFIG_PM_SLEEP
static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
-static void __iomem *evp_reset =
- IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100;
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+static int tegra_last_pclk;
#endif
+struct suspend_context tegra_sctx;
+
#define TEGRA_POWER_PWRREQ_POLARITY (1 << 8) /* core power request polarity */
#define TEGRA_POWER_PWRREQ_OE (1 << 9) /* core power request enable */
#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */
#define PMC_COREPWRGOOD_TIMER 0x3c
#define PMC_SCRATCH0 0x50
#define PMC_SCRATCH1 0x54
+#define PMC_SCRATCH4 0x60
#define PMC_CPUPWRGOOD_TIMER 0xc8
#define PMC_CPUPWROFF_TIMER 0xcc
#define PMC_COREPWROFF_TIMER PMC_WAKE_DELAY
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+#define PMC_SCRATCH4_WAKE_CLUSTER_MASK (1<<31)
+#endif
+
#define CLK_RESET_CCLK_BURST 0x20
#define CLK_RESET_CCLK_DIVIDER 0x24
#define CLK_RESET_PLLC_BASE 0x80
#define MC_SECURITY_SIZE 0x70
#define MC_SECURITY_CFG2 0x7c
-phys_addr_t tegra_pgd_phys; /* pgd used by hotplug & LP2 bootup */
-static pgd_t *tegra_pgd;
-
-static int tegra_last_pclk;
static struct clk *tegra_pclk;
static const struct tegra_suspend_platform_data *pdata;
static enum tegra_suspend_mode current_suspend_mode = TEGRA_SUSPEND_NONE;
-static DEFINE_SPINLOCK(tegra_lp2_lock);
-static cpumask_t tegra_in_lp2;
-
-static struct kobject *suspend_kobj;
-
static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
[TEGRA_SUSPEND_NONE] = "none",
[TEGRA_SUSPEND_LP2] = "lp2",
[TEGRA_SUSPEND_LP0] = "lp0",
};
-#if INSTRUMENT_CLUSTER_SWITCH
+#if defined(CONFIG_TEGRA_CLUSTER_CONTROL) && INSTRUMENT_CLUSTER_SWITCH
enum tegra_cluster_switch_time_id {
tegra_cluster_switch_time_id_start = 0,
tegra_cluster_switch_time_id_prolog,
#define tegra_cluster_switch_time(flags, id) do {} while(0)
#endif
-static void tegra_suspend_check_pwr_stats(void)
-{
- /* cpus and l2 are powered off later */
- unsigned long pwrgate_partid_mask =
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
- (1 << TEGRA_POWERGATE_HEG) |
- (1 << TEGRA_POWERGATE_SATA) |
- (1 << TEGRA_POWERGATE_3D1) |
-#endif
- (1 << TEGRA_POWERGATE_3D) |
- (1 << TEGRA_POWERGATE_VENC) |
- (1 << TEGRA_POWERGATE_PCIE) |
- (1 << TEGRA_POWERGATE_VDEC) |
- (1 << TEGRA_POWERGATE_MPE);
-
- int partid;
-
- for_each_set_bit(partid, &pwrgate_partid_mask, BITS_PER_LONG)
- if (tegra_powergate_is_powered(partid) == 1)
- pr_warning("partition %s is left on before suspend\n",
- tegra_powergate_get_name(partid));
-
- return;
-}
-
+#ifdef CONFIG_PM_SLEEP
unsigned long tegra_cpu_power_good_time(void)
{
if (WARN_ON_ONCE(!pdata))
return pdata->cpu_lp2_min_residency;
}
-/* 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);
- udelay(130);
-}
-
-static void set_power_timers(unsigned long us_on, unsigned long us_off,
- long rate)
-{
- unsigned long long ticks;
- unsigned long long pclk;
-
- if (WARN_ON_ONCE(rate <= 0))
- pclk = 100000000;
- else
- pclk = rate;
-
- if (rate != tegra_last_pclk) {
- ticks = (us_on * pclk) + 999999ull;
- do_div(ticks, 1000000);
- writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
-
- ticks = (us_off * pclk) + 999999ull;
- do_div(ticks, 1000000);
- writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
- wmb();
- }
- tegra_last_pclk = pclk;
-}
-
/*
* create_suspend_pgtable
*
* Creates a page table with identity mappings of physical memory and IRAM
* for use when the MMU is off, in addition to all the regular kernel mappings.
*/
-static int create_suspend_pgtable(void)
+static __init int create_suspend_pgtable(void)
{
tegra_pgd = pgd_alloc(&init_mm);
if (!tegra_pgd)
return -ENOMEM;
identity_mapping_add(tegra_pgd, phys_to_virt(PHYS_OFFSET),
- IO_IRAM_VIRT, 0);
+ phys_to_virt(PHYS_OFFSET) + memblock_phys_mem_size(), 0);
identity_mapping_add(tegra_pgd, IO_IRAM_VIRT,
IO_IRAM_VIRT + SECTION_SIZE, 0);
- tegra_pgd_phys = virt_to_phys(tegra_pgd);
+ /* inner/outer write-back/write-allocate, sharable */
+ tegra_pgd_phys = (virt_to_phys(tegra_pgd) & PAGE_MASK) | 0x4A;
return 0;
}
-#ifdef CONFIG_SMP
-static int tegra_reset_sleeping_cpu(int cpu)
+/*
+ * alloc_suspend_context
+ *
+ * Allocate a non-cacheable page to hold the CPU contexts.
+ * The standard ARM CPU context save functions don't work if there's
+ * an external L2 cache controller (like a PL310) in system.
+ */
+static __init int alloc_suspend_context(void)
{
- int ret = 0;
-
- BUG_ON(cpu == smp_processor_id());
- tegra_pen_lock();
-
- if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
- tegra_cpu_reset(cpu);
- else
- ret = -EINVAL;
+#if USE_TEGRA_CPU_SUSPEND
+ pgprot_t prot = __pgprot_modify(pgprot_kernel, L_PTE_MT_MASK,
+ L_PTE_MT_BUFFERABLE | L_PTE_XN);
+ struct page *ctx_page;
+ unsigned long ctx_virt = 0;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ ctx_page = alloc_pages(GFP_KERNEL, 0);
+ if (IS_ERR_OR_NULL(ctx_page))
+ goto fail;
+
+ tegra_cpu_context = vm_map_ram(&ctx_page, 1, -1, prot);
+ if (IS_ERR_OR_NULL(tegra_cpu_context))
+ goto fail;
+
+ /* Add the context page to our private pgd. */
+ ctx_virt = (unsigned long)tegra_cpu_context;
+
+ pgd = tegra_pgd + pgd_index(ctx_virt);
+ if (!pgd_present(*pgd))
+ goto fail;
+ pmd = pmd_offset(pgd, ctx_virt);
+ if (!pmd_none(*pmd))
+ goto fail;
+ pte = pte_alloc_kernel(pmd, ctx_virt);
+ if (!pte)
+ goto fail;
+
+ set_pte_ext(pte, mk_pte(ctx_page, prot), 0);
+
+ outer_clean_range(__pa(pmd), __pa(pmd + 1));
- tegra_pen_unlock();
+ return 0;
- return ret;
+fail:
+ if (ctx_page)
+ __free_page(ctx_page);
+ if (ctx_virt)
+ vm_unmap_ram((void*)ctx_virt, 1);
+ tegra_cpu_context = NULL;
+ return -ENOMEM;
+#else
+ return 0;
+#endif
}
-static void tegra_wake_reset_cpu(int cpu)
+/* 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)
{
- u32 reg;
-
- writel(virt_to_phys(tegra_secondary_resume), evp_reset);
+ writel(val, pmc + offs);
+ udelay(130);
+}
- /* enable cpu clock on cpu */
- reg = readl(clk_rst + 0x4c);
- writel(reg & ~(1 << (8 + cpu)), clk_rst + 0x4c);
+static void set_power_timers(unsigned long us_on, unsigned long us_off,
+ long rate)
+{
+ static unsigned long last_us_off = 0;
+ unsigned long long ticks;
+ unsigned long long pclk;
- reg = 0x1111 << cpu;
- writel(reg, clk_rst + 0x344);
+ if (WARN_ON_ONCE(rate <= 0))
+ pclk = 100000000;
+ else
+ pclk = rate;
- /* unhalt the cpu */
- flowctrl_write_cpu_halt(1, 0);
-}
-#else
-static int tegra_reset_sleeping_cpu(int cpu)
-{
- return 0;
-}
+ if ((rate != tegra_last_pclk) || (us_off != last_us_off)) {
+ ticks = (us_on * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER);
-static void tegra_wake_reset_cpu(int cpu)
-{
+ ticks = (us_off * pclk) + 999999ull;
+ do_div(ticks, 1000000);
+ writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER);
+ wmb();
+ }
+ tegra_last_pclk = pclk;
+ last_us_off = us_off;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
/*
* restore_cpu_complex
*
* restores cpu clock setting, clears flow controller
*
- * always called on cpu 0, even when suspend_cpu_complex was called on cpu 1
- * in idle
+ * Always called on CPU 0.
*/
-static void restore_cpu_complex(void)
+static void restore_cpu_complex(u32 mode)
{
+ int cpu = smp_processor_id();
unsigned int reg;
- int i;
- /* restore original burst policy setting */
+ BUG_ON(cpu != 0);
+ /* restore original PLL settings */
writel(tegra_sctx.pllx_misc, clk_rst + CLK_RESET_PLLX_MISC);
writel(tegra_sctx.pllx_base, clk_rst + CLK_RESET_PLLX_BASE);
writel(tegra_sctx.pllp_misc, clk_rst + CLK_RESET_PLLP_MISC);
* by CPU boot-up code - wait for PLL stabilization if PLLX
* was enabled */
- BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) !=
- tegra_sctx.pllx_base);
+ reg = readl(clk_rst + CLK_RESET_PLLX_BASE);
+ /* mask out bit 27 - not to check PLL lock bit */
+ BUG_ON((reg & (~(1 << 27))) !=
+ (tegra_sctx.pllx_base & (~(1 << 27))));
if (tegra_sctx.pllx_base & (1<<30)) {
#if USE_PLL_LOCK_BITS
writel(tegra_sctx.clk_csite_src, clk_rst + CLK_RESET_SOURCE_CSITE);
- /* do not power-gate the CPU when flow controlled */
- for (i = 0; i < num_possible_cpus(); i++) {
- reg = flowctrl_read_cpu_csr(i);
- reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
- reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
- reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
- reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
- reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
- flowctrl_write_cpu_csr(i, reg);
- }
+ /* Do not power-gate CPU 0 when flow controlled */
+ reg = flowctrl_read_cpu_csr(cpu);
+ reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
+ reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
+ reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
+ flowctrl_write_cpu_csr(cpu, reg);
+
+ /* If an immedidate cluster switch is being perfomed, restore the
+ local timer registers. For calls resulting from CPU LP2 in
+ idle or system suspend, the local timer was shut down and
+ timekeeping switched over to the global system timer. In this
+ case keep local timer disabled, and restore only periodic load. */
+ if (!(mode & (TEGRA_POWER_CLUSTER_MASK |
+ TEGRA_POWER_CLUSTER_IMMEDIATE)))
+ tegra_sctx.twd.twd_ctrl = 0;
+ tegra_twd_resume(&tegra_sctx.twd);
}
/*
* saves pll state for use by restart_plls, prepares flow controller for
* transition to suspend state
*
- * in suspend, always called on cpu 0
- * in idle, called on the last cpu to request lp2
+ * Must always be called on cpu 0.
*/
-static void suspend_cpu_complex(void)
+static void suspend_cpu_complex(u32 mode)
{
+ int cpu = smp_processor_id();
unsigned int reg;
int i;
- int cpu = smp_processor_id();
+
+ BUG_ON(cpu != 0);
/* switch coresite to clk_m, save off original source */
tegra_sctx.clk_csite_src = readl(clk_rst + CLK_RESET_SOURCE_CSITE);
tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC);
tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
+ tegra_twd_suspend(&tegra_sctx.twd);
+
reg = flowctrl_read_cpu_csr(cpu);
reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
+ reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
reg |= FLOW_CTRL_CSR_WFE_CPU0 << cpu; /* enable power gating on wfe */
reg |= FLOW_CTRL_CSR_EVENT_FLAG;
reg |= FLOW_CTRL_CSR_INTR_FLAG;
flowctrl_write_cpu_csr(i, reg);
- flowctrl_write_cpu_halt(i, 0);
}
+
+ tegra_gic_cpu_disable();
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Tegra3 enters LPx states via WFI - do not propagate legacy IRQs
+ to CPU core to avoid fall through WFI (IRQ-to-flow controller wake
+ path is not affected). */
+ tegra_gic_pass_through_disable();
+#endif
}
-#ifdef CONFIG_SMP
-int tegra_reset_other_cpus(int cpu)
+void tegra_clear_cpu_in_lp2(int cpu)
{
- int i;
- int abort = -1;
-
- for_each_online_cpu(i) {
- if (i != cpu) {
- if (tegra_reset_sleeping_cpu(i)) {
- abort = i;
- break;
- }
- }
- }
+ spin_lock(&tegra_lp2_lock);
+ BUG_ON(!cpumask_test_cpu(cpu, &tegra_in_lp2));
+ cpumask_clear_cpu(cpu, &tegra_in_lp2);
- if (abort >= 0) {
- for_each_online_cpu(i) {
- if (i != cpu && i < abort)
- tegra_wake_reset_cpu(i);
- }
- return -EINVAL;
- }
+ /* Update the IRAM copy used by the reset handler. The IRAM copy
+ can't use used directly by cpumask_clear_cpu() because it uses
+ LDREX/STREX which requires the addressed location to be inner
+ cacheable and sharable which IRAM isn't. */
+ writel(tegra_in_lp2.bits[0], iram_cpu_lp2_mask);
+ dsb();
- return 0;
-}
-#else
-int tegra_reset_other_cpus(int cpu)
-{
- return 0;
+ spin_unlock(&tegra_lp2_lock);
}
-#endif
-#ifdef CONFIG_SMP
-void tegra_idle_lp2_last(unsigned int flags)
+bool tegra_set_cpu_in_lp2(int cpu)
{
- u32 reg;
- int i;
- int cpu = smp_processor_id();
+ bool last_cpu = false;
+
+ spin_lock(&tegra_lp2_lock);
+ BUG_ON(cpumask_test_cpu(cpu, &tegra_in_lp2));
+ cpumask_set_cpu(cpu, &tegra_in_lp2);
- while (tegra_cpu_is_resettable_soon())
- cpu_relax();
+ /* Update the IRAM copy used by the reset handler. The IRAM copy
+ can't use used directly by cpumask_set_cpu() because it uses
+ LDREX/STREX which requires the addressed location to be inner
+ cacheable and sharable which IRAM isn't. */
+ writel(tegra_in_lp2.bits[0], iram_cpu_lp2_mask);
+ dsb();
- if (tegra_reset_other_cpus(cpu))
- return;
+ if ((cpu == 0) && cpumask_equal(&tegra_in_lp2, cpu_online_mask))
+ last_cpu = true;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ else
+ tegra2_cpu_set_resettable_soon();
+#endif
+
+ spin_unlock(&tegra_lp2_lock);
+ return last_cpu;
+}
+
+unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
+{
+ u32 mode; /* hardware + software power mode flags */
+ unsigned int remain;
/* Only the last cpu down does the final suspend steps */
- reg = readl(pmc + PMC_CTRL);
- reg |= TEGRA_POWER_CPU_PWRREQ_OE;
- reg |= TEGRA_POWER_PWRREQ_OE;
- reg |= flags;
- reg &= ~TEGRA_POWER_EFFECT_LP0;
- pmc_32kwritel(reg, PMC_CTRL);
+ mode = readl(pmc + PMC_CTRL);
+ mode |= TEGRA_POWER_CPU_PWRREQ_OE;
+ mode |= TEGRA_POWER_PWRREQ_OE;
+ mode &= ~TEGRA_POWER_EFFECT_LP0;
+ pmc_32kwritel(mode, PMC_CTRL);
+ mode |= flags;
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_start);
- writel(virt_to_phys(tegra_resume), evp_reset);
-
/*
- * we can use the locked call here, because all other cpus are in reset
- * and irqs are disabled
+ * We can use clk_get_rate_all_locked() here, because all other cpus
+ * are in LP2 state and irqs are disabled
*/
- set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
- clk_get_rate_all_locked(tegra_pclk));
+ if (flags & TEGRA_POWER_CLUSTER_MASK) {
+ set_power_timers(pdata->cpu_timer, 0,
+ clk_get_rate_all_locked(tegra_pclk));
+ tegra_cluster_switch_prolog(mode);
+ } else {
+ set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
+ clk_get_rate_all_locked(tegra_pclk));
+ }
- if (flags & TEGRA_POWER_CLUSTER_MASK)
- tegra_cluster_switch_prolog(reg);
+ if (sleep_time)
+ tegra_lp2_set_trigger(sleep_time);
cpu_cluster_pm_enter();
-
- suspend_cpu_complex();
+ suspend_cpu_complex(mode);
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_prolog);
flush_cache_all();
outer_flush_all();
tegra_sleep_cpu(PHYS_OFFSET - PAGE_OFFSET);
+#ifdef CONFIG_CACHE_L2X0
l2x0_enable();
+#endif
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_switch);
- restore_cpu_complex();
+ restore_cpu_complex(mode);
cpu_cluster_pm_exit();
- if (flags & TEGRA_POWER_CLUSTER_MASK)
- tegra_cluster_switch_epilog(reg);
+ remain = tegra_lp2_timer_remain();
+ if (sleep_time)
+ tegra_lp2_set_trigger(0);
- for_each_online_cpu(i)
- if (i != cpu)
- tegra_wake_reset_cpu(i);
+ if (flags & TEGRA_POWER_CLUSTER_MASK)
+ tegra_cluster_switch_epilog(mode);
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_epilog);
tegra_cluster_switch_times[tegra_cluster_switch_time_id_start]);
}
#endif
-}
-#else
-void tegra_idle_lp2_last(unsigned int flags)
-{
-}
-#endif
-
-void tegra_idle_lp2(void)
-{
- bool last_cpu = false;
- int cpu = smp_processor_id();
-
- spin_lock(&tegra_lp2_lock);
-
- cpumask_set_cpu(cpu, &tegra_in_lp2);
- if (cpumask_equal(&tegra_in_lp2, cpu_online_mask))
- last_cpu = true;
- else
- tegra_cpu_set_resettable_soon();
-
- spin_unlock(&tegra_lp2_lock);
-
- cpu_pm_enter();
-
-#ifdef CONFIG_SMP
- if (last_cpu)
- tegra_idle_lp2_last(0);
- else
-#endif
- tegra_sleep_wfi(PHYS_OFFSET - PAGE_OFFSET);
-
- cpu_pm_exit();
-
- spin_lock(&tegra_lp2_lock);
- cpumask_clear_cpu(cpu, &tegra_in_lp2);
-
- /*
- * cpus coming out of idle muck with page tables that belong to the
- * last process executed before idle. Don't release any cpus back to
- * the scheduler until all cpus have booted to avoid modifying the
- * page table of a running process on another cpu.
- */
- while (!cpumask_empty(&tegra_in_lp2)) {
- spin_unlock(&tegra_lp2_lock);
- cpu_relax();
- spin_lock(&tegra_lp2_lock);
- }
-
- spin_unlock(&tegra_lp2_lock);
+ return remain;
}
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;
}
static void tegra_pm_set(enum tegra_suspend_mode mode)
{
- u32 reg;
+ u32 reg, boot_flag;
unsigned long rate = 32768;
reg = readl(pmc + PMC_CTRL);
reg &= ~TEGRA_POWER_EFFECT_LP0;
switch (mode) {
-#ifdef CONFIG_SMP
case TEGRA_SUSPEND_LP0:
/*
- * lp0 boots through the AVP, which then resumes the AVP to
+ * LP0 boots through the AVP, which then resumes the AVP to
* the address in scratch 39, and the cpu to the address in
* scratch 41 to tegra_resume
*/
writel(0x0, pmc + PMC_SCRATCH39);
- __raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41);
- reg |= TEGRA_POWER_EFFECT_LP0;
/* Enable DPD sample to trigger sampling pads data and direction
* in which pad will be driven during lp0 mode*/
writel(0x1, pmc + PMC_DPD_SAMPLE);
- break;
+
+ /* Set warmboot flag */
+ boot_flag = readl(pmc + PMC_SCRATCH0);
+ pmc_32kwritel(boot_flag | 1, PMC_SCRATCH0);
+
+ pmc_32kwritel(tegra_lp0_vec_start, PMC_SCRATCH1);
+
+ reg |= TEGRA_POWER_EFFECT_LP0;
+ /* No break here. LP0 code falls through to write SCRATCH41 */
case TEGRA_SUSPEND_LP1:
- /*
- * lp1 boots through the normal cpu reset vector pointing to
- * 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 +
- TEGRA_IRAM_CODE_AREA, evp_reset);
__raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41);
+ wmb();
break;
case TEGRA_SUSPEND_LP2:
- /*
- * lp2 boots through the normal cpu reset vector directly to
- * tegra_resume
- */
- writel(virt_to_phys(tegra_resume), evp_reset);
rate = clk_get_rate(tegra_pclk);
break;
-#endif
+ case TEGRA_SUSPEND_NONE:
+ return;
default:
BUG();
}
set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer, rate);
pmc_32kwritel(reg, PMC_CTRL);
-
- /* Set warmboot flag */
- reg = readl(pmc + PMC_SCRATCH0);
- pmc_32kwritel(reg | 1, PMC_SCRATCH0);
-
- pmc_32kwritel(tegra_lp0_vec_start, PMC_SCRATCH1);
}
static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
static int tegra_suspend_enter(suspend_state_t state)
{
- return tegra_suspend_dram(current_suspend_mode);
+ int ret;
+
+ if (pdata && pdata->board_suspend)
+ pdata->board_suspend(current_suspend_mode, TEGRA_SUSPEND_BEFORE_PERIPHERAL);
+
+ ret = tegra_suspend_dram(current_suspend_mode, 0);
+
+ if (pdata && pdata->board_resume)
+ pdata->board_resume(current_suspend_mode, TEGRA_RESUME_AFTER_PERIPHERAL);
+
+ return ret;
+}
+
+static void tegra_suspend_check_pwr_stats(void)
+{
+ /* cpus and l2 are powered off later */
+ unsigned long pwrgate_partid_mask =
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ (1 << TEGRA_POWERGATE_HEG) |
+ (1 << TEGRA_POWERGATE_SATA) |
+ (1 << TEGRA_POWERGATE_3D1) |
+#endif
+ (1 << TEGRA_POWERGATE_3D) |
+ (1 << TEGRA_POWERGATE_VENC) |
+ (1 << TEGRA_POWERGATE_PCIE) |
+ (1 << TEGRA_POWERGATE_VDEC) |
+ (1 << TEGRA_POWERGATE_MPE);
+
+ int partid;
+
+ for_each_set_bit(partid, &pwrgate_partid_mask, BITS_PER_LONG)
+ if (tegra_powergate_is_powered(partid) == 1)
+ pr_warning("partition %s is left on before suspend\n",
+ tegra_powergate_get_name(partid));
+
+ return;
}
-int tegra_suspend_dram(enum tegra_suspend_mode mode)
+int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
{
BUG_ON(mode < 0 || mode >= TEGRA_MAX_SUSPEND_MODE);
tegra_common_suspend();
- pr_info("Entering suspend state %s\n", lp_state[mode]);
-
tegra_pm_set(mode);
+ if (pdata && pdata->board_suspend)
+ pdata->board_suspend(mode, TEGRA_SUSPEND_BEFORE_CPU);
+
local_fiq_disable();
cpu_pm_enter();
cpu_cluster_pm_enter();
- if (mode == TEGRA_SUSPEND_LP0)
+ if (mode == TEGRA_SUSPEND_LP0) {
+#ifdef CONFIG_TEGRA_CLUSTER_CONTROL
+ u32 reg = readl(pmc + PMC_SCRATCH4);
+ if (is_lp_cluster())
+ reg |= PMC_SCRATCH4_WAKE_CLUSTER_MASK;
+ else
+ reg &= (~PMC_SCRATCH4_WAKE_CLUSTER_MASK);
+ pmc_32kwritel(reg, PMC_SCRATCH4);
+#endif
tegra_lp0_suspend_mc();
+ tegra_cpu_reset_handler_save();
+
+ }
+ else if (mode == TEGRA_SUSPEND_LP1)
+ *iram_cpu_lp1_mask = 1;
+
+ suspend_cpu_complex(flags);
- suspend_cpu_complex();
flush_cache_all();
outer_flush_all();
outer_disable();
tegra_init_cache();
- if (mode == TEGRA_SUSPEND_LP0)
+ if (mode == TEGRA_SUSPEND_LP0) {
+ tegra_cpu_reset_handler_restore();
tegra_lp0_resume_mc();
+ } else if (mode == TEGRA_SUSPEND_LP1)
+ *iram_cpu_lp1_mask = 0;
- restore_cpu_complex();
+ restore_cpu_complex(flags);
cpu_cluster_pm_exit();
cpu_pm_exit();
+ if (pdata && pdata->board_resume)
+ pdata->board_resume(mode, TEGRA_RESUME_AFTER_CPU);
+
local_fiq_enable();
tegra_common_resume();
}
static struct kobj_attribute suspend_mode_attribute =
- __ATTR(mode, 0666, suspend_mode_show, suspend_mode_store);
+ __ATTR(mode, 0644, suspend_mode_show, suspend_mode_store);
+
+static struct kobject *suspend_kobj;
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_pm_enter_suspend(void)
+{
+ pr_info("Entering suspend state %s\n", lp_state[current_suspend_mode]);
+ if (current_suspend_mode == TEGRA_SUSPEND_LP0)
+ tegra_lp0_cpu_mode(true);
+ return 0;
+}
+
+static void tegra_pm_enter_resume(void)
+{
+ if (current_suspend_mode == TEGRA_SUSPEND_LP0)
+ tegra_lp0_cpu_mode(false);
+ pr_info("Exited suspend state %s\n", lp_state[current_suspend_mode]);
+}
+
+static struct syscore_ops tegra_pm_enter_syscore_ops = {
+ .suspend = tegra_pm_enter_suspend,
+ .resume = tegra_pm_enter_resume,
+};
+
+static __init int tegra_pm_enter_syscore_init(void)
+{
+ register_syscore_ops(&tegra_pm_enter_syscore_ops);
+ return 0;
+}
+subsys_initcall(tegra_pm_enter_syscore_init);
#endif
void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
(void)reg;
(void)mode;
- preset_lpj = loops_per_jiffy;
-
- create_suspend_pgtable();
+#ifndef CONFIG_PM_SLEEP
+ if (plat->suspend_mode != TEGRA_SUSPEND_NONE) {
+ pr_warning("%s: Suspend requires CONFIG_PM_SLEEP -- "
+ "disabling suspend\n", __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ }
+#else
+ if (create_suspend_pgtable() < 0) {
+ pr_err("%s: PGD memory alloc failed -- LP0/LP1/LP2 unavailable\n",
+ __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ goto fail;
+ }
-#ifdef CONFIG_PM_SLEEP
+ if (alloc_suspend_context() < 0) {
+ pr_err("%s: CPU context alloc failed -- LP0/LP1/LP2 unavailable\n",
+ __func__);
+ plat->suspend_mode = TEGRA_SUSPEND_NONE;
+ goto fail;
+ }
if ((tegra_chip_id == TEGRA30) &&
(tegra_revision == TEGRA_REVISION_A01) &&
"-- disabling LP0\n", __func__);
plat->suspend_mode = TEGRA_SUSPEND_LP1;
}
+ if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && tegra_lp0_vec_size &&
+ tegra_lp0_vec_relocate) {
+ unsigned char *reloc_lp0;
+ unsigned long tmp;
+ void __iomem *orig;
+ reloc_lp0 = kmalloc(tegra_lp0_vec_size + L1_CACHE_BYTES - 1,
+ GFP_KERNEL);
+ WARN_ON(!reloc_lp0);
+ if (!reloc_lp0) {
+ pr_err("%s: Failed to allocate reloc_lp0\n",
+ __func__);
+ goto out;
+ }
+
+ orig = ioremap(tegra_lp0_vec_start, tegra_lp0_vec_size);
+ WARN_ON(!orig);
+ if (!orig) {
+ pr_err("%s: Failed to map tegra_lp0_vec_start %08lx\n",
+ __func__, tegra_lp0_vec_start);
+ kfree(reloc_lp0);
+ goto out;
+ }
+
+ tmp = (unsigned long) reloc_lp0;
+ tmp = (tmp + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1);
+ reloc_lp0 = (unsigned char *)tmp;
+ memcpy(reloc_lp0, orig, tegra_lp0_vec_size);
+ iounmap(orig);
+ tegra_lp0_vec_start = virt_to_phys(reloc_lp0);
+ }
+out:
if (plat->suspend_mode == TEGRA_SUSPEND_LP0 && !tegra_lp0_vec_size) {
pr_warning("%s: Suspend mode LP0 requested, no lp0_vec "
"provided by bootlader -- disabling LP0\n",
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) {
+ if (!iram_save && (plat->suspend_mode >= TEGRA_SUSPEND_LP1)) {
pr_err("%s: unable to allocate memory for SDRAM self-refresh "
"-- LP0/LP1 unavailable\n", __func__);
plat->suspend_mode = TEGRA_SUSPEND_LP2;
pmc_32kwritel(reg, PMC_CTRL);
if (pdata->suspend_mode == TEGRA_SUSPEND_LP0)
- tegra2_lp0_suspend_init();
+ tegra_lp0_suspend_init();
suspend_set_ops(&tegra_suspend_ops);
pr_err("%s: sysfs_create_file suspend type failed!\n",
__func__);
}
-#else
- if ((plat->suspend_mode == TEGRA_SUSPEND_LP0) ||
- (plat->suspend_mode == TEGRA_SUSPEND_LP1)) {
- pr_warning("%s: Suspend mode LP0 or LP1 requires "
- "CONFIG_PM_SLEEP -- limiting to LP2\n", __func__);
- plat->suspend_mode = TEGRA_SUSPEND_LP2;
- }
-#endif
-#ifdef CONFIG_CPU_IDLE
+ iram_cpu_lp2_mask = tegra_cpu_lp2_mask;
+ iram_cpu_lp1_mask = tegra_cpu_lp1_mask;
+fail:
+#endif
if (plat->suspend_mode == TEGRA_SUSPEND_NONE)
tegra_lp2_in_idle(false);
-#endif
current_suspend_mode = plat->suspend_mode;
}
+unsigned long debug_uart_port_base = 0;
+EXPORT_SYMBOL(debug_uart_port_base);
+
static int tegra_debug_uart_suspend(void)
{
void __iomem *uart;
u32 lcr;
- if (TEGRA_DEBUG_UART_BASE == 0)
+ if (!debug_uart_port_base)
return 0;
- uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+ uart = IO_ADDRESS(debug_uart_port_base);
lcr = readb(uart + UART_LCR * 4);
void __iomem *uart;
u32 lcr;
- if (TEGRA_DEBUG_UART_BASE == 0)
+ if (!debug_uart_port_base)
return;
- uart = IO_ADDRESS(TEGRA_DEBUG_UART_BASE);
+ uart = IO_ADDRESS(debug_uart_port_base);
lcr = tegra_sctx.uart[0];
/* DLAB = 0 */
writeb(lcr & ~UART_LCR_DLAB, uart + UART_LCR * 4);
+ writeb(UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_01 | UART_FCR_R_TRIG_01,
+ uart + UART_FCR * 4);
+
writeb(tegra_sctx.uart[2], uart + UART_IER * 4);
/* DLAB = 1 */
.resume = tegra_debug_uart_resume,
};
+struct clk *debug_uart_clk = NULL;
+EXPORT_SYMBOL(debug_uart_clk);
+
+void tegra_console_uart_suspend(void)
+{
+ if (console_suspend_enabled && debug_uart_clk)
+ clk_disable(debug_uart_clk);
+}
+
+void tegra_console_uart_resume(void)
+{
+ if (console_suspend_enabled && debug_uart_clk)
+ clk_enable(debug_uart_clk);
+}
+
static int tegra_debug_uart_syscore_init(void)
{
register_syscore_ops(&tegra_debug_uart_syscore_ops);