diff options
author | John Stultz <john.stultz@linaro.org> | 2011-01-20 18:26:12 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-01-21 11:38:19 -0500 |
commit | aa0be0f4659f91f31e45adc422b1788cb36ffddc (patch) | |
tree | 02eed33d672f36e41e735c5060acb9d2446c23af | |
parent | db6b175fa6ad1408cbb2fb62949a6d55cfece03e (diff) |
RTC: Propagate error handling via rtc_timer_enqueue properly
In cases where RTC hardware does not support alarms, the virtualized
RTC interfaces did not have a way to propagate the error up to userland.
This patch extends rtc_timer_enqueue so it catches errors from the hardware
and returns them upwards to the virtualized interfaces. To simplify error
handling, it also internalizes the management of the timer->enabled bit
into rtc_timer_enqueue and rtc_timer_remove.
Also makes rtc_timer_enqueue and rtc_timer_remove static.
Reported-by: David Daney <ddaney@caviumnetworks.com>
Reported-by: Andreas Schwab <schwab@linux-m68k.org>
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Diagnosed-by: David Daney <ddaney@caviumnetworks.com>
Tested-by: David Daney <ddaney@caviumnetworks.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
LKML-Reference: <1295565973-14358-1-git-send-email-john.stultz@linaro.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/rtc/interface.c | 49 | ||||
-rw-r--r-- | include/linux/rtc.h | 2 |
2 files changed, 28 insertions, 23 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 90384b9f6b2c..f1ba2c696528 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c | |||
@@ -16,6 +16,9 @@ | |||
16 | #include <linux/log2.h> | 16 | #include <linux/log2.h> |
17 | #include <linux/workqueue.h> | 17 | #include <linux/workqueue.h> |
18 | 18 | ||
19 | static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); | ||
20 | static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); | ||
21 | |||
19 | static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) | 22 | static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) |
20 | { | 23 | { |
21 | int err; | 24 | int err; |
@@ -175,16 +178,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
175 | return err; | 178 | return err; |
176 | if (rtc->aie_timer.enabled) { | 179 | if (rtc->aie_timer.enabled) { |
177 | rtc_timer_remove(rtc, &rtc->aie_timer); | 180 | rtc_timer_remove(rtc, &rtc->aie_timer); |
178 | rtc->aie_timer.enabled = 0; | ||
179 | } | 181 | } |
180 | rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); | 182 | rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); |
181 | rtc->aie_timer.period = ktime_set(0, 0); | 183 | rtc->aie_timer.period = ktime_set(0, 0); |
182 | if (alarm->enabled) { | 184 | if (alarm->enabled) { |
183 | rtc->aie_timer.enabled = 1; | 185 | err = rtc_timer_enqueue(rtc, &rtc->aie_timer); |
184 | rtc_timer_enqueue(rtc, &rtc->aie_timer); | ||
185 | } | 186 | } |
186 | mutex_unlock(&rtc->ops_lock); | 187 | mutex_unlock(&rtc->ops_lock); |
187 | return 0; | 188 | return err; |
188 | } | 189 | } |
189 | EXPORT_SYMBOL_GPL(rtc_set_alarm); | 190 | EXPORT_SYMBOL_GPL(rtc_set_alarm); |
190 | 191 | ||
@@ -195,15 +196,15 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) | |||
195 | return err; | 196 | return err; |
196 | 197 | ||
197 | if (rtc->aie_timer.enabled != enabled) { | 198 | if (rtc->aie_timer.enabled != enabled) { |
198 | if (enabled) { | 199 | if (enabled) |
199 | rtc->aie_timer.enabled = 1; | 200 | err = rtc_timer_enqueue(rtc, &rtc->aie_timer); |
200 | rtc_timer_enqueue(rtc, &rtc->aie_timer); | 201 | else |
201 | } else { | ||
202 | rtc_timer_remove(rtc, &rtc->aie_timer); | 202 | rtc_timer_remove(rtc, &rtc->aie_timer); |
203 | rtc->aie_timer.enabled = 0; | ||
204 | } | ||
205 | } | 203 | } |
206 | 204 | ||
205 | if (err) | ||
206 | return err; | ||
207 | |||
207 | if (!rtc->ops) | 208 | if (!rtc->ops) |
208 | err = -ENODEV; | 209 | err = -ENODEV; |
209 | else if (!rtc->ops->alarm_irq_enable) | 210 | else if (!rtc->ops->alarm_irq_enable) |
@@ -235,12 +236,9 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) | |||
235 | now = rtc_tm_to_ktime(tm); | 236 | now = rtc_tm_to_ktime(tm); |
236 | rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); | 237 | rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); |
237 | rtc->uie_rtctimer.period = ktime_set(1, 0); | 238 | rtc->uie_rtctimer.period = ktime_set(1, 0); |
238 | rtc->uie_rtctimer.enabled = 1; | 239 | err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); |
239 | rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); | 240 | } else |
240 | } else { | ||
241 | rtc_timer_remove(rtc, &rtc->uie_rtctimer); | 241 | rtc_timer_remove(rtc, &rtc->uie_rtctimer); |
242 | rtc->uie_rtctimer.enabled = 0; | ||
243 | } | ||
244 | 242 | ||
245 | out: | 243 | out: |
246 | mutex_unlock(&rtc->ops_lock); | 244 | mutex_unlock(&rtc->ops_lock); |
@@ -488,10 +486,13 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); | |||
488 | * Enqueues a timer onto the rtc devices timerqueue and sets | 486 | * Enqueues a timer onto the rtc devices timerqueue and sets |
489 | * the next alarm event appropriately. | 487 | * the next alarm event appropriately. |
490 | * | 488 | * |
489 | * Sets the enabled bit on the added timer. | ||
490 | * | ||
491 | * Must hold ops_lock for proper serialization of timerqueue | 491 | * Must hold ops_lock for proper serialization of timerqueue |
492 | */ | 492 | */ |
493 | void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | 493 | static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) |
494 | { | 494 | { |
495 | timer->enabled = 1; | ||
495 | timerqueue_add(&rtc->timerqueue, &timer->node); | 496 | timerqueue_add(&rtc->timerqueue, &timer->node); |
496 | if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { | 497 | if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { |
497 | struct rtc_wkalrm alarm; | 498 | struct rtc_wkalrm alarm; |
@@ -501,7 +502,13 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | |||
501 | err = __rtc_set_alarm(rtc, &alarm); | 502 | err = __rtc_set_alarm(rtc, &alarm); |
502 | if (err == -ETIME) | 503 | if (err == -ETIME) |
503 | schedule_work(&rtc->irqwork); | 504 | schedule_work(&rtc->irqwork); |
505 | else if (err) { | ||
506 | timerqueue_del(&rtc->timerqueue, &timer->node); | ||
507 | timer->enabled = 0; | ||
508 | return err; | ||
509 | } | ||
504 | } | 510 | } |
511 | return 0; | ||
505 | } | 512 | } |
506 | 513 | ||
507 | /** | 514 | /** |
@@ -512,13 +519,15 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | |||
512 | * Removes a timer onto the rtc devices timerqueue and sets | 519 | * Removes a timer onto the rtc devices timerqueue and sets |
513 | * the next alarm event appropriately. | 520 | * the next alarm event appropriately. |
514 | * | 521 | * |
522 | * Clears the enabled bit on the removed timer. | ||
523 | * | ||
515 | * Must hold ops_lock for proper serialization of timerqueue | 524 | * Must hold ops_lock for proper serialization of timerqueue |
516 | */ | 525 | */ |
517 | void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) | 526 | static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) |
518 | { | 527 | { |
519 | struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); | 528 | struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); |
520 | timerqueue_del(&rtc->timerqueue, &timer->node); | 529 | timerqueue_del(&rtc->timerqueue, &timer->node); |
521 | 530 | timer->enabled = 0; | |
522 | if (next == &timer->node) { | 531 | if (next == &timer->node) { |
523 | struct rtc_wkalrm alarm; | 532 | struct rtc_wkalrm alarm; |
524 | int err; | 533 | int err; |
@@ -626,8 +635,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, | |||
626 | timer->node.expires = expires; | 635 | timer->node.expires = expires; |
627 | timer->period = period; | 636 | timer->period = period; |
628 | 637 | ||
629 | timer->enabled = 1; | 638 | ret = rtc_timer_enqueue(rtc, timer); |
630 | rtc_timer_enqueue(rtc, timer); | ||
631 | 639 | ||
632 | mutex_unlock(&rtc->ops_lock); | 640 | mutex_unlock(&rtc->ops_lock); |
633 | return ret; | 641 | return ret; |
@@ -645,7 +653,6 @@ int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer) | |||
645 | mutex_lock(&rtc->ops_lock); | 653 | mutex_lock(&rtc->ops_lock); |
646 | if (timer->enabled) | 654 | if (timer->enabled) |
647 | rtc_timer_remove(rtc, timer); | 655 | rtc_timer_remove(rtc, timer); |
648 | timer->enabled = 0; | ||
649 | mutex_unlock(&rtc->ops_lock); | 656 | mutex_unlock(&rtc->ops_lock); |
650 | return ret; | 657 | return ret; |
651 | } | 658 | } |
diff --git a/include/linux/rtc.h b/include/linux/rtc.h index ea760698e6fe..a0b639f8e805 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h | |||
@@ -244,8 +244,6 @@ int rtc_register(rtc_task_t *task); | |||
244 | int rtc_unregister(rtc_task_t *task); | 244 | int rtc_unregister(rtc_task_t *task); |
245 | int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); | 245 | int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); |
246 | 246 | ||
247 | void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); | ||
248 | void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); | ||
249 | void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data); | 247 | void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data); |
250 | int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, | 248 | int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, |
251 | ktime_t expires, ktime_t period); | 249 | ktime_t expires, ktime_t period); |