From e5642795c4c75493a255f27646d784cd70f4ef70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 14 Oct 2008 17:38:04 -0700 Subject: rtc: Add android alarm driver. --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/alarm.c | 568 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+) create mode 100644 drivers/rtc/alarm.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 48ca7132cc05..ae2e0c3384d4 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -109,6 +109,16 @@ config RTC_INTF_DEV_UIE_EMUL clock several times per second, please enable this option only if you know that you really need it. +config RTC_INTF_ALARM + bool "Android alarm driver" + depends on RTC_CLASS + default y + help + Provides non-wakeup and rtc backed wakeup alarms based on rtc or + elapsed realtime, and a non-wakeup alarm on the monotonic clock. + Also provides an ioctl to set the wall time which must be used + for elapsed realtime to work. + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0f207b3b5833..36ffc1cdc20f 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o obj-$(CONFIG_RTC_CLASS) += rtc-core.o rtc-core-y := class.o interface.o +rtc-core-$(CONFIG_RTC_INTF_ALARM) += alarm.o rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c new file mode 100644 index 000000000000..cd660eee3b27 --- /dev/null +++ b/drivers/rtc/alarm.c @@ -0,0 +1,568 @@ +/* drivers/rtc/alarm.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include + +#define ANDROID_ALARM_PRINT_ERRORS (1U << 0) +#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) +#define ANDROID_ALARM_PRINT_INFO (1U << 2) +#define ANDROID_ALARM_PRINT_IO (1U << 3) +#define ANDROID_ALARM_PRINT_INT (1U << 4) +#define ANDROID_ALARM_PRINT_FLOW (1U << 5) + +#if 0 +#define ANDROID_ALARM_DPRINTF_MASK (~0) +#define ANDROID_ALARM_DPRINTF(debug_level_mask, args...) \ + do { \ + if (ANDROID_ALARM_DPRINTF_MASK & debug_level_mask) { \ + printk(args); \ + } \ + } while (0) +#else +#define ANDROID_ALARM_DPRINTF(args...) +#endif + +#define ANDROID_ALARM_WAKEUP_MASK ( \ + ANDROID_ALARM_RTC_WAKEUP_MASK | \ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + +/* support old usespace code */ +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +static struct rtc_device *alarm_rtc_dev; +static int alarm_opened; +static DEFINE_SPINLOCK(alarm_slock); +static DEFINE_MUTEX(alarm_setrtc_mutex); +static struct wake_lock alarm_wake_lock; +static struct wake_lock alarm_rtc_wake_lock; +static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +static uint32_t alarm_pending; +static uint32_t alarm_enabled; +static uint32_t wait_pending; +static struct platform_device *alarm_platform_dev; +static struct hrtimer alarm_timer[ANDROID_ALARM_TYPE_COUNT]; +static struct timespec alarm_time[ANDROID_ALARM_TYPE_COUNT]; +static struct timespec elapsed_rtc_delta; + +static void alarm_start_hrtimer(enum android_alarm_type alarm_type) +{ + struct timespec hr_alarm_time; + if (!(alarm_enabled & (1U << alarm_type))) + return; + hr_alarm_time = alarm_time[alarm_type]; + if (alarm_type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP || + alarm_type == ANDROID_ALARM_ELAPSED_REALTIME) + set_normalized_timespec(&hr_alarm_time, + hr_alarm_time.tv_sec + elapsed_rtc_delta.tv_sec, + hr_alarm_time.tv_nsec + elapsed_rtc_delta.tv_nsec); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, + "alarm start hrtimer %d at %ld.%09ld\n", + alarm_type, hr_alarm_time.tv_sec, hr_alarm_time.tv_nsec); + hrtimer_start(&alarm_timer[alarm_type], + timespec_to_ktime(hr_alarm_time), HRTIMER_MODE_ABS); +} + +static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + unsigned long flags; + int i; + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; + struct rtc_time rtc_new_rtc_time; + enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + + if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) + return -EINVAL; + + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + if (file->private_data == NULL && + cmd != ANDROID_ALARM_SET_RTC) { + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_opened) { + spin_unlock_irqrestore(&alarm_slock, flags); + return -EBUSY; + } + alarm_opened = 1; + file->private_data = (void *)1; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + + switch (ANDROID_ALARM_BASE_CMD(cmd)) { + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, + "alarm %d clear\n", alarm_type); + hrtimer_try_to_cancel(&alarm_timer[alarm_type]); + if (alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if (!alarm_pending && !wait_pending) + wake_unlock(&alarm_wake_lock); + } + alarm_enabled &= ~alarm_type_mask; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + + case ANDROID_ALARM_SET_OLD: + case ANDROID_ALARM_SET_AND_WAIT_OLD: + if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { + rv = -EFAULT; + goto err1; + } + new_alarm_time.tv_nsec = 0; + goto from_old_alarm_set; + + case ANDROID_ALARM_SET_AND_WAIT(0): + case ANDROID_ALARM_SET(0): + if (copy_from_user(&new_alarm_time, (void __user *)arg, + sizeof(new_alarm_time))) { + rv = -EFAULT; + goto err1; + } +from_old_alarm_set: + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, + "alarm %d set %ld.%09ld\n", alarm_type, + new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_time[alarm_type] = new_alarm_time; + alarm_enabled |= alarm_type_mask; + alarm_start_hrtimer(alarm_type); + 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) + break; + /* fall though */ + case ANDROID_ALARM_WAIT: + spin_lock_irqsave(&alarm_slock, flags); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm wait\n"); + if (!alarm_pending && wait_pending) { + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); + if (rv) + goto err1; + spin_lock_irqsave(&alarm_slock, flags); + rv = alarm_pending; + wait_pending = 1; + alarm_pending = 0; + if (rv & ANDROID_ALARM_WAKEUP_MASK) + wake_unlock(&alarm_rtc_wake_lock); + spin_unlock_irqrestore(&alarm_slock, flags); + break; + case ANDROID_ALARM_SET_RTC: + if (copy_from_user(&new_rtc_time, (void __user *)arg, + sizeof(new_rtc_time))) { + rv = -EFAULT; + goto err1; + } + rtc_time_to_tm(new_rtc_time.tv_sec, &rtc_new_rtc_time); + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, + "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", + new_rtc_time.tv_sec, new_rtc_time.tv_nsec, + rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, + rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, + rtc_new_rtc_time.tm_mday, + rtc_new_rtc_time.tm_year + 1900); + + mutex_lock(&alarm_setrtc_mutex); + spin_lock_irqsave(&alarm_slock, flags); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) + hrtimer_try_to_cancel(&alarm_timer[i]); + getnstimeofday(&tmp_time); + elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, + timespec_sub(tmp_time, new_rtc_time)); + spin_unlock_irqrestore(&alarm_slock, flags); + rv = do_settimeofday(&new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) + alarm_start_hrtimer(i); + spin_unlock_irqrestore(&alarm_slock, flags); + if (rv < 0) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, + "Failed to set time\n"); + mutex_unlock(&alarm_setrtc_mutex); + goto err1; + } + rv = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + mutex_unlock(&alarm_setrtc_mutex); + if (rv < 0) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, + "Failed to set RTC, time will be lost on reboot\n"); + goto err1; + } + break; + case ANDROID_ALARM_GET_TIME(0): + mutex_lock(&alarm_setrtc_mutex); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_type != ANDROID_ALARM_SYSTEMTIME) { + getnstimeofday(&tmp_time); + if (alarm_type >= ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) + tmp_time = timespec_sub(tmp_time, + elapsed_rtc_delta); + } else + ktime_get_ts(&tmp_time); + spin_unlock_irqrestore(&alarm_slock, flags); + mutex_unlock(&alarm_setrtc_mutex); + if (copy_to_user((void __user *)arg, &tmp_time, + sizeof(tmp_time))) { + rv = -EFAULT; + goto err1; + } + break; + + default: + rv = -EINVAL; + goto err1; + } +err1: + return rv; +} + +static int alarm_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int alarm_release(struct inode *inode, struct file *file) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + if (file->private_data != 0) { + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + uint32_t alarm_type_mask = 1U << i; + if (alarm_enabled & alarm_type_mask) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm_release: clear alarm, " + "pending %d\n", + !!(alarm_pending & alarm_type_mask)); + alarm_enabled &= ~alarm_type_mask; + } + spin_unlock_irqrestore(&alarm_slock, flags); + hrtimer_cancel(&alarm_timer[i]); + spin_lock_irqsave(&alarm_slock, flags); + } + if (alarm_pending | wait_pending) { + if (alarm_pending) + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm_release: clear pending alarms " + "%x\n", alarm_pending); + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + alarm_pending = 0; + } + alarm_opened = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +{ + unsigned long flags; + enum android_alarm_type alarm_type = (timer - alarm_timer); + uint32_t alarm_type_mask = 1U << alarm_type; + + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, + "alarm_timer_triggered type %d\n", alarm_type); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_enabled & alarm_type_mask) { + wake_lock_timeout(&alarm_wake_lock, 5 * HZ); + alarm_enabled &= ~alarm_type_mask; + alarm_pending |= alarm_type_mask; + wake_up(&alarm_wait_queue); + } + spin_unlock_irqrestore(&alarm_slock, flags); + return HRTIMER_NORESTART; +} + +static void alarm_triggered_func(void *p) +{ + struct rtc_device *rtc = alarm_rtc_dev; + if (!(rtc->irq_data & RTC_AF)) + return; + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, "rtc alarm triggered\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); +} + +int alarm_suspend(struct platform_device *pdev, pm_message_t state) +{ + int err = 0; + unsigned long flags; + struct rtc_wkalrm rtc_alarm; + struct rtc_time rtc_current_rtc_time; + unsigned long rtc_current_time; + unsigned long rtc_alarm_time; + struct timespec rtc_current_timespec; + struct timespec rtc_delta; + struct timespec elapsed_realtime_alarm_time; + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, + "alarm_suspend(%p, %d)\n", pdev, state.event); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_pending && !wake_lock_active(&alarm_wake_lock)) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm pending\n"); + err = -EBUSY; + goto err1; + } + if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { + spin_unlock_irqrestore(&alarm_slock, flags); + if (alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) + hrtimer_cancel(&alarm_timer[ANDROID_ALARM_RTC_WAKEUP]); + if (alarm_enabled & ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + hrtimer_cancel(&alarm_timer[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]); + + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_current_timespec.tv_nsec = 0; + rtc_tm_to_time(&rtc_current_rtc_time, + &rtc_current_timespec.tv_sec); + save_time_delta(&rtc_delta, &rtc_current_timespec); + set_normalized_timespec(&elapsed_realtime_alarm_time, + alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] + .tv_sec + elapsed_rtc_delta.tv_sec, + alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] + .tv_nsec + elapsed_rtc_delta.tv_nsec); + if ((alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) && + (!(alarm_enabled & + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) || + timespec_compare(&alarm_time[ANDROID_ALARM_RTC_WAKEUP], + &elapsed_realtime_alarm_time) < 0)) + rtc_alarm_time = timespec_sub( + alarm_time[ANDROID_ALARM_RTC_WAKEUP], + rtc_delta).tv_sec; + else + rtc_alarm_time = timespec_sub( + elapsed_realtime_alarm_time, rtc_delta).tv_sec; + rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); + rtc_alarm.enabled = 1; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", + rtc_alarm_time, rtc_current_time, + rtc_delta.tv_sec, rtc_delta.tv_nsec); + if (rtc_current_time + 1 >= rtc_alarm_time) { + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm about to go off\n"); + memset(&rtc_alarm, 0, sizeof(rtc_alarm)); + rtc_alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + + spin_lock_irqsave(&alarm_slock, flags); + wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ); + alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); + alarm_start_hrtimer( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + err = -EBUSY; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } else { +err1: + spin_unlock_irqrestore(&alarm_slock, flags); + } + return err; +} + +int alarm_resume(struct platform_device *pdev) +{ + struct rtc_wkalrm alarm; + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, + "alarm_resume(%p)\n", pdev); + if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { + memset(&alarm, 0, sizeof(alarm)); + alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &alarm); + alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); + alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + } + return 0; +} + +static struct rtc_task alarm_rtc_task = { + .func = alarm_triggered_func +}; + +static struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, + .open = alarm_open, + .release = alarm_release, +}; + +static struct miscdevice alarm_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "alarm", + .fops = &alarm_fops, +}; + +static int rtc_alarm_add_device(struct device *dev, + struct class_interface *class_intf) +{ + int err; + struct rtc_device *rtc = to_rtc_device(dev); + + mutex_lock(&alarm_setrtc_mutex); + + if (alarm_rtc_dev) { + err = -EBUSY; + goto err1; + } + + err = misc_register(&alarm_device); + if (err) + goto err1; + alarm_platform_dev = + platform_device_register_simple("alarm", -1, NULL, 0); + if (IS_ERR(alarm_platform_dev)) { + err = PTR_ERR(alarm_platform_dev); + goto err2; + } + err = rtc_irq_register(rtc, &alarm_rtc_task); + if (err) + goto err3; + alarm_rtc_dev = rtc; + mutex_unlock(&alarm_setrtc_mutex); + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm: parent %p\n", + alarm_platform_dev->dev.power.pm_parent); + return 0; + +err3: + platform_device_unregister(alarm_platform_dev); +err2: + misc_deregister(&alarm_device); +err1: + mutex_unlock(&alarm_setrtc_mutex); + return err; +} + +static void rtc_alarm_remove_device(struct device *dev, + struct class_interface *class_intf) +{ + if (dev == &alarm_rtc_dev->dev) { + rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); + platform_device_unregister(alarm_platform_dev); + misc_deregister(&alarm_device); + alarm_rtc_dev = NULL; + } +} + +static struct class_interface rtc_alarm_interface = { + .add_dev = &rtc_alarm_add_device, + .remove_dev = &rtc_alarm_remove_device, +}; + +static struct platform_driver alarm_driver = { + .suspend = alarm_suspend, + .resume = alarm_resume, + .driver = { + .name = "alarm" + } +}; + +static int __init alarm_late_init(void) +{ + unsigned long flags; + struct timespec system_time; + + /* this needs to run after the rtc is read at boot */ + spin_lock_irqsave(&alarm_slock, flags); + /* We read the current rtc and system time so we can later calulate + * elasped realtime to be (boot_systemtime + rtc - boot_rtc) == + * (rtc - (boot_rtc - boot_systemtime)) + */ + getnstimeofday(&elapsed_rtc_delta); + ktime_get_ts(&system_time); + elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, system_time); + spin_unlock_irqrestore(&alarm_slock, flags); + + ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + "alarm_late_init: rtc to elapsed realtime delta %ld.%09ld\n", + elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec); + return 0; +} + +static int __init alarm_init(void) +{ + int err; + int i; + + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_init(&alarm_timer[i], CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarm_timer[i].function = alarm_timer_triggered; + } + hrtimer_init(&alarm_timer[ANDROID_ALARM_SYSTEMTIME], + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + alarm_timer[ANDROID_ALARM_SYSTEMTIME].function = alarm_timer_triggered; + err = platform_driver_register(&alarm_driver); + if (err < 0) + goto err1; + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); + wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); + rtc_alarm_interface.class = rtc_class; + err = class_interface_register(&rtc_alarm_interface); + if (err < 0) + goto err2; + + return 0; + +err2: + wake_lock_destroy(&alarm_rtc_wake_lock); + wake_lock_destroy(&alarm_wake_lock); + platform_driver_unregister(&alarm_driver); +err1: + return err; +} + +static void __exit alarm_exit(void) +{ + class_interface_unregister(&rtc_alarm_interface); + wake_lock_destroy(&alarm_rtc_wake_lock); + wake_lock_destroy(&alarm_wake_lock); + platform_driver_unregister(&alarm_driver); +} + +late_initcall(alarm_late_init); +module_init(alarm_init); +module_exit(alarm_exit); + -- cgit v1.2.3 From c4606c120818603260362b3440a60d46e7feb44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 4 May 2009 14:09:15 -0700 Subject: rtc: alarm: Add in-kernel alarm interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers can now create alarms that will use an hrtimer while the system is running and the rtc to wake up from suspend. Signed-off-by: Arve Hjønnevåg --- drivers/rtc/Kconfig | 10 +- drivers/rtc/Makefile | 3 +- drivers/rtc/alarm-dev.c | 286 +++++++++++++++++++++ drivers/rtc/alarm.c | 664 +++++++++++++++++++++++++----------------------- 4 files changed, 638 insertions(+), 325 deletions(-) create mode 100644 drivers/rtc/alarm-dev.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index ae2e0c3384d4..08015230b031 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -116,9 +116,17 @@ config RTC_INTF_ALARM help Provides non-wakeup and rtc backed wakeup alarms based on rtc or elapsed realtime, and a non-wakeup alarm on the monotonic clock. - Also provides an ioctl to set the wall time which must be used + Also provides an interface to set the wall time which must be used for elapsed realtime to work. +config RTC_INTF_ALARM_DEV + bool "Android alarm device" + depends on RTC_INTF_ALARM + default y + help + Exports the alarm interface to user-space. + + config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 36ffc1cdc20f..1fa4b96d501e 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -11,7 +11,8 @@ obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o obj-$(CONFIG_RTC_CLASS) += rtc-core.o rtc-core-y := class.o interface.o -rtc-core-$(CONFIG_RTC_INTF_ALARM) += alarm.o +obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o +obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c new file mode 100644 index 000000000000..686e6f7ed480 --- /dev/null +++ b/drivers/rtc/alarm-dev.c @@ -0,0 +1,286 @@ +/* drivers/rtc/alarm-dev.c + * + * Copyright (C) 2007-2009 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 +#include +#include +#include +#include +#include +#include + +#define ANDROID_ALARM_PRINT_INFO (1U << 0) +#define ANDROID_ALARM_PRINT_IO (1U << 1) +#define ANDROID_ALARM_PRINT_INT (1U << 2) + +static int debug_mask = ANDROID_ALARM_PRINT_INFO; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define pr_alarm(debug_level_mask, args...) \ + do { \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ + } \ + } while (0) + +#define ANDROID_ALARM_WAKEUP_MASK ( \ + ANDROID_ALARM_RTC_WAKEUP_MASK | \ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + +/* support old usespace code */ +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +static int alarm_opened; +static DEFINE_SPINLOCK(alarm_slock); +static struct wake_lock alarm_wake_lock; +static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +static uint32_t alarm_pending; +static uint32_t alarm_enabled; +static uint32_t wait_pending; + +static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT]; + +static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + unsigned long flags; + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; + enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + + if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) + return -EINVAL; + + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + if (file->private_data == NULL && + cmd != ANDROID_ALARM_SET_RTC) { + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_opened) { + spin_unlock_irqrestore(&alarm_slock, flags); + return -EBUSY; + } + alarm_opened = 1; + file->private_data = (void *)1; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + + switch (ANDROID_ALARM_BASE_CMD(cmd)) { + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d clear\n", alarm_type); + alarm_try_to_cancel(&alarms[alarm_type]); + if (alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if (!alarm_pending && !wait_pending) + wake_unlock(&alarm_wake_lock); + } + alarm_enabled &= ~alarm_type_mask; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + + case ANDROID_ALARM_SET_OLD: + case ANDROID_ALARM_SET_AND_WAIT_OLD: + if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { + rv = -EFAULT; + goto err1; + } + new_alarm_time.tv_nsec = 0; + goto from_old_alarm_set; + + case ANDROID_ALARM_SET_AND_WAIT(0): + case ANDROID_ALARM_SET(0): + if (copy_from_user(&new_alarm_time, (void __user *)arg, + sizeof(new_alarm_time))) { + rv = -EFAULT; + goto err1; + } +from_old_alarm_set: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, + new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_enabled |= alarm_type_mask; + alarm_start_range(&alarms[alarm_type], + timespec_to_ktime(new_alarm_time), + timespec_to_ktime(new_alarm_time)); + 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) + break; + /* fall though */ + case ANDROID_ALARM_WAIT: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm wait\n"); + if (!alarm_pending && wait_pending) { + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); + if (rv) + goto err1; + spin_lock_irqsave(&alarm_slock, flags); + rv = alarm_pending; + wait_pending = 1; + alarm_pending = 0; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + case ANDROID_ALARM_SET_RTC: + if (copy_from_user(&new_rtc_time, (void __user *)arg, + sizeof(new_rtc_time))) { + rv = -EFAULT; + goto err1; + } + rv = alarm_set_rtc(new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + if (rv < 0) + goto err1; + break; + case ANDROID_ALARM_GET_TIME(0): + switch (alarm_type) { + case ANDROID_ALARM_RTC_WAKEUP: + case ANDROID_ALARM_RTC: + getnstimeofday(&tmp_time); + break; + case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: + case ANDROID_ALARM_ELAPSED_REALTIME: + tmp_time = + ktime_to_timespec(alarm_get_elapsed_realtime()); + break; + case ANDROID_ALARM_TYPE_COUNT: + case ANDROID_ALARM_SYSTEMTIME: + ktime_get_ts(&tmp_time); + break; + } + if (copy_to_user((void __user *)arg, &tmp_time, + sizeof(tmp_time))) { + rv = -EFAULT; + goto err1; + } + break; + + default: + rv = -EINVAL; + goto err1; + } +err1: + return rv; +} + +static int alarm_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int alarm_release(struct inode *inode, struct file *file) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + if (file->private_data != 0) { + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + uint32_t alarm_type_mask = 1U << i; + if (alarm_enabled & alarm_type_mask) { + pr_alarm(INFO, "alarm_release: clear alarm, " + "pending %d\n", + !!(alarm_pending & alarm_type_mask)); + alarm_enabled &= ~alarm_type_mask; + } + spin_unlock_irqrestore(&alarm_slock, flags); + alarm_cancel(&alarms[i]); + spin_lock_irqsave(&alarm_slock, flags); + } + if (alarm_pending | wait_pending) { + if (alarm_pending) + pr_alarm(INFO, "alarm_release: clear " + "pending alarms %x\n", alarm_pending); + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + alarm_pending = 0; + } + alarm_opened = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static void alarm_triggered(struct alarm *alarm) +{ + unsigned long flags; + uint32_t alarm_type_mask = 1U << alarm->type; + + pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_enabled & alarm_type_mask) { + wake_lock_timeout(&alarm_wake_lock, 5 * HZ); + alarm_enabled &= ~alarm_type_mask; + alarm_pending |= alarm_type_mask; + wake_up(&alarm_wait_queue); + } + spin_unlock_irqrestore(&alarm_slock, flags); +} + +static const struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, + .open = alarm_open, + .release = alarm_release, +}; + +static struct miscdevice alarm_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "alarm", + .fops = &alarm_fops, +}; + +static int __init alarm_dev_init(void) +{ + int err; + int i; + + err = misc_register(&alarm_device); + if (err) + return err; + + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) + alarm_init(&alarms[i], i, alarm_triggered); + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); + + return 0; +} + +static void __exit alarm_dev_exit(void) +{ + misc_deregister(&alarm_device); + wake_lock_destroy(&alarm_wake_lock); +} + +module_init(alarm_dev_init); +module_exit(alarm_dev_exit); + diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index cd660eee3b27..7232a538d399 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -1,6 +1,6 @@ /* drivers/rtc/alarm.c * - * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2007-2009 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 @@ -24,24 +24,24 @@ #include #include -#define ANDROID_ALARM_PRINT_ERRORS (1U << 0) +#define ANDROID_ALARM_PRINT_ERROR (1U << 0) #define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) -#define ANDROID_ALARM_PRINT_INFO (1U << 2) -#define ANDROID_ALARM_PRINT_IO (1U << 3) -#define ANDROID_ALARM_PRINT_INT (1U << 4) -#define ANDROID_ALARM_PRINT_FLOW (1U << 5) - -#if 0 -#define ANDROID_ALARM_DPRINTF_MASK (~0) -#define ANDROID_ALARM_DPRINTF(debug_level_mask, args...) \ +#define ANDROID_ALARM_PRINT_TSET (1U << 2) +#define ANDROID_ALARM_PRINT_CALL (1U << 3) +#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4) +#define ANDROID_ALARM_PRINT_INT (1U << 5) +#define ANDROID_ALARM_PRINT_FLOW (1U << 6) + +static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \ + ANDROID_ALARM_PRINT_INIT_STATUS; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define pr_alarm(debug_level_mask, args...) \ do { \ - if (ANDROID_ALARM_DPRINTF_MASK & debug_level_mask) { \ - printk(args); \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ } \ } while (0) -#else -#define ANDROID_ALARM_DPRINTF(args...) -#endif #define ANDROID_ALARM_WAKEUP_MASK ( \ ANDROID_ALARM_RTC_WAKEUP_MASK | \ @@ -51,264 +51,307 @@ #define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ #define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) +struct alarm_queue { + struct rb_root alarms; + struct rb_node *first; + struct hrtimer timer; + ktime_t delta; + bool stopped; + ktime_t stopped_time; +}; + static struct rtc_device *alarm_rtc_dev; -static int alarm_opened; static DEFINE_SPINLOCK(alarm_slock); static DEFINE_MUTEX(alarm_setrtc_mutex); -static struct wake_lock alarm_wake_lock; static struct wake_lock alarm_rtc_wake_lock; -static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); -static uint32_t alarm_pending; -static uint32_t alarm_enabled; -static uint32_t wait_pending; static struct platform_device *alarm_platform_dev; -static struct hrtimer alarm_timer[ANDROID_ALARM_TYPE_COUNT]; -static struct timespec alarm_time[ANDROID_ALARM_TYPE_COUNT]; -static struct timespec elapsed_rtc_delta; +struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT]; +static bool suspended; -static void alarm_start_hrtimer(enum android_alarm_type alarm_type) +static void update_timer_locked(struct alarm_queue *base, bool head_removed) { - struct timespec hr_alarm_time; - if (!(alarm_enabled & (1U << alarm_type))) + struct alarm *alarm; + bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] || + base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + + if (base->stopped) { + pr_alarm(FLOW, "changed alarm while setting the wall time\n"); + return; + } + + if (is_wakeup && !suspended && head_removed) + wake_unlock(&alarm_rtc_wake_lock); + + if (!base->first) return; - hr_alarm_time = alarm_time[alarm_type]; - if (alarm_type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP || - alarm_type == ANDROID_ALARM_ELAPSED_REALTIME) - set_normalized_timespec(&hr_alarm_time, - hr_alarm_time.tv_sec + elapsed_rtc_delta.tv_sec, - hr_alarm_time.tv_nsec + elapsed_rtc_delta.tv_nsec); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm start hrtimer %d at %ld.%09ld\n", - alarm_type, hr_alarm_time.tv_sec, hr_alarm_time.tv_nsec); - hrtimer_start(&alarm_timer[alarm_type], - timespec_to_ktime(hr_alarm_time), HRTIMER_MODE_ABS); + + alarm = container_of(base->first, struct alarm, node); + + pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (is_wakeup && suspended) { + pr_alarm(FLOW, "changed alarm while suspened\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); + return; + } + + hrtimer_try_to_cancel(&base->timer); + base->timer._expires = ktime_add(base->delta, alarm->expires); + base->timer._softexpires = ktime_add(base->delta, alarm->softexpires); + hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS); } -static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static void alarm_enqueue_locked(struct alarm *alarm) { - int rv = 0; - unsigned long flags; - int i; - struct timespec new_alarm_time; - struct timespec new_rtc_time; - struct timespec tmp_time; - struct rtc_time rtc_new_rtc_time; - enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); - uint32_t alarm_type_mask = 1U << alarm_type; - - if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) - return -EINVAL; + struct alarm_queue *base = &alarms[alarm->type]; + struct rb_node **link = &base->alarms.rb_node; + struct rb_node *parent = NULL; + struct alarm *entry; + int leftmost = 1; + + pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (base->first == &alarm->node) + base->first = rb_next(&alarm->node); + if (!RB_EMPTY_NODE(&alarm->node)) { + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + } - if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EPERM; - if (file->private_data == NULL && - cmd != ANDROID_ALARM_SET_RTC) { - spin_lock_irqsave(&alarm_slock, flags); - if (alarm_opened) { - spin_unlock_irqrestore(&alarm_slock, flags); - return -EBUSY; - } - alarm_opened = 1; - file->private_data = (void *)1; - spin_unlock_irqrestore(&alarm_slock, flags); + while (*link) { + parent = *link; + entry = rb_entry(parent, struct alarm, node); + /* + * We dont care about collisions. Nodes with + * the same expiry time stay together. + */ + if (alarm->expires.tv64 < entry->expires.tv64) { + link = &(*link)->rb_left; + } else { + link = &(*link)->rb_right; + leftmost = 0; } } + if (leftmost) { + base->first = &alarm->node; + update_timer_locked(base, false); + } - switch (ANDROID_ALARM_BASE_CMD(cmd)) { - case ANDROID_ALARM_CLEAR(0): - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "alarm %d clear\n", alarm_type); - hrtimer_try_to_cancel(&alarm_timer[alarm_type]); - if (alarm_pending) { - alarm_pending &= ~alarm_type_mask; - if (!alarm_pending && !wait_pending) - wake_unlock(&alarm_wake_lock); - } - alarm_enabled &= ~alarm_type_mask; - spin_unlock_irqrestore(&alarm_slock, flags); - break; + rb_link_node(&alarm->node, parent, link); + rb_insert_color(&alarm->node, &base->alarms); +} - case ANDROID_ALARM_SET_OLD: - case ANDROID_ALARM_SET_AND_WAIT_OLD: - if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { - rv = -EFAULT; - goto err1; - } - new_alarm_time.tv_nsec = 0; - goto from_old_alarm_set; - - case ANDROID_ALARM_SET_AND_WAIT(0): - case ANDROID_ALARM_SET(0): - if (copy_from_user(&new_alarm_time, (void __user *)arg, - sizeof(new_alarm_time))) { - rv = -EFAULT; - goto err1; - } -from_old_alarm_set: - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "alarm %d set %ld.%09ld\n", alarm_type, - new_alarm_time.tv_sec, new_alarm_time.tv_nsec); - alarm_time[alarm_type] = new_alarm_time; - alarm_enabled |= alarm_type_mask; - alarm_start_hrtimer(alarm_type); - 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) - break; - /* fall though */ - case ANDROID_ALARM_WAIT: - spin_lock_irqsave(&alarm_slock, flags); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm wait\n"); - if (!alarm_pending && wait_pending) { - wake_unlock(&alarm_wake_lock); - wait_pending = 0; - } - spin_unlock_irqrestore(&alarm_slock, flags); - rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); - if (rv) - goto err1; - spin_lock_irqsave(&alarm_slock, flags); - rv = alarm_pending; - wait_pending = 1; - alarm_pending = 0; - if (rv & ANDROID_ALARM_WAKEUP_MASK) - wake_unlock(&alarm_rtc_wake_lock); - spin_unlock_irqrestore(&alarm_slock, flags); - break; - case ANDROID_ALARM_SET_RTC: - if (copy_from_user(&new_rtc_time, (void __user *)arg, - sizeof(new_rtc_time))) { - rv = -EFAULT; - goto err1; - } - rtc_time_to_tm(new_rtc_time.tv_sec, &rtc_new_rtc_time); +/** + * alarm_init - initialize an alarm + * @alarm: the alarm to be initialized + * @type: the alarm type to be used + * @function: alarm callback function + */ +void alarm_init(struct alarm *alarm, + enum android_alarm_type type, void (*function)(struct alarm *)) +{ + RB_CLEAR_NODE(&alarm->node); + alarm->type = type; + alarm->function = function; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, - "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", - new_rtc_time.tv_sec, new_rtc_time.tv_nsec, - rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, - rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, - rtc_new_rtc_time.tm_mday, - rtc_new_rtc_time.tm_year + 1900); + pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function); +} - mutex_lock(&alarm_setrtc_mutex); - spin_lock_irqsave(&alarm_slock, flags); - for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) - hrtimer_try_to_cancel(&alarm_timer[i]); - getnstimeofday(&tmp_time); - elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, - timespec_sub(tmp_time, new_rtc_time)); - spin_unlock_irqrestore(&alarm_slock, flags); - rv = do_settimeofday(&new_rtc_time); - spin_lock_irqsave(&alarm_slock, flags); - for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) - alarm_start_hrtimer(i); - spin_unlock_irqrestore(&alarm_slock, flags); - if (rv < 0) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, - "Failed to set time\n"); - mutex_unlock(&alarm_setrtc_mutex); - goto err1; - } - rv = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); - spin_lock_irqsave(&alarm_slock, flags); - alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; - wake_up(&alarm_wait_queue); - spin_unlock_irqrestore(&alarm_slock, flags); - mutex_unlock(&alarm_setrtc_mutex); - if (rv < 0) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS, - "Failed to set RTC, time will be lost on reboot\n"); - goto err1; - } - break; - case ANDROID_ALARM_GET_TIME(0): - mutex_lock(&alarm_setrtc_mutex); - spin_lock_irqsave(&alarm_slock, flags); - if (alarm_type != ANDROID_ALARM_SYSTEMTIME) { - getnstimeofday(&tmp_time); - if (alarm_type >= ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP) - tmp_time = timespec_sub(tmp_time, - elapsed_rtc_delta); - } else - ktime_get_ts(&tmp_time); - spin_unlock_irqrestore(&alarm_slock, flags); - mutex_unlock(&alarm_setrtc_mutex); - if (copy_to_user((void __user *)arg, &tmp_time, - sizeof(tmp_time))) { - rv = -EFAULT; - goto err1; - } - break; - default: - rv = -EINVAL; - goto err1; - } -err1: - return rv; +/** + * alarm_start_range - (re)start an alarm + * @alarm: the alarm to be added + * @start: earliest expiry time + * @end: expiry time + */ +void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end) +{ + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + alarm->softexpires = start; + alarm->expires = end; + alarm_enqueue_locked(alarm); + spin_unlock_irqrestore(&alarm_slock, flags); } -static int alarm_open(struct inode *inode, struct file *file) +/** + * alarm_try_to_cancel - try to deactivate an alarm + * @alarm: alarm to stop + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + * -1 when the alarm may currently be excuting the callback function and + * cannot be stopped (it may also be inactive) + */ +int alarm_try_to_cancel(struct alarm *alarm) { - file->private_data = NULL; - return 0; + struct alarm_queue *base = &alarms[alarm->type]; + unsigned long flags; + bool first = false; + int ret = 0; + + spin_lock_irqsave(&alarm_slock, flags); + if (!RB_EMPTY_NODE(&alarm->node)) { + pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires)); + ret = 1; + if (base->first == &alarm->node) { + base->first = rb_next(&alarm->node); + first = true; + } + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + if (first) + update_timer_locked(base, true); + } else + pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n", + alarm->type, alarm->function); + spin_unlock_irqrestore(&alarm_slock, flags); + if (!ret && hrtimer_callback_running(&base->timer)) + ret = -1; + return ret; } -static int alarm_release(struct inode *inode, struct file *file) +/** + * alarm_cancel - cancel an alarm and wait for the handler to finish. + * @alarm: the alarm to be cancelled + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + */ +int alarm_cancel(struct alarm *alarm) +{ + for (;;) { + int ret = alarm_try_to_cancel(alarm); + if (ret >= 0) + return ret; + cpu_relax(); + } +} + +/** + * alarm_set_rtc - set the kernel and rtc walltime + * @new_time: timespec value containing the new time + */ +int alarm_set_rtc(struct timespec new_time) { int i; + int ret; unsigned long flags; + struct rtc_time rtc_new_rtc_time; + struct timespec tmp_time; + + rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time); + + pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", + new_time.tv_sec, new_time.tv_nsec, + rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, + rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, + rtc_new_rtc_time.tm_mday, + rtc_new_rtc_time.tm_year + 1900); + mutex_lock(&alarm_setrtc_mutex); spin_lock_irqsave(&alarm_slock, flags); - if (file->private_data != 0) { - for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { - uint32_t alarm_type_mask = 1U << i; - if (alarm_enabled & alarm_type_mask) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_release: clear alarm, " - "pending %d\n", - !!(alarm_pending & alarm_type_mask)); - alarm_enabled &= ~alarm_type_mask; - } - spin_unlock_irqrestore(&alarm_slock, flags); - hrtimer_cancel(&alarm_timer[i]); - spin_lock_irqsave(&alarm_slock, flags); - } - if (alarm_pending | wait_pending) { - if (alarm_pending) - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_release: clear pending alarms " - "%x\n", alarm_pending); - wake_unlock(&alarm_wake_lock); - wait_pending = 0; - alarm_pending = 0; - } - alarm_opened = 0; + wake_lock(&alarm_rtc_wake_lock); + getnstimeofday(&tmp_time); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_try_to_cancel(&alarms[i].timer); + alarms[i].stopped = true; + alarms[i].stopped_time = timespec_to_ktime(tmp_time); } + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta, + timespec_to_ktime(timespec_sub(tmp_time, new_time))); spin_unlock_irqrestore(&alarm_slock, flags); - return 0; + ret = do_settimeofday(&new_time); + spin_lock_irqsave(&alarm_slock, flags); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + alarms[i].stopped = false; + update_timer_locked(&alarms[i], false); + } + spin_unlock_irqrestore(&alarm_slock, flags); + if (ret < 0) { + pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n"); + goto err; + } + if (!alarm_rtc_dev) { + pr_alarm(ERROR, + "alarm_set_rtc: no RTC, time will be lost on reboot\n"); + goto err; + } + ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); + if (ret < 0) + pr_alarm(ERROR, "alarm_set_rtc: " + "Failed to set RTC, time will be lost on reboot\n"); +err: + wake_unlock(&alarm_rtc_wake_lock); + mutex_unlock(&alarm_setrtc_mutex); + return ret; } -static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +/** + * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format + * + * returns the time in ktime_t format + */ +ktime_t alarm_get_elapsed_realtime(void) { + ktime_t now; unsigned long flags; - enum android_alarm_type alarm_type = (timer - alarm_timer); - uint32_t alarm_type_mask = 1U << alarm_type; + struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME]; + spin_lock_irqsave(&alarm_slock, flags); + now = base->stopped ? base->stopped_time : ktime_get_real(); + now = ktime_sub(now, base->delta); + spin_unlock_irqrestore(&alarm_slock, flags); + return now; +} + +static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +{ + struct alarm_queue *base; + struct alarm *alarm; + unsigned long flags; + ktime_t now; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, - "alarm_timer_triggered type %d\n", alarm_type); spin_lock_irqsave(&alarm_slock, flags); - if (alarm_enabled & alarm_type_mask) { - wake_lock_timeout(&alarm_wake_lock, 5 * HZ); - alarm_enabled &= ~alarm_type_mask; - alarm_pending |= alarm_type_mask; - wake_up(&alarm_wait_queue); + + base = container_of(timer, struct alarm_queue, timer); + now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer); + now = ktime_sub(now, base->delta); + + pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n", + base - alarms, ktime_to_ns(now)); + + while (base->first) { + alarm = container_of(base->first, struct alarm, node); + if (alarm->softexpires.tv64 > now.tv64) { + pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n", + alarm->function, ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + break; + } + base->first = rb_next(&alarm->node); + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + spin_unlock_irqrestore(&alarm_slock, flags); + alarm->function(alarm); + spin_lock_irqsave(&alarm_slock, flags); } + if (!base->first) + pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms); + update_timer_locked(base, true); spin_unlock_irqrestore(&alarm_slock, flags); return HRTIMER_NORESTART; } @@ -318,11 +361,11 @@ static void alarm_triggered_func(void *p) struct rtc_device *rtc = alarm_rtc_dev; if (!(rtc->irq_data & RTC_AF)) return; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT, "rtc alarm triggered\n"); + pr_alarm(INT, "rtc alarm triggered\n"); wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); } -int alarm_suspend(struct platform_device *pdev, pm_message_t state) +static int alarm_suspend(struct platform_device *pdev, pm_message_t state) { int err = 0; unsigned long flags; @@ -332,89 +375,85 @@ int alarm_suspend(struct platform_device *pdev, pm_message_t state) unsigned long rtc_alarm_time; struct timespec rtc_current_timespec; struct timespec rtc_delta; - struct timespec elapsed_realtime_alarm_time; + struct alarm_queue *wakeup_queue = NULL; + struct alarm_queue *tmp_queue = NULL; + + pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm_suspend(%p, %d)\n", pdev, state.event); spin_lock_irqsave(&alarm_slock, flags); - if (alarm_pending && !wake_lock_active(&alarm_wake_lock)) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm pending\n"); - err = -EBUSY; - goto err1; - } - if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { - spin_unlock_irqrestore(&alarm_slock, flags); - if (alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) - hrtimer_cancel(&alarm_timer[ANDROID_ALARM_RTC_WAKEUP]); - if (alarm_enabled & ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) - hrtimer_cancel(&alarm_timer[ - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]); + suspended = true; + spin_unlock_irqrestore(&alarm_slock, flags); + hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer); + hrtimer_cancel(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK].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; + if (wakeup_queue) { rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); rtc_current_timespec.tv_nsec = 0; rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_timespec.tv_sec); save_time_delta(&rtc_delta, &rtc_current_timespec); - set_normalized_timespec(&elapsed_realtime_alarm_time, - alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] - .tv_sec + elapsed_rtc_delta.tv_sec, - alarm_time[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP] - .tv_nsec + elapsed_rtc_delta.tv_nsec); - if ((alarm_enabled & ANDROID_ALARM_RTC_WAKEUP_MASK) && - (!(alarm_enabled & - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) || - timespec_compare(&alarm_time[ANDROID_ALARM_RTC_WAKEUP], - &elapsed_realtime_alarm_time) < 0)) - rtc_alarm_time = timespec_sub( - alarm_time[ANDROID_ALARM_RTC_WAKEUP], - rtc_delta).tv_sec; - else - rtc_alarm_time = timespec_sub( - elapsed_realtime_alarm_time, rtc_delta).tv_sec; + + rtc_alarm_time = timespec_sub(ktime_to_timespec( + hrtimer_get_expires(&wakeup_queue->timer)), + rtc_delta).tv_sec; + rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); rtc_alarm.enabled = 1; rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, + pr_alarm(SUSPEND, "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", rtc_alarm_time, rtc_current_time, rtc_delta.tv_sec, rtc_delta.tv_nsec); if (rtc_current_time + 1 >= rtc_alarm_time) { - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm about to go off\n"); + pr_alarm(SUSPEND, "alarm about to go off\n"); memset(&rtc_alarm, 0, sizeof(rtc_alarm)); rtc_alarm.enabled = 0; rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); spin_lock_irqsave(&alarm_slock, flags); + suspended = false; wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ); - alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); - alarm_start_hrtimer( - ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], + false); + update_timer_locked(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false); err = -EBUSY; spin_unlock_irqrestore(&alarm_slock, flags); } - } else { -err1: - spin_unlock_irqrestore(&alarm_slock, flags); } return err; } -int alarm_resume(struct platform_device *pdev) +static int alarm_resume(struct platform_device *pdev) { struct rtc_wkalrm alarm; - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW, - "alarm_resume(%p)\n", pdev); - if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) { - memset(&alarm, 0, sizeof(alarm)); - alarm.enabled = 0; - rtc_set_alarm(alarm_rtc_dev, &alarm); - alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP); - alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP); - } + unsigned long flags; + + pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev); + + memset(&alarm, 0, sizeof(alarm)); + alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &alarm); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = false; + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false); + update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], + false); + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; } @@ -422,19 +461,6 @@ static struct rtc_task alarm_rtc_task = { .func = alarm_triggered_func }; -static struct file_operations alarm_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = alarm_ioctl, - .open = alarm_open, - .release = alarm_release, -}; - -static struct miscdevice alarm_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "alarm", - .fops = &alarm_fops, -}; - static int rtc_alarm_add_device(struct device *dev, struct class_interface *class_intf) { @@ -448,9 +474,6 @@ static int rtc_alarm_add_device(struct device *dev, goto err1; } - err = misc_register(&alarm_device); - if (err) - goto err1; alarm_platform_dev = platform_device_register_simple("alarm", -1, NULL, 0); if (IS_ERR(alarm_platform_dev)) { @@ -461,16 +484,14 @@ static int rtc_alarm_add_device(struct device *dev, if (err) goto err3; alarm_rtc_dev = rtc; + pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name); mutex_unlock(&alarm_setrtc_mutex); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, "alarm: parent %p\n", - alarm_platform_dev->dev.power.pm_parent); return 0; err3: platform_device_unregister(alarm_platform_dev); err2: - misc_deregister(&alarm_device); err1: mutex_unlock(&alarm_setrtc_mutex); return err; @@ -480,9 +501,9 @@ static void rtc_alarm_remove_device(struct device *dev, struct class_interface *class_intf) { if (dev == &alarm_rtc_dev->dev) { + pr_alarm(INIT_STATUS, "lost rtc device for alarms"); rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); platform_device_unregister(alarm_platform_dev); - misc_deregister(&alarm_device); alarm_rtc_dev = NULL; } } @@ -503,7 +524,7 @@ static struct platform_driver alarm_driver = { static int __init alarm_late_init(void) { unsigned long flags; - struct timespec system_time; + struct timespec tmp_time, system_time; /* this needs to run after the rtc is read at boot */ spin_lock_irqsave(&alarm_slock, flags); @@ -511,33 +532,32 @@ static int __init alarm_late_init(void) * elasped realtime to be (boot_systemtime + rtc - boot_rtc) == * (rtc - (boot_rtc - boot_systemtime)) */ - getnstimeofday(&elapsed_rtc_delta); + getnstimeofday(&tmp_time); ktime_get_ts(&system_time); - elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, system_time); - spin_unlock_irqrestore(&alarm_slock, flags); + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + timespec_to_ktime(timespec_sub(tmp_time, system_time)); - ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO, - "alarm_late_init: rtc to elapsed realtime delta %ld.%09ld\n", - elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec); + spin_unlock_irqrestore(&alarm_slock, flags); return 0; } -static int __init alarm_init(void) +static int __init alarm_driver_init(void) { int err; int i; for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { - hrtimer_init(&alarm_timer[i], CLOCK_REALTIME, HRTIMER_MODE_ABS); - alarm_timer[i].function = alarm_timer_triggered; + hrtimer_init(&alarms[i].timer, + CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarms[i].timer.function = alarm_timer_triggered; } - hrtimer_init(&alarm_timer[ANDROID_ALARM_SYSTEMTIME], + hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - alarm_timer[ANDROID_ALARM_SYSTEMTIME].function = alarm_timer_triggered; + alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; err = platform_driver_register(&alarm_driver); if (err < 0) goto err1; - wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); rtc_alarm_interface.class = rtc_class; err = class_interface_register(&rtc_alarm_interface); @@ -548,7 +568,6 @@ static int __init alarm_init(void) err2: wake_lock_destroy(&alarm_rtc_wake_lock); - wake_lock_destroy(&alarm_wake_lock); platform_driver_unregister(&alarm_driver); err1: return err; @@ -558,11 +577,10 @@ static void __exit alarm_exit(void) { class_interface_unregister(&rtc_alarm_interface); wake_lock_destroy(&alarm_rtc_wake_lock); - wake_lock_destroy(&alarm_wake_lock); platform_driver_unregister(&alarm_driver); } late_initcall(alarm_late_init); -module_init(alarm_init); +module_init(alarm_driver_init); module_exit(alarm_exit); -- cgit v1.2.3 From c7b52dfe8aaeb3b319dd970ab1d869a00de55296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 25 Jul 2008 20:58:15 -0700 Subject: rtc: Try to prevent RTC errors from accumulating. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we resume we only know how many whole seconds has elapsed. These errors would accumulate in delta. We now only set the delta if it would change by more than two seconds. If we drift back by by more than a second add one in resume. Signed-off-by: Arve Hjønnevåg --- drivers/rtc/class.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 565562ba6ac9..11d7ab90a67c 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -41,25 +41,32 @@ static void rtc_device_release(struct device *dev) */ static struct timespec delta; +static struct timespec delta_delta; static time_t oldtime; static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec ts = current_kernel_time(); + struct timespec ts; + struct timespec new_delta; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; + getnstimeofday(&ts); rtc_read_time(rtc, &tm); rtc_tm_to_time(&tm, &oldtime); /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ - set_normalized_timespec(&delta, + set_normalized_timespec(&new_delta, ts.tv_sec - oldtime, ts.tv_nsec - (NSEC_PER_SEC >> 1)); + /* prevent 1/2 sec errors from accumulating */ + delta_delta = timespec_sub(new_delta, delta); + if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) + delta = new_delta; return 0; } @@ -79,6 +86,8 @@ static int rtc_resume(struct device *dev) return 0; } rtc_tm_to_time(&tm, &newtime); + if (delta_delta.tv_sec < -1) + newtime++; if (newtime <= oldtime) { if (newtime < oldtime) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); -- cgit v1.2.3 From cf23783c7aca3ebd5974611b1295c1c99f5254c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 12 Mar 2010 20:05:32 -0800 Subject: rtc: alarm: Don't use save_time_delta. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iaefeca497de02fe36b7f5d79075912f6e349ec53 Signed-off-by: Arve HjønnevÃ¥g --- drivers/rtc/alarm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c index 7232a538d399..1250edfbdf33 100644 --- a/drivers/rtc/alarm.c +++ b/drivers/rtc/alarm.c @@ -373,8 +373,8 @@ static int alarm_suspend(struct platform_device *pdev, pm_message_t state) struct rtc_time rtc_current_rtc_time; unsigned long rtc_current_time; unsigned long rtc_alarm_time; - struct timespec rtc_current_timespec; struct timespec rtc_delta; + struct timespec wall_time; struct alarm_queue *wakeup_queue = NULL; struct alarm_queue *tmp_queue = NULL; @@ -398,10 +398,11 @@ static int alarm_suspend(struct platform_device *pdev, pm_message_t state) wakeup_queue = tmp_queue; if (wakeup_queue) { rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); - rtc_current_timespec.tv_nsec = 0; - rtc_tm_to_time(&rtc_current_rtc_time, - &rtc_current_timespec.tv_sec); - save_time_delta(&rtc_delta, &rtc_current_timespec); + getnstimeofday(&wall_time); + rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); + set_normalized_timespec(&rtc_delta, + wall_time.tv_sec - rtc_current_time, + wall_time.tv_nsec); rtc_alarm_time = timespec_sub(ktime_to_timespec( hrtimer_get_expires(&wakeup_queue->timer)), -- cgit v1.2.3