aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/interface.c')
-rw-r--r--drivers/rtc/interface.c59
1 files changed, 40 insertions, 19 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index df68618f6db..a86f3013747 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -227,11 +227,11 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
227 alarm->time.tm_hour = now.tm_hour; 227 alarm->time.tm_hour = now.tm_hour;
228 228
229 /* For simplicity, only support date rollover for now */ 229 /* For simplicity, only support date rollover for now */
230 if (alarm->time.tm_mday == -1) { 230 if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) {
231 alarm->time.tm_mday = now.tm_mday; 231 alarm->time.tm_mday = now.tm_mday;
232 missing = day; 232 missing = day;
233 } 233 }
234 if (alarm->time.tm_mon == -1) { 234 if ((unsigned)alarm->time.tm_mon >= 12) {
235 alarm->time.tm_mon = now.tm_mon; 235 alarm->time.tm_mon = now.tm_mon;
236 if (missing == none) 236 if (missing == none)
237 missing = month; 237 missing = month;
@@ -636,6 +636,29 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
636} 636}
637EXPORT_SYMBOL_GPL(rtc_irq_unregister); 637EXPORT_SYMBOL_GPL(rtc_irq_unregister);
638 638
639static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
640{
641 /*
642 * We always cancel the timer here first, because otherwise
643 * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
644 * when we manage to start the timer before the callback
645 * returns HRTIMER_RESTART.
646 *
647 * We cannot use hrtimer_cancel() here as a running callback
648 * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
649 * would spin forever.
650 */
651 if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
652 return -1;
653
654 if (enabled) {
655 ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
656
657 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
658 }
659 return 0;
660}
661
639/** 662/**
640 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs 663 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
641 * @rtc: the rtc device 664 * @rtc: the rtc device
@@ -651,21 +674,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
651 int err = 0; 674 int err = 0;
652 unsigned long flags; 675 unsigned long flags;
653 676
677retry:
654 spin_lock_irqsave(&rtc->irq_task_lock, flags); 678 spin_lock_irqsave(&rtc->irq_task_lock, flags);
655 if (rtc->irq_task != NULL && task == NULL) 679 if (rtc->irq_task != NULL && task == NULL)
656 err = -EBUSY; 680 err = -EBUSY;
657 if (rtc->irq_task != task) 681 if (rtc->irq_task != task)
658 err = -EACCES; 682 err = -EACCES;
659 683 if (!err) {
660 if (enabled) { 684 if (rtc_update_hrtimer(rtc, enabled) < 0) {
661 ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 685 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
662 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); 686 cpu_relax();
663 } else { 687 goto retry;
664 hrtimer_cancel(&rtc->pie_timer); 688 }
689 rtc->pie_enabled = enabled;
665 } 690 }
666 rtc->pie_enabled = enabled;
667 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 691 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
668
669 return err; 692 return err;
670} 693}
671EXPORT_SYMBOL_GPL(rtc_irq_set_state); 694EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -685,22 +708,20 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
685 int err = 0; 708 int err = 0;
686 unsigned long flags; 709 unsigned long flags;
687 710
688 if (freq <= 0) 711 if (freq <= 0 || freq > RTC_MAX_FREQ)
689 return -EINVAL; 712 return -EINVAL;
690 713retry:
691 spin_lock_irqsave(&rtc->irq_task_lock, flags); 714 spin_lock_irqsave(&rtc->irq_task_lock, flags);
692 if (rtc->irq_task != NULL && task == NULL) 715 if (rtc->irq_task != NULL && task == NULL)
693 err = -EBUSY; 716 err = -EBUSY;
694 if (rtc->irq_task != task) 717 if (rtc->irq_task != task)
695 err = -EACCES; 718 err = -EACCES;
696 if (err == 0) { 719 if (!err) {
697 rtc->irq_freq = freq; 720 rtc->irq_freq = freq;
698 if (rtc->pie_enabled) { 721 if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
699 ktime_t period; 722 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
700 hrtimer_cancel(&rtc->pie_timer); 723 cpu_relax();
701 period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 724 goto retry;
702 hrtimer_start(&rtc->pie_timer, period,
703 HRTIMER_MODE_REL);
704 } 725 }
705 } 726 }
706 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 727 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);