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.c56
1 files changed, 39 insertions, 17 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index df68618f6dbb..8e286259a007 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -13,6 +13,7 @@
13 13
14#include <linux/rtc.h> 14#include <linux/rtc.h>
15#include <linux/sched.h> 15#include <linux/sched.h>
16#include <linux/module.h>
16#include <linux/log2.h> 17#include <linux/log2.h>
17#include <linux/workqueue.h> 18#include <linux/workqueue.h>
18 19
@@ -636,6 +637,29 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
636} 637}
637EXPORT_SYMBOL_GPL(rtc_irq_unregister); 638EXPORT_SYMBOL_GPL(rtc_irq_unregister);
638 639
640static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
641{
642 /*
643 * We always cancel the timer here first, because otherwise
644 * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
645 * when we manage to start the timer before the callback
646 * returns HRTIMER_RESTART.
647 *
648 * We cannot use hrtimer_cancel() here as a running callback
649 * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
650 * would spin forever.
651 */
652 if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
653 return -1;
654
655 if (enabled) {
656 ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
657
658 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
659 }
660 return 0;
661}
662
639/** 663/**
640 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs 664 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
641 * @rtc: the rtc device 665 * @rtc: the rtc device
@@ -651,21 +675,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
651 int err = 0; 675 int err = 0;
652 unsigned long flags; 676 unsigned long flags;
653 677
678retry:
654 spin_lock_irqsave(&rtc->irq_task_lock, flags); 679 spin_lock_irqsave(&rtc->irq_task_lock, flags);
655 if (rtc->irq_task != NULL && task == NULL) 680 if (rtc->irq_task != NULL && task == NULL)
656 err = -EBUSY; 681 err = -EBUSY;
657 if (rtc->irq_task != task) 682 if (rtc->irq_task != task)
658 err = -EACCES; 683 err = -EACCES;
659 684 if (!err) {
660 if (enabled) { 685 if (rtc_update_hrtimer(rtc, enabled) < 0) {
661 ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 686 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
662 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); 687 cpu_relax();
663 } else { 688 goto retry;
664 hrtimer_cancel(&rtc->pie_timer); 689 }
690 rtc->pie_enabled = enabled;
665 } 691 }
666 rtc->pie_enabled = enabled;
667 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 692 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
668
669 return err; 693 return err;
670} 694}
671EXPORT_SYMBOL_GPL(rtc_irq_set_state); 695EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -685,22 +709,20 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
685 int err = 0; 709 int err = 0;
686 unsigned long flags; 710 unsigned long flags;
687 711
688 if (freq <= 0) 712 if (freq <= 0 || freq > RTC_MAX_FREQ)
689 return -EINVAL; 713 return -EINVAL;
690 714retry:
691 spin_lock_irqsave(&rtc->irq_task_lock, flags); 715 spin_lock_irqsave(&rtc->irq_task_lock, flags);
692 if (rtc->irq_task != NULL && task == NULL) 716 if (rtc->irq_task != NULL && task == NULL)
693 err = -EBUSY; 717 err = -EBUSY;
694 if (rtc->irq_task != task) 718 if (rtc->irq_task != task)
695 err = -EACCES; 719 err = -EACCES;
696 if (err == 0) { 720 if (!err) {
697 rtc->irq_freq = freq; 721 rtc->irq_freq = freq;
698 if (rtc->pie_enabled) { 722 if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
699 ktime_t period; 723 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
700 hrtimer_cancel(&rtc->pie_timer); 724 cpu_relax();
701 period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 725 goto retry;
702 hrtimer_start(&rtc->pie_timer, period,
703 HRTIMER_MODE_REL);
704 } 726 }
705 } 727 }
706 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 728 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);