From c0afabd3d553c521e003779c127143ffde55a16f Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 22 Nov 2011 11:03:14 +0100 Subject: rtc: Disable the alarm in the hardware Currently, the RTC code does not disable the alarm in the hardware. This means that after a sequence such as the one below (the files are in the RTC sysfs), the box will boot up after 2 minutes even though we've asked for the alarm to be turned off. # echo $((`cat since_epoch`)+120) > wakealarm # echo 0 > wakealarm # poweroff Fix this by disabling the alarm when there are no timers to run. Cc: stable@kernel.org Cc: John Stultz Signed-off-by: Rabin Vincent Signed-off-by: John Stultz --- drivers/rtc/interface.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 3195dbd3ec3..e3cf2f54221 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -318,6 +318,20 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_read_alarm); +static int ___rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->set_alarm) + err = -EINVAL; + else + err = rtc->ops->set_alarm(rtc->dev.parent, alarm); + + return err; +} + static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { struct rtc_time tm; @@ -341,14 +355,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) * over right here, before we set the alarm. */ - if (!rtc->ops) - err = -ENODEV; - else if (!rtc->ops->set_alarm) - err = -EINVAL; - else - err = rtc->ops->set_alarm(rtc->dev.parent, alarm); - - return err; + return ___rtc_set_alarm(rtc, alarm); } int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) @@ -762,6 +769,20 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) return 0; } +static void rtc_alarm_disable(struct rtc_device *rtc) +{ + struct rtc_wkalrm alarm; + struct rtc_time tm; + + __rtc_read_time(rtc, &tm); + + alarm.time = rtc_ktime_to_tm(ktime_add(rtc_tm_to_ktime(tm), + ktime_set(300, 0))); + alarm.enabled = 0; + + ___rtc_set_alarm(rtc, &alarm); +} + /** * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue * @rtc rtc device @@ -783,8 +804,10 @@ static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) struct rtc_wkalrm alarm; int err; next = timerqueue_getnext(&rtc->timerqueue); - if (!next) + if (!next) { + rtc_alarm_disable(rtc); return; + } alarm.time = rtc_ktime_to_tm(next->expires); alarm.enabled = 1; err = __rtc_set_alarm(rtc, &alarm); @@ -846,7 +869,8 @@ again: err = __rtc_set_alarm(rtc, &alarm); if (err == -ETIME) goto again; - } + } else + rtc_alarm_disable(rtc); mutex_unlock(&rtc->ops_lock); } -- cgit v1.2.3-70-g09d2 From 93b2ec0128c431148b216b8f7337c1a52131ef03 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 9 Dec 2011 09:39:15 +1100 Subject: rtc: Expire alarms after the time is set. If the alarm time programming in the rtc is ever in the past, it won't fire, and any other alarm will be queued after it so they won't fire either. So any time that the alarm might be in the past, we need to trigger the irq handler to ensure the old alarm is cleared and the timer queue is fully in the future. This can happen: - when we first initialise the alarm - when we set the time in the rtc. so follow both of these by scheduling the timer work function. CC: stable@kernel.org Signed-off-by: NeilBrown [Also catch set_mmss case -jstultz] Signed-off-by: John Stultz --- drivers/rtc/interface.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index fa4d9f32418..3bcc7cfcaba 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -73,6 +73,8 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) err = -EINVAL; mutex_unlock(&rtc->ops_lock); + /* A timer might have just expired */ + schedule_work(&rtc->irqwork); return err; } EXPORT_SYMBOL_GPL(rtc_set_time); @@ -112,6 +114,8 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) err = -EINVAL; mutex_unlock(&rtc->ops_lock); + /* A timer might have just expired */ + schedule_work(&rtc->irqwork); return err; } @@ -403,6 +407,8 @@ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); } mutex_unlock(&rtc->ops_lock); + /* maybe that was in the past.*/ + schedule_work(&rtc->irqwork); return err; } EXPORT_SYMBOL_GPL(rtc_initialize_alarm); -- cgit v1.2.3-70-g09d2