diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c index bfcaebc9ce4..1d60e9784a5 100644 --- a/drivers/rtc/alarm-dev.c +++ b/drivers/rtc/alarm-dev.c @@ -98,6 +98,8 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) wake_unlock(&alarm_wake_lock); } alarm_enabled &= ~alarm_type_mask; + if (alarm_type == ANDROID_ALARM_RTC_WAKEUP) + set_power_on_alarm(0); spin_unlock_irqrestore(&alarm_slock, flags); break; @@ -125,6 +127,10 @@ from_old_alarm_set: alarm_start_range(&alarms[alarm_type], timespec_to_ktime(new_alarm_time), timespec_to_ktime(new_alarm_time)); + if ((alarm_type == ANDROID_ALARM_RTC_WAKEUP) && + (ANDROID_ALARM_BASE_CMD(cmd) == + ANDROID_ALARM_SET(0))) + set_power_on_alarm(new_alarm_time.tv_sec); spin_unlock_irqrestore(&alarm_slock, flags); if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index 9340af7dc86..e318ecf7f0f 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -68,6 +68,13 @@ 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 long power_on_alarm; + +void set_power_on_alarm(long secs) +{ + power_on_alarm = secs; +} + static void update_timer_locked(struct alarm_queue *base, bool head_removed) { @@ -486,6 +493,45 @@ static int alarm_resume(struct platform_device *pdev) return 0; } +static void alarm_shutdown(struct platform_device *dev) +{ + struct timespec wall_time; + struct rtc_time rtc_time; + struct rtc_wkalrm alarm; + unsigned long flags; + long rtc_secs, alarm_delta, alarm_time; + int rc; + + spin_lock_irqsave(&alarm_slock, flags); + + if (!power_on_alarm) + goto disable_alarm; + + rtc_read_time(alarm_rtc_dev, &rtc_time); + getnstimeofday(&wall_time); + rtc_tm_to_time(&rtc_time, &rtc_secs); + alarm_delta = wall_time.tv_sec - rtc_secs; + alarm_time = power_on_alarm - alarm_delta; + if (alarm_time <= rtc_secs) + goto disable_alarm; + + rtc_time_to_tm(alarm_time, &alarm.time); + alarm.enabled = 1; + rc = rtc_set_alarm(alarm_rtc_dev, &alarm); + if (rc) + pr_alarm(ERROR, "Unable to set power-on alarm\n"); + else + pr_alarm(FLOW, "Power-on alarm set to %lu\n", + alarm_time); + + spin_unlock_irqrestore(&alarm_slock, flags); + return; + +disable_alarm: + rtc_alarm_irq_enable(alarm_rtc_dev, 0); + spin_unlock_irqrestore(&alarm_slock, flags); +} + static struct rtc_task alarm_rtc_task = { .func = alarm_triggered_func }; @@ -545,6 +591,7 @@ static struct class_interface rtc_alarm_interface = { static struct platform_driver alarm_driver = { .suspend = alarm_suspend, .resume = alarm_resume, + .shutdown = alarm_shutdown, .driver = { .name = "alarm" } diff --git a/include/linux/android_alarm.h b/include/linux/android_alarm.h index cbfeafce329..096f77711b1 100644 --- a/include/linux/android_alarm.h +++ b/include/linux/android_alarm.h @@ -70,6 +70,7 @@ void alarm_init(struct alarm *alarm, void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); +void set_power_on_alarm(long secs); ktime_t alarm_get_elapsed_realtime(void); /* set rtc while preserving elapsed realtime */