Commit 998a0605 authored by Derek Basehore's avatar Derek Basehore Committed by Linus Torvalds

drivers/rtc/rtc-cmos.c: work around bios clearing rtc control

The bios may clear the rtc control register when resuming the system. Since the
cmos interrupt handler may now be run before the rtc_cmos is resumed, this can
cause the interrupt handler to ignore an alarm since the alarm bit is not set in
the rtc control register. To work around this, check if the rtc_cmos is
suspended and use the stored value for the rtc control register.
Signed-off-by: default avatarDerek Basehore <dbasehore@chromium.org>
Reviewed-by: default avatarSameer Nanda <snanda@chromium.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Jingoo Han <jg1.han@samsung.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: John Stultz <john.stultz@linaro.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ae845894
...@@ -556,17 +556,24 @@ static irqreturn_t cmos_interrupt(int irq, void *p) ...@@ -556,17 +556,24 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
rtc_control = CMOS_READ(RTC_CONTROL); rtc_control = CMOS_READ(RTC_CONTROL);
if (is_hpet_enabled()) if (is_hpet_enabled())
irqstat = (unsigned long)irq & 0xF0; irqstat = (unsigned long)irq & 0xF0;
irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
/* If we were suspended, RTC_CONTROL may not be accurate since the
* bios may have cleared it.
*/
if (!cmos_rtc.suspend_ctrl)
irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
else
irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
/* All Linux RTC alarms should be treated as if they were oneshot. /* All Linux RTC alarms should be treated as if they were oneshot.
* Similar code may be needed in system wakeup paths, in case the * Similar code may be needed in system wakeup paths, in case the
* alarm woke the system. * alarm woke the system.
*/ */
if (irqstat & RTC_AIE) { if (irqstat & RTC_AIE) {
cmos_rtc.suspend_ctrl &= ~RTC_AIE;
rtc_control &= ~RTC_AIE; rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL); CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(RTC_AIE); hpet_mask_rtc_irq_bit(RTC_AIE);
CMOS_READ(RTC_INTR_FLAGS); CMOS_READ(RTC_INTR_FLAGS);
} }
spin_unlock(&rtc_lock); spin_unlock(&rtc_lock);
...@@ -839,21 +846,23 @@ static inline int cmos_poweroff(struct device *dev) ...@@ -839,21 +846,23 @@ static inline int cmos_poweroff(struct device *dev)
static int cmos_resume(struct device *dev) static int cmos_resume(struct device *dev)
{ {
struct cmos_rtc *cmos = dev_get_drvdata(dev); struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char tmp = cmos->suspend_ctrl; unsigned char tmp;
if (cmos->enabled_wake) {
if (cmos->wake_off)
cmos->wake_off(dev);
else
disable_irq_wake(cmos->irq);
cmos->enabled_wake = 0;
}
spin_lock_irq(&rtc_lock);
tmp = cmos->suspend_ctrl;
cmos->suspend_ctrl = 0;
/* re-enable any irqs previously active */ /* re-enable any irqs previously active */
if (tmp & RTC_IRQMASK) { if (tmp & RTC_IRQMASK) {
unsigned char mask; unsigned char mask;
if (cmos->enabled_wake) {
if (cmos->wake_off)
cmos->wake_off(dev);
else
disable_irq_wake(cmos->irq);
cmos->enabled_wake = 0;
}
spin_lock_irq(&rtc_lock);
if (device_may_wakeup(dev)) if (device_may_wakeup(dev))
hpet_rtc_timer_init(); hpet_rtc_timer_init();
...@@ -873,8 +882,8 @@ static int cmos_resume(struct device *dev) ...@@ -873,8 +882,8 @@ static int cmos_resume(struct device *dev)
tmp &= ~RTC_AIE; tmp &= ~RTC_AIE;
hpet_mask_rtc_irq_bit(RTC_AIE); hpet_mask_rtc_irq_bit(RTC_AIE);
} while (mask & RTC_AIE); } while (mask & RTC_AIE);
spin_unlock_irq(&rtc_lock);
} }
spin_unlock_irq(&rtc_lock);
dev_dbg(dev, "resume, ctrl %02x\n", tmp); dev_dbg(dev, "resume, ctrl %02x\n", tmp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment