Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+22 −13
Original line number Diff line number Diff line
@@ -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;

	/* 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,11 +846,7 @@ 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;

	/* re-enable any irqs previously active */
	if (tmp & RTC_IRQMASK) {
		unsigned char	mask;
	unsigned char tmp;

	if (cmos->enabled_wake) {
		if (cmos->wake_off)
@@ -854,6 +857,12 @@ static int cmos_resume(struct device *dev)
	}

	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 (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);