Commit 7e2a31da authored by David Brownell's avatar David Brownell Committed by Linus Torvalds

rtc-cmos: avoid spurious irqs

This fixes kernel http://bugzilla.kernel.org/show_bug.cgi?id=11112 (bogus
RTC update IRQs reported) for rtc-cmos, in two ways:

  - When HPET is stealing the IRQs, use the first IRQ to grab
    the seconds counter which will be monitored (instead of
    using whatever was previously in that memory);

  - In sane IRQ handling modes, scrub out old IRQ status before
    enabling IRQs.

That latter is done by tightening up IRQ handling for rtc-cmos everywhere,
also ensuring that when HPET is used it's the only thing triggering IRQ
reports to userspace; net object shrink.

Also fix a bogus HPET message related to its RTC emulation.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Report-by: default avatarW Unruh <unruh@physics.ubc.ca>
Cc: Andrew Victor <avictor.za@gmail.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 449321b3
...@@ -468,7 +468,7 @@ void hpet_disable(void) ...@@ -468,7 +468,7 @@ void hpet_disable(void)
#define RTC_NUM_INTS 1 #define RTC_NUM_INTS 1
static unsigned long hpet_rtc_flags; static unsigned long hpet_rtc_flags;
static unsigned long hpet_prev_update_sec; static int hpet_prev_update_sec;
static struct rtc_time hpet_alarm_time; static struct rtc_time hpet_alarm_time;
static unsigned long hpet_pie_count; static unsigned long hpet_pie_count;
static unsigned long hpet_t1_cmp; static unsigned long hpet_t1_cmp;
...@@ -575,6 +575,9 @@ int hpet_set_rtc_irq_bit(unsigned long bit_mask) ...@@ -575,6 +575,9 @@ int hpet_set_rtc_irq_bit(unsigned long bit_mask)
hpet_rtc_flags |= bit_mask; hpet_rtc_flags |= bit_mask;
if ((bit_mask & RTC_UIE) && !(oldbits & RTC_UIE))
hpet_prev_update_sec = -1;
if (!oldbits) if (!oldbits)
hpet_rtc_timer_init(); hpet_rtc_timer_init();
...@@ -652,7 +655,7 @@ static void hpet_rtc_timer_reinit(void) ...@@ -652,7 +655,7 @@ static void hpet_rtc_timer_reinit(void)
if (hpet_rtc_flags & RTC_PIE) if (hpet_rtc_flags & RTC_PIE)
hpet_pie_count += lost_ints; hpet_pie_count += lost_ints;
if (printk_ratelimit()) if (printk_ratelimit())
printk(KERN_WARNING "rtc: lost %d interrupts\n", printk(KERN_WARNING "hpet1: lost %d rtc interrupts\n",
lost_ints); lost_ints);
} }
} }
...@@ -670,7 +673,8 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) ...@@ -670,7 +673,8 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
if (hpet_rtc_flags & RTC_UIE && if (hpet_rtc_flags & RTC_UIE &&
curr_time.tm_sec != hpet_prev_update_sec) { curr_time.tm_sec != hpet_prev_update_sec) {
rtc_int_flag = RTC_UF; if (hpet_prev_update_sec >= 0)
rtc_int_flag = RTC_UF;
hpet_prev_update_sec = curr_time.tm_sec; hpet_prev_update_sec = curr_time.tm_sec;
} }
......
...@@ -235,11 +235,56 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) ...@@ -235,11 +235,56 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
return 0; return 0;
} }
static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
{
unsigned char rtc_intr;
/* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
* allegedly some older rtcs need that to handle irqs properly
*/
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
if (is_hpet_enabled())
return;
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(cmos->rtc, 1, rtc_intr);
}
static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
{
unsigned char rtc_control;
/* flush any pending IRQ status, notably for update irqs,
* before we enable new IRQs
*/
rtc_control = CMOS_READ(RTC_CONTROL);
cmos_checkintr(cmos, rtc_control);
rtc_control |= mask;
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_set_rtc_irq_bit(mask);
cmos_checkintr(cmos, rtc_control);
}
static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
{
unsigned char rtc_control;
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~mask;
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(mask);
cmos_checkintr(cmos, rtc_control);
}
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{ {
struct cmos_rtc *cmos = dev_get_drvdata(dev); struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char mon, mday, hrs, min, sec; unsigned char mon, mday, hrs, min, sec;
unsigned char rtc_control, rtc_intr;
if (!is_valid_irq(cmos->irq)) if (!is_valid_irq(cmos->irq))
return -EIO; return -EIO;
...@@ -266,15 +311,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) ...@@ -266,15 +311,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
spin_lock_irq(&rtc_lock); spin_lock_irq(&rtc_lock);
/* next rtc irq must not be from previous alarm setting */ /* next rtc irq must not be from previous alarm setting */
rtc_control = CMOS_READ(RTC_CONTROL); cmos_irq_disable(cmos, RTC_AIE);
rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(RTC_AIE);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(cmos->rtc, 1, rtc_intr);
/* update alarm */ /* update alarm */
CMOS_WRITE(hrs, RTC_HOURS_ALARM); CMOS_WRITE(hrs, RTC_HOURS_ALARM);
...@@ -293,16 +330,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) ...@@ -293,16 +330,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
*/ */
hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
if (t->enabled) { if (t->enabled)
rtc_control |= RTC_AIE; cmos_irq_enable(cmos, RTC_AIE);
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_set_rtc_irq_bit(RTC_AIE);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(cmos->rtc, 1, rtc_intr);
}
spin_unlock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock);
...@@ -335,28 +364,17 @@ static int cmos_irq_set_freq(struct device *dev, int freq) ...@@ -335,28 +364,17 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
static int cmos_irq_set_state(struct device *dev, int enabled) static int cmos_irq_set_state(struct device *dev, int enabled)
{ {
struct cmos_rtc *cmos = dev_get_drvdata(dev); struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control, rtc_intr;
unsigned long flags; unsigned long flags;
if (!is_valid_irq(cmos->irq)) if (!is_valid_irq(cmos->irq))
return -ENXIO; return -ENXIO;
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
rtc_control = CMOS_READ(RTC_CONTROL);
if (enabled) {
rtc_control |= RTC_PIE;
hpet_set_rtc_irq_bit(RTC_PIE);
} else {
rtc_control &= ~RTC_PIE;
hpet_mask_rtc_irq_bit(RTC_PIE);
}
CMOS_WRITE(rtc_control, RTC_CONTROL);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS); if (enabled)
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; cmos_irq_enable(cmos, RTC_PIE);
if (is_intr(rtc_intr)) else
rtc_update_irq(cmos->rtc, 1, rtc_intr); cmos_irq_disable(cmos, RTC_PIE);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return 0; return 0;
...@@ -368,7 +386,6 @@ static int ...@@ -368,7 +386,6 @@ static int
cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{ {
struct cmos_rtc *cmos = dev_get_drvdata(dev); struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control, rtc_intr;
unsigned long flags; unsigned long flags;
switch (cmd) { switch (cmd) {
...@@ -385,32 +402,20 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) ...@@ -385,32 +402,20 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
} }
spin_lock_irqsave(&rtc_lock, flags); spin_lock_irqsave(&rtc_lock, flags);
rtc_control = CMOS_READ(RTC_CONTROL);
switch (cmd) { switch (cmd) {
case RTC_AIE_OFF: /* alarm off */ case RTC_AIE_OFF: /* alarm off */
rtc_control &= ~RTC_AIE; cmos_irq_disable(cmos, RTC_AIE);
hpet_mask_rtc_irq_bit(RTC_AIE);
break; break;
case RTC_AIE_ON: /* alarm on */ case RTC_AIE_ON: /* alarm on */
rtc_control |= RTC_AIE; cmos_irq_enable(cmos, RTC_AIE);
hpet_set_rtc_irq_bit(RTC_AIE);
break; break;
case RTC_UIE_OFF: /* update off */ case RTC_UIE_OFF: /* update off */
rtc_control &= ~RTC_UIE; cmos_irq_disable(cmos, RTC_UIE);
hpet_mask_rtc_irq_bit(RTC_UIE);
break; break;
case RTC_UIE_ON: /* update on */ case RTC_UIE_ON: /* update on */
rtc_control |= RTC_UIE; cmos_irq_enable(cmos, RTC_UIE);
hpet_set_rtc_irq_bit(RTC_UIE);
break; break;
} }
CMOS_WRITE(rtc_control, RTC_CONTROL);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(cmos->rtc, 1, rtc_intr);
spin_unlock_irqrestore(&rtc_lock, flags); spin_unlock_irqrestore(&rtc_lock, flags);
return 0; return 0;
} }
...@@ -571,7 +576,6 @@ static irqreturn_t cmos_interrupt(int irq, void *p) ...@@ -571,7 +576,6 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
* alarm woke the system. * alarm woke the system.
*/ */
if (irqstat & RTC_AIE) { if (irqstat & RTC_AIE) {
rtc_control = CMOS_READ(RTC_CONTROL);
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);
...@@ -685,17 +689,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) ...@@ -685,17 +689,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
/* disable irqs. /* disable irqs */
* cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
* allegedly some older rtcs need that to handle irqs properly
*/
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE);
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE);
CMOS_READ(RTC_INTR_FLAGS); rtc_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock);
...@@ -768,15 +765,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) ...@@ -768,15 +765,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
static void cmos_do_shutdown(void) static void cmos_do_shutdown(void)
{ {
unsigned char rtc_control;
spin_lock_irq(&rtc_lock); spin_lock_irq(&rtc_lock);
rtc_control = CMOS_READ(RTC_CONTROL); cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
rtc_control &= ~RTC_IRQMASK;
CMOS_WRITE(rtc_control, RTC_CONTROL);
hpet_mask_rtc_irq_bit(RTC_IRQMASK);
CMOS_READ(RTC_INTR_FLAGS);
spin_unlock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock);
} }
...@@ -817,7 +807,6 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) ...@@ -817,7 +807,6 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
spin_lock_irq(&rtc_lock); spin_lock_irq(&rtc_lock);
cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
unsigned char irqstat;
unsigned char mask; unsigned char mask;
if (do_wake) if (do_wake)
...@@ -828,10 +817,7 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) ...@@ -828,10 +817,7 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
CMOS_WRITE(tmp, RTC_CONTROL); CMOS_WRITE(tmp, RTC_CONTROL);
hpet_mask_rtc_irq_bit(mask); hpet_mask_rtc_irq_bit(mask);
irqstat = CMOS_READ(RTC_INTR_FLAGS); cmos_checkintr(cmos, tmp);
irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(irqstat))
rtc_update_irq(cmos->rtc, 1, irqstat);
} }
spin_unlock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock);
...@@ -875,7 +861,7 @@ static int cmos_resume(struct device *dev) ...@@ -875,7 +861,7 @@ static int cmos_resume(struct device *dev)
mask = CMOS_READ(RTC_INTR_FLAGS); mask = CMOS_READ(RTC_INTR_FLAGS);
mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
if (!is_intr(mask)) if (!is_hpet_enabled() || !is_intr(mask))
break; break;
/* force one-shot behavior if HPET blocked /* force one-shot behavior if HPET blocked
......
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