On Mon, Dec 17, 2012 at 6:30 PM, Joseph Lo <josephl@xxxxxxxxxx> wrote: > The powered-down state of Tegra20 requires power gating both CPU cores. > When the secondary CPU requests to enter powered-down state, it saves > its own contexts and then enters WFI. The Tegra20 had a limition to > power down both CPU cores. The secondary CPU must waits for CPU0 in > powered-down state too. If the secondary CPU be woken up before CPU0 > entering powered-down state, then it needs to restore its CPU states > and waits for next chance. > > Be aware of that, you may see the legacy power state "LP2" in the code > which is exactly the same meaning of "CPU power down". > > Based on the work by: > Colin Cross <ccross@xxxxxxxxxxx> > Gary King <gking@xxxxxxxxxx> > > Signed-off-by: Joseph Lo <josephl@xxxxxxxxxx> > --- > V3: > * dynamic checking of the number of the state counts > * fix the code sequence for aborting cpu_suspend in > tegra20_sleep_cpu_secondary_finish > V2: > * no change > --- > arch/arm/mach-tegra/cpuidle-tegra20.c | 94 ++++++++++++++++++++- > arch/arm/mach-tegra/pm.c | 2 + > arch/arm/mach-tegra/sleep-tegra20.S | 147 +++++++++++++++++++++++++++++++++ > arch/arm/mach-tegra/sleep.h | 23 +++++ > 4 files changed, 261 insertions(+), 5 deletions(-) > > diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c > index d32e8b0..716aef3 100644 > --- a/arch/arm/mach-tegra/cpuidle-tegra20.c > +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c > @@ -22,28 +22,112 @@ > #include <linux/kernel.h> > #include <linux/module.h> > #include <linux/cpuidle.h> > +#include <linux/cpu_pm.h> > +#include <linux/clockchips.h> > > #include <asm/cpuidle.h> > +#include <asm/proc-fns.h> > +#include <asm/suspend.h> > +#include <asm/smp_plat.h> > + > +#include "pm.h" > +#include "sleep.h" > + > +#ifdef CONFIG_PM_SLEEP > +static int tegra20_idle_lp2(struct cpuidle_device *dev, > + struct cpuidle_driver *drv, > + int index); > +#endif > + > +static struct cpuidle_state tegra_idle_states[] = { > + [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), > +#ifdef CONFIG_PM_SLEEP > + [1] = { > + .enter = tegra20_idle_lp2, > + .exit_latency = 5000, > + .target_residency = 10000, > + .power_usage = 0, > + .flags = CPUIDLE_FLAG_TIME_VALID, > + .name = "powered-down", > + .desc = "CPU power gated", > + }, > +#endif > +}; > > static struct cpuidle_driver tegra_idle_driver = { > .name = "tegra_idle", > .owner = THIS_MODULE, > .en_core_tk_irqen = 1, > - .state_count = 1, > - .states = { > - [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), > - }, > }; > > static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); > > +#ifdef CONFIG_PM_SLEEP > +#ifdef CONFIG_SMP > +static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev, > + struct cpuidle_driver *drv, > + int index) > +{ > + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); > + > + cpu_suspend(0, tegra20_sleep_cpu_secondary_finish); > + > + tegra20_cpu_clear_resettable(); > + > + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); > + > + return true; > +} > +#else > +static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev, > + struct cpuidle_driver *drv, > + int index) > +{ > + return true; > +} > +#endif > + > +static int __cpuinit tegra20_idle_lp2(struct cpuidle_device *dev, > + struct cpuidle_driver *drv, > + int index) > +{ > + u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; > + bool entered_lp2 = false; > + > + local_fiq_disable(); > + > + tegra_set_cpu_in_lp2(cpu); > + cpu_pm_enter(); You must check the return value from cpu_pm_enter and synchronize and abort both cpus. > + > + if (cpu == 0) > + cpu_do_idle(); > + else > + entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); > + > + cpu_pm_exit(); > + tegra_clear_cpu_in_lp2(cpu); > + > + local_fiq_enable(); > + > + smp_rmb(); > + > + return entered_lp2 ? index : 0; > +} > +#endif > + > int __init tegra20_cpuidle_init(void) > { > - int ret; > + int ret, i; > unsigned int cpu; > struct cpuidle_device *dev; > struct cpuidle_driver *drv = &tegra_idle_driver; > > + drv->state_count = sizeof(tegra_idle_states) / > + sizeof(struct cpuidle_state); > + for (i = 0; i < drv->state_count; i++) > + memcpy(&drv->states[i], &tegra_idle_states[i], > + sizeof(struct cpuidle_state)); > + > ret = cpuidle_register_driver(&tegra_idle_driver); > if (ret) { > pr_err("CPUidle driver registration failed\n"); Is there a call to cpu_cluster_pm_enter/exit somewhere else? -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html