ARM: tegra: Redesign Tegra CPU reset handling
Scott Williams [Wed, 6 Jul 2011 01:05:26 +0000 (18:05 -0700)]
- Add a single unified handler for all CPU resets that is copied to
  IRAM.
- Add state information to direct the flow of execution through the
  reset handler based on the reason a CPU was reset.
- Write the EVP CPU reset vector only once per cold/warm boot session.
- Prevent modification of the EVP CPU reset vector in Tegra3.

Bug 786290
Bug 790458

Change-Id: Ica6707f3514986ee914e73a2d9766a4e06ce2d29
Signed-off-by: Scott Williams <scwilliams@nvidia.com>
DW: Split into logical changes
Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com>

Rebase-Id: R7b9859a83717e76c3c083bdde724bd5fef9ce089

arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/cpuidle-t2.c
arch/arm/mach-tegra/headsmp.S
arch/arm/mach-tegra/platsmp.c
arch/arm/mach-tegra/pm-t3.c
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/reset.c
arch/arm/mach-tegra/reset.h
arch/arm/mach-tegra/sleep-t20.S
arch/arm/mach-tegra/sleep.h

index 82c5597..64c81b2 100644 (file)
@@ -50,7 +50,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC)               += pinmux-tegra30-tables.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += tegra30_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += sleep-t30.o
 obj-$(CONFIG_SMP)                      += platsmp.o
-obj-$(CONFIG_SMP)                       += reset.o
+obj-y                                   += reset.o
 obj-y                                   += headsmp.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += headsmp-t3.o
 obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
index 81b75c2..36034a7 100644 (file)
@@ -70,7 +70,6 @@ static inline unsigned int time_to_bin(unsigned int time)
 #ifdef CONFIG_SMP
 
 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 s64 tegra_cpu1_idle_time = LLONG_MAX;
 
@@ -95,8 +94,6 @@ static void tegra2_wake_reset_cpu(int cpu)
 {
        u32 reg;
 
-       writel(virt_to_phys(tegra_secondary_resume), evp_reset);
-
        /* enable cpu clock on cpu */
        reg = readl(clk_rst + 0x4c);
        writel(reg & ~(1 << (8 + cpu)), clk_rst + 0x4c);
index 8b45aaf..6f3f9ed 100644 (file)
@@ -1,13 +1,36 @@
+/*
+ * arch/arm/mach-tegra/headsmp.S
+ *
+ * CPU initialization routines for Tegra SoCs
+ *
+ * Copyright (c) 2009-2011, NVIDIA Corporation.
+ * Copyright (c) 2011 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ *         Gary King <gking@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
 #include <linux/linkage.h>
 #include <linux/init.h>
 
 #include <asm/cache.h>
+#include <asm/page.h>
 
 #include "flowctrl.h"
 #include "iomap.h"
 #include "reset.h"
 #include "sleep.h"
 
+#define DEBUG_CPU_RESET_HANDLER        0       /* Non-zero enables debug code */
+
 #define APB_MISC_GP_HIDREV     0x804
 #define PMC_SCRATCH41  0x140
 
@@ -17,7 +40,6 @@
        __CPUINIT
 
 /*
- * Tegra specific entry point for secondary CPUs.
  *   The secondary kernel init calls v7_flush_dcache_all before it enables
  *   the L1; however, the L1 comes out of reset in an undefined state, so
  *   the clean + invalidate performed by v7_flush_dcache_all causes a bunch
@@ -61,6 +83,13 @@ ENDPROC(v7_invalidate_l1)
 
 
 #ifdef CONFIG_SMP
+/* 
+ *     tegra_secondary_startup
+ *
+ *      Initial secondary processor boot vector; jumps to kernel's
+ *      secondary_startup routine. Used for initial boot and hotplug
+ *      of secondary CPUs.
+ */
 ENTRY(tegra_secondary_startup)
         bl      v7_invalidate_l1
        /* Enable coresight */
@@ -74,13 +103,17 @@ ENDPROC(tegra_secondary_startup)
 /*
  *     tegra_resume
  *
- *       CPU boot vector when restarting the master CPU following
+ *       CPU boot vector when restarting the a CPU following
  *       an LP2 transition. Also branched to by LP0 and LP1 resume after
  *       re-enabling sdram.
  */
 ENTRY(tegra_resume)
-       bl      tegra_invalidate_l1
        bl      tegra_enable_coresite
+       bl      tegra_invalidate_l1
+
+       cpu_id  r0
+       cmp     r0, #0                          @ CPU0?
+       bne     cpu_resume                      @ no
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
        @ Clear the flow controller flags for this CPU.
@@ -126,6 +159,11 @@ ENTRY(__tegra_cpu_reset_handler_start)
        .align L1_CACHE_SHIFT
 ENTRY(__tegra_cpu_reset_handler)
 
+#if DEBUG_CPU_RESET_HANDLER
+       mov32   r0, 0xC5ACCE55
+       mcr     p14, 0, r0, c7, c12, 6          @ Enable CoreSight access
+       b       .
+#endif
        cpsid   aif, 0x13                       @ SVC mode, interrupts disabled
        mrc     p15, 0, r10, c0, c0, 5          @ MPIDR
        and     r10, r10, #0x3                  @ R10 = CPU number
@@ -155,6 +193,35 @@ ENTRY(__tegra_cpu_reset_handler)
 1:
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+       /* 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
+       bx      lr
+__is_not_lp1:
+#endif
+
+       /* Waking up from LP2? */
+       ldr     r9, [r12, #RESET_DATA(MASK_LP2)]
+       tst     r9, r11                         @ if in_lp2
+       beq     __is_not_lp2
+#if defined(CONFIG_SMP) && defined(CONFIG_ARCH_TEGRA_2x_SOC)
+       /* Tegra2 CPU1 LP2 wakeup uses the secondary startup handler */
+       cmp     r10, #1
+       bne     __is_not_lp2
+#endif
+       ldr     lr, [r12, #RESET_DATA(STARTUP_LP2)]
+       cmp     lr, #0
+       bleq    __die                           @ no LP2 startup handler
+       bx      lr
+
+__is_not_lp2:
 #ifdef CONFIG_SMP
        /*
         * Can only be secondary boot (initial or hotplug) but CPU 0
index 553f8fd..28836a5 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/clk/tegra.h>
+#include <linux/cpumask.h>
 
 #include <asm/smp_scu.h>
 
 #include "common.h"
 #include "iomap.h"
 
-#define EVP_CPU_RESET_VECTOR \
-       (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
+bool tegra_all_cpus_booted;
 
-static unsigned int available_cpus(void);
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
-static inline int is_g_cluster_available(unsigned int cpu)
-{ return -EPERM; }
-static inline bool is_cpu_powered(unsigned int cpu)
-{ return true; }
-static inline int power_up_cpu(unsigned int cpu)
-{ return 0; }
+static DECLARE_BITMAP(tegra_cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits);
+#define tegra_cpu_init_map     (*(cpumask_t *)tegra_cpu_init_mask)
 
-/* For Tegra2 use the software-written value of the reset regsiter for status.*/
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+/* For Tegra2 use the software-written value of the reset register for status.*/
 #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
-
 #else
-static int is_g_cluster_available(unsigned int cpu);
-static bool is_cpu_powered(unsigned int cpu);
-static int power_up_cpu(unsigned int cpu);
-
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
 #define CAR_BOND_OUT_V \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
 #define CAR_BOND_OUT_V_CPU_G   (1<<0)
 #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
-
 #endif
 
-extern void tegra_secondary_startup(void);
-
 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
 
-static void __cpuinit tegra_secondary_init(unsigned int cpu)
+static unsigned int available_cpus(void)
 {
+       static unsigned int ncores;
+
+       if (ncores == 0) {
+               ncores = scu_get_core_count(scu_base);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+               if (ncores > 1) {
+                       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+                       ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
+                       BUG_ON((int)ncores <= 0);
+               }
+#endif
+       }
+       return ncores;
 }
 
-static int tegra20_power_up_cpu(unsigned int cpu)
+static int is_g_cluster_available(unsigned int cpu)
 {
-       int status;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+       return -EPERM;
+#else
+       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+       u32 bond_out = readl(CAR_BOND_OUT_V);
 
-       if (is_lp_cluster()) {
-               struct clk *cpu_clk, *cpu_g_clk;
+       /* Does the G CPU complex exist at all? */
+       if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
+           (bond_out & CAR_BOND_OUT_V_CPU_G))
+               return -EPERM;
 
-               /* The G CPU may not be available for a variety of reasons. */
-               status = is_g_cluster_available(cpu);
-               if (status)
-                       goto done;
+       if (cpu >= available_cpus())
+               return -EPERM;
 
-               cpu_clk = tegra_get_clock_by_name("cpu");
-               cpu_g_clk = tegra_get_clock_by_name("cpu_g");
+       /* FIXME: The G CPU can be unavailable for a number of reasons
+        *        (e.g., low battery, over temperature, etc.). Add checks for
+        *        these conditions. */
+       return 0;
+#endif
+}
 
-               /* Switch to G CPU before continuing. */
-               if (!cpu_clk || !cpu_g_clk) {
-                       /* Early boot, clock infrastructure is not initialized
-                          - CPU mode switch is not allowed */
-                       status = -EINVAL;
-               } else
-                       status = clk_set_parent(cpu_clk, cpu_g_clk);
+static void __cpuinit tegra_secondary_init(unsigned int cpu)
+{
+       cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
+       if (!tegra_all_cpus_booted)
+               if (cpumask_equal(tegra_cpu_init_mask, cpu_present_mask))
+                       tegra_all_cpus_booted = true;
+}
 
-               if (status)
-                       goto done;
-       }
+static int tegra20_power_up_cpu(unsigned int cpu)
+{
+       int status;
 
        /* Enable the CPU clock. */
        tegra_enable_cpu_clock(cpu);
@@ -114,10 +125,26 @@ static int tegra30_power_up_cpu(unsigned int cpu)
        int ret, pwrgateid;
        unsigned long timeout;
 
+       BUG_ON(is_lp_cluster());
+
        pwrgateid = tegra_cpu_powergate_id(cpu);
        if (pwrgateid < 0)
                return pwrgateid;
 
+       /* If this cpu has booted this function is entered after
+        * CPU has been already un-gated by flow controller. Wait
+        * for confirmation that cpu is powered and remove clamps.
+        * On first boot entry do not wait - go to direct ungate.
+        */
+       if (cpu_isset(cpu, tegra_cpu_init_map)) {
+               timeout = jiffies + HZ;
+               do {
+                       if (tegra_powergate_is_powered(pwrgateid))
+                               goto remove_clamps;
+                       udelay(10);
+               } while (time_before(jiffies, timeout));
+       }
+
        /* If this is the first boot, toggle powergates directly. */
        if (!tegra_powergate_is_powered(pwrgateid)) {
                ret = tegra_powergate_power_on(pwrgateid);
@@ -133,6 +160,7 @@ static int tegra30_power_up_cpu(unsigned int cpu)
                }
        }
 
+remove_clamps:
        /* CPU partition is powered. Enable the CPU clock. */
        tegra_enable_cpu_clock(cpu);
        udelay(10);
@@ -155,6 +183,8 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
 {
        int status;
 
+       BUG_ON(cpu == smp_processor_id());
+
        /*
         * Force the CPU into reset. The CPU must remain in reset when the
         * flow controller state is cleared (which will cause the flow
@@ -214,114 +244,15 @@ static void __init tegra_smp_init_cpus(void)
 
 static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
 {
-       tegra_cpu_reset_handler_init();
-       scu_enable(scu_base);
-}
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
-
-static bool is_cpu_powered(unsigned int cpu)
-{
-       if (is_lp_cluster())
-               return true;
-       else
-               return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
-}
-
-static int power_up_cpu(unsigned int cpu)
-{
-       int ret;
-       u32 reg;
-       unsigned long timeout;
-
-       BUG_ON(cpu == smp_processor_id());
-       BUG_ON(is_lp_cluster());
-
-       /* If this cpu has booted this function is entered after
-        * CPU has been already un-gated by flow controller. Wait
-        * for confirmation that cpu is powered and remove clamps.
-        * On first boot entry do not wait - go to direct ungate.
-        */
-#if 0 /* FIXME! */
-       if (cpu_isset(cpu,*(cpumask_t*)&tegra_cpu_init_map))
-       {
-               timeout = jiffies + HZ;
-               do {
-                       if (is_cpu_powered(cpu))
-                               goto remove_clamps;
-                       udelay(10);
-               } while (time_before(jiffies, timeout));
-       }
-#endif
-       /* 1'st boot or Flow controller did not work as expected - try directly toggle
-          power gates. Bail out if direct power on also failed */
-       if (!is_cpu_powered(cpu))
-       {
-               ret = tegra_powergate_power_on(TEGRA_CPU_POWERGATE_ID(cpu));
-               if (ret)
-                       goto fail;
-
-               /* Wait for the power to come up. */
-               timeout = jiffies + 10*HZ;
-
-               do {
-                       if (is_cpu_powered(cpu))
-                               goto remove_clamps;
-                       udelay(10);
-               } while (time_before(jiffies, timeout));
-               ret = -ETIMEDOUT;
-               goto fail;
-       }
-
-remove_clamps:
-       /* now CPU is up: enable clock, propagate reset, and remove clamps */
-       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       barrier();
-       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-
-       udelay(10);
-       ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
-fail:
-       return ret;
-}
-
-static int is_g_cluster_available(unsigned int cpu)
-{
-       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
-       u32 bond_out = readl(CAR_BOND_OUT_V);
-
-       /* Does the G CPU complex exist at all? */
-       if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
-           (bond_out & CAR_BOND_OUT_V_CPU_G))
-               return -EPERM;
 
-       if (cpu >= available_cpus())
-               return -EPERM;
-
-       /* FIXME: The G CPU can be unavailable for a number of reasons
-        *        (e.g., low battery, over temperature, etc.). Add checks for
-        *        these conditions. */
-
-       return 0;
-}
-#endif
+       /* Always mark the boot CPU as initialized. */
+       cpumask_set_cpu(0, to_cpumask(tegra_cpu_init_bits));
 
-static unsigned int available_cpus(void)
-{
-       static unsigned int ncores = 0;
+       if (max_cpus == 1)
+               tegra_all_cpus_booted = true;
 
-       if (ncores == 0) {
-               ncores = scu_get_core_count(scu_base);
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
-               if (ncores > 1) {
-                       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
-                       ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
-                       BUG_ON((int)ncores <= 0);
-               }
-#endif
-       }
-       return ncores;
+       tegra_cpu_reset_handler_init();
+       scu_enable(scu_base);
 }
 
 struct smp_operations tegra_smp_ops __initdata = {
index 5419fc7..f847f74 100644 (file)
@@ -32,6 +32,7 @@
 #include "gpio-names.h"
 #include "iomap.h"
 #include "pm.h"
+#include "sleep.h"
 #include "tegra3_emc.h"
 
 #define CAR_CCLK_BURST_POLICY \
index d8ea636..a3998f4 100644 (file)
@@ -59,6 +59,7 @@
 #include "iomap.h"
 #include "pm.h"
 #include "pm-irq.h"
+#include "reset.h"
 #include "sleep.h"
 #include "fuse.h"
 
@@ -90,8 +91,6 @@ static u8 *iram_save;
 static unsigned long iram_save_size;
 static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
 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);
 #endif
 
@@ -369,6 +368,7 @@ static void suspend_cpu_complex(void)
        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 */
@@ -393,6 +393,13 @@ void tegra_clear_cpu_in_lp2(int cpu)
 {
        spin_lock(&tegra_lp2_lock);
        cpumask_clear_cpu(cpu, &tegra_in_lp2);
+
+       /* 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. */
+       *tegra_cpu_lp2_mask = tegra_in_lp2;
+
        spin_unlock(&tegra_lp2_lock);
 }
 
@@ -401,8 +408,14 @@ bool tegra_set_cpu_in_lp2(int cpu)
        bool last_cpu = false;
 
        spin_lock(&tegra_lp2_lock);
-
        cpumask_set_cpu(cpu, &tegra_in_lp2);
+
+       /* 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. */
+       *tegra_cpu_lp2_mask = tegra_in_lp2;
+
        if (cpumask_equal(&tegra_in_lp2, cpu_online_mask))
                last_cpu = true;
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -429,8 +442,6 @@ unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
 
        tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_start);
 
-       writel(virt_to_phys(tegra_resume), evp_reset);
-
        /*
         * We can use clk_get_rate_all_locked() here, because all other cpus
         * are in LP2 state and irqs are disabled
@@ -568,21 +579,8 @@ static void tegra_pm_set(enum tegra_suspend_mode mode)
                writel(0x1, pmc + PMC_DPD_SAMPLE);
                break;
        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);
                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;
        default:
index eaa2431..75ce1f5 100644 (file)
@@ -192,4 +192,10 @@ static inline void tegra_cluster_switch_set_parameters(
 { }
 #endif
 
+#ifdef CONFIG_SMP
+extern bool tegra_all_cpus_booted __read_mostly;
+#else
+#define tegra_all_cpus_booted (true)
+#endif
+
 #endif /* _MACH_TEGRA_PM_H_ */
index e05da7d..853edde 100644 (file)
@@ -26,6 +26,7 @@
 #include "irammap.h"
 #include "reset.h"
 #include "fuse.h"
+#include "sleep.h"
 
 #define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
                                TEGRA_IRAM_RESET_HANDLER_OFFSET)
@@ -69,15 +70,52 @@ static void __init tegra_cpu_reset_handler_enable(void)
        is_enabled = true;
 }
 
-void __init tegra_cpu_reset_handler_init(void)
+#ifdef CONFIG_PM_SLEEP
+static unsigned long cpu_reset_handler_save[TEGRA_RESET_DATA_SIZE];
+
+void tegra_cpu_reset_handler_save(void)
 {
+       unsigned int i;
+       BUG_ON(!is_enabled);
+       for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+               cpu_reset_handler_save[i] =
+                                       tegra_cpu_reset_handler_ptr[i];
+       is_enabled = false;
+}
+
+void tegra_cpu_reset_handler_restore(void)
+{
+       unsigned int i;
+       BUG_ON(is_enabled);
+       for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+               tegra_cpu_reset_handler_ptr[i] =
+                                       cpu_reset_handler_save[i];
+       is_enabled = true;
+}
+#endif
 
+void __init tegra_cpu_reset_handler_init(void)
+{
 #ifdef CONFIG_SMP
        __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
                *((u32 *)cpu_present_mask);
        __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
                virt_to_phys((void *)tegra_secondary_startup);
 #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
+
+       /* Push all of reset handler data out to the L3 memory system. */
+       __cpuc_coherent_kern_range(
+               (unsigned long)&__tegra_cpu_reset_handler_data[0],
+               (unsigned long)&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]);
+
+       outer_clean_range(__pa(&__tegra_cpu_reset_handler_data[0]),
+                         __pa(&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]));
 
        tegra_cpu_reset_handler_enable();
 }
index de88bf8..6d51149 100644 (file)
@@ -29,6 +29,8 @@
 
 #ifndef __ASSEMBLY__
 
+#include <linux/cpumask.h>
+
 extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
 
 void __tegra_cpu_reset_handler_start(void);
@@ -44,7 +46,25 @@ void tegra_secondary_startup(void);
                (__tegra_cpu_reset_handler_end - \
                 __tegra_cpu_reset_handler_start)
 
+#ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp1_map (*(unsigned long *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+               ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \
+                (u32)__tegra_cpu_reset_handler_start))))
+
+#define tegra_cpu_reset_handler_ptr ((u32 *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+               ((u32)__tegra_cpu_reset_handler_data - \
+                (u32)__tegra_cpu_reset_handler_start))))
+
+#define tegra_cpu_lp2_mask ((cpumask_t *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+               ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
+                (u32)__tegra_cpu_reset_handler_start))))
+#endif
+
 void __init tegra_cpu_reset_handler_init(void);
 
+#ifdef CONFIG_PM_SLEEP
+void tegra_cpu_reset_handler_save(void);
+void tegra_cpu_reset_handler_restore(void);
+#endif
 #endif
 #endif
index 908d54a..3e64823 100644 (file)
@@ -189,7 +189,7 @@ ENTRY(tegra2_sleep_wfi)
 
        /*
         * cpu may be reset while in wfi, which will return through
-        * tegra_secondary_resume to cpu_resume to tegra_cpu_resume
+        * tegra_resume to cpu_resume to tegra_cpu_resume
         * or interrupt may wake wfi, which will return here
         * cpu state is unchanged - MMU is on, cache is on, coherency is off
         *
index 599eaf6..e77bcfa 100644 (file)
@@ -102,7 +102,6 @@ void tegra_cpu_wfi(void);
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
 extern void tegra2_iram_start;
 extern void tegra2_iram_end;
-extern void tegra2_lp1_reset;
 int  tegra2_cpu_is_resettable_soon(void);
 void tegra2_cpu_reset(int cpu);
 void tegra2_cpu_set_resettable_soon(void);
@@ -125,13 +124,6 @@ static inline void *tegra_iram_end(void)
 #endif
 }
 
-static inline void *tegra_lp1_reset(void)
-{
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-       return &tegra2_lp1_reset;
-#endif
-}
-
 static inline void tegra_sleep_core(unsigned long v2p)
 {
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -141,7 +133,6 @@ static inline void tegra_sleep_core(unsigned long v2p)
 
 void tegra_sleep_cpu(unsigned long v2p);
 void tegra_resume(void);
-void tegra_secondary_resume(void);
 
 #endif