Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d948730 authored by Preeti U Murthy's avatar Preeti U Murthy Committed by Benjamin Herrenschmidt
Browse files

cpuidle/powernv: Add "Fast-Sleep" CPU idle state



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: default avatarPreeti U Murthy <preeti@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 97eb001f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+3 −1
Original line number Diff line number Diff line
@@ -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();
}


+34 −0
Original line number Diff line number Diff line
@@ -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,