From 47458a52e0c61631964a4ece7f499262789f3635 Mon Sep 17 00:00:00 2001 From: Aymen Zayet Date: Wed, 7 Mar 2012 14:19:03 +0100 Subject: [PATCH] alarm: Adding new threshold to avoid intensive wakeup from S3 BZ: 26623 For Mobile Internet Devices, saving power is one of the main requirement. In general, such devices supports already some power management machanism that allows to to optimize the power consumption (such as suspend to ram and hibernation). But using such mechanisms could be very penalizing in term of power consumption in the case where the wakeup from deep sleep is triggered periodically. Actually the suspend process starts by waken up all the devices in order to well prepare them for a deep sleep. Experimental results shows that, depending on the devices presents on the system, the suspend increases significantly the power if the wakeup capable alarms are configured to be triggered before a certain threshold. Be careful while setting this parameter: a wrong value may increase the power consumption. So, if no full study was performed on the target platform, please leave it at its default value. Change-Id: I6ccc031f4f6991e762e74b8c92a7fa850347f0a5 Signed-off-by: Vishwesh M Rudramuni Signed-off-by: Aymen Zayet Reviewed-on: http://android.intel.com:8080/51243 Reviewed-by: Martin, LoicX Tested-by: Martin, LoicX Reviewed-by: Gross, Mark Reviewed-by: buildbot Tested-by: buildbot --- drivers/rtc/Kconfig | 23 +++++++++++++++++ drivers/rtc/alarm.c | 65 +++++++++++++++++++++++++++++++++++++------------ include/linux/rtc.h | 2 +- kernel/power/wakelock.c | 3 ++- 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6552aec..959fd9f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -116,6 +116,29 @@ config RTC_INTF_ALARM Also provides an interface to set the wall time which must be used for elapsed realtime to work. +config ALARM_PM_THRESHOLD + int "Wakeup Alarm Threshold for deep sleep (Experimental)" + range 0 3600 + depends on RTC_INTF_ALARM_DEV && ANDROID && PM_SLEEP && PM_RUNTIME + default 0 + help + For Mobile Internet Devices, saving power is one of the main requirement. + In general, such devices supports already some power management machanism + that allows to to optimize the power consumption (such as suspend to + ram and hibernation). But using such mechanisms could be very penalizing + in term of power consumption in the case where the wakeup from deep sleep + is triggered periodically. + + Actually the suspend process starts by waken up all the devices in order + to well prepare them for a deep sleep. Experimental results shows that, + depending on the devices presents on the system, the suspend increases + significantly the power if the wakeup capable alarms are configured to be + triggered before a certain threshold. + + Be careful while setting this parameter: a wrong value may increase the + power consumption. So, if no full study was performed on the target + platform, please leave it at its default value. + config RTC_INTF_ALARM_DEV bool "Android alarm device" depends on RTC_INTF_ALARM diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index 55cc50f..7f3de25 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -28,7 +28,6 @@ #include #include - #define ANDROID_ALARM_PRINT_ERROR (1U << 0) #define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) #define ANDROID_ALARM_PRINT_TSET (1U << 2) @@ -69,6 +68,7 @@ static struct wake_lock alarm_rtc_wake_lock; static struct platform_device *alarm_platform_dev; struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT]; static bool suspended; +static u32 config_pm_threshold = (u32)CONFIG_ALARM_PM_THRESHOLD; static int alarm_reboot_callback(struct notifier_block *nfb, unsigned long event, void *data); @@ -427,6 +427,28 @@ static void alarm_triggered_func(void *p) wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); } +static struct alarm_queue *alarm_find_next_wakeup_hrtimer(void) +{ + struct alarm_queue *wakeup_queue = NULL; + struct alarm_queue *tmp_queue = NULL; + + tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP]; + if (tmp_queue->first) + wakeup_queue = tmp_queue; + tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + if (tmp_queue->first && (!wakeup_queue || + hrtimer_get_expires(&tmp_queue->timer).tv64 < + hrtimer_get_expires(&wakeup_queue->timer).tv64)) + wakeup_queue = tmp_queue; + tmp_queue = &alarms[ANDROID_ALARM_POWER_OFF_WAKEUP]; + if (tmp_queue->first && (!wakeup_queue || + hrtimer_get_expires(&tmp_queue->timer).tv64 < + hrtimer_get_expires(&wakeup_queue->timer).tv64)) + wakeup_queue = tmp_queue; + + return wakeup_queue; +} + static int alarm_suspend(struct device *dev) { int err = 0; @@ -438,7 +460,6 @@ static int alarm_suspend(struct device *dev) struct timespec rtc_delta; struct timespec wall_time; struct alarm_queue *wakeup_queue = NULL; - struct alarm_queue *tmp_queue = NULL; spin_lock_irqsave(&alarm_slock, flags); suspended = true; @@ -449,19 +470,7 @@ static int alarm_suspend(struct device *dev) ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].timer); hrtimer_cancel(&alarms[ANDROID_ALARM_POWER_OFF_WAKEUP].timer); - tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP]; - if (tmp_queue->first) - wakeup_queue = tmp_queue; - tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; - if (tmp_queue->first && (!wakeup_queue || - hrtimer_get_expires(&tmp_queue->timer).tv64 < - hrtimer_get_expires(&wakeup_queue->timer).tv64)) - wakeup_queue = tmp_queue; - tmp_queue = &alarms[ANDROID_ALARM_POWER_OFF_WAKEUP]; - if (tmp_queue->first && (!wakeup_queue || - hrtimer_get_expires(&tmp_queue->timer).tv64 < - hrtimer_get_expires(&wakeup_queue->timer).tv64)) - wakeup_queue = tmp_queue; + wakeup_queue = alarm_find_next_wakeup_hrtimer(); if (wakeup_queue) { rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); getnstimeofday(&wall_time); @@ -529,6 +538,31 @@ static int alarm_resume(struct device *dev) return 0; } +int alarm_pm_wake_check(void) +{ + struct alarm_queue *wk_queue = alarm_find_next_wakeup_hrtimer(); + + if (wk_queue && config_pm_threshold) { + /* + * Compute the timestamp of the next wakeup alarm in + * second. + */ + ktime_t expire = hrtimer_get_remaining( + &wk_queue->timer); + struct timespec x = ktime_to_timespec(expire); + unsigned long timeout = timespec_to_jiffies(&x); + unsigned long next_alarm_secs = timeout / HZ; + + if (next_alarm_secs < config_pm_threshold) { + wake_lock_timeout(&alarm_rtc_wake_lock, + timeout + HZ); + return 0; + } + } + + return 1; +} + static struct rtc_task alarm_rtc_task = { .func = alarm_triggered_func }; @@ -657,7 +691,6 @@ static int __init alarm_driver_init(void) goto err2; register_reboot_notifier(&alarm_reboot_notifier_block); - return 0; err2: diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 93f4d03..024c68e 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -104,7 +104,7 @@ struct rtc_pll_info { #include #include - +extern int alarm_pm_wake_check(void); extern int rtc_month_days(unsigned int month, unsigned int year); extern int rtc_year_days(unsigned int day, unsigned int month, unsigned int year); extern int rtc_valid_tm(struct rtc_time *tm); diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index 678d0bf..c588935 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -280,7 +280,7 @@ static void suspend(struct work_struct *work) int entry_event_num; struct timespec ts_entry, ts_exit; - if (has_wake_lock(WAKE_LOCK_SUSPEND)) { + if (has_wake_lock(WAKE_LOCK_SUSPEND) || !alarm_pm_wake_check()) { if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: abort suspend\n"); return; @@ -289,6 +289,7 @@ static void suspend(struct work_struct *work) entry_event_num = current_event_num; if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: enter suspend\n"); + getnstimeofday(&ts_entry); ret = pm_suspend(requested_suspend_state); getnstimeofday(&ts_exit); -- 2.7.4