]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
drivers/rtc/rtc-cmos.c: work around bios clearing rtc control
authorDerek Basehore <dbasehore@chromium.org>
Wed, 3 Jul 2013 22:07:54 +0000 (15:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 Jul 2013 23:08:00 +0000 (16:08 -0700)
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>
drivers/rtc/rtc-cmos.c

index a6727d9773305fca6c8e5db196de7a42539b47e0..be06d7150de5d4b81f6c84e9c5cc874e26360e70 100644 (file)
@@ -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);
@@ -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();
 
@@ -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);