diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 12 | ||||
-rw-r--r-- | drivers/rtc/class.c | 1 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 64 | ||||
-rw-r--r-- | drivers/rtc/rtc-max8998.c | 54 | ||||
-rw-r--r-- | drivers/rtc/rtc-proc.c | 6 |
5 files changed, 95 insertions, 42 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4941cade319f..cdd97192dc69 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -97,18 +97,6 @@ config RTC_INTF_DEV If unsure, say Y. -config RTC_INTF_DEV_UIE_EMUL - bool "RTC UIE emulation on dev interface" - depends on RTC_INTF_DEV - help - Provides an emulation for RTC_UIE if the underlying rtc chip - driver does not expose RTC_UIE ioctls. Those requests generate - once-per-second update interrupts, used for synchronization. - - The emulation code will read the time from the hardware - clock several times per second, please enable this option - only if you know that you really need it. - config RTC_DRV_TEST tristate "Test driver/device" help diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 9583cbcc6b79..c404b61386bf 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -143,6 +143,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc->id = id; rtc->ops = ops; rtc->owner = owner; + rtc->irq_freq = 1; rtc->max_user_freq = 64; rtc->dev.parent = dev; rtc->dev.class = rtc_class; diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 90384b9f6b2c..a0c01967244d 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -16,6 +16,9 @@ #include <linux/log2.h> #include <linux/workqueue.h> +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); + static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) { int err; @@ -120,12 +123,18 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; - alarm->enabled = rtc->aie_timer.enabled; - if (alarm->enabled) + if (rtc->ops == NULL) + err = -ENODEV; + else if (!rtc->ops->read_alarm) + err = -EINVAL; + else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->enabled = rtc->aie_timer.enabled; alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires); + } mutex_unlock(&rtc->ops_lock); - return 0; + return err; } EXPORT_SYMBOL_GPL(rtc_read_alarm); @@ -175,16 +184,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) return err; if (rtc->aie_timer.enabled) { rtc_timer_remove(rtc, &rtc->aie_timer); - rtc->aie_timer.enabled = 0; } rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); rtc->aie_timer.period = ktime_set(0, 0); if (alarm->enabled) { - rtc->aie_timer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->aie_timer); + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); } mutex_unlock(&rtc->ops_lock); - return 0; + return err; } EXPORT_SYMBOL_GPL(rtc_set_alarm); @@ -195,15 +202,15 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) return err; if (rtc->aie_timer.enabled != enabled) { - if (enabled) { - rtc->aie_timer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->aie_timer); - } else { + if (enabled) + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); + else rtc_timer_remove(rtc, &rtc->aie_timer); - rtc->aie_timer.enabled = 0; - } } + if (err) + return err; + if (!rtc->ops) err = -ENODEV; else if (!rtc->ops->alarm_irq_enable) @@ -235,12 +242,9 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) now = rtc_tm_to_ktime(tm); rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); rtc->uie_rtctimer.period = ktime_set(1, 0); - rtc->uie_rtctimer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); - } else { + err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); + } else rtc_timer_remove(rtc, &rtc->uie_rtctimer); - rtc->uie_rtctimer.enabled = 0; - } out: mutex_unlock(&rtc->ops_lock); @@ -460,6 +464,9 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) int err = 0; unsigned long flags; + if (freq <= 0) + return -EINVAL; + spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; @@ -488,10 +495,13 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); * Enqueues a timer onto the rtc devices timerqueue and sets * the next alarm event appropriately. * + * Sets the enabled bit on the added timer. + * * Must hold ops_lock for proper serialization of timerqueue */ -void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) { + timer->enabled = 1; timerqueue_add(&rtc->timerqueue, &timer->node); if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { struct rtc_wkalrm alarm; @@ -501,7 +511,13 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) err = __rtc_set_alarm(rtc, &alarm); if (err == -ETIME) schedule_work(&rtc->irqwork); + else if (err) { + timerqueue_del(&rtc->timerqueue, &timer->node); + timer->enabled = 0; + return err; + } } + return 0; } /** @@ -512,13 +528,15 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) * Removes a timer onto the rtc devices timerqueue and sets * the next alarm event appropriately. * + * Clears the enabled bit on the removed timer. + * * Must hold ops_lock for proper serialization of timerqueue */ -void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) { struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); timerqueue_del(&rtc->timerqueue, &timer->node); - + timer->enabled = 0; if (next == &timer->node) { struct rtc_wkalrm alarm; int err; @@ -626,8 +644,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, timer->node.expires = expires; timer->period = period; - timer->enabled = 1; - rtc_timer_enqueue(rtc, timer); + ret = rtc_timer_enqueue(rtc, timer); mutex_unlock(&rtc->ops_lock); return ret; @@ -645,7 +662,6 @@ int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer) mutex_lock(&rtc->ops_lock); if (timer->enabled) rtc_timer_remove(rtc, timer); - timer->enabled = 0; mutex_unlock(&rtc->ops_lock); return ret; } diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index f22dee35f330..3f7bc6b9fefa 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/mfd/max8998.h> #include <linux/mfd/max8998-private.h> +#include <linux/delay.h> #define MAX8998_RTC_SEC 0x00 #define MAX8998_RTC_MIN 0x01 @@ -73,6 +74,7 @@ struct max8998_rtc_info { struct i2c_client *rtc; struct rtc_device *rtc_dev; int irq; + bool lp3974_bug_workaround; }; static void max8998_data_to_tm(u8 *data, struct rtc_time *tm) @@ -124,10 +126,16 @@ static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct max8998_rtc_info *info = dev_get_drvdata(dev); u8 data[8]; + int ret; max8998_tm_to_data(tm, data); - return max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data); + ret = max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; } static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -163,12 +171,29 @@ static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info) { - return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0); + int ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; } static int max8998_rtc_start_alarm(struct max8998_rtc_info *info) { - return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0x77); + int ret; + u8 alarm0_conf = 0x77; + + /* LP3974 with delay bug chips has rtc alarm bugs with "MONTH" field */ + if (info->lp3974_bug_workaround) + alarm0_conf = 0x57; + + ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, alarm0_conf); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; } static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -187,10 +212,13 @@ static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) if (ret < 0) return ret; + if (info->lp3974_bug_workaround) + msleep(2000); + if (alrm->enabled) - return max8998_rtc_start_alarm(info); + ret = max8998_rtc_start_alarm(info); - return 0; + return ret; } static int max8998_rtc_alarm_irq_enable(struct device *dev, @@ -224,6 +252,7 @@ static const struct rtc_class_ops max8998_rtc_ops = { static int __devinit max8998_rtc_probe(struct platform_device *pdev) { struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent); + struct max8998_platform_data *pdata = dev_get_platdata(max8998->dev); struct max8998_rtc_info *info; int ret; @@ -249,10 +278,18 @@ static int __devinit max8998_rtc_probe(struct platform_device *pdev) ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0, "rtc-alarm0", info); + if (ret < 0) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->irq, ret); + dev_info(&pdev->dev, "RTC CHIP NAME: %s\n", pdev->id_entry->name); + if (pdata->rtc_delay) { + info->lp3974_bug_workaround = true; + dev_warn(&pdev->dev, "LP3974 with RTC REGERR option." + " RTC updates will be extremely slow.\n"); + } + return 0; out_rtc: @@ -273,6 +310,12 @@ static int __devexit max8998_rtc_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id max8998_rtc_id[] = { + { "max8998-rtc", TYPE_MAX8998 }, + { "lp3974-rtc", TYPE_LP3974 }, + { } +}; + static struct platform_driver max8998_rtc_driver = { .driver = { .name = "max8998-rtc", @@ -280,6 +323,7 @@ static struct platform_driver max8998_rtc_driver = { }, .probe = max8998_rtc_probe, .remove = __devexit_p(max8998_rtc_remove), + .id_table = max8998_rtc_id, }; static int __init max8998_rtc_init(void) diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index c086fc30a84c..242bbf86c74a 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -81,12 +81,16 @@ static int rtc_proc_show(struct seq_file *seq, void *offset) static int rtc_proc_open(struct inode *inode, struct file *file) { + int ret; struct rtc_device *rtc = PDE(inode)->data; if (!try_module_get(THIS_MODULE)) return -ENODEV; - return single_open(file, rtc_proc_show, rtc); + ret = single_open(file, rtc_proc_show, rtc); + if (ret) + module_put(THIS_MODULE); + return ret; } static int rtc_proc_release(struct inode *inode, struct file *file) |