diff options
Diffstat (limited to 'drivers/rtc/interface.c')
| -rw-r--r-- | drivers/rtc/interface.c | 117 |
1 files changed, 75 insertions, 42 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 794a4f036b99..b8b298efd9a9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -186,7 +186,7 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc, if (!rtc->ops) { err = -ENODEV; - } else if (!rtc->ops->read_alarm) { + } else if (!test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->read_alarm) { err = -EINVAL; } else { alarm->enabled = 0; @@ -205,7 +205,7 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc, mutex_unlock(&rtc->ops_lock); - trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); + trace_rtc_read_alarm(err?0:rtc_tm_to_time64(&alarm->time), err); return err; } @@ -256,7 +256,7 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) * * This could all instead be done in the lower level driver, * but since more than one lower level RTC implementation needs it, - * then it's probably best best to do it here instead of there.. + * then it's probably best to do it here instead of there.. */ /* Get the "before" timestamp */ @@ -274,10 +274,9 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) return err; /* full-function RTCs won't have such missing fields */ - if (rtc_valid_tm(&alarm->time) == 0) { - rtc_add_offset(rtc, &alarm->time); - return 0; - } + err = rtc_valid_tm(&alarm->time); + if (!err) + goto done; /* get the "after" timestamp, to detect wrapped fields */ err = rtc_read_time(rtc, &now); @@ -376,9 +375,11 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = rtc_valid_tm(&alarm->time); done: - if (err) + if (err && alarm->enabled) dev_warn(&rtc->dev, "invalid alarm value: %ptR\n", &alarm->time); + else + rtc_add_offset(rtc, &alarm->time); return err; } @@ -392,7 +393,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) return err; if (!rtc->ops) { err = -ENODEV; - } else if (!rtc->ops->read_alarm) { + } else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) { err = -EINVAL; } else { memset(alarm, 0, sizeof(struct rtc_wkalrm)); @@ -423,6 +424,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (err) return err; now = rtc_tm_to_time64(&tm); + if (scheduled <= now) return -ETIME; /* @@ -436,22 +438,46 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (!rtc->ops) err = -ENODEV; - else if (!rtc->ops->set_alarm) + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) err = -EINVAL; else err = rtc->ops->set_alarm(rtc->dev.parent, alarm); + /* + * Check for potential race described above. If the waiting for next + * second, and the second just ticked since the check above, either + * + * 1) It ticked after the alarm was set, and an alarm irq should be + * generated. + * + * 2) It ticked before the alarm was set, and alarm irq most likely will + * not be generated. + * + * While we cannot easily check for which of these two scenarios we + * are in, we can return -ETIME to signal that the timer has already + * expired, which is true in both cases. + */ + if ((scheduled - now) <= 1) { + err = __rtc_read_time(rtc, &tm); + if (err) + return err; + now = rtc_tm_to_time64(&tm); + if (scheduled <= now) + return -ETIME; + } + trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); return err; } int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { + ktime_t alarm_time; int err; if (!rtc->ops) return -ENODEV; - else if (!rtc->ops->set_alarm) + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) return -EINVAL; err = rtc_valid_tm(&alarm->time); @@ -468,7 +494,15 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) if (rtc->aie_timer.enabled) rtc_timer_remove(rtc, &rtc->aie_timer); - rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); + alarm_time = rtc_tm_to_ktime(alarm->time); + /* + * Round down so we never miss a deadline, checking for past deadline is + * done in __rtc_set_alarm + */ + if (test_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features)) + alarm_time = ktime_sub_ns(alarm_time, (u64)alarm->time.tm_sec * NSEC_PER_SEC); + + rtc->aie_timer.node.expires = alarm_time; rtc->aie_timer.period = 0; if (alarm->enabled) err = rtc_timer_enqueue(rtc, &rtc->aie_timer); @@ -531,7 +565,7 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) /* nothing */; else if (!rtc->ops) err = -ENODEV; - else if (!rtc->ops->alarm_irq_enable) + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->alarm_irq_enable) err = -EINVAL; else err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); @@ -545,7 +579,7 @@ EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) { - int rc = 0, err; + int err; err = mutex_lock_interruptible(&rtc->ops_lock); if (err) @@ -561,23 +595,32 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) if (rtc->uie_rtctimer.enabled == enabled) goto out; - if (rtc->uie_unsupported) { - err = -EINVAL; - goto out; + if (!test_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features) || + !test_bit(RTC_FEATURE_ALARM, rtc->features)) { + mutex_unlock(&rtc->ops_lock); +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + return rtc_dev_update_irq_enable_emul(rtc, enabled); +#else + return -EINVAL; +#endif } if (enabled) { struct rtc_time tm; ktime_t now, onesec; - rc = __rtc_read_time(rtc, &tm); - if (rc) + err = __rtc_read_time(rtc, &tm); + if (err) goto out; onesec = ktime_set(1, 0); now = rtc_tm_to_ktime(tm); rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); rtc->uie_rtctimer.period = ktime_set(1, 0); err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); + if (!err && rtc->ops && rtc->ops->alarm_irq_enable) + err = rtc->ops->alarm_irq_enable(rtc->dev.parent, 1); + if (err) + goto out; } else { rtc_timer_remove(rtc, &rtc->uie_rtctimer); } @@ -585,24 +628,6 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) out: mutex_unlock(&rtc->ops_lock); - /* - * __rtc_read_time() failed, this probably means that the RTC time has - * never been set or less probably there is a transient error on the - * bus. In any case, avoid enabling emulation has this will fail when - * reading the time too. - */ - if (rc) - return rc; - -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL - /* - * Enable emulation if the driver returned -EINVAL to signal that it has - * been configured without interrupts or they are not available at the - * moment. - */ - if (err == -EINVAL) - err = rtc_dev_update_irq_enable_emul(rtc, enabled); -#endif return err; } EXPORT_SYMBOL_GPL(rtc_update_irq_enable); @@ -699,7 +724,7 @@ struct rtc_device *rtc_class_open(const char *name) struct device *dev; struct rtc_device *rtc = NULL; - dev = class_find_device_by_name(rtc_class, name); + dev = class_find_device_by_name(&rtc_class, name); if (dev) rtc = to_rtc_device(dev); @@ -807,9 +832,13 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); struct rtc_time tm; ktime_t now; + int err; + + err = __rtc_read_time(rtc, &tm); + if (err) + return err; timer->enabled = 1; - __rtc_read_time(rtc, &tm); now = rtc_tm_to_ktime(tm); /* Skip over expired timers */ @@ -823,7 +852,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) trace_rtc_timer_enqueue(timer); if (!next || ktime_before(timer->node.expires, next->expires)) { struct rtc_wkalrm alarm; - int err; alarm.time = rtc_ktime_to_tm(timer->node.expires); alarm.enabled = 1; @@ -843,7 +871,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) static void rtc_alarm_disable(struct rtc_device *rtc) { - if (!rtc->ops || !rtc->ops->alarm_irq_enable) + if (!rtc->ops || !test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->alarm_irq_enable) return; rtc->ops->alarm_irq_enable(rtc->dev.parent, false); @@ -903,13 +931,18 @@ void rtc_timer_do_work(struct work_struct *work) struct timerqueue_node *next; ktime_t now; struct rtc_time tm; + int err; struct rtc_device *rtc = container_of(work, struct rtc_device, irqwork); mutex_lock(&rtc->ops_lock); again: - __rtc_read_time(rtc, &tm); + err = __rtc_read_time(rtc, &tm); + if (err) { + mutex_unlock(&rtc->ops_lock); + return; + } now = rtc_tm_to_ktime(tm); while ((next = timerqueue_getnext(&rtc->timerqueue))) { if (next->expires > now) |
