From 24b7d5ca843cd63f1ee1ae7ebdb00f78dd3f62b9 Mon Sep 17 00:00:00 2001 From: Ke Qinghua Date: Fri, 4 Apr 2014 16:22:34 +0800 Subject: ENGR00305254-7 imx6sx sarbesd board bring up Enable early suspend/late resume code Signed-off-by: Ke Qinghua Acked-by: Jason Liu --- kernel/power/Kconfig | 9 ++ kernel/power/Makefile | 3 + kernel/power/earlysuspend.c | 187 ++++++++++++++++++++++++++++++++++++++++++ kernel/power/fbearlysuspend.c | 152 ++++++++++++++++++++++++++++++++++ kernel/power/main.c | 122 --------------------------- kernel/power/power.h | 12 +++ 6 files changed, 363 insertions(+), 122 deletions(-) create mode 100644 kernel/power/earlysuspend.c create mode 100644 kernel/power/fbearlysuspend.c (limited to 'kernel') diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index f8cc6c43efec..cbedafb24ea0 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -21,6 +21,15 @@ config SUSPEND_FREEZER config HAS_WAKELOCK bool default y +config HAS_EARLYSUSPEND + bool + default y +config EARLYSUSPEND + bool + default y +config FB_EARLYSUSPEND + bool + default y config WAKELOCK bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 74c713ba61b0..fd354a5e8a24 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,9 @@ obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o +obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o +obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o +obj-$(CONFIG_CPUFREQ_GOV_ON_EARLYSUPSEND) += cpufreq_earlysuspend.o obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c new file mode 100644 index 000000000000..5971c78cf606 --- /dev/null +++ b/kernel/power/earlysuspend.c @@ -0,0 +1,187 @@ +/* kernel/power/earlysuspend.c + * + * Copyright (C) 2005-2008 Google, Inc. + * + * 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 +#include /* sys_sync */ +#include +#include + +#include "power.h" + +enum { + DEBUG_USER_STATE = 1U << 0, + DEBUG_SUSPEND = 1U << 2, + DEBUG_VERBOSE = 1U << 3, +}; +static int debug_mask = DEBUG_USER_STATE; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +static DEFINE_MUTEX(early_suspend_lock); +static LIST_HEAD(early_suspend_handlers); +static void early_suspend(struct work_struct *work); +static void late_resume(struct work_struct *work); +static DECLARE_WORK(early_suspend_work, early_suspend); +static DECLARE_WORK(late_resume_work, late_resume); +static DEFINE_SPINLOCK(state_lock); + +enum { + SUSPEND_REQUESTED = 0x1, + SUSPENDED = 0x2, + SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED, +}; +static int state; +void register_early_suspend(struct early_suspend *handler) +{ + struct list_head *pos; + + mutex_lock(&early_suspend_lock); + list_for_each(pos, &early_suspend_handlers) { + struct early_suspend *e; + e = list_entry(pos, struct early_suspend, link); + if (e->level > handler->level) + break; + } + list_add_tail(&handler->link, pos); + if ((state & SUSPENDED) && handler->suspend) + handler->suspend(handler); + mutex_unlock(&early_suspend_lock); +} +EXPORT_SYMBOL(register_early_suspend); + +void unregister_early_suspend(struct early_suspend *handler) +{ + mutex_lock(&early_suspend_lock); + list_del(&handler->link); + mutex_unlock(&early_suspend_lock); +} +EXPORT_SYMBOL(unregister_early_suspend); + +static void early_suspend(struct work_struct *work) +{ + struct early_suspend *pos; + unsigned long irqflags; + int abort = 0; + + mutex_lock(&early_suspend_lock); + spin_lock_irqsave(&state_lock, irqflags); + if (state == SUSPEND_REQUESTED) + state |= SUSPENDED; + else + abort = 1; + spin_unlock_irqrestore(&state_lock, irqflags); + + if (abort) { + if (debug_mask & DEBUG_SUSPEND) + pr_info("early_suspend: abort, state %d\n", state); + mutex_unlock(&early_suspend_lock); + goto abort; + } + + if (debug_mask & DEBUG_SUSPEND) + pr_info("early_suspend: call handlers\n"); + list_for_each_entry(pos, &early_suspend_handlers, link) { + if (pos->suspend != NULL) { + if (debug_mask & DEBUG_VERBOSE) + pr_info("early_suspend: calling %pf\n", pos->suspend); + pos->suspend(pos); + } + } + mutex_unlock(&early_suspend_lock); + + if (debug_mask & DEBUG_SUSPEND) + pr_info("early_suspend: sync\n"); + + sys_sync(); +abort: + spin_lock_irqsave(&state_lock, irqflags); + if (state == SUSPEND_REQUESTED_AND_SUSPENDED) + android_wake_unlock(&main_wake_lock); + spin_unlock_irqrestore(&state_lock, irqflags); +} + +static void late_resume(struct work_struct *work) +{ + struct early_suspend *pos; + unsigned long irqflags; + int abort = 0; + + mutex_lock(&early_suspend_lock); + spin_lock_irqsave(&state_lock, irqflags); + if (state == SUSPENDED) + state &= ~SUSPENDED; + else + abort = 1; + spin_unlock_irqrestore(&state_lock, irqflags); + + if (abort) { + if (debug_mask & DEBUG_SUSPEND) + pr_info("late_resume: abort, state %d\n", state); + goto abort; + } + if (debug_mask & DEBUG_SUSPEND) + pr_info("late_resume: call handlers\n"); + list_for_each_entry_reverse(pos, &early_suspend_handlers, link) { + if (pos->resume != NULL) { + if (debug_mask & DEBUG_VERBOSE) + pr_info("late_resume: calling %pf\n", pos->resume); + + pos->resume(pos); + } + } + if (debug_mask & DEBUG_SUSPEND) + pr_info("late_resume: done\n"); +abort: + mutex_unlock(&early_suspend_lock); +} + +void request_suspend_state(suspend_state_t new_state) +{ + unsigned long irqflags; + int old_sleep; + + spin_lock_irqsave(&state_lock, irqflags); + old_sleep = state & SUSPEND_REQUESTED; + if (debug_mask & DEBUG_USER_STATE) { + struct timespec ts; + struct rtc_time tm; + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("request_suspend_state: %s (%d->%d) at %lld " + "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", + new_state != PM_SUSPEND_ON ? "sleep" : "wakeup", + requested_suspend_state, new_state, + ktime_to_ns(ktime_get()), + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + } + if (!old_sleep && new_state != PM_SUSPEND_ON) { + state |= SUSPEND_REQUESTED; + queue_work(suspend_work_queue, &early_suspend_work); + } else if (old_sleep && new_state == PM_SUSPEND_ON) { + state &= ~SUSPEND_REQUESTED; + android_wake_lock(&main_wake_lock); + queue_work(suspend_work_queue, &late_resume_work); + } + requested_suspend_state = new_state; + spin_unlock_irqrestore(&state_lock, irqflags); +} + +suspend_state_t get_suspend_state(void) +{ + return requested_suspend_state; +} diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c new file mode 100644 index 000000000000..0142c9d30ce5 --- /dev/null +++ b/kernel/power/fbearlysuspend.c @@ -0,0 +1,152 @@ +/* kernel/power/fbearlysuspend.c + * + * Copyright (C) 2005-2008 Google, Inc. + * + * 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 "power.h" + +static wait_queue_head_t fb_state_wq; +static DEFINE_SPINLOCK(fb_state_lock); +static enum { + FB_STATE_STOPPED_DRAWING, + FB_STATE_REQUEST_STOP_DRAWING, + FB_STATE_DRAWING_OK, +} fb_state; + +/* tell userspace to stop drawing, wait for it to stop */ +static void stop_drawing_early_suspend(struct early_suspend *h) +{ + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + fb_state = FB_STATE_REQUEST_STOP_DRAWING; + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + + wake_up_all(&fb_state_wq); + ret = wait_event_timeout(fb_state_wq, + fb_state == FB_STATE_STOPPED_DRAWING, + HZ); + if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING)) + pr_warning("stop_drawing_early_suspend: timeout waiting for " + "userspace to stop drawing\n"); +} + +/* tell userspace to start drawing */ +static void start_drawing_late_resume(struct early_suspend *h) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + fb_state = FB_STATE_DRAWING_OK; + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + wake_up(&fb_state_wq); +} + +static struct early_suspend stop_drawing_early_suspend_desc = { + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = stop_drawing_early_suspend, + .resume = start_drawing_late_resume, +}; + +static ssize_t wait_for_fb_sleep_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + int ret; + + ret = wait_event_interruptible(fb_state_wq, + fb_state != FB_STATE_DRAWING_OK); + if (ret && fb_state == FB_STATE_DRAWING_OK) + return ret; + else + s += sprintf(buf, "sleeping"); + return s - buf; +} + +static ssize_t wait_for_fb_wake_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) { + fb_state = FB_STATE_STOPPED_DRAWING; + wake_up(&fb_state_wq); + } + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + + ret = wait_event_interruptible(fb_state_wq, + fb_state == FB_STATE_DRAWING_OK); + if (ret && fb_state != FB_STATE_DRAWING_OK) + return ret; + else + s += sprintf(buf, "awake"); + + return s - buf; +} + +#define power_ro_attr(_name) \ +static struct kobj_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0444, \ + }, \ + .show = _name##_show, \ + .store = NULL, \ +} + +power_ro_attr(wait_for_fb_sleep); +power_ro_attr(wait_for_fb_wake); + +static struct attribute *g[] = { + &wait_for_fb_sleep_attr.attr, + &wait_for_fb_wake_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = g, +}; + +static int __init android_power_init(void) +{ + int ret; + + init_waitqueue_head(&fb_state_wq); + fb_state = FB_STATE_DRAWING_OK; + + ret = sysfs_create_group(power_kobj, &attr_group); + if (ret) { + pr_err("android_power_init: sysfs_create_group failed\n"); + return ret; + } + register_early_suspend(&stop_drawing_early_suspend_desc); + return 0; +} + +static void __exit android_power_exit(void) +{ + unregister_early_suspend(&stop_drawing_early_suspend_desc); + sysfs_remove_group(power_kobj, &attr_group); +} + +module_init(android_power_init); +module_exit(android_power_exit); + diff --git a/kernel/power/main.c b/kernel/power/main.c index 1d4da28f9067..63fbe7347b3e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -338,128 +338,6 @@ static suspend_state_t decode_state(const char *buf, size_t n) return PM_SUSPEND_ON; } -#include -#include - -enum { - DEBUG_USER_STATE = 1U << 0, - DEBUG_SUSPEND = 1U << 2, - DEBUG_VERBOSE = 1U << 3, -}; -static int debug_mask = DEBUG_USER_STATE; -module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); - -extern struct workqueue_struct *suspend_work_queue; -extern struct wake_lock main_wake_lock; -extern suspend_state_t requested_suspend_state; - -static DEFINE_MUTEX(early_suspend_lock); -static LIST_HEAD(early_suspend_handlers); -static void early_suspend(struct work_struct *work); -static void late_resume(struct work_struct *work); -static DECLARE_WORK(early_suspend_work, early_suspend); -static DECLARE_WORK(late_resume_work, late_resume); -static DEFINE_SPINLOCK(state_lock); - -enum { - SUSPEND_REQUESTED = 0x1, - SUSPENDED = 0x2, - SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED, -}; -static int state; - -static void early_suspend(struct work_struct *work) -{ - struct early_suspend *pos; - unsigned long irqflags; - int abort = 0; - - mutex_lock(&early_suspend_lock); - spin_lock_irqsave(&state_lock, irqflags); - if (state == SUSPEND_REQUESTED) - state |= SUSPENDED; - else - abort = 1; - spin_unlock_irqrestore(&state_lock, irqflags); - - if (abort) { - if (debug_mask & DEBUG_SUSPEND) - pr_info("early_suspend: abort, state %d\n", state); - mutex_unlock(&early_suspend_lock); - goto abort; - } - if (debug_mask & DEBUG_SUSPEND) - pr_info("early_suspend: call handlers\n"); - /* early suspend not enable */ - mutex_unlock(&early_suspend_lock); - - if (debug_mask & DEBUG_SUSPEND) - pr_info("early_suspend: sync\n"); - - sys_sync(); -abort: - spin_lock_irqsave(&state_lock, irqflags); - if (state == SUSPEND_REQUESTED_AND_SUSPENDED) - android_wake_unlock(&main_wake_lock); - spin_unlock_irqrestore(&state_lock, irqflags); -} - -static void late_resume(struct work_struct *work) -{ - struct early_suspend *pos; - unsigned long irqflags; - int abort = 0; - - mutex_lock(&early_suspend_lock); - spin_lock_irqsave(&state_lock, irqflags); - if (state == SUSPENDED) - state &= ~SUSPENDED; - else - abort = 1; - spin_unlock_irqrestore(&state_lock, irqflags); - - if (abort) { - if (debug_mask & DEBUG_SUSPEND) - pr_info("late_resume: abort, state %d\n", state); - goto abort; - } - /* early suspend not enable */ -abort: - mutex_unlock(&early_suspend_lock); -} - -static void request_suspend_state(suspend_state_t new_state) -{ - unsigned long irqflags; - int old_sleep; - - spin_lock_irqsave(&state_lock, irqflags); - old_sleep = state & SUSPEND_REQUESTED; - if (debug_mask & DEBUG_USER_STATE) { - struct timespec ts; - struct rtc_time tm; - getnstimeofday(&ts); - rtc_time_to_tm(ts.tv_sec, &tm); - pr_info("request_suspend_state: %s (%d->%d) at %lld " - "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", - new_state != PM_SUSPEND_ON ? "sleep" : "wakeup", - requested_suspend_state, new_state, - ktime_to_ns(ktime_get()), - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); - } - if (!old_sleep && new_state != PM_SUSPEND_ON) { - state |= SUSPEND_REQUESTED; - queue_work(suspend_work_queue, &early_suspend_work); - } else if (old_sleep && new_state == PM_SUSPEND_ON) { - state &= ~SUSPEND_REQUESTED; - android_wake_lock(&main_wake_lock); - queue_work(suspend_work_queue, &late_resume_work); - } - requested_suspend_state = new_state; - spin_unlock_irqrestore(&state_lock, irqflags); -} - static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { diff --git a/kernel/power/power.h b/kernel/power/power.h index 7d4b7ffb3c1d..e101cac0ef2d 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -294,3 +294,15 @@ extern int pm_wake_lock(const char *buf); extern int pm_wake_unlock(const char *buf); #endif /* !CONFIG_PM_WAKELOCKS */ +#ifdef CONFIG_WAKELOCK +/* kernel/power/wakelock.c */ +extern struct workqueue_struct *suspend_work_queue; +extern struct wake_lock main_wake_lock; +extern suspend_state_t requested_suspend_state; +#endif + +#ifdef CONFIG_EARLYSUSPEND +/* kernel/power/earlysuspend.c */ +void request_suspend_state(suspend_state_t state); +suspend_state_t get_suspend_state(void); +#endif -- cgit v1.2.3