From fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 Mon Sep 17 00:00:00 2001 From: Jonathan Herman Date: Tue, 22 Jan 2013 10:38:37 -0500 Subject: Added missing tegra files. --- drivers/rtc/alarm-dev.c | 286 +++++++++++++++++++++ drivers/rtc/alarm.c | 590 +++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-max77663.c | 617 +++++++++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-max8907c.c | 323 ++++++++++++++++++++++++ drivers/rtc/rtc-ricoh583.c | 403 +++++++++++++++++++++++++++++ drivers/rtc/rtc-tps6591x.c | 546 +++++++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-tps80031.c | 449 +++++++++++++++++++++++++++++++++ 7 files changed, 3214 insertions(+) create mode 100644 drivers/rtc/alarm-dev.c create mode 100644 drivers/rtc/alarm.c create mode 100644 drivers/rtc/rtc-max77663.c create mode 100644 drivers/rtc/rtc-max8907c.c create mode 100644 drivers/rtc/rtc-ricoh583.c create mode 100644 drivers/rtc/rtc-tps6591x.c create mode 100644 drivers/rtc/rtc-tps80031.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/alarm-dev.c b/drivers/rtc/alarm-dev.c new file mode 100644 index 00000000000..686e6f7ed48 --- /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 new file mode 100644 index 00000000000..28b0df836a3 --- /dev/null +++ b/drivers/rtc/alarm.c @@ -0,0 +1,590 @@ +/* drivers/rtc/alarm.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 + +#define ANDROID_ALARM_PRINT_ERROR (1U << 0) +#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) +#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 (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) + +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 DEFINE_SPINLOCK(alarm_slock); +static DEFINE_MUTEX(alarm_setrtc_mutex); +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 void update_timer_locked(struct alarm_queue *base, bool head_removed) +{ + 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; + + 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.node.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 void alarm_enqueue_locked(struct alarm *alarm) +{ + 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; + bool was_first = false; + + 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); + was_first = true; + } + if (!RB_EMPTY_NODE(&alarm->node)) { + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + } + + 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; + if (leftmost || was_first) + update_timer_locked(base, was_first); + + rb_link_node(&alarm->node, parent, link); + rb_insert_color(&alarm->node, &base->alarms); +} + +/** + * 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; + + pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function); +} + + +/** + * 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); +} + +/** + * 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) +{ + 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; +} + +/** + * 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); + 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); + 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; +} + +/** + * 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; + 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; + + spin_lock_irqsave(&alarm_slock, flags); + + 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; +} + +static void alarm_triggered_func(void *p) +{ + struct rtc_device *rtc = alarm_rtc_dev; + if (!(rtc->irq_data & RTC_AF)) + return; + pr_alarm(INT, "rtc alarm triggered\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); +} + +static 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_delta; + struct timespec wall_time; + struct alarm_queue *wakeup_queue = NULL; + struct alarm_queue *tmp_queue = NULL; + + pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = true; + spin_unlock_irqrestore(&alarm_slock, flags); + + hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer); + hrtimer_cancel(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_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; + if (wakeup_queue) { + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + 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)), + 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); + 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) { + 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); + 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); + } + } + return err; +} + +static int alarm_resume(struct platform_device *pdev) +{ + struct rtc_wkalrm alarm; + 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; +} + +static struct rtc_task alarm_rtc_task = { + .func = alarm_triggered_func +}; + +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; + } + + 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; + pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name); + mutex_unlock(&alarm_setrtc_mutex); + + return 0; + +err3: + platform_device_unregister(alarm_platform_dev); +err2: +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) { + pr_alarm(INIT_STATUS, "lost rtc device for alarms"); + rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); + platform_device_unregister(alarm_platform_dev); + 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 tmp_time, 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(&tmp_time); + ktime_get_ts(&system_time); + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + timespec_to_ktime(timespec_sub(tmp_time, system_time)); + + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static int __init alarm_driver_init(void) +{ + int err; + int i; + + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_init(&alarms[i].timer, + CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarms[i].timer.function = alarm_timer_triggered; + } + hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; + err = platform_driver_register(&alarm_driver); + if (err < 0) + goto err1; + 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); + 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); + platform_driver_unregister(&alarm_driver); +} + +late_initcall(alarm_late_init); +module_init(alarm_driver_init); +module_exit(alarm_exit); + diff --git a/drivers/rtc/rtc-max77663.c b/drivers/rtc/rtc-max77663.c new file mode 100644 index 00000000000..874a2df86dc --- /dev/null +++ b/drivers/rtc/rtc-max77663.c @@ -0,0 +1,617 @@ +/* + * drivers/rtc/rtc-max77663.c + * Max77663 RTC driver + * + * Copyright 2011 Maxim Integrated Products, Inc. + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include + +/* RTC Registers */ +#define MAX77663_RTC_IRQ 0x00 +#define MAX77663_RTC_IRQ_MASK 0x01 +#define MAX77663_RTC_CTRL_MODE 0x02 +#define MAX77663_RTC_CTRL 0x03 +#define MAX77663_RTC_UPDATE0 0x04 +#define MAX77663_RTC_UPDATE1 0x05 +#define MAX77663_RTC_SEC 0x07 +#define MAX77663_RTC_MIN 0x08 +#define MAX77663_RTC_HOUR 0x09 +#define MAX77663_RTC_WEEKDAY 0x0A +#define MAX77663_RTC_MONTH 0x0B +#define MAX77663_RTC_YEAR 0x0C +#define MAX77663_RTC_MONTHDAY 0x0D +#define MAX77663_RTC_ALARM_SEC1 0x0E +#define MAX77663_RTC_ALARM_MIN1 0x0F +#define MAX77663_RTC_ALARM_HOUR1 0x10 +#define MAX77663_RTC_ALARM_WEEKDAY1 0x11 +#define MAX77663_RTC_ALARM_MONTH1 0x12 +#define MAX77663_RTC_ALARM_YEAR1 0x13 +#define MAX77663_RTC_ALARM_MONTHDAY1 0x14 + +#define RTC_IRQ_60SEC_MASK (1 << 0) +#define RTC_IRQ_ALARM1_MASK (1 << 1) +#define RTC_IRQ_ALARM2_MASK (1 << 2) +#define RTC_IRQ_SMPL_MASK (1 << 3) +#define RTC_IRQ_1SEC_MASK (1 << 4) +#define RTC_IRQ_MASK 0x1F + +#define BCD_MODE_MASK (1 << 0) +#define HR_MODE_MASK (1 << 1) + +#define WB_UPDATE_MASK (1 << 0) +#define FLAG_AUTO_CLEAR_MASK (1 << 1) +#define FREEZE_SEC_MASK (1 << 2) +#define RTC_WAKE_MASK (1 << 3) +#define RB_UPDATE_MASK (1 << 4) + +#define WB_UPDATE_FLAG_MASK (1 << 0) +#define RB_UPDATE_FLAG_MASK (1 << 1) + +#define SEC_MASK 0x7F +#define MIN_MASK 0x7F +#define HOUR_MASK 0x3F +#define WEEKDAY_MASK 0x7F +#define MONTH_MASK 0x1F +#define YEAR_MASK 0xFF +#define MONTHDAY_MASK 0x3F + +#define ALARM_EN_MASK 0x80 +#define ALARM_EN_SHIFT 7 + +#define RTC_YEAR_BASE 100 +#define RTC_YEAR_MAX 99 + +/* ON/OFF Registers */ +#define MAX77663_REG_ONOFF_CFG2 0x42 + +#define ONOFF_WK_ALARM1_MASK (1 << 2) + +enum { + RTC_SEC, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_MONTHDAY, + RTC_NR +}; + +struct max77663_rtc { + struct rtc_device *rtc; + struct device *dev; + + struct mutex io_lock; + int irq; + u8 irq_mask; +}; + +static inline struct device *_to_parent(struct max77663_rtc *rtc) +{ + return rtc->dev->parent; +} + +static inline int max77663_rtc_update_buffer(struct max77663_rtc *rtc, + int write) +{ + struct device *parent = _to_parent(rtc); + u8 val = FLAG_AUTO_CLEAR_MASK | RTC_WAKE_MASK; + int ret; + + if (write) + val |= WB_UPDATE_MASK; + else + val |= RB_UPDATE_MASK; + + dev_dbg(rtc->dev, "rtc_update_buffer: write=%d, addr=0x%x, val=0x%x\n", + write, MAX77663_RTC_UPDATE0, val); + ret = max77663_write(parent, MAX77663_RTC_UPDATE0, &val, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_update_buffer: " + "Failed to get rtc update0\n"); + return ret; + } + + /* + * Must wait 14ms for buffer update. + * If the sleeping time is 10us - 20ms, usleep_range() is recommended. + * Please refer Documentation/timers/timers-howto.txt. + */ + usleep_range(14000, 14000); + + return 0; +} + +static inline int max77663_rtc_write(struct max77663_rtc *rtc, u8 addr, + void *values, u32 len, int update_buffer) +{ + struct device *parent = _to_parent(rtc); + int ret; + + mutex_lock(&rtc->io_lock); + + dev_dbg(rtc->dev, "rtc_write: addr=0x%x, values=0x%x, len=%u, " + "update_buffer=%d\n", + addr, *((u8 *)values), len, update_buffer); + ret = max77663_write(parent, addr, values, len, 1); + if (ret < 0) + goto out; + + if (update_buffer) + ret = max77663_rtc_update_buffer(rtc, 1); + +out: + mutex_unlock(&rtc->io_lock); + return ret; +} + +static inline int max77663_rtc_read(struct max77663_rtc *rtc, u8 addr, + void *values, u32 len, int update_buffer) +{ + struct device *parent = _to_parent(rtc); + int ret; + + mutex_lock(&rtc->io_lock); + + if (update_buffer) { + ret = max77663_rtc_update_buffer(rtc, 0); + if (ret < 0) + goto out; + } + + ret = max77663_read(parent, addr, values, len, 1); + dev_dbg(rtc->dev, "rtc_read: addr=0x%x, values=0x%x, len=%u, " + "update_buffer=%d\n", + addr, *((u8 *)values), len, update_buffer); + +out: + mutex_unlock(&rtc->io_lock); + return ret; +} + +static inline int max77663_rtc_reg_to_tm(struct max77663_rtc *rtc, u8 *buf, + struct rtc_time *tm) +{ + int wday = buf[RTC_WEEKDAY] & WEEKDAY_MASK; + + if (unlikely(!wday)) { + dev_err(rtc->dev, + "rtc_reg_to_tm: Invalid day of week, %d\n", wday); + return -EINVAL; + } + + tm->tm_sec = (int)(buf[RTC_SEC] & SEC_MASK); + tm->tm_min = (int)(buf[RTC_MIN] & MIN_MASK); + tm->tm_hour = (int)(buf[RTC_HOUR] & HOUR_MASK); + tm->tm_mday = (int)(buf[RTC_MONTHDAY] & MONTHDAY_MASK); + tm->tm_mon = (int)(buf[RTC_MONTH] & MONTH_MASK) - 1; + tm->tm_year = (int)(buf[RTC_YEAR] & YEAR_MASK) + RTC_YEAR_BASE; + tm->tm_wday = ffs(wday) - 1; + + return 0; +} + +static inline int max77663_rtc_tm_to_reg(struct max77663_rtc *rtc, u8 *buf, + struct rtc_time *tm, int alarm) +{ + u8 alarm_mask = alarm ? ALARM_EN_MASK : 0; + + if (unlikely((tm->tm_year < RTC_YEAR_BASE) || + (tm->tm_year > RTC_YEAR_BASE + RTC_YEAR_MAX))) { + dev_err(rtc->dev, + "rtc_tm_to_reg: Invalid year, %d\n", tm->tm_year); + return -EINVAL; + } + + buf[RTC_SEC] = tm->tm_sec | alarm_mask; + buf[RTC_MIN] = tm->tm_min | alarm_mask; + buf[RTC_HOUR] = tm->tm_hour | alarm_mask; + buf[RTC_MONTHDAY] = tm->tm_mday | alarm_mask; + buf[RTC_MONTH] = (tm->tm_mon + 1) | alarm_mask; + buf[RTC_YEAR] = (tm->tm_year - RTC_YEAR_BASE) | alarm_mask; + + /* The wday is configured only when disabled alarm. */ + if (!alarm) + buf[RTC_WEEKDAY] = (1 << tm->tm_wday); + else + buf[RTC_WEEKDAY] = 0; + + return 0; +} + +static inline int max77663_rtc_irq_mask(struct max77663_rtc *rtc, u8 irq) +{ + struct device *parent = _to_parent(rtc); + u8 irq_mask = rtc->irq_mask | irq; + int ret = 0; + + ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_irq_mask: Failed to set rtc irq mask\n"); + goto out; + } + rtc->irq_mask = irq_mask; + +out: + return ret; +} + +static inline int max77663_rtc_irq_unmask(struct max77663_rtc *rtc, u8 irq) +{ + struct device *parent = _to_parent(rtc); + u8 irq_mask = rtc->irq_mask & ~irq; + int ret = 0; + + ret = max77663_write(parent, MAX77663_RTC_IRQ_MASK, &irq_mask, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_irq_unmask: Failed to set rtc irq mask\n"); + goto out; + } + rtc->irq_mask = irq_mask; + +out: + return ret; +} + +static inline int max77663_rtc_do_irq(struct max77663_rtc *rtc) +{ + struct device *parent = _to_parent(rtc); + u8 irq_status; + int ret; + + ret = max77663_rtc_update_buffer(rtc, 0); + if (ret < 0) { + dev_err(rtc->dev, "rtc_irq: Failed to get rtc update buffer\n"); + return ret; + } + + ret = max77663_read(parent, MAX77663_RTC_IRQ, &irq_status, 1, 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_irq: Failed to get rtc irq status\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_do_irq: irq_mask=0x%02x, irq_status=0x%02x\n", + rtc->irq_mask, irq_status); + + if (!(rtc->irq_mask & RTC_IRQ_ALARM1_MASK) && + (irq_status & RTC_IRQ_ALARM1_MASK)) + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + + if (!(rtc->irq_mask & RTC_IRQ_1SEC_MASK) && + (irq_status & RTC_IRQ_1SEC_MASK)) + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF); + + return ret; +} + +static irqreturn_t max77663_rtc_irq(int irq, void *data) +{ + struct max77663_rtc *rtc = (struct max77663_rtc *)data; + + max77663_rtc_do_irq(rtc); + + return IRQ_HANDLED; +} + +static int max77663_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + if (rtc->irq < 0) + return -ENXIO; + + mutex_lock(&rtc->io_lock); + + /* Handle pending interrupt */ + ret = max77663_rtc_do_irq(rtc); + if (ret < 0) + goto out; + + /* Config alarm interrupt */ + if (enabled) { + ret = max77663_rtc_irq_unmask(rtc, RTC_IRQ_ALARM1_MASK); + if (ret < 0) + goto out; + } else { + ret = max77663_rtc_irq_mask(rtc, RTC_IRQ_ALARM1_MASK); + if (ret < 0) + goto out; + } +out: + mutex_unlock(&rtc->io_lock); + return ret; +} + +static int max77663_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + u8 buf[RTC_NR]; + int ret; + + ret = max77663_rtc_read(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_read_time: Failed to read rtc time\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_read_time: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + + ret = max77663_rtc_reg_to_tm(rtc, buf, tm); + if (ret < 0) { + dev_err(rtc->dev, "rtc_read_time: " + "Failed to convert register format into time format\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_read_time: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tm->tm_wday); + + return ret; +} + +static int max77663_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + u8 buf[RTC_NR]; + int ret; + + dev_dbg(rtc->dev, "rtc_set_time: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tm->tm_wday); + + ret = max77663_rtc_tm_to_reg(rtc, buf, tm, 0); + if (ret < 0) { + dev_err(rtc->dev, "rtc_set_time: " + "Failed to convert time format into register format\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_set_time: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + + return max77663_rtc_write(rtc, MAX77663_RTC_SEC, buf, sizeof(buf), 1); +} + +static int max77663_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + u8 buf[RTC_NR]; + int ret; + + ret = max77663_rtc_read(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf), + 1); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_read_alarm: Failed to read rtc alarm time\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_read_alarm: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + + ret = max77663_rtc_reg_to_tm(rtc, buf, &alrm->time); + if (ret < 0) { + dev_err(rtc->dev, "rtc_read_alarm: " + "Failed to convert register format into time format\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_read_alarm: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d\n", + alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday, + alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, + alrm->time.tm_wday); + + if (rtc->irq_mask & RTC_IRQ_ALARM1_MASK) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int max77663_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77663_rtc *rtc = dev_get_drvdata(dev); + u8 buf[RTC_NR]; + int ret; + + dev_dbg(rtc->dev, "rtc_set_alarm: " + "tm: %d-%02d-%02d %02d:%02d:%02d, wday=%d [%s]\n", + alrm->time.tm_year, alrm->time.tm_mon, alrm->time.tm_mday, + alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec, + alrm->time.tm_wday, alrm->enabled?"enable":"disable"); + + ret = max77663_rtc_tm_to_reg(rtc, buf, &alrm->time, 1); + if (ret < 0) { + dev_err(rtc->dev, "rtc_set_alarm: " + "Failed to convert time format into register format\n"); + return ret; + } + + dev_dbg(rtc->dev, "rtc_set_alarm: " + "buf: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + buf[RTC_SEC], buf[RTC_MIN], buf[RTC_HOUR], buf[RTC_WEEKDAY], + buf[RTC_MONTH], buf[RTC_YEAR], buf[RTC_MONTHDAY]); + + ret = max77663_rtc_write(rtc, MAX77663_RTC_ALARM_SEC1, buf, sizeof(buf), + 1); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_set_alarm: Failed to write rtc alarm time\n"); + return ret; + } + + ret = max77663_rtc_alarm_irq_enable(dev, alrm->enabled); + if (ret < 0) { + dev_err(rtc->dev, + "rtc_set_alarm: Failed to enable rtc alarm\n"); + return ret; + } + + return ret; +} + +static const struct rtc_class_ops max77663_rtc_ops = { + .read_time = max77663_rtc_read_time, + .set_time = max77663_rtc_set_time, + .read_alarm = max77663_rtc_read_alarm, + .set_alarm = max77663_rtc_set_alarm, + .alarm_irq_enable = max77663_rtc_alarm_irq_enable, +}; + +static int max77663_rtc_preinit(struct max77663_rtc *rtc) +{ + struct device *parent = _to_parent(rtc); + u8 val; + int ret; + + /* Mask all interrupts */ + rtc->irq_mask = 0xFF; + ret = max77663_rtc_write(rtc, MAX77663_RTC_IRQ_MASK, &rtc->irq_mask, 1, + 0); + if (ret < 0) { + dev_err(rtc->dev, "preinit: Failed to set rtc irq mask\n"); + return ret; + } + + /* Configure Binary mode and 24hour mode */ + val = HR_MODE_MASK; + ret = max77663_rtc_write(rtc, MAX77663_RTC_CTRL, &val, 1, 0); + if (ret < 0) { + dev_err(rtc->dev, "preinit: Failed to set rtc control\n"); + return ret; + } + + /* It should be disabled alarm wakeup to wakeup from sleep + * by EN1 input signal */ + ret = max77663_set_bits(parent, MAX77663_REG_ONOFF_CFG2, + ONOFF_WK_ALARM1_MASK, 0, 0); + if (ret < 0) { + dev_err(rtc->dev, "preinit: Failed to set onoff cfg2\n"); + return ret; + } + + return 0; +} + +static int max77663_rtc_probe(struct platform_device *pdev) +{ + struct max77663_platform_data *parent_pdata = + pdev->dev.parent->platform_data; + static struct max77663_rtc *rtc; + int ret = 0; + + rtc = kzalloc(sizeof(struct max77663_rtc), GFP_KERNEL); + if (!rtc) { + dev_err(&pdev->dev, "probe: kzalloc() failed\n"); + return -ENOMEM; + } + + dev_set_drvdata(&pdev->dev, rtc); + rtc->dev = &pdev->dev; + mutex_init(&rtc->io_lock); + + ret = max77663_rtc_preinit(rtc); + if (ret) { + dev_err(&pdev->dev, "probe: Failed to rtc preinit\n"); + goto out_kfree; + } + + rtc->rtc = rtc_device_register("max77663-rtc", &pdev->dev, + &max77663_rtc_ops, THIS_MODULE); + if (IS_ERR_OR_NULL(rtc->rtc)) { + dev_err(&pdev->dev, "probe: Failed to register rtc\n"); + ret = PTR_ERR(rtc->rtc); + goto out_kfree; + } + + if (parent_pdata->irq_base < 0) + goto out; + + rtc->irq = parent_pdata->irq_base + MAX77663_IRQ_RTC; + ret = request_threaded_irq(rtc->irq, NULL, max77663_rtc_irq, + IRQF_ONESHOT, "max77663-rtc", rtc); + if (ret < 0) { + dev_err(rtc->dev, "probe: Failed to request irq %d\n", + rtc->irq); + rtc->irq = -1; + } else { + device_init_wakeup(rtc->dev, 1); + enable_irq_wake(rtc->irq); + } + + return 0; + +out_kfree: + mutex_destroy(&rtc->io_lock); + kfree(rtc->rtc); +out: + return ret; +} + +static int __devexit max77663_rtc_remove(struct platform_device *pdev) +{ + struct max77663_rtc *rtc = dev_get_drvdata(&pdev->dev); + + if (rtc->irq != -1) + free_irq(rtc->irq, rtc); + + rtc_device_unregister(rtc->rtc); + mutex_destroy(&rtc->io_lock); + kfree(rtc); + + return 0; +} + +static struct platform_driver max77663_rtc_driver = { + .probe = max77663_rtc_probe, + .remove = __devexit_p(max77663_rtc_remove), + .driver = { + .name = "max77663-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init max77663_rtc_init(void) +{ + return platform_driver_register(&max77663_rtc_driver); +} +module_init(max77663_rtc_init); + +static void __exit max77663_rtc_exit(void) +{ + platform_driver_unregister(&max77663_rtc_driver); +} +module_exit(max77663_rtc_exit); + +MODULE_DESCRIPTION("max77663 RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); diff --git a/drivers/rtc/rtc-max8907c.c b/drivers/rtc/rtc-max8907c.c new file mode 100644 index 00000000000..f7287021da3 --- /dev/null +++ b/drivers/rtc/rtc-max8907c.c @@ -0,0 +1,323 @@ +/* + * RTC driver for Maxim MAX8907c + * + * Copyright (c) 2011, NVIDIA Corporation. + * Based on drivers/rtc/rtc-max8925.c, Copyright (C) 2009-2010 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + +#define TIME_NUM 8 +#define ALARM_1SEC (1 << 7) +#define HOUR_12 (1 << 7) +#define HOUR_AM_PM (1 << 5) +#define ALARM0_IRQ (1 << 3) +#define ALARM1_IRQ (1 << 2) +#define ALARM0_STATUS (1 << 2) +#define ALARM1_STATUS (1 << 1) + +struct max8907c_rtc_info { + struct rtc_device *rtc_dev; + struct i2c_client *i2c; + struct max8907c *chip; +}; + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct max8907c_rtc_info *info = (struct max8907c_rtc_info *)data; + + /* disable ALARM0 except for 1SEC alarm */ + max8907c_set_bits(info->i2c, MAX8907C_REG_ALARM0_CNTL, 0x7f, 0); + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int tm_calc(struct rtc_time *tm, u8 *buf, int len) +{ + if (len < TIME_NUM) + return -EINVAL; + tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000 + + (buf[RTC_YEAR2] & 0xf) * 100 + + (buf[RTC_YEAR1] >> 4) * 10 + + (buf[RTC_YEAR1] & 0xf); + tm->tm_year -= 1900; + /* RTC month index issue in max8907c + : January index is 1 but kernel assumes it as 0 */ + tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10 + + (buf[RTC_MONTH] & 0x0f) - 1; + tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10 + + (buf[RTC_DATE] & 0x0f); + tm->tm_wday = buf[RTC_WEEKDAY] & 0x07; + if (buf[RTC_HOUR] & HOUR_12) { + tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10 + + (buf[RTC_HOUR] & 0x0f); + if (buf[RTC_HOUR] & HOUR_AM_PM) + tm->tm_hour += 12; + } else { + tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10 + + (buf[RTC_HOUR] & 0x0f); + } + tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10 + + (buf[RTC_MIN] & 0x0f); + tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10 + + (buf[RTC_SEC] & 0x0f); + return 0; +} + +static int data_calc(u8 *buf, struct rtc_time *tm, int len) +{ + u8 high, low; + + if (len < TIME_NUM) + return -EINVAL; + + high = (tm->tm_year + 1900) / 1000; + low = (tm->tm_year + 1900) / 100; + low = low - high * 10; + buf[RTC_YEAR2] = (high << 4) + low; + high = (tm->tm_year + 1900) / 10; + low = tm->tm_year + 1900; + low = low - high * 10; + high = high - (high / 10) * 10; + buf[RTC_YEAR1] = (high << 4) + low; + + /* RTC month index issue in max8907c + : January index is 1 but kernel assumes it as 0 */ + high = (tm->tm_mon + 1) / 10; + low = (tm->tm_mon + 1) % 10; + buf[RTC_MONTH] = (high << 4) + low; + + high = tm->tm_mday / 10; + low = tm->tm_mday; + low = low - high * 10; + buf[RTC_DATE] = (high << 4) + low; + buf[RTC_WEEKDAY] = tm->tm_wday; + high = tm->tm_hour / 10; + low = tm->tm_hour; + low = low - high * 10; + buf[RTC_HOUR] = (high << 4) + low; + high = tm->tm_min / 10; + low = tm->tm_min; + low = low - high * 10; + buf[RTC_MIN] = (high << 4) + low; + high = tm->tm_sec / 10; + low = tm->tm_sec; + low = low - high * 10; + buf[RTC_SEC] = (high << 4) + low; + return 0; +} + +static int max8907c_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8907c_rtc_info *info = dev_get_drvdata(dev); + u8 buf[TIME_NUM]; + int ret; + + ret = max8907c_reg_bulk_read(info->i2c, MAX8907C_REG_RTC_SEC, TIME_NUM, buf); + + if (ret < 0) + return ret; + ret = tm_calc(tm, buf, TIME_NUM); + + return ret; +} + +static int max8907c_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8907c_rtc_info *info = dev_get_drvdata(dev); + u8 buf[TIME_NUM]; + int ret; + + ret = data_calc(buf, tm, TIME_NUM); + + if (ret < 0) + return ret; + ret = max8907c_reg_bulk_write(info->i2c, MAX8907C_REG_RTC_SEC, TIME_NUM, buf); + + return ret; +} + +static int max8907c_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8907c_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = max8907c_reg_bulk_read(info->i2c, MAX8907C_REG_ALARM0_SEC, TIME_NUM, buf); + if (ret < 0) + return ret; + ret = tm_calc(&alrm->time, buf, TIME_NUM); + if (ret < 0) + return ret; + ret = max8907c_reg_read(info->i2c, MAX8907C_REG_RTC_IRQ_MASK); + if (ret < 0) + return ret; + if ((ret & ALARM0_IRQ) == 0) + alrm->enabled = 1; + else + alrm->enabled = 0; + ret = max8907c_reg_read(info->i2c, MAX8907C_REG_RTC_STATUS); + if (ret < 0) + return ret; + if (ret & ALARM0_STATUS) + alrm->pending = 1; + else + alrm->pending = 0; + + return ret; +} + +static int max8907c_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8907c_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = data_calc(buf, &alrm->time, TIME_NUM); + if (ret < 0) + return ret; + ret = max8907c_reg_bulk_write(info->i2c, MAX8907C_REG_ALARM0_SEC, TIME_NUM, buf); + if (ret < 0) + return ret; + /* only enable alarm on year/month/day/hour/min/sec */ + ret = max8907c_reg_write(info->i2c, MAX8907C_REG_ALARM0_CNTL, 0x77); + + return ret; +} + +static const struct rtc_class_ops max8907c_rtc_ops = { + .read_time = max8907c_rtc_read_time, + .set_time = max8907c_rtc_set_time, + .read_alarm = max8907c_rtc_read_alarm, + .set_alarm = max8907c_rtc_set_alarm, +}; + +static int __devinit max8907c_rtc_probe(struct platform_device *pdev) +{ + struct max8907c *chip = dev_get_drvdata(pdev->dev.parent); + struct max8907c_rtc_info *info; + int irq, ret; + + info = kzalloc(sizeof(struct max8907c_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->i2c = chip->i2c_rtc; + info->chip = chip; + + irq = chip->irq_base + MAX8907C_IRQ_RTC_ALARM0; + + ret = request_threaded_irq(irq, NULL, rtc_update_handler, + IRQF_ONESHOT, "rtc-alarm0", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + irq, ret); + goto out_irq; + } + + dev_set_drvdata(&pdev->dev, info); + info->rtc_dev = rtc_device_register("max8907c-rtc", &pdev->dev, + &max8907c_rtc_ops, THIS_MODULE); + ret = PTR_ERR(info->rtc_dev); + if (IS_ERR(info->rtc_dev)) { + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + goto out_rtc; + } + + max8907c_set_bits(chip->i2c_power, MAX8907C_REG_SYSENSEL, 0x2, 0x2); + + platform_set_drvdata(pdev, info); + + device_init_wakeup(&pdev->dev, 1); + + return 0; +out_rtc: + free_irq(chip->irq_base + MAX8907C_IRQ_RTC_ALARM0, info); + +out_irq: + kfree(info); + return ret; +} + +static int __devexit max8907c_rtc_remove(struct platform_device *pdev) +{ + struct max8907c_rtc_info *info = platform_get_drvdata(pdev); + + if (info) { + free_irq(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0, info); + + rtc_device_unregister(info->rtc_dev); + kfree(info); + } + return 0; +} + +#ifdef CONFIG_PM +static int max8907c_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct device *dev=&pdev->dev; + struct max8907c_rtc_info *info = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + enable_irq_wake(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0); + return 0; +} + +static int max8907c_rtc_resume(struct platform_device *pdev) +{ + struct device *dev=&pdev->dev; + struct max8907c_rtc_info *info = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + disable_irq_wake(info->chip->irq_base + MAX8907C_IRQ_RTC_ALARM0); + return 0; +} +#endif + +static struct platform_driver max8907c_rtc_driver = { + .driver = { + .name = "max8907c-rtc", + .owner = THIS_MODULE, + }, + .probe = max8907c_rtc_probe, + .remove = __devexit_p(max8907c_rtc_remove), +#ifdef CONFIG_PM + .suspend = max8907c_rtc_suspend, + .resume = max8907c_rtc_resume, +#endif +}; + +static int __init max8907c_rtc_init(void) +{ + return platform_driver_register(&max8907c_rtc_driver); +} +module_init(max8907c_rtc_init); + +static void __exit max8907c_rtc_exit(void) +{ + platform_driver_unregister(&max8907c_rtc_driver); +} +module_exit(max8907c_rtc_exit); + +MODULE_DESCRIPTION("Maxim MAX8907C RTC driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-ricoh583.c b/drivers/rtc/rtc-ricoh583.c new file mode 100644 index 00000000000..8bc17d9a101 --- /dev/null +++ b/drivers/rtc/rtc-ricoh583.c @@ -0,0 +1,403 @@ +/* + * drivers/rtc/rtc_ricoh583.c + * + * rtc driver for ricoh rc5t583 pmu + * + * copyright (c) 2011, nvidia corporation. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * 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. + * + * you should have received a copy of the gnu general public license along + * with this program; if not, write to the free software foundation, inc., + * 51 franklin street, fifth floor, boston, ma 02110-1301, usa. + */ + +/* #define debug 1 */ +/* #define verbose_debug 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define rtc_ctrl1 0xED +#define rtc_ctrl2 0xEE +#define rtc_seconds_reg 0xE0 +#define rtc_alarm_y 0xF0 +#define rtc_adjust 0xE7 + +/* +linux rtc driver refers 1900 as base year in many calculations. +(e.g. refer drivers/rtc/rtc-lib.c) +*/ +#define os_ref_year 1900 + +/* + pmu rtc have only 2 nibbles to store year information, so using an + offset of 100 to set the base year as 2000 for our driver. +*/ +#define rtc_year_offset 100 + +struct ricoh583_rtc { + unsigned long epoch_start; + int irq; + struct rtc_device *rtc; + bool irq_en; +}; + +static int ricoh583_read_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + + ret = ricoh583_bulk_reads(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed reading from 0x%02x\n", + __func__, reg); + WARN_ON(1); + } + return ret; +} + +static int ricoh583_write_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + ret = ricoh583_bulk_writes(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed writing\n", __func__); + WARN_ON(1); + } + + return ret; +} + +static int ricoh583_rtc_valid_tm(struct device *dev, struct rtc_time *tm) +{ + if (tm->tm_year >= (rtc_year_offset + 99) + || tm->tm_mon > 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, + tm->tm_year + os_ref_year) + || tm->tm_hour >= 24 + || tm->tm_min >= 60 + || tm->tm_sec >= 60) { + dev_err(dev->parent, "\n returning error due to time" + "%d/%d/%d %d:%d:%d", tm->tm_mon, tm->tm_mday, + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); + return -EINVAL; + } + return 0; +} + +static u8 dec2bcd(u8 dec) +{ + return ((dec/10)<<4)+(dec%10); +} + +static u8 bcd2dec(u8 bcd) +{ + return (bcd >> 4)*10+(bcd & 0xf); +} + +static void convert_bcd_to_decimal(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = bcd2dec(buf[i]); +} + +static void convert_decimal_to_bcd(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = dec2bcd(buf[i]); +} + +static void print_time(struct device *dev, struct rtc_time *tm) +{ + dev_info(dev, "rtc-time : %d/%d/%d %d:%d\n", + (tm->tm_mon + 1), tm->tm_mday, (tm->tm_year + os_ref_year), + tm->tm_hour, tm->tm_min); +} + +static int ricoh583_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + err = ricoh583_read_regs(dev, rtc_seconds_reg, sizeof(buff), buff); + if (err < 0) { + dev_err(dev, "\n %s :: failed to read time\n", __FILE__); + return err; + } + convert_bcd_to_decimal(buff, sizeof(buff)); + tm->tm_sec = buff[0]; + tm->tm_min = buff[1]; + tm->tm_hour = buff[2]; + tm->tm_wday = buff[3]; + tm->tm_mday = buff[4]; + tm->tm_mon = buff[5] - 1; + tm->tm_year = buff[6] + rtc_year_offset; + print_time(dev, tm); + return ricoh583_rtc_valid_tm(dev, tm); +} + +static int ricoh583_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + + print_time(dev, tm); + buff[0] = tm->tm_sec; + buff[1] = tm->tm_min; + buff[2] = tm->tm_hour; + buff[3] = tm->tm_wday; + buff[4] = tm->tm_mday; + buff[5] = tm->tm_mon + 1; + buff[6] = tm->tm_year - rtc_year_offset; + + convert_decimal_to_bcd(buff, sizeof(buff)); + err = ricoh583_write_regs(dev, rtc_seconds_reg, sizeof(buff), buff); + if (err < 0) { + dev_err(dev->parent, "\n failed to program new time\n"); + return err; + } + + return 0; +} +static int ricoh583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm); + +static int ricoh583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ricoh583_rtc *rtc = dev_get_drvdata(dev); + unsigned long seconds; + u8 buff[5]; + int err; + struct rtc_time tm; + + if (rtc->irq == -1) + return -EIO; + + rtc_tm_to_time(&alrm->time, &seconds); + ricoh583_rtc_read_time(dev, &tm); + rtc_tm_to_time(&tm, &rtc->epoch_start); + /* + work around: As YAL does not provide the seconds register, + program minute register to next minute, in cases when alarm + is requested within a minute from the current time. + */ + if (seconds - rtc->epoch_start < 60) + alrm->time.tm_min += 1; + dev_info(dev->parent, "\n setting alarm to requested time::\n"); + print_time(dev->parent, &alrm->time); + + if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) { + dev_err(dev->parent, "\n can't set alarm to requested time\n"); + return -EINVAL; + } + + if (alrm->enabled && !rtc->irq_en) + rtc->irq_en = true; + else if (!alrm->enabled && rtc->irq_en) + rtc->irq_en = false; + + buff[0] = alrm->time.tm_min; + buff[1] = alrm->time.tm_hour; + buff[2] = alrm->time.tm_mday; + buff[3] = alrm->time.tm_mon + 1; + buff[4] = alrm->time.tm_year - rtc_year_offset; + convert_decimal_to_bcd(buff, sizeof(buff)); + err = ricoh583_write_regs(dev, rtc_alarm_y, sizeof(buff), buff); + if (err) { + dev_err(dev->parent, "\n unable to set alarm\n"); + return -EBUSY; + } + buff[0] = 0x20; /* to enable alarm_y */ + buff[1] = 0x20; /* to enable 24-hour format */ + err = ricoh583_write_regs(dev, rtc_ctrl1, 2, buff); + if (err) { + dev_err(dev, "failed programming rtc ctrl regs\n"); + return -EBUSY; + } +return err; +} + +static int ricoh583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[5]; + int err; + + err = ricoh583_read_regs(dev, rtc_alarm_y, sizeof(buff), buff); + if (err) + return err; + convert_bcd_to_decimal(buff, sizeof(buff)); + + alrm->time.tm_min = buff[0]; + alrm->time.tm_hour = buff[1]; + alrm->time.tm_mday = buff[2]; + alrm->time.tm_mon = buff[3] - 1; + alrm->time.tm_year = buff[4] + rtc_year_offset; + + dev_info(dev->parent, "\n getting alarm time::\n"); + print_time(dev, &alrm->time); + + return 0; +} + +static const struct rtc_class_ops ricoh583_rtc_ops = { + .read_time = ricoh583_rtc_read_time, + .set_time = ricoh583_rtc_set_time, + .set_alarm = ricoh583_rtc_set_alarm, + .read_alarm = ricoh583_rtc_read_alarm, +}; + +static irqreturn_t ricoh583_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct ricoh583_rtc *rtc = dev_get_drvdata(dev); + u8 reg; + int err; + + /* clear alarm-Y status bits.*/ + err = ricoh583_read_regs(dev, rtc_ctrl2, 1, ®); + if (err) { + dev_err(dev->parent, "unable to read rtc_ctrl2 reg\n"); + return -EBUSY; + } + reg &= ~0x8; + err = ricoh583_write_regs(dev, rtc_ctrl2, 1, ®); + if (err) { + dev_err(dev->parent, "unable to program rtc_status reg\n"); + return -EBUSY; + } + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int __devinit ricoh583_rtc_probe(struct platform_device *pdev) +{ + struct ricoh583_rtc_platform_data *pdata = pdev->dev.platform_data; + struct ricoh583_rtc *rtc; + struct rtc_time tm; + int err; + u8 reg[2]; + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + + if (!rtc) + return -ENOMEM; + + rtc->irq = -1; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data specified\n"); + return -EINVAL; + } + + if (pdata->irq < 0) + dev_err(&pdev->dev, "\n no irq specified, wakeup is disabled\n"); + + dev_set_drvdata(&pdev->dev, rtc); + device_init_wakeup(&pdev->dev, 1); + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &ricoh583_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + goto fail; + } + reg[0] = 0; /* clearing RTC Adjust register */ + err = ricoh583_write_regs(&pdev->dev, rtc_adjust, 1, reg); + if (err) { + dev_err(&pdev->dev, "unable to program rtc_adjust reg\n"); + return -EBUSY; + } + + reg[0] = 0x20; /* to enable alarm_y */ + reg[1] = 0x20; /* to enable 24-hour format */ + err = ricoh583_write_regs(&pdev->dev, rtc_ctrl1, 2, reg); + if (err) { + dev_err(&pdev->dev, "failed rtc setup\n"); + return -EBUSY; + } + + ricoh583_rtc_read_time(&pdev->dev, &tm); + if (ricoh583_rtc_valid_tm(&pdev->dev, &tm)) { + if (pdata->time.tm_year < 2000 || pdata->time.tm_year > 2100) { + memset(&pdata->time, 0, sizeof(pdata->time)); + pdata->time.tm_year = rtc_year_offset; + pdata->time.tm_mday = 1; + } else + pdata->time.tm_year -= os_ref_year; + ricoh583_rtc_set_time(&pdev->dev, &pdata->time); + } + if (pdata && (pdata->irq >= 0)) { + rtc->irq = pdata->irq; + err = request_threaded_irq(pdata->irq, NULL, ricoh583_rtc_irq, + IRQF_ONESHOT, "rtc_ricoh583", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, 1); + enable_irq_wake(rtc->irq); + } + } + return 0; + +fail: + if (!IS_ERR_OR_NULL(rtc->rtc)) + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return err; +} + +static int __devexit ricoh583_rtc_remove(struct platform_device *pdev) +{ + struct ricoh583_rtc *rtc = dev_get_drvdata(&pdev->dev); + + if (rtc->irq != -1) + free_irq(rtc->irq, rtc); + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return 0; +} + +static struct platform_driver ricoh583_rtc_driver = { + .driver = { + .name = "rtc_ricoh583", + .owner = THIS_MODULE, + }, + .probe = ricoh583_rtc_probe, + .remove = __devexit_p(ricoh583_rtc_remove), +}; + +static int __init ricoh583_rtc_init(void) +{ + return platform_driver_register(&ricoh583_rtc_driver); +} +module_init(ricoh583_rtc_init); + +static void __exit ricoh583_rtc_exit(void) +{ + platform_driver_unregister(&ricoh583_rtc_driver); +} +module_exit(ricoh583_rtc_exit); + +MODULE_DESCRIPTION("RICOH PMU ricoh583 RTC driver"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc_ricoh583"); diff --git a/drivers/rtc/rtc-tps6591x.c b/drivers/rtc/rtc-tps6591x.c new file mode 100644 index 00000000000..cab3e8874df --- /dev/null +++ b/drivers/rtc/rtc-tps6591x.c @@ -0,0 +1,546 @@ +/* + * drivers/rtc/rtc_tps6591x.c + * + * RTC driver for TI TPS6591x + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* #define DEBUG 1 */ +/* #define VERBOSE_DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_CTRL 0x10 +#define RTC_STATUS 0x11 +#define RTC_SECONDS_REG 0x0 +#define RTC_ALARM 0x8 +#define RTC_INT 0x12 +#define RTC_RESET_STATUS 0x16 +#define RTC_BBCH_REG 0x39 + +#define RTC_BBCH_SEL 0x02 +#define RTC_BBCH_EN 0x01 +#define ENABLE_ALARM_INT 0x8 +#define RTC_RESET_VALUE 0x80 +#define ALARM_INT_STATUS 0x40 + +/* +Linux RTC driver refers 1900 as base year in many calculations. +(e.g. refer drivers/rtc/rtc-lib.c) +*/ +#define OS_REF_YEAR 1900 + +/* + PMU RTC have only 2 nibbles to store year information, so using an offset + of 100 to set the base year as 2000 for our driver. +*/ +#define RTC_YEAR_OFFSET 100 + +struct tps6591x_rtc { + unsigned long epoch_start; + int irq; + struct rtc_device *rtc; + bool irq_en; +}; + +static int tps6591x_read_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + + /* dummy read of STATUS_REG as per data sheet */ + ret = tps6591x_reads(dev->parent, RTC_STATUS, 1, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed reading from RTC_STATUS\n", + __func__); + WARN_ON(1); + return ret; + } + + ret = tps6591x_reads(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed reading from 0x%02x\n", + __func__, reg); + WARN_ON(1); + return ret; + } + return 0; +} + +static int tps6591x_write_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + ret = tps6591x_writes(dev->parent, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "\n %s failed writing\n", __func__); + WARN_ON(1); + return ret; + } + + return 0; +} + +static int tps6591x_rtc_valid_tm(struct rtc_time *tm) +{ + if (tm->tm_year >= (RTC_YEAR_OFFSET + 99) + || tm->tm_mon >= 12 + || tm->tm_mday < 1 + || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + OS_REF_YEAR) + || tm->tm_hour >= 24 + || tm->tm_min >= 60 + || tm->tm_sec >= 60) + return -EINVAL; + return 0; +} + +static u8 dec2bcd(u8 dec) +{ + return ((dec/10)<<4)+(dec%10); +} + +static u8 bcd2dec(u8 bcd) +{ + return (bcd >> 4)*10+(bcd & 0xF); +} + +static void convert_bcd_to_decimal(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = bcd2dec(buf[i]); +} + +static void convert_decimal_to_bcd(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = dec2bcd(buf[i]); +} + +static void print_time(struct device *dev, struct rtc_time *tm) +{ + dev_info(dev, "RTC Time : %d/%d/%d %d:%d:%d\n", + (tm->tm_mon + 1), tm->tm_mday, (tm->tm_year + OS_REF_YEAR), + tm->tm_hour, tm->tm_min , tm->tm_sec); +} + +static int tps6591x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + err = tps6591x_read_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff); + if (err < 0) { + dev_err(dev, "\n %s :: failed to read time\n", __FILE__); + return err; + } + convert_bcd_to_decimal(buff, sizeof(buff)); + tm->tm_sec = buff[0]; + tm->tm_min = buff[1]; + tm->tm_hour = buff[2]; + tm->tm_mday = buff[3]; + tm->tm_mon = buff[4] - 1; + tm->tm_year = buff[5]; + tm->tm_wday = buff[6]; + print_time(dev, tm); + return tps6591x_rtc_valid_tm(tm); +} + +static int tps6591x_rtc_stop(struct device *dev) +{ + u8 reg = 0; + u8 retries = 0; + int err; + do { + err = tps6591x_read_regs(dev, RTC_CTRL, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n"); + return err; + } + + /* clear STOP bit alone */ + reg &= ~0x1; + + err = tps6591x_write_regs(dev, RTC_CTRL, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to program RTC_CTRL reg\n"); + return err; + } + + err = tps6591x_read_regs(dev, RTC_STATUS, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n"); + return err; + } + /* FixMe: Is allowing up to 5 retries sufficient?? */ + if (retries++ == 5) { + dev_err(dev->parent, "\n failed to stop RTC\n"); + return -EBUSY; + } + } while (reg & 2); + return 0; +} + +static int tps6591x_rtc_start(struct device *dev) +{ + u8 reg = 0; + u8 retries = 0; + int err; + + do { + err = tps6591x_read_regs(dev, RTC_CTRL, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n"); + return err; + } + + /* set STOP bit alone */ + reg |= 0x1; + + err = tps6591x_write_regs(dev, RTC_CTRL, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to program RTC_CTRL reg\n"); + return err; + } + + err = tps6591x_read_regs(dev, RTC_STATUS, 1, ®); + if (err < 0) { + dev_err(dev->parent, "\n failed to read RTC_CTRL reg\n"); + return err; + } + /* FixMe: Is allowing up to 5 retries sufficient?? */ + if (retries++ == 5) { + dev_err(dev->parent, "\n failed to stop RTC\n"); + return -EBUSY; + } + } while (!(reg & 2)); + return 0; +} + + +static int tps6591x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + + buff[0] = tm->tm_sec; + buff[1] = tm->tm_min; + buff[2] = tm->tm_hour; + buff[3] = tm->tm_mday; + buff[4] = tm->tm_mon + 1; + buff[5] = tm->tm_year; + buff[6] = tm->tm_wday; + + print_time(dev, tm); + convert_decimal_to_bcd(buff, sizeof(buff)); + err = tps6591x_rtc_stop(dev); + if (err < 0) { + dev_err(dev->parent, "\n failed to clear RTC_ENABLE\n"); + return err; + } + + err = tps6591x_write_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff); + if (err < 0) { + dev_err(dev->parent, "\n failed to program new time\n"); + return err; + } + + err = tps6591x_rtc_start(dev); + if (err < 0) { + dev_err(dev->parent, "\n failed to set RTC_ENABLE\n"); + return err; + } + + return 0; +} + +static int tps6591x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct tps6591x_rtc *rtc = dev_get_drvdata(dev); + unsigned long seconds; + u8 buff[6]; + int err; + struct rtc_time tm; + + if (rtc->irq == -1) + return -EIO; + + dev_info(dev->parent, "\n setting alarm to requested time::\n"); + print_time(dev->parent, &alrm->time); + rtc_tm_to_time(&alrm->time, &seconds); + tps6591x_rtc_read_time(dev, &tm); + rtc_tm_to_time(&tm, &rtc->epoch_start); + + if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) { + dev_err(dev->parent, "\n can't set alarm to requested time\n"); + return -EINVAL; + } + + if (alrm->enabled && !rtc->irq_en) { + rtc->irq_en = true; + } else if (!alrm->enabled && rtc->irq_en) { + rtc->irq_en = false; + } + + buff[0] = alrm->time.tm_sec; + buff[1] = alrm->time.tm_min; + buff[2] = alrm->time.tm_hour; + buff[3] = alrm->time.tm_mday; + buff[4] = alrm->time.tm_mon + 1; + buff[5] = alrm->time.tm_year; + convert_decimal_to_bcd(buff, sizeof(buff)); + err = tps6591x_write_regs(dev, RTC_ALARM, sizeof(buff), buff); + if (err) + dev_err(dev->parent, "\n unable to program alarm\n"); + + return err; +} + +static int tps6591x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[6]; + int err; + + err = tps6591x_read_regs(dev, RTC_ALARM, sizeof(buff), buff); + if (err) + return err; + convert_bcd_to_decimal(buff, sizeof(buff)); + + alrm->time.tm_sec = buff[0]; + alrm->time.tm_min = buff[1]; + alrm->time.tm_hour = buff[2]; + alrm->time.tm_mday = buff[3]; + alrm->time.tm_mon = buff[4] - 1; + alrm->time.tm_year = buff[5]; + + dev_info(dev->parent, "\n getting alarm time::\n"); + print_time(dev, &alrm->time); + + return 0; +} + +static int tps6591x_rtc_alarm_irq_enable(struct device *dev, + unsigned int enable) +{ + struct tps6591x_rtc *rtc = dev_get_drvdata(dev); + u8 reg; + int err; + + if (rtc->irq == -1) + return -EIO; + + if (enable) { + if (rtc->irq_en == true) + return 0; + err = tps6591x_read_regs(dev, RTC_INT, 1, ®); + if (err) + return err; + reg |= 0x8; + err = tps6591x_write_regs(dev, RTC_INT, 1, ®); + if (err) + return err; + rtc->irq_en = true; + } else { + if (rtc->irq_en == false) + return 0; + err = tps6591x_read_regs(dev, RTC_INT, 1, ®); + if (err) + return err; + reg &= ~0x8; + err = tps6591x_write_regs(dev, RTC_INT, 1, ®); + if (err) + return err; + rtc->irq_en = false; + } + return 0; +} + +static const struct rtc_class_ops tps6591x_rtc_ops = { + .read_time = tps6591x_rtc_read_time, + .set_time = tps6591x_rtc_set_time, + .set_alarm = tps6591x_rtc_set_alarm, + .read_alarm = tps6591x_rtc_read_alarm, + .alarm_irq_enable = tps6591x_rtc_alarm_irq_enable, +}; + +static irqreturn_t tps6591x_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct tps6591x_rtc *rtc = dev_get_drvdata(dev); + u8 reg; + int err; + + /* clear Alarm status bits.*/ + err = tps6591x_read_regs(dev, RTC_STATUS, 1, ®); + if (err) { + dev_err(dev->parent, "unable to read RTC_STATUS reg\n"); + return -EBUSY; + } + + reg = ALARM_INT_STATUS; + err = tps6591x_write_regs(dev, RTC_STATUS, 1, ®); + if (err) { + dev_err(dev->parent, "unable to program RTC_STATUS reg\n"); + return -EBUSY; + } + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int __devinit tps6591x_rtc_probe(struct platform_device *pdev) +{ + struct tps6591x_rtc_platform_data *pdata = pdev->dev.platform_data; + struct tps6591x_rtc *rtc; + struct rtc_time tm; + int err; + u8 reg; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + + if (!rtc) + return -ENOMEM; + + rtc->irq = -1; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data specified\n"); + return -EINVAL; + } + + if (pdata->irq < 0) + dev_err(&pdev->dev, "\n no IRQ specified, wakeup is disabled\n"); + + dev_set_drvdata(&pdev->dev, rtc); + device_init_wakeup(&pdev->dev, 1); + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &tps6591x_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + goto fail; + } + + if ((int)pdev && (int)&pdev->dev) + err = tps6591x_read_regs(&pdev->dev, RTC_STATUS, 1, ®); + else { + dev_err(&pdev->dev, "\n %s Input params incorrect\n", __func__); + return -EBUSY; + } + if (err) { + dev_err(&pdev->dev, "\n %s unable to read status\n", __func__); + return -EBUSY; + } + + reg = RTC_BBCH_SEL | RTC_BBCH_EN; + tps6591x_write_regs(&pdev->dev, RTC_BBCH_REG, 1, ®); + if (err) { + dev_err(&pdev->dev, "unable to program Charger reg\n"); + return -EBUSY; + } + + tps6591x_rtc_read_time(&pdev->dev, &tm); + if ((tm.tm_year < RTC_YEAR_OFFSET || tm.tm_year > (RTC_YEAR_OFFSET + 99))){ + if (pdata->time.tm_year < 2000 || pdata->time.tm_year > 2100) { + memset(&pdata->time, 0, sizeof(pdata->time)); + pdata->time.tm_year = RTC_YEAR_OFFSET; + pdata->time.tm_mday = 1; + } else + pdata->time.tm_year -= OS_REF_YEAR; + tps6591x_rtc_set_time(&pdev->dev, &pdata->time); + } + + reg = ALARM_INT_STATUS; + err = tps6591x_write_regs(&pdev->dev, RTC_STATUS, 1, ®); + if (err) { + dev_err(&pdev->dev, "unable to program RTC_STATUS reg\n"); + return -EBUSY; + } + + reg = ENABLE_ALARM_INT; + tps6591x_write_regs(&pdev->dev, RTC_INT, 1, ®); + if (err) { + dev_err(&pdev->dev, "unable to program Interrupt Mask reg\n"); + return -EBUSY; + } + + if (pdata && (pdata->irq >= 0)) { + rtc->irq = pdata->irq; + err = request_threaded_irq(pdata->irq, NULL, tps6591x_rtc_irq, + IRQF_ONESHOT, "rtc_tps6591x", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, 1); + enable_irq_wake(rtc->irq); + } + } + return 0; + +fail: + if (!IS_ERR_OR_NULL(rtc->rtc)) + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return err; +} + +static int __devexit tps6591x_rtc_remove(struct platform_device *pdev) +{ + struct tps6591x_rtc *rtc = dev_get_drvdata(&pdev->dev); + + if (rtc->irq != -1) + free_irq(rtc->irq, rtc); + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return 0; +} + +static struct platform_driver tps6591x_rtc_driver = { + .driver = { + .name = "rtc_tps6591x", + .owner = THIS_MODULE, + }, + .probe = tps6591x_rtc_probe, + .remove = __devexit_p(tps6591x_rtc_remove), +}; + +static int __init tps6591x_rtc_init(void) +{ + return platform_driver_register(&tps6591x_rtc_driver); +} +module_init(tps6591x_rtc_init); + +static void __exit tps6591x_rtc_exit(void) +{ + platform_driver_unregister(&tps6591x_rtc_driver); +} +module_exit(tps6591x_rtc_exit); + +MODULE_DESCRIPTION("TI TPS6591x RTC driver"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc_tps6591x"); diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c new file mode 100644 index 00000000000..70d6734a570 --- /dev/null +++ b/drivers/rtc/rtc-tps80031.c @@ -0,0 +1,449 @@ +/* + * drivers/rtc/rtc_tps80031.c + * + * RTC driver for TI TPS80031 + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* #define DEBUG 1 */ +/* #define VERBOSE_DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_CTRL 0x10 +#define RTC_STATUS 0x11 +#define RTC_SECONDS_REG 0x0 +#define RTC_ALARM 0x8 +#define RTC_INT 0x12 +#define RTC_RESET_STATUS 0x16 + +#define ENABLE_ALARM_INT 0x8 +#define ALARM_INT_STATUS 0x40 +#define STOP_RTC 1 + +/* Power on reset Values of RTC registers */ +#define RTC_POR_YEAR 0 +#define RTC_POR_MONTH 1 +#define RTC_POR_DAY 1 + +/* +Linux RTC driver refers 19th centaury as base year in many +calculations. (e.g. refer drivers/rtc/rtc-lib.c) +*/ +#define OS_REF_YEAR 1900 + +/* + PMU RTC have only 2 nibbles to store year information, so using an + offset of 100 to set the base year as 2000 for our driver. +*/ +#define RTC_YEAR_OFFSET 100 + +struct tps80031_rtc { + unsigned long epoch_start; + int irq; + struct rtc_device *rtc; + u8 alarm_irq_enabled; +}; + +static int tps80031_read_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + + /* dummy read of STATUS_REG as per data sheet */ + ret = tps80031_reads(dev->parent, 1, RTC_STATUS, 1, val); + if (ret < 0) { + dev_err(dev->parent, "failed reading RTC_STATUS\n"); + WARN_ON(1); + return ret; + } + + ret = tps80031_reads(dev->parent, 1, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "failed reading from reg %d\n", reg); + WARN_ON(1); + return ret; + } + return 0; +} + +static int tps80031_write_regs(struct device *dev, int reg, int len, + uint8_t *val) +{ + int ret; + ret = tps80031_writes(dev->parent, 1, reg, len, val); + if (ret < 0) { + dev_err(dev->parent, "failed writing reg: %d\n", reg); + WARN_ON(1); + return ret; + } + + return 0; +} + +static u8 dec2bcd(u8 dec) +{ + return ((dec/10)<<4)+(dec%10); +} + +static u8 bcd2dec(u8 bcd) +{ + return (bcd >> 4)*10+(bcd & 0xF); +} + +static void convert_bcd_to_decimal(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = bcd2dec(buf[i]); +} + +static void convert_decimal_to_bcd(u8 *buf, u8 len) +{ + int i = 0; + for (i = 0; i < len; i++) + buf[i] = dec2bcd(buf[i]); +} + +static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + err = tps80031_read_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff); + if (err < 0) { + dev_err(dev->parent, "failed reading time\n"); + return err; + } + convert_bcd_to_decimal(buff, sizeof(buff)); + tm->tm_sec = buff[0]; + tm->tm_min = buff[1]; + tm->tm_hour = buff[2]; + tm->tm_mday = buff[3]; + tm->tm_mon = buff[4] - 1; + tm->tm_year = buff[5] + RTC_YEAR_OFFSET; + tm->tm_wday = buff[6]; + return 0; +} + +static int tps80031_rtc_stop(struct device *dev) +{ + int err; + err = tps80031_clr_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); + if (err < 0) + dev_err(dev->parent, "failed to stop RTC. err: %d\n", err); + return err; +} + +static int tps80031_rtc_start(struct device *dev) +{ + int err; + err = tps80031_set_bits(dev->parent, 1, RTC_CTRL, STOP_RTC); + if (err < 0) + dev_err(dev->parent, "failed to start RTC. err: %d\n", err); + return err; +} + + +static int tps80031_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + u8 buff[7]; + int err; + + buff[0] = tm->tm_sec; + buff[1] = tm->tm_min; + buff[2] = tm->tm_hour; + buff[3] = tm->tm_mday; + buff[4] = tm->tm_mon + 1; + buff[5] = tm->tm_year % RTC_YEAR_OFFSET; + buff[6] = tm->tm_wday; + + convert_decimal_to_bcd(buff, sizeof(buff)); + err = tps80031_rtc_stop(dev); + if (err < 0) + return err; + + err = tps80031_write_regs(dev, RTC_SECONDS_REG, sizeof(buff), buff); + if (err < 0) { + dev_err(dev->parent, "failed to program new time\n"); + return err; + } + + err = tps80031_rtc_start(dev); + return err; +} + +static int tps80031_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + unsigned long seconds; + u8 buff[6]; + int err; + struct rtc_time tm; + + if (rtc->irq == -1) + return -EIO; + + rtc_tm_to_time(&alrm->time, &seconds); + tps80031_rtc_read_time(dev, &tm); + rtc_tm_to_time(&tm, &rtc->epoch_start); + + if (WARN_ON(alrm->enabled && (seconds < rtc->epoch_start))) { + dev_err(dev->parent, "can't set alarm to requested time\n"); + return -EINVAL; + } + + buff[0] = alrm->time.tm_sec; + buff[1] = alrm->time.tm_min; + buff[2] = alrm->time.tm_hour; + buff[3] = alrm->time.tm_mday; + buff[4] = alrm->time.tm_mon + 1; + buff[5] = alrm->time.tm_year % RTC_YEAR_OFFSET; + convert_decimal_to_bcd(buff, sizeof(buff)); + err = tps80031_write_regs(dev, RTC_ALARM, sizeof(buff), buff); + if (err) + dev_err(dev->parent, "unable to program alarm\n"); + + return err; +} + +static int tps80031_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u8 buff[6]; + int err; + + err = tps80031_read_regs(dev, RTC_ALARM, sizeof(buff), buff); + if (err) + return err; + convert_bcd_to_decimal(buff, sizeof(buff)); + + alrm->time.tm_sec = buff[0]; + alrm->time.tm_min = buff[1]; + alrm->time.tm_hour = buff[2]; + alrm->time.tm_mday = buff[3]; + alrm->time.tm_mon = buff[4] - 1; + alrm->time.tm_year = buff[5] + RTC_YEAR_OFFSET; + + return 0; +} + +static int tps80031_rtc_alarm_irq_enable(struct device *dev, + unsigned int enable) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + int err; + struct device *p = dev->parent; + + if (rtc->irq == -1) + return -EIO; + + if (enable) { + if (rtc->alarm_irq_enabled) + return 0; + + err = tps80031_set_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + if (err < 0) { + dev_err(p, "failed to set ALRM int. err: %d\n", err); + return err; + } else + rtc->alarm_irq_enabled = 1; + } else { + if(!rtc->alarm_irq_enabled) + return 0; + err = tps80031_clr_bits(p, 1, RTC_INT, ENABLE_ALARM_INT); + if (err < 0) { + dev_err(p, "failed to clear ALRM int. err: %d\n", err); + return err; + } else + rtc->alarm_irq_enabled = 0; + } + return 0; +} + +static const struct rtc_class_ops tps80031_rtc_ops = { + .read_time = tps80031_rtc_read_time, + .set_time = tps80031_rtc_set_time, + .set_alarm = tps80031_rtc_set_alarm, + .read_alarm = tps80031_rtc_read_alarm, + .alarm_irq_enable = tps80031_rtc_alarm_irq_enable, +}; + +static irqreturn_t tps80031_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct tps80031_rtc *rtc = dev_get_drvdata(dev); + u8 reg; + int err; + + /* clear Alarm status bits.*/ + err = tps80031_read_regs(dev, RTC_STATUS, 1, ®); + if (err) { + dev_err(dev->parent, "unable to read RTC_STATUS reg\n"); + return -EBUSY; + } + + err = tps80031_force_update(dev->parent, 1, RTC_STATUS, + ALARM_INT_STATUS, ALARM_INT_STATUS); + if (err) { + dev_err(dev->parent, "unable to set Alarm INT\n"); + return -EBUSY; + } + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int __devinit tps80031_rtc_probe(struct platform_device *pdev) +{ + struct tps80031_rtc_platform_data *pdata = pdev->dev.platform_data; + struct tps80031_rtc *rtc; + struct rtc_time tm; + int err; + u8 reg; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data specified\n"); + return -EINVAL; + } + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->irq = -1; + if (pdata->irq < 0) + dev_err(&pdev->dev, "no IRQ specified, wakeup is disabled\n"); + + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &tps80031_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + goto fail; + } + + if ((int)pdev && (int)&pdev->dev) + err = tps80031_read_regs(&pdev->dev, RTC_STATUS, 1, ®); + else { + dev_err(&pdev->dev, "%s Input params incorrect\n", __func__); + err = -EBUSY; + goto fail; + } + + if (err) { + dev_err(&pdev->dev, "%s unable to read status\n", __func__); + err = -EBUSY; + goto fail; + } + + /* If RTC have POR values, set time using platform data*/ + tps80031_rtc_read_time(&pdev->dev, &tm); + if ((tm.tm_year == RTC_YEAR_OFFSET + RTC_POR_YEAR) && + (tm.tm_mon == (RTC_POR_MONTH - 1)) && + (tm.tm_mday == RTC_POR_DAY)) { + if (pdata->time.tm_year < 2000 || + pdata->time.tm_year > 2100) { + dev_err(&pdev->dev, "Invalid platform data\n"); + memset(&pdata->time, 0, sizeof(pdata->time)); + pdata->time.tm_year = 2011; + pdata->time.tm_mday = 1; + } + tps80031_rtc_set_time(&pdev->dev, &pdata->time); + } + + reg = ALARM_INT_STATUS; + err = tps80031_write_regs(&pdev->dev, RTC_STATUS, 1, ®); + if (err) { + dev_err(&pdev->dev, "unable to program RTC_STATUS reg\n"); + return -EBUSY; + } + + err = tps80031_set_bits(pdev->dev.parent, 1, RTC_INT, ENABLE_ALARM_INT); + if (err) { + dev_err(&pdev->dev, "unable to program Interrupt Mask reg\n"); + err = -EBUSY; + rtc->alarm_irq_enabled = 0; + goto fail; + } else + rtc->alarm_irq_enabled = 1; + + dev_set_drvdata(&pdev->dev, rtc); + if (pdata && (pdata->irq >= 0)) { + rtc->irq = pdata->irq; + err = request_threaded_irq(pdata->irq, NULL, tps80031_rtc_irq, + IRQF_ONESHOT, "rtc_tps80031", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, 1); + enable_irq_wake(rtc->irq); + } + } + return 0; + +fail: + if (!IS_ERR_OR_NULL(rtc->rtc)) + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return err; +} + +static int __devexit tps80031_rtc_remove(struct platform_device *pdev) +{ + struct tps80031_rtc *rtc = dev_get_drvdata(&pdev->dev); + + if (rtc->irq != -1) + free_irq(rtc->irq, rtc); + rtc_device_unregister(rtc->rtc); + kfree(rtc); + return 0; +} + +static struct platform_driver tps80031_rtc_driver = { + .driver = { + .name = "rtc_tps80031", + .owner = THIS_MODULE, + }, + .probe = tps80031_rtc_probe, + .remove = __devexit_p(tps80031_rtc_remove), +}; + +static int __init tps80031_rtc_init(void) +{ + return platform_driver_register(&tps80031_rtc_driver); +} +module_init(tps80031_rtc_init); + +static void __exit tps80031_rtc_exit(void) +{ + platform_driver_unregister(&tps80031_rtc_driver); +} +module_exit(tps80031_rtc_exit); + +MODULE_DESCRIPTION("TI TPS80031 RTC driver"); +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc_tps80031"); -- cgit v1.2.2