Skip to content

Commit

Permalink
drivers/rtc/rtc-cmos.c: work around bios clearing rtc control
Browse files Browse the repository at this point in the history
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: Derek Basehore <dbasehore@chromium.org>
Reviewed-by: Sameer 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Derek Basehore authored and Linus Torvalds committed Jul 3, 2013
1 parent ae84589 commit 998a060
Showing 1 changed file with 22 additions and 13 deletions.
35 changes: 22 additions & 13 deletions drivers/rtc/rtc-cmos.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,17 +556,24 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
rtc_control = CMOS_READ(RTC_CONTROL);
if (is_hpet_enabled())
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.
* Similar code may be needed in system wakeup paths, in case the
* alarm woke the system.
*/
if (irqstat & RTC_AIE) {
cmos_rtc.suspend_ctrl &= ~RTC_AIE;
rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(RTC_AIE);

CMOS_READ(RTC_INTR_FLAGS);
}
spin_unlock(&rtc_lock);
Expand Down Expand Up @@ -839,21 +846,23 @@ static inline int cmos_poweroff(struct device *dev)
static int cmos_resume(struct device *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 */
if (tmp & RTC_IRQMASK) {
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))
hpet_rtc_timer_init();

Expand All @@ -873,8 +882,8 @@ static int cmos_resume(struct device *dev)
tmp &= ~RTC_AIE;
hpet_mask_rtc_irq_bit(RTC_AIE);
} while (mask & RTC_AIE);
spin_unlock_irq(&rtc_lock);
}
spin_unlock_irq(&rtc_lock);

dev_dbg(dev, "resume, ctrl %02x\n", tmp);

Expand Down

0 comments on commit 998a060

Please sign in to comment.