cpuidle/powernv: Add "Fast-Sleep" CPU idle state
authorPreeti U Murthy <preeti@linux.vnet.ibm.com>
Wed, 26 Feb 2014 00:09:06 +0000 (05:39 +0530)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Wed, 5 Mar 2014 04:57:04 +0000 (15:57 +1100)
Fast sleep is one of the deep idle states on Power8 in which local timers of
CPUs stop. On PowerPC we do not have an external clock device which can
handle wakeup of such CPUs. Now that we have the support in the tick broadcast
framework for archs that do not sport such a device and the low level support
for fast sleep, enable it in the cpuidle framework on PowerNV.

Signed-off-by: Preeti U Murthy <preeti@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/Kconfig
arch/powerpc/kernel/time.c
drivers/cpuidle/cpuidle-powernv.c

index 957bf344c0f533e4ebf0c6248b3ba02caf3d8595..b84142000a4d2c0dff5a76a42f38c3aa6b77e663 100644 (file)
@@ -130,6 +130,8 @@ config PPC
        select GENERIC_CMOS_UPDATE
        select GENERIC_TIME_VSYSCALL_OLD
        select GENERIC_CLOCKEVENTS
+       select GENERIC_CLOCKEVENTS_BROADCAST if SMP
+       select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
        select HAVE_MOD_ARCH_SPECIFIC
index df2989b0d4c0997491c67af5d8d645fd61586bde..122a580f732246c02c5e31c301078cb725435919 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/timex.h>
 #include <linux/kernel_stat.h>
 #include <linux/time.h>
+#include <linux/clockchips.h>
 #include <linux/init.h>
 #include <linux/profile.h>
 #include <linux/cpu.h>
@@ -106,7 +107,7 @@ struct clock_event_device decrementer_clockevent = {
        .irq            = 0,
        .set_next_event = decrementer_set_next_event,
        .set_mode       = decrementer_set_mode,
-       .features       = CLOCK_EVT_FEAT_ONESHOT,
+       .features       = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP,
 };
 EXPORT_SYMBOL(decrementer_clockevent);
 
@@ -944,6 +945,7 @@ void __init time_init(void)
        clocksource_init();
 
        init_decrementer_clockevent();
+       tick_setup_hrtimer_broadcast();
 }
 
 
index 78fd174c57e86dfb4608a12f4af44d334ab1da7c..4fb97cef82fde8b115ad3ad1c969186083bbfebd 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/cpuidle.h>
 #include <linux/cpu.h>
 #include <linux/notifier.h>
+#include <linux/clockchips.h>
 
 #include <asm/machdep.h>
 #include <asm/firmware.h>
@@ -49,6 +50,32 @@ static int nap_loop(struct cpuidle_device *dev,
        return index;
 }
 
+static int fastsleep_loop(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv,
+                               int index)
+{
+       unsigned long old_lpcr = mfspr(SPRN_LPCR);
+       unsigned long new_lpcr;
+
+       if (unlikely(system_state < SYSTEM_RUNNING))
+               return index;
+
+       new_lpcr = old_lpcr;
+       new_lpcr &= ~(LPCR_MER | LPCR_PECE); /* lpcr[mer] must be 0 */
+
+       /* exit powersave upon external interrupt, but not decrementer
+        * interrupt.
+        */
+       new_lpcr |= LPCR_PECE0;
+
+       mtspr(SPRN_LPCR, new_lpcr);
+       power7_sleep();
+
+       mtspr(SPRN_LPCR, old_lpcr);
+
+       return index;
+}
+
 /*
  * States for dedicated partition case.
  */
@@ -67,6 +94,13 @@ static struct cpuidle_state powernv_states[] = {
                .exit_latency = 10,
                .target_residency = 100,
                .enter = &nap_loop },
+        { /* Fastsleep */
+               .name = "fastsleep",
+               .desc = "fastsleep",
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 10,
+               .target_residency = 100,
+               .enter = &fastsleep_loop },
 };
 
 static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,