From a12f5cc01dd4ed811e5edaca429dcbbb129e0e50 Mon Sep 17 00:00:00 2001 From: Tad 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/ + resume=/dev/ - 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 +#include +#include +#include + +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; +}