diff options
Diffstat (limited to 'drivers/rtc/interface.c')
-rw-r--r-- | drivers/rtc/interface.c | 83 |
1 files changed, 57 insertions, 26 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 90384b9f6b2c..cb2f0728fd70 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; |
@@ -120,12 +123,18 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
120 | err = mutex_lock_interruptible(&rtc->ops_lock); | 123 | err = mutex_lock_interruptible(&rtc->ops_lock); |
121 | if (err) | 124 | if (err) |
122 | return err; | 125 | return err; |
123 | alarm->enabled = rtc->aie_timer.enabled; | 126 | if (rtc->ops == NULL) |
124 | if (alarm->enabled) | 127 | err = -ENODEV; |
128 | else if (!rtc->ops->read_alarm) | ||
129 | err = -EINVAL; | ||
130 | else { | ||
131 | memset(alarm, 0, sizeof(struct rtc_wkalrm)); | ||
132 | alarm->enabled = rtc->aie_timer.enabled; | ||
125 | alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires); | 133 | alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires); |
134 | } | ||
126 | mutex_unlock(&rtc->ops_lock); | 135 | mutex_unlock(&rtc->ops_lock); |
127 | 136 | ||
128 | return 0; | 137 | return err; |
129 | } | 138 | } |
130 | EXPORT_SYMBOL_GPL(rtc_read_alarm); | 139 | EXPORT_SYMBOL_GPL(rtc_read_alarm); |
131 | 140 | ||
@@ -175,16 +184,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
175 | return err; | 184 | return err; |
176 | if (rtc->aie_timer.enabled) { | 185 | if (rtc->aie_timer.enabled) { |
177 | rtc_timer_remove(rtc, &rtc->aie_timer); | 186 | rtc_timer_remove(rtc, &rtc->aie_timer); |
178 | rtc->aie_timer.enabled = 0; | ||
179 | } | 187 | } |
180 | rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); | 188 | rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); |
181 | rtc->aie_timer.period = ktime_set(0, 0); | 189 | rtc->aie_timer.period = ktime_set(0, 0); |
182 | if (alarm->enabled) { | 190 | if (alarm->enabled) { |
183 | rtc->aie_timer.enabled = 1; | 191 | err = rtc_timer_enqueue(rtc, &rtc->aie_timer); |
184 | rtc_timer_enqueue(rtc, &rtc->aie_timer); | ||
185 | } | 192 | } |
186 | mutex_unlock(&rtc->ops_lock); | 193 | mutex_unlock(&rtc->ops_lock); |
187 | return 0; | 194 | return err; |
188 | } | 195 | } |
189 | EXPORT_SYMBOL_GPL(rtc_set_alarm); | 196 | EXPORT_SYMBOL_GPL(rtc_set_alarm); |
190 | 197 | ||
@@ -195,16 +202,15 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) | |||
195 | return err; | 202 | return err; |
196 | 203 | ||
197 | if (rtc->aie_timer.enabled != enabled) { | 204 | if (rtc->aie_timer.enabled != enabled) { |
198 | if (enabled) { | 205 | if (enabled) |
199 | rtc->aie_timer.enabled = 1; | 206 | err = rtc_timer_enqueue(rtc, &rtc->aie_timer); |
200 | rtc_timer_enqueue(rtc, &rtc->aie_timer); | 207 | else |
201 | } else { | ||
202 | rtc_timer_remove(rtc, &rtc->aie_timer); | 208 | rtc_timer_remove(rtc, &rtc->aie_timer); |
203 | rtc->aie_timer.enabled = 0; | ||
204 | } | ||
205 | } | 209 | } |
206 | 210 | ||
207 | if (!rtc->ops) | 211 | if (err) |
212 | /* nothing */; | ||
213 | else if (!rtc->ops) | ||
208 | err = -ENODEV; | 214 | err = -ENODEV; |
209 | else if (!rtc->ops->alarm_irq_enable) | 215 | else if (!rtc->ops->alarm_irq_enable) |
210 | err = -EINVAL; | 216 | err = -EINVAL; |
@@ -222,6 +228,12 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) | |||
222 | if (err) | 228 | if (err) |
223 | return err; | 229 | return err; |
224 | 230 | ||
231 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
232 | if (enabled == 0 && rtc->uie_irq_active) { | ||
233 | mutex_unlock(&rtc->ops_lock); | ||
234 | return rtc_dev_update_irq_enable_emul(rtc, 0); | ||
235 | } | ||
236 | #endif | ||
225 | /* make sure we're changing state */ | 237 | /* make sure we're changing state */ |
226 | if (rtc->uie_rtctimer.enabled == enabled) | 238 | if (rtc->uie_rtctimer.enabled == enabled) |
227 | goto out; | 239 | goto out; |
@@ -235,15 +247,22 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) | |||
235 | now = rtc_tm_to_ktime(tm); | 247 | now = rtc_tm_to_ktime(tm); |
236 | rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); | 248 | rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); |
237 | rtc->uie_rtctimer.period = ktime_set(1, 0); | 249 | rtc->uie_rtctimer.period = ktime_set(1, 0); |
238 | rtc->uie_rtctimer.enabled = 1; | 250 | err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); |
239 | rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); | 251 | } else |
240 | } else { | ||
241 | rtc_timer_remove(rtc, &rtc->uie_rtctimer); | 252 | rtc_timer_remove(rtc, &rtc->uie_rtctimer); |
242 | rtc->uie_rtctimer.enabled = 0; | ||
243 | } | ||
244 | 253 | ||
245 | out: | 254 | out: |
246 | mutex_unlock(&rtc->ops_lock); | 255 | mutex_unlock(&rtc->ops_lock); |
256 | #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL | ||
257 | /* | ||
258 | * Enable emulation if the driver did not provide | ||
259 | * the update_irq_enable function pointer or if returned | ||
260 | * -EINVAL to signal that it has been configured without | ||
261 | * interrupts or that are not available at the moment. | ||
262 | */ | ||
263 | if (err == -EINVAL) | ||
264 | err = rtc_dev_update_irq_enable_emul(rtc, enabled); | ||
265 | #endif | ||
247 | return err; | 266 | return err; |
248 | 267 | ||
249 | } | 268 | } |
@@ -259,7 +278,7 @@ EXPORT_SYMBOL_GPL(rtc_update_irq_enable); | |||
259 | * | 278 | * |
260 | * Triggers the registered irq_task function callback. | 279 | * Triggers the registered irq_task function callback. |
261 | */ | 280 | */ |
262 | static void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode) | 281 | void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode) |
263 | { | 282 | { |
264 | unsigned long flags; | 283 | unsigned long flags; |
265 | 284 | ||
@@ -460,6 +479,9 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) | |||
460 | int err = 0; | 479 | int err = 0; |
461 | unsigned long flags; | 480 | unsigned long flags; |
462 | 481 | ||
482 | if (freq <= 0) | ||
483 | return -EINVAL; | ||
484 | |||
463 | spin_lock_irqsave(&rtc->irq_task_lock, flags); | 485 | spin_lock_irqsave(&rtc->irq_task_lock, flags); |
464 | if (rtc->irq_task != NULL && task == NULL) | 486 | if (rtc->irq_task != NULL && task == NULL) |
465 | err = -EBUSY; | 487 | err = -EBUSY; |
@@ -488,10 +510,13 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); | |||
488 | * Enqueues a timer onto the rtc devices timerqueue and sets | 510 | * Enqueues a timer onto the rtc devices timerqueue and sets |
489 | * the next alarm event appropriately. | 511 | * the next alarm event appropriately. |
490 | * | 512 | * |
513 | * Sets the enabled bit on the added timer. | ||
514 | * | ||
491 | * Must hold ops_lock for proper serialization of timerqueue | 515 | * Must hold ops_lock for proper serialization of timerqueue |
492 | */ | 516 | */ |
493 | void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | 517 | static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) |
494 | { | 518 | { |
519 | timer->enabled = 1; | ||
495 | timerqueue_add(&rtc->timerqueue, &timer->node); | 520 | timerqueue_add(&rtc->timerqueue, &timer->node); |
496 | if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { | 521 | if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { |
497 | struct rtc_wkalrm alarm; | 522 | struct rtc_wkalrm alarm; |
@@ -501,7 +526,13 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | |||
501 | err = __rtc_set_alarm(rtc, &alarm); | 526 | err = __rtc_set_alarm(rtc, &alarm); |
502 | if (err == -ETIME) | 527 | if (err == -ETIME) |
503 | schedule_work(&rtc->irqwork); | 528 | schedule_work(&rtc->irqwork); |
529 | else if (err) { | ||
530 | timerqueue_del(&rtc->timerqueue, &timer->node); | ||
531 | timer->enabled = 0; | ||
532 | return err; | ||
533 | } | ||
504 | } | 534 | } |
535 | return 0; | ||
505 | } | 536 | } |
506 | 537 | ||
507 | /** | 538 | /** |
@@ -512,13 +543,15 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) | |||
512 | * Removes a timer onto the rtc devices timerqueue and sets | 543 | * Removes a timer onto the rtc devices timerqueue and sets |
513 | * the next alarm event appropriately. | 544 | * the next alarm event appropriately. |
514 | * | 545 | * |
546 | * Clears the enabled bit on the removed timer. | ||
547 | * | ||
515 | * Must hold ops_lock for proper serialization of timerqueue | 548 | * Must hold ops_lock for proper serialization of timerqueue |
516 | */ | 549 | */ |
517 | void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) | 550 | static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) |
518 | { | 551 | { |
519 | struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); | 552 | struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); |
520 | timerqueue_del(&rtc->timerqueue, &timer->node); | 553 | timerqueue_del(&rtc->timerqueue, &timer->node); |
521 | 554 | timer->enabled = 0; | |
522 | if (next == &timer->node) { | 555 | if (next == &timer->node) { |
523 | struct rtc_wkalrm alarm; | 556 | struct rtc_wkalrm alarm; |
524 | int err; | 557 | int err; |
@@ -626,8 +659,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, | |||
626 | timer->node.expires = expires; | 659 | timer->node.expires = expires; |
627 | timer->period = period; | 660 | timer->period = period; |
628 | 661 | ||
629 | timer->enabled = 1; | 662 | ret = rtc_timer_enqueue(rtc, timer); |
630 | rtc_timer_enqueue(rtc, timer); | ||
631 | 663 | ||
632 | mutex_unlock(&rtc->ops_lock); | 664 | mutex_unlock(&rtc->ops_lock); |
633 | return ret; | 665 | return ret; |
@@ -645,7 +677,6 @@ int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer) | |||
645 | mutex_lock(&rtc->ops_lock); | 677 | mutex_lock(&rtc->ops_lock); |
646 | if (timer->enabled) | 678 | if (timer->enabled) |
647 | rtc_timer_remove(rtc, timer); | 679 | rtc_timer_remove(rtc, timer); |
648 | timer->enabled = 0; | ||
649 | mutex_unlock(&rtc->ops_lock); | 680 | mutex_unlock(&rtc->ops_lock); |
650 | return ret; | 681 | return ret; |
651 | } | 682 | } |