From a12f5cc01dd4ed811e5edaca429dcbbb129e0e50 Mon Sep 17 00:00:00 2001
From: Tad <tad@spotco.us>
Date: Sat, 17 Oct 2015 20:52:02 -0400
Subject: [PATCH] Implement Quick Wakeup

---
 arch/arm/configs/cyanogenmod_bacon_defconfig |   2 +
 include/linux/quickwakeup.h                  |  46 ++++++++++++
 kernel/power/Kconfig                         |  20 ++++--
 kernel/power/Makefile                        |   1 +
 kernel/power/quickwakeup.c                   | 104 +++++++++++++++++++++++++++
 5 files changed, 167 insertions(+), 6 deletions(-)
 create mode 100644 include/linux/quickwakeup.h
 create mode 100644 kernel/power/quickwakeup.c

diff --git a/arch/arm/configs/cyanogenmod_bacon_defconfig b/arch/arm/configs/cyanogenmod_bacon_defconfig
index ed05004..86aed0e 100644
--- a/arch/arm/configs/cyanogenmod_bacon_defconfig
+++ b/arch/arm/configs/cyanogenmod_bacon_defconfig
@@ -3629,3 +3629,5 @@ CONFIG_NLATTR=y
 # CONFIG_CORDIC is not set
 CONFIG_QMI_ENCDEC=y
 # CONFIG_QMI_ENCDEC_DEBUG is not set
+
+CONFIG_QUICK_WAKEUP=y
diff --git a/include/linux/quickwakeup.h b/include/linux/quickwakeup.h
new file mode 100644
index 0000000..000effa
--- /dev/null
+++ b/include/linux/quickwakeup.h
@@ -0,0 +1,46 @@
+/* include/linux/quickwakeup.h
+ *
+ * Copyright (C) 2014 Motorola Mobility LLC.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _QUICKWAKEUP_H_
+#define _QUICKWAKEUP_H_
+
+#ifdef __KERNEL__
+
+struct quickwakeup_ops {
+	struct list_head list;
+	char *name;
+	int (*qw_execute)(void *data);
+	int (*qw_check)(void *data);
+	int execute;
+	void *data;  /* arbitrary data passed back to user */
+};
+
+#ifdef CONFIG_QUICK_WAKEUP
+
+int quickwakeup_register(struct quickwakeup_ops *ops);
+void quickwakeup_unregister(struct quickwakeup_ops *ops);
+bool quickwakeup_suspend_again(void);
+
+#else
+
+static inline int quickwakeup_register(struct quickwakeup_ops *ops) { return 0; };
+static inline void quickwakeup_unregister(struct quickwakeup_ops *ops) {};
+static inline bool quickwakeup_suspend_again(void) { return 0; };
+
+#endif /* CONFIG_QUICK_WAKEUP */
+
+#endif /* __KERNEL__ */
+
+#endif /* _QUICKWAKEUP_H_ */
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e536c8d..8006962 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -83,20 +83,20 @@ config PM_STD_PARTITION
 	default ""
 	---help---
 	  The default resume partition is the partition that the suspend-
-	  to-disk implementation will look for a suspended disk image. 
+	  to-disk implementation will look for a suspended disk image.
 
-	  The partition specified here will be different for almost every user. 
+	  The partition specified here will be different for almost every user.
 	  It should be a valid swap partition (at least for now) that is turned
-	  on before suspending. 
+	  on before suspending.
 
 	  The partition specified can be overridden by specifying:
 
-		resume=/dev/<other device> 
+		resume=/dev/<other device>
 
-	  which will set the resume partition to the device specified. 
+	  which will set the resume partition to the device specified.
 
 	  Note there is currently not a way to specify which device to save the
-	  suspended image to. It will simply pick the first available swap 
+	  suspended image to. It will simply pick the first available swap
 	  device.
 
 config PM_SLEEP
@@ -285,6 +285,14 @@ config SUSPEND_TIME
 	  Prints the time spent in suspend in the kernel log, and
 	  keeps statistics on the time spent in suspend in
 	  /sys/kernel/debug/suspend_time
+	  
+config QUICK_WAKEUP
+	bool "Quick wakeup"
+	depends on SUSPEND
+	default n
+	---help---
+	  Allow kernel driver to do periodic jobs without resuming the full system
+	  This option can increase battery life on android powered smartphone.
 
 config DEDUCE_WAKEUP_REASONS
 	bool
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 74c713b..e5bebbc 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_PM_WAKELOCKS)	+= wakelock.o
 obj-$(CONFIG_SUSPEND_TIME)	+= suspend_time.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
+obj-$(CONFIG_QUICK_WAKEUP)	+= quickwakeup.o
 
 obj-$(CONFIG_SUSPEND)	+= wakeup_reason.o
diff --git a/kernel/power/quickwakeup.c b/kernel/power/quickwakeup.c
new file mode 100644
index 0000000..46f9dda
--- /dev/null
+++ b/kernel/power/quickwakeup.c
@@ -0,0 +1,104 @@
+/* kernel/power/quickwakeup.c
+ *
+ * Copyright (C) 2014 Motorola Mobility LLC.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/quickwakeup.h>
+
+static LIST_HEAD(qw_head);
+static DEFINE_MUTEX(list_lock);
+
+int quickwakeup_register(struct quickwakeup_ops *ops)
+{
+	mutex_lock(&list_lock);
+	list_add(&ops->list, &qw_head);
+	mutex_unlock(&list_lock);
+
+	return 0;
+}
+
+void quickwakeup_unregister(struct quickwakeup_ops *ops)
+{
+	mutex_lock(&list_lock);
+	list_del(&ops->list);
+	mutex_unlock(&list_lock);
+}
+
+static int quickwakeup_check(void)
+{
+	int check = 0;
+	struct quickwakeup_ops *index;
+
+	mutex_lock(&list_lock);
+
+	list_for_each_entry(index, &qw_head, list) {
+		int ret = index->qw_check(index->data);
+		index->execute = ret;
+		check |= ret;
+		pr_debug("%s: %s votes for %s\n", __func__, index->name,
+			ret ? "execute" : "dont care");
+	}
+
+	mutex_unlock(&list_lock);
+
+	return check;
+}
+
+/* return 1 => suspend again
+   return 0 => continue wakeup
+ */
+static int quickwakeup_execute(void)
+{
+	int suspend_again = 0;
+	int final_vote = 1;
+	struct quickwakeup_ops *index;
+
+	mutex_lock(&list_lock);
+
+	list_for_each_entry(index, &qw_head, list) {
+		if (index->execute) {
+			int ret = index->qw_execute(index->data);
+			index->execute = 0;
+			final_vote &= ret;
+			suspend_again = final_vote;
+			pr_debug("%s: %s votes for %s\n", __func__, index->name,
+				ret ? "suspend again" : "wakeup");
+		}
+	}
+
+	mutex_unlock(&list_lock);
+
+	pr_debug("%s: %s\n", __func__,
+		suspend_again ? "suspend again" : "wakeup");
+
+	return suspend_again;
+}
+
+/* return 1 => suspend again
+   return 0 => continue wakeup
+ */
+bool quickwakeup_suspend_again(void)
+{
+	int ret = 0;
+
+	if (quickwakeup_check())
+		ret = quickwakeup_execute();
+
+	pr_debug("%s- returning %d %s\n", __func__, ret,
+		ret ? "suspend again" : "wakeup");
+
+	return ret;
+}
