From: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Subject: powerpc/cpuidle: Use pm_idle to call on to cpuidle framework.
Patch-mainline: 3.3
References: fate#314019,bnc#795230
Git-commit: 707827f3387d9b260d50fa697885a4042cea3bf4

[The git-commit above is Deepthi's rewrite. This patch is the SLE11 backport]

This patch is an essential enabler that is needed to get
the current mainline pseries cpuidle code working on 3.0 kernel.

As 3.0 kernel does not have cpuidle framework modification
patches listed as follows:

cpuidle framework commits in 3.2 mainline kernel:

-----------------------------------------------------
[1] 46bcfad7 cpuidle: Single/Global registration of idle states
[2] 4202735e cpuidle: Split cpuidle_state structure and move per-cpu
  statistics fields
[3] b25edc42 cpuidle: Remove CPUIDLE_FLAG_IGNORE and dev->prepare()
[4] e978aa7d7 cpuidle: Move dev->last_residency update to driver enter
  routine; remove dev->last_state

Cpuidle framework commits in mainline 3.1:
-------------------------------------------------
[1] 62027aea  cpuidle: create bootparam "cpuidle.off=1"
[2] d91ee586 cpuidle: replace xen access to x86 pm_idle and default_idle
[3] a0bfa1373 cpuidle: stop depending on pm_idle

It becomes essential for the pseries cpuidle
driver to adhere to the old framework which has a global
pointer called pm_idle, which calls on to the cpuidle
framework on all architectures.
Also, on the 3.0 kernel the various idle routines
returned the time spent by the cpu in that idle state routine
rather than state along with per-cpu registration of idle
states.
All of it modifications have been applied to the latest
pseries idle code without hampering the functionality
as part of this patch.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Acked-by: Torsten Duwe <duwe@suse.de>
---

 arch/powerpc/platforms/pseries/processor_idle.c |   91 +++++++++--------------
 arch/powerpc/platforms/pseries/setup.c          |   22 ++++--
 2 files changed, 51 insertions(+), 62 deletions(-)


Index: linux-3.0.53-67/arch/powerpc/platforms/pseries/processor_idle.c
===================================================================
--- linux-3.0.53-67.orig/arch/powerpc/platforms/pseries/processor_idle.c	2012-12-19 13:54:41.000000000 +0530
+++ linux-3.0.53-67/arch/powerpc/platforms/pseries/processor_idle.c	2012-12-19 13:54:42.000000000 +0530
@@ -54,11 +54,11 @@
 }
 
 static int snooze_loop(struct cpuidle_device *dev,
-			struct cpuidle_driver *drv,
-			int index)
+			struct cpuidle_state *state)
 {
 	unsigned long in_purr;
 	ktime_t kt_before;
+	s64 usec_delta;
 	int cpu = dev->cpu;
 
 	idle_loop_prolog(&in_purr, &kt_before);
@@ -75,17 +75,16 @@
 	clear_thread_flag(TIF_POLLING_NRFLAG);
 	smp_mb();
 
-	dev->last_residency =
-		(int)idle_loop_epilog(in_purr, kt_before);
-	return index;
+	usec_delta = idle_loop_epilog(in_purr, kt_before);
+	return usec_delta;
 }
 
 static int dedicated_cede_loop(struct cpuidle_device *dev,
-				struct cpuidle_driver *drv,
-				int index)
+				struct cpuidle_state *state)
 {
 	unsigned long in_purr;
 	ktime_t kt_before;
+	s64 usec_delta;
 
 	idle_loop_prolog(&in_purr, &kt_before);
 	get_lppaca()->donate_dedicated_cpu = 1;
@@ -95,17 +94,16 @@
 	cede_processor();
 
 	get_lppaca()->donate_dedicated_cpu = 0;
-	dev->last_residency =
-		(int)idle_loop_epilog(in_purr, kt_before);
-	return index;
+	usec_delta = idle_loop_epilog(in_purr, kt_before);
+	return usec_delta;
 }
 
 static int shared_cede_loop(struct cpuidle_device *dev,
-			struct cpuidle_driver *drv,
-			int index)
+			struct cpuidle_state *state)
 {
 	unsigned long in_purr;
 	ktime_t kt_before;
+	s64 usec_delta;
 
 	idle_loop_prolog(&in_purr, &kt_before);
 
@@ -118,9 +116,8 @@
 	 */
 	cede_processor();
 
-	dev->last_residency =
-		(int)idle_loop_epilog(in_purr, kt_before);
-	return index;
+	usec_delta = idle_loop_epilog(in_purr, kt_before);
+	return usec_delta;
 }
 
 /*
@@ -158,19 +155,23 @@
 
 void update_smt_snooze_delay(int cpu, int residency)
 {
-	struct cpuidle_driver *drv = cpuidle_get_driver();
-	struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+	struct cpuidle_device *dev;
 
 	if (cpuidle_state_table != dedicated_states)
 		return;
 
 	if (residency < 0) {
+		dev = per_cpu_ptr(pseries_cpuidle_devices, cpu);
 		/* Disable the Nap state on that cpu */
 		if (dev)
-			dev->states_usage[1].disable = 1;
-	} else
-		if (drv)
-			drv->states[1].target_residency = residency;
+			dev->states[1].flags |= CPUIDLE_FLAG_IGNORE;
+	} else {
+		dev = per_cpu_ptr(pseries_cpuidle_devices, cpu);
+		if (dev) {
+			dev->states[1].flags ^= CPUIDLE_FLAG_IGNORE;
+			dev->states[1].target_residency = residency;
+		}
+	}
 }
 
 static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
@@ -207,34 +208,6 @@
 	.notifier_call = pseries_cpuidle_add_cpu_notifier,
 };
 
-/*
- * pseries_cpuidle_driver_init()
- */
-static int pseries_cpuidle_driver_init(void)
-{
-	int idle_state;
-	struct cpuidle_driver *drv = &pseries_idle_driver;
-
-	drv->state_count = 0;
-
-	for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
-
-		if (idle_state > max_idle_state)
-			break;
-
-		/* is the state not enabled? */
-		if (cpuidle_state_table[idle_state].enter == NULL)
-			continue;
-
-		drv->states[drv->state_count] =	/* structure copy */
-			cpuidle_state_table[idle_state];
-
-		drv->state_count += 1;
-	}
-
-	return 0;
-}
-
 /* pseries_idle_devices_uninit(void)
  * unregister cpuidle devices and de-allocate memory
  */
@@ -257,8 +230,7 @@
  */
 static int pseries_idle_devices_init(void)
 {
-	int i;
-	struct cpuidle_driver *drv = &pseries_idle_driver;
+	int i, state;
 	struct cpuidle_device *dev;
 
 	pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
@@ -267,7 +239,20 @@
 
 	for_each_possible_cpu(i) {
 		dev = per_cpu_ptr(pseries_cpuidle_devices, i);
-		dev->state_count = drv->state_count;
+		dev->state_count = 0;
+		for (state = 0; state < MAX_IDLE_STATE_COUNT; ++state) {
+			if (state > max_idle_state)
+				break;
+
+			/* Is the state not enabled? */
+			if (cpuidle_state_table[state].enter == NULL)
+				continue;
+
+			dev->states[dev->state_count] = /* structure copy */
+					cpuidle_state_table[state];
+			dev->state_count += 1;
+		}
+
 		dev->cpu = i;
 		if (cpuidle_register_device(dev)) {
 			printk(KERN_DEBUG \
@@ -275,7 +260,6 @@
 			return -EIO;
 		}
 	}
-
 	return 0;
 }
 
@@ -313,7 +297,6 @@
 	if (retval)
 		return retval;
 
-	pseries_cpuidle_driver_init();
 	retval = cpuidle_register_driver(&pseries_idle_driver);
 	if (retval) {
 		printk(KERN_DEBUG "Registration of pseries driver failed.\n");
Index: linux-3.0.53-67/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- linux-3.0.53-67.orig/arch/powerpc/platforms/pseries/setup.c	2012-12-19 13:54:27.000000000 +0530
+++ linux-3.0.53-67/arch/powerpc/platforms/pseries/setup.c	2012-12-19 13:54:42.000000000 +0530
@@ -351,19 +351,25 @@
 }
 early_initcall(alloc_dispatch_log_kmem_cache);
 
+void default_idle(void)
+{
+	HMT_low();
+	HMT_very_low();
+}
+EXPORT_SYMBOL(default_idle);
+
+void (*pm_idle)(void) = default_idle;
+EXPORT_SYMBOL(pm_idle);
+
 static void pSeries_idle(void)
 {
 	/* This would call on the cpuidle framework, and the back-end pseries
 	 * driver to  go to idle states
 	 */
-	if (cpuidle_idle_call()) {
-		/* On error, execute default handler
-		 * to go into low thread priority and possibly
-		 * low power mode.
-		 */
-		HMT_low();
-		HMT_very_low();
-	}
+	if (pm_idle)
+		pm_idle();
+	else
+		default_idle();
 }
 
 static void __init pSeries_setup_arch(void)
