| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- /* 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.
- *
- */
- #ifdef pr_fmt
- #undef pr_fmt
- #endif
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/time.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/platform_device.h>
- #include <linux/sched.h>
- #include <linux/spinlock.h>
- #include <linux/uaccess.h>
- #include <linux/rtc.h>
- #include <linux/alarmtimer.h>
- #include "android_alarm.h"
- #include <linux/ioctl.h>
- #define LOG_MYTAG "Power/Alarm"
- #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 alarm_dbg(debug_level_mask, fmt, args...) \
- do { \
- if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) \
- pr_debug(LOG_MYTAG fmt, ##args); \
- } while (0)
- #define ANDROID_ALARM_WAKEUP_MASK ( \
- ANDROID_ALARM_RTC_WAKEUP_MASK | \
- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
- static int alarm_opened;
- static DEFINE_SPINLOCK(alarm_slock);
- static struct wakeup_source 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;
- struct devalarm {
- union {
- struct hrtimer hrt;
- struct alarm alrm;
- } u;
- enum android_alarm_type type;
- };
- static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
- static int is_wakeup(enum android_alarm_type type)
- {
- return (type == ANDROID_ALARM_RTC_WAKEUP ||
- type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
- }
- static void devalarm_start(struct devalarm *alrm, ktime_t exp)
- {
- if (is_wakeup(alrm->type))
- alarm_start(&alrm->u.alrm, exp);
- else
- hrtimer_start(&alrm->u.hrt, exp, HRTIMER_MODE_ABS);
- }
- static int devalarm_try_to_cancel(struct devalarm *alrm)
- {
- if (is_wakeup(alrm->type))
- return alarm_try_to_cancel(&alrm->u.alrm);
- return hrtimer_try_to_cancel(&alrm->u.hrt);
- }
- static void devalarm_cancel(struct devalarm *alrm)
- {
- if (is_wakeup(alrm->type))
- alarm_cancel(&alrm->u.alrm);
- else
- hrtimer_cancel(&alrm->u.hrt);
- }
- void alarm_set_power_on(struct timespec new_pwron_time, bool logo)
- {
- unsigned long pwron_time;
- struct rtc_wkalrm alm;
- struct rtc_device *alarm_rtc_dev;
- alarm_dbg(INFO, "alarm set power on\n");
- #ifdef RTC_PWRON_SEC
- /* round down the second */
- new_pwron_time.tv_sec = (new_pwron_time.tv_sec / 60) * 60;
- #endif
- if (new_pwron_time.tv_sec > 0) {
- pwron_time = new_pwron_time.tv_sec;
- #ifdef RTC_PWRON_SEC
- pwron_time += RTC_PWRON_SEC;
- #endif
- alm.enabled = (logo ? 3 : 2);
- } else {
- pwron_time = 0;
- alm.enabled = 4;
- }
- alarm_rtc_dev = alarmtimer_get_rtcdev();
- rtc_time_to_tm(pwron_time, &alm.time);
- rtc_set_alarm(alarm_rtc_dev, &alm);
- rtc_set_alarm_poweron(alarm_rtc_dev, &alm);
- }
- void alarm_get_power_on(struct rtc_wkalrm *alm)
- {
- if (!alm)
- return;
- memset(alm, 0, sizeof(struct rtc_wkalrm));
- #ifndef CONFIG_MTK_FPGA
- rtc_read_pwron_alarm(alm);
- #endif
- }
- static void alarm_clear(enum android_alarm_type alarm_type,
- struct timespec *ts)
- {
- uint32_t alarm_type_mask = 1U << alarm_type;
- unsigned long flags;
- alarm_dbg(IO, "alarm %d clear\n", alarm_type);
- if (alarm_type == ANDROID_ALARM_POWER_ON ||
- alarm_type == ANDROID_ALARM_POWER_ON_LOGO) {
- ts->tv_sec = 0;
- alarm_set_power_on(*ts, false);
- return;
- }
- spin_lock_irqsave(&alarm_slock, flags);
- devalarm_try_to_cancel(&alarms[alarm_type]);
- if (alarm_pending) {
- alarm_pending &= ~alarm_type_mask;
- if (!alarm_pending && !wait_pending)
- __pm_relax(&alarm_wake_lock);
- }
- alarm_enabled &= ~alarm_type_mask;
- spin_unlock_irqrestore(&alarm_slock, flags);
- }
- static void alarm_set(enum android_alarm_type alarm_type,
- struct timespec *ts)
- {
- uint32_t alarm_type_mask = 1U << alarm_type;
- unsigned long flags;
- if (alarm_type == ANDROID_ALARM_POWER_ON) {
- alarm_set_power_on(*ts, false);
- return;
- }
- if (alarm_type == ANDROID_ALARM_POWER_ON_LOGO) {
- alarm_set_power_on(*ts, true);
- return;
- }
- spin_lock_irqsave(&alarm_slock, flags);
- alarm_enabled |= alarm_type_mask;
- devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
- spin_unlock_irqrestore(&alarm_slock, flags);
- }
- static int alarm_wait(void)
- {
- unsigned long flags;
- int rv = 0;
- spin_lock_irqsave(&alarm_slock, flags);
- alarm_dbg(IO, "alarm wait\n");
- if (!alarm_pending && wait_pending) {
- __pm_relax(&alarm_wake_lock);
- wait_pending = 0;
- }
- spin_unlock_irqrestore(&alarm_slock, flags);
- rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
- if (rv)
- return rv;
- spin_lock_irqsave(&alarm_slock, flags);
- rv = alarm_pending;
- wait_pending = 1;
- alarm_pending = 0;
- spin_unlock_irqrestore(&alarm_slock, flags);
- return rv;
- }
- static int alarm_set_rtc(struct timespec *ts)
- {
- struct rtc_time new_rtc_tm;
- struct rtc_device *rtc_dev;
- unsigned long flags;
- int rv = 0;
- rtc_time_to_tm(ts->tv_sec, &new_rtc_tm);
- alarm_dbg(INFO, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n",
- ts->tv_sec, ts->tv_nsec,
- new_rtc_tm.tm_hour, new_rtc_tm.tm_min,
- new_rtc_tm.tm_sec, new_rtc_tm.tm_mon + 1,
- new_rtc_tm.tm_mday,
- new_rtc_tm.tm_year + 1900);
- rtc_dev = alarmtimer_get_rtcdev();
- rv = do_settimeofday(ts);
- if (rv < 0)
- return rv;
- if (rtc_dev)
- rv = rtc_set_time(rtc_dev, &new_rtc_tm);
- spin_lock_irqsave(&alarm_slock, flags);
- alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
- wake_up(&alarm_wait_queue);
- spin_unlock_irqrestore(&alarm_slock, flags);
- return rv;
- }
- static int alarm_get_time(enum android_alarm_type alarm_type,
- struct timespec *ts)
- {
- int rv = 0;
- switch (alarm_type) {
- case ANDROID_ALARM_RTC_WAKEUP:
- case ANDROID_ALARM_RTC:
- getnstimeofday(ts);
- break;
- case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
- case ANDROID_ALARM_ELAPSED_REALTIME:
- get_monotonic_boottime(ts);
- break;
- case ANDROID_ALARM_SYSTEMTIME:
- ktime_get_ts(ts);
- break;
- case ANDROID_ALARM_POWER_ON:
- case ANDROID_ALARM_POWER_ON_LOGO:
- break;
- default:
- rv = -EINVAL;
- }
- return rv;
- }
- static long alarm_do_ioctl(struct file *file, unsigned int cmd,
- struct timespec *ts, struct rtc_wkalrm *alm)
- {
- int rv = 0;
- unsigned long flags;
- enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
- alarm_dbg(INFO, "alarm_do_ioctl cmd:%d type:%d (%lu)\n",
- cmd, alarm_type, (uintptr_t)file->private_data);
- if (alarm_type >= ANDROID_ALARM_TYPE_COUNT &&
- alarm_type != ANDROID_ALARM_POWER_ON &&
- alarm_type != ANDROID_ALARM_POWER_ON_LOGO) {
- return -EINVAL;
- }
- if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)
- && ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_IPO(0)
- && ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_POWER_ON_IPO) {
- 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);
- alarm_dbg(INFO, "alarm_do_ioctl EBUSY\n");
- file->private_data = NULL;
- return -EBUSY;
- }
- alarm_opened = 1;
- file->private_data = (void *)1;
- spin_unlock_irqrestore(&alarm_slock, flags);
- alarm_dbg(INFO, "alarm_do_ioctl opened\n");
- }
- }
- switch (ANDROID_ALARM_BASE_CMD(cmd)) {
- case ANDROID_ALARM_CLEAR(0):
- alarm_clear(alarm_type, ts);
- break;
- case ANDROID_ALARM_SET(0):
- alarm_set(alarm_type, ts);
- break;
- case ANDROID_ALARM_SET_AND_WAIT(0):
- alarm_set(alarm_type, ts);
- /* fall though */
- case ANDROID_ALARM_WAIT:
- rv = alarm_wait();
- break;
- case ANDROID_ALARM_SET_IPO(0):
- alarm_set(alarm_type, ts);
- break;
- case ANDROID_ALARM_SET_AND_WAIT_IPO(0):
- alarm_set(alarm_type, ts);
- /* fall though */
- case ANDROID_ALARM_WAIT_IPO:
- rv = alarm_wait();
- break;
- case ANDROID_ALARM_SET_RTC:
- rv = alarm_set_rtc(ts);
- break;
- case ANDROID_ALARM_GET_TIME(0):
- rv = alarm_get_time(alarm_type, ts);
- break;
- case ANDROID_ALARM_GET_POWER_ON:
- alarm_get_power_on(alm);
- break;
- case ANDROID_ALARM_GET_POWER_ON_IPO:
- alarm_get_power_on(alm);
- break;
- default:
- rv = -EINVAL;
- }
- return rv;
- }
- static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- struct timespec ts;
- struct rtc_wkalrm pwron_alm;
- int rv;
- switch (ANDROID_ALARM_BASE_CMD(cmd)) {
- case ANDROID_ALARM_SET_AND_WAIT(0):
- case ANDROID_ALARM_SET(0):
- case ANDROID_ALARM_SET_RTC:
- case ANDROID_ALARM_SET_IPO(0):
- if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
- return -EFAULT;
- break;
- }
- rv = alarm_do_ioctl(file, cmd, &ts, &pwron_alm);
- if (rv)
- return rv;
- switch (ANDROID_ALARM_BASE_CMD(cmd)) {
- case ANDROID_ALARM_GET_TIME(0):
- if (copy_to_user((void __user *)arg, &ts, sizeof(ts)))
- return -EFAULT;
- break;
- case ANDROID_ALARM_GET_POWER_ON:
- case ANDROID_ALARM_GET_POWER_ON_IPO:
- if (copy_to_user((void __user *)arg,
- &pwron_alm, sizeof(struct rtc_wkalrm))) {
- rv = -EFAULT;
- return rv;
- }
- break;
- default:
- break;
- }
- return 0;
- }
- #ifdef CONFIG_COMPAT
- static long alarm_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- struct timespec ts;
- struct rtc_wkalrm pwron_alm;
- int rv;
- switch (ANDROID_ALARM_BASE_CMD(cmd)) {
- case ANDROID_ALARM_SET_AND_WAIT_COMPAT(0):
- case ANDROID_ALARM_SET_COMPAT(0):
- case ANDROID_ALARM_SET_RTC_COMPAT:
- case ANDROID_ALARM_SET_IPO_COMPAT(0):
- if (compat_get_timespec(&ts, (void __user *)arg))
- return -EFAULT;
- /* fall through */
- case ANDROID_ALARM_GET_TIME_COMPAT(0):
- cmd = ANDROID_ALARM_COMPAT_TO_NORM(cmd);
- break;
- }
- rv = alarm_do_ioctl(file, cmd, &ts, &pwron_alm);
- if (rv)
- return rv;
- switch (ANDROID_ALARM_BASE_CMD(cmd)) {
- case ANDROID_ALARM_GET_TIME(0): /* NOTE: we modified cmd above */
- if (compat_put_timespec(&ts, (void __user *)arg))
- return -EFAULT;
- break;
- case ANDROID_ALARM_GET_POWER_ON:
- case ANDROID_ALARM_GET_POWER_ON_IPO:
- if (copy_to_user((void __user *)arg, &pwron_alm,
- sizeof(struct rtc_wkalrm))) {
- rv = -EFAULT;
- return rv;
- }
- break;
- }
- return 0;
- }
- #endif
- static int alarm_open(struct inode *inode, struct file *file)
- {
- file->private_data = NULL;
- alarm_dbg(INFO, "alarm_open (%d:%d)\n", current->tgid, current->pid);
- 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) {
- for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
- uint32_t alarm_type_mask = 1U << i;
- if (alarm_enabled & alarm_type_mask) {
- alarm_dbg(INFO,
- "%s: clear alarm, pending %d\n",
- __func__,
- !!(alarm_pending & alarm_type_mask));
- alarm_enabled &= ~alarm_type_mask;
- }
- spin_unlock_irqrestore(&alarm_slock, flags);
- devalarm_cancel(&alarms[i]);
- spin_lock_irqsave(&alarm_slock, flags);
- }
- if (alarm_pending | wait_pending) {
- if (alarm_pending)
- alarm_dbg(INFO, "%s: clear pending alarms %x\n",
- __func__, alarm_pending);
- __pm_relax(&alarm_wake_lock);
- wait_pending = 0;
- alarm_pending = 0;
- }
- alarm_opened = 0;
- }
- spin_unlock_irqrestore(&alarm_slock, flags);
- alarm_dbg(INFO, "alarm_release (%d:%d)(%lu)\n",
- current->tgid, current->pid, (uintptr_t)file->private_data);
- return 0;
- }
- static void devalarm_triggered(struct devalarm *alarm)
- {
- unsigned long flags;
- uint32_t alarm_type_mask = 1U << alarm->type;
- alarm_dbg(INT, "%s: type %d\n", __func__, alarm->type);
- spin_lock_irqsave(&alarm_slock, flags);
- if (alarm_enabled & alarm_type_mask) {
- __pm_wakeup_event(&alarm_wake_lock, 5000); /* 5secs */
- alarm_enabled &= ~alarm_type_mask;
- alarm_pending |= alarm_type_mask;
- wake_up(&alarm_wait_queue);
- }
- spin_unlock_irqrestore(&alarm_slock, flags);
- }
- static enum hrtimer_restart devalarm_hrthandler(struct hrtimer *hrt)
- {
- struct devalarm *devalrm = container_of(hrt, struct devalarm, u.hrt);
- alarm_dbg(INT, "devalarm_hrthandler\n");
- devalarm_triggered(devalrm);
- return HRTIMER_NORESTART;
- }
- static enum alarmtimer_restart devalarm_alarmhandler(struct alarm *alrm,
- ktime_t now)
- {
- struct devalarm *devalrm = container_of(alrm, struct devalarm, u.alrm);
- alarm_dbg(INT, "devalarm_alarmhandler\n");
- devalarm_triggered(devalrm);
- return ALARMTIMER_NORESTART;
- }
- static const struct file_operations alarm_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = alarm_ioctl,
- .open = alarm_open,
- .release = alarm_release,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = alarm_compat_ioctl,
- #endif
- };
- 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;
- alarm_init(&alarms[ANDROID_ALARM_RTC_WAKEUP].u.alrm,
- ALARM_REALTIME, devalarm_alarmhandler);
- hrtimer_init(&alarms[ANDROID_ALARM_RTC].u.hrt,
- CLOCK_REALTIME, HRTIMER_MODE_ABS);
- alarm_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].u.alrm,
- ALARM_BOOTTIME, devalarm_alarmhandler);
- hrtimer_init(&alarms[ANDROID_ALARM_ELAPSED_REALTIME].u.hrt,
- CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
- hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt,
- CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
- alarms[i].type = i;
- if (!is_wakeup(i))
- alarms[i].u.hrt.function = devalarm_hrthandler;
- }
- wakeup_source_init(&alarm_wake_lock, "alarm");
- return 0;
- }
- static void __exit alarm_dev_exit(void)
- {
- misc_deregister(&alarm_device);
- wakeup_source_trash(&alarm_wake_lock);
- }
- module_init(alarm_dev_init);
- module_exit(alarm_dev_exit);
|