From 30c2639346a3fab2df3eac7ded37404d818802e4 Mon Sep 17 00:00:00 2001
From: Lianwei Wang <lianwei.wang@gmail.com>
Date: Fri, 26 Apr 2013 10:59:24 +0800
Subject: [PATCH] cpuidle: wakeup processor on a smaller latency

Checking the PM-Qos latency and cpu idle sleep latency, and only
wakeup the cpu if the requested PM-Qos latency is smaller than its
idle sleep latency. This can reduce at least 50% cpu wakeup count
on PM-Qos updated.

The PM-Qos is not updated most of time, especially for home idle
case. But for some specific case, the PM-Qos may be updated too
frequently. (E.g. my measurement show that it is changed frequently
between 2us/3us/200us/200s for bootup and usb case.)

The battery current drain is measured from PMIC or battery eliminator.
Although this is just a little saving, it is still reasonable to
improve it.

Change-Id: If564fd0d9c53cf100bd85247bfd509dfeaf54c13
Signed-off-by: Lianwei Wang <lianwei.wang@gmail.com>
---
 drivers/cpuidle/cpuidle.c |   26 +++++++++++++++++++++++++-
 1 files changed, 25 insertions(+), 1 deletions(-)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 2f0083a..e1f618f 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -18,6 +18,7 @@
 #include <linux/ktime.h>
 #include <linux/hrtimer.h>
 #include <linux/module.h>
+#include <linux/tick.h>
 #include <trace/events/power.h>
 
 #include "cpuidle.h"
@@ -462,11 +463,34 @@ static void smp_callback(void *v)
  * requirement.  This means we need to get all processors out of their C-state,
  * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that
  * wakes them all right up.
+ * l - > latency in us
  */
 static int cpuidle_latency_notify(struct notifier_block *b,
 		unsigned long l, void *v)
 {
-	smp_call_function(smp_callback, NULL, 1);
+	cpumask_var_t cpus;
+
+	if (!alloc_cpumask_var(&cpus, GFP_ATOMIC))
+		smp_call_function(smp_callback, NULL, 1);
+	else {
+		int cpu, rcpu = smp_processor_id();
+		s64 s; /* sleep_length in us */
+		struct tick_device *td;
+
+		for_each_online_cpu(cpu) {
+			if (cpu == rcpu)
+				continue;
+			td = tick_get_device(cpu);
+			s = ktime_us_delta(td->evtdev->next_event, ktime_get());
+			if ((long)l < (long)s)
+				cpumask_set_cpu(cpu, cpus);
+		}
+		preempt_disable();
+		smp_call_function_many(cpus, smp_callback, NULL, 1);
+		preempt_enable();
+		free_cpumask_var(cpus);
+	}
+
 	return NOTIFY_OK;
 }
 
-- 
1.7.4.1

