diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2008-07-24 00:30:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:34 -0400 |
commit | 7e2a31da854dcf8324012a83a31b40bc11e52589 (patch) | |
tree | fb982e22f3d9ac6ec2fc67a96a297dc743ef67e6 /drivers/rtc | |
parent | 449321b39f6c6ebfa15d6da24f134240bd51db29 (diff) |
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: David Brownell <dbrownell@users.sourceforge.net>
Report-by: W Unruh <unruh@physics.ubc.ca>
Cc: Andrew Victor <avictor.za@gmail.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 140 |
1 files changed, 63 insertions, 77 deletions
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index e9984650ea95..6ea349aba3ba 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c | |||
@@ -235,11 +235,56 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
235 | return 0; | 235 | return 0; |
236 | } | 236 | } |
237 | 237 | ||
238 | static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control) | ||
239 | { | ||
240 | unsigned char rtc_intr; | ||
241 | |||
242 | /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS; | ||
243 | * allegedly some older rtcs need that to handle irqs properly | ||
244 | */ | ||
245 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | ||
246 | |||
247 | if (is_hpet_enabled()) | ||
248 | return; | ||
249 | |||
250 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
251 | if (is_intr(rtc_intr)) | ||
252 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | ||
253 | } | ||
254 | |||
255 | static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask) | ||
256 | { | ||
257 | unsigned char rtc_control; | ||
258 | |||
259 | /* flush any pending IRQ status, notably for update irqs, | ||
260 | * before we enable new IRQs | ||
261 | */ | ||
262 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
263 | cmos_checkintr(cmos, rtc_control); | ||
264 | |||
265 | rtc_control |= mask; | ||
266 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
267 | hpet_set_rtc_irq_bit(mask); | ||
268 | |||
269 | cmos_checkintr(cmos, rtc_control); | ||
270 | } | ||
271 | |||
272 | static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask) | ||
273 | { | ||
274 | unsigned char rtc_control; | ||
275 | |||
276 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
277 | rtc_control &= ~mask; | ||
278 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
279 | hpet_mask_rtc_irq_bit(mask); | ||
280 | |||
281 | cmos_checkintr(cmos, rtc_control); | ||
282 | } | ||
283 | |||
238 | static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) | 284 | static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) |
239 | { | 285 | { |
240 | struct cmos_rtc *cmos = dev_get_drvdata(dev); | 286 | struct cmos_rtc *cmos = dev_get_drvdata(dev); |
241 | unsigned char mon, mday, hrs, min, sec; | 287 | unsigned char mon, mday, hrs, min, sec; |
242 | unsigned char rtc_control, rtc_intr; | ||
243 | 288 | ||
244 | if (!is_valid_irq(cmos->irq)) | 289 | if (!is_valid_irq(cmos->irq)) |
245 | return -EIO; | 290 | return -EIO; |
@@ -266,15 +311,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
266 | spin_lock_irq(&rtc_lock); | 311 | spin_lock_irq(&rtc_lock); |
267 | 312 | ||
268 | /* next rtc irq must not be from previous alarm setting */ | 313 | /* next rtc irq must not be from previous alarm setting */ |
269 | rtc_control = CMOS_READ(RTC_CONTROL); | 314 | cmos_irq_disable(cmos, RTC_AIE); |
270 | rtc_control &= ~RTC_AIE; | ||
271 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
272 | hpet_mask_rtc_irq_bit(RTC_AIE); | ||
273 | |||
274 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | ||
275 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
276 | if (is_intr(rtc_intr)) | ||
277 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | ||
278 | 315 | ||
279 | /* update alarm */ | 316 | /* update alarm */ |
280 | CMOS_WRITE(hrs, RTC_HOURS_ALARM); | 317 | CMOS_WRITE(hrs, RTC_HOURS_ALARM); |
@@ -293,16 +330,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
293 | */ | 330 | */ |
294 | hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); | 331 | hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec); |
295 | 332 | ||
296 | if (t->enabled) { | 333 | if (t->enabled) |
297 | rtc_control |= RTC_AIE; | 334 | cmos_irq_enable(cmos, RTC_AIE); |
298 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
299 | hpet_set_rtc_irq_bit(RTC_AIE); | ||
300 | |||
301 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | ||
302 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
303 | if (is_intr(rtc_intr)) | ||
304 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | ||
305 | } | ||
306 | 335 | ||
307 | spin_unlock_irq(&rtc_lock); | 336 | spin_unlock_irq(&rtc_lock); |
308 | 337 | ||
@@ -335,28 +364,17 @@ static int cmos_irq_set_freq(struct device *dev, int freq) | |||
335 | static int cmos_irq_set_state(struct device *dev, int enabled) | 364 | static int cmos_irq_set_state(struct device *dev, int enabled) |
336 | { | 365 | { |
337 | struct cmos_rtc *cmos = dev_get_drvdata(dev); | 366 | struct cmos_rtc *cmos = dev_get_drvdata(dev); |
338 | unsigned char rtc_control, rtc_intr; | ||
339 | unsigned long flags; | 367 | unsigned long flags; |
340 | 368 | ||
341 | if (!is_valid_irq(cmos->irq)) | 369 | if (!is_valid_irq(cmos->irq)) |
342 | return -ENXIO; | 370 | return -ENXIO; |
343 | 371 | ||
344 | spin_lock_irqsave(&rtc_lock, flags); | 372 | spin_lock_irqsave(&rtc_lock, flags); |
345 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
346 | |||
347 | if (enabled) { | ||
348 | rtc_control |= RTC_PIE; | ||
349 | hpet_set_rtc_irq_bit(RTC_PIE); | ||
350 | } else { | ||
351 | rtc_control &= ~RTC_PIE; | ||
352 | hpet_mask_rtc_irq_bit(RTC_PIE); | ||
353 | } | ||
354 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
355 | 373 | ||
356 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | 374 | if (enabled) |
357 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | 375 | cmos_irq_enable(cmos, RTC_PIE); |
358 | if (is_intr(rtc_intr)) | 376 | else |
359 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | 377 | cmos_irq_disable(cmos, RTC_PIE); |
360 | 378 | ||
361 | spin_unlock_irqrestore(&rtc_lock, flags); | 379 | spin_unlock_irqrestore(&rtc_lock, flags); |
362 | return 0; | 380 | return 0; |
@@ -368,7 +386,6 @@ static int | |||
368 | cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | 386 | cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) |
369 | { | 387 | { |
370 | struct cmos_rtc *cmos = dev_get_drvdata(dev); | 388 | struct cmos_rtc *cmos = dev_get_drvdata(dev); |
371 | unsigned char rtc_control, rtc_intr; | ||
372 | unsigned long flags; | 389 | unsigned long flags; |
373 | 390 | ||
374 | switch (cmd) { | 391 | switch (cmd) { |
@@ -385,32 +402,20 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |||
385 | } | 402 | } |
386 | 403 | ||
387 | spin_lock_irqsave(&rtc_lock, flags); | 404 | spin_lock_irqsave(&rtc_lock, flags); |
388 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
389 | switch (cmd) { | 405 | switch (cmd) { |
390 | case RTC_AIE_OFF: /* alarm off */ | 406 | case RTC_AIE_OFF: /* alarm off */ |
391 | rtc_control &= ~RTC_AIE; | 407 | cmos_irq_disable(cmos, RTC_AIE); |
392 | hpet_mask_rtc_irq_bit(RTC_AIE); | ||
393 | break; | 408 | break; |
394 | case RTC_AIE_ON: /* alarm on */ | 409 | case RTC_AIE_ON: /* alarm on */ |
395 | rtc_control |= RTC_AIE; | 410 | cmos_irq_enable(cmos, RTC_AIE); |
396 | hpet_set_rtc_irq_bit(RTC_AIE); | ||
397 | break; | 411 | break; |
398 | case RTC_UIE_OFF: /* update off */ | 412 | case RTC_UIE_OFF: /* update off */ |
399 | rtc_control &= ~RTC_UIE; | 413 | cmos_irq_disable(cmos, RTC_UIE); |
400 | hpet_mask_rtc_irq_bit(RTC_UIE); | ||
401 | break; | 414 | break; |
402 | case RTC_UIE_ON: /* update on */ | 415 | case RTC_UIE_ON: /* update on */ |
403 | rtc_control |= RTC_UIE; | 416 | cmos_irq_enable(cmos, RTC_UIE); |
404 | hpet_set_rtc_irq_bit(RTC_UIE); | ||
405 | break; | 417 | break; |
406 | } | 418 | } |
407 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
408 | |||
409 | rtc_intr = CMOS_READ(RTC_INTR_FLAGS); | ||
410 | rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; | ||
411 | if (is_intr(rtc_intr)) | ||
412 | rtc_update_irq(cmos->rtc, 1, rtc_intr); | ||
413 | |||
414 | spin_unlock_irqrestore(&rtc_lock, flags); | 419 | spin_unlock_irqrestore(&rtc_lock, flags); |
415 | return 0; | 420 | return 0; |
416 | } | 421 | } |
@@ -571,7 +576,6 @@ static irqreturn_t cmos_interrupt(int irq, void *p) | |||
571 | * alarm woke the system. | 576 | * alarm woke the system. |
572 | */ | 577 | */ |
573 | if (irqstat & RTC_AIE) { | 578 | if (irqstat & RTC_AIE) { |
574 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
575 | rtc_control &= ~RTC_AIE; | 579 | rtc_control &= ~RTC_AIE; |
576 | CMOS_WRITE(rtc_control, RTC_CONTROL); | 580 | CMOS_WRITE(rtc_control, RTC_CONTROL); |
577 | hpet_mask_rtc_irq_bit(RTC_AIE); | 581 | hpet_mask_rtc_irq_bit(RTC_AIE); |
@@ -685,17 +689,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) | |||
685 | hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); | 689 | hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); |
686 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); | 690 | CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); |
687 | 691 | ||
688 | /* disable irqs. | 692 | /* disable irqs */ |
689 | * | 693 | cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE); |
690 | * NOTE after changing RTC_xIE bits we always read INTR_FLAGS; | ||
691 | * allegedly some older rtcs need that to handle irqs properly | ||
692 | */ | ||
693 | rtc_control = CMOS_READ(RTC_CONTROL); | ||
694 | rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE); | ||
695 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
696 | hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE); | ||
697 | 694 | ||
698 | CMOS_READ(RTC_INTR_FLAGS); | 695 | rtc_control = CMOS_READ(RTC_CONTROL); |
699 | 696 | ||
700 | spin_unlock_irq(&rtc_lock); | 697 | spin_unlock_irq(&rtc_lock); |
701 | 698 | ||
@@ -768,15 +765,8 @@ cleanup0: | |||
768 | 765 | ||
769 | static void cmos_do_shutdown(void) | 766 | static void cmos_do_shutdown(void) |
770 | { | 767 | { |
771 | unsigned char rtc_control; | ||
772 | |||
773 | spin_lock_irq(&rtc_lock); | 768 | spin_lock_irq(&rtc_lock); |
774 | rtc_control = CMOS_READ(RTC_CONTROL); | 769 | cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); |
775 | rtc_control &= ~RTC_IRQMASK; | ||
776 | CMOS_WRITE(rtc_control, RTC_CONTROL); | ||
777 | hpet_mask_rtc_irq_bit(RTC_IRQMASK); | ||
778 | |||
779 | CMOS_READ(RTC_INTR_FLAGS); | ||
780 | spin_unlock_irq(&rtc_lock); | 770 | spin_unlock_irq(&rtc_lock); |
781 | } | 771 | } |
782 | 772 | ||
@@ -817,7 +807,6 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) | |||
817 | spin_lock_irq(&rtc_lock); | 807 | spin_lock_irq(&rtc_lock); |
818 | cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); | 808 | cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); |
819 | if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { | 809 | if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { |
820 | unsigned char irqstat; | ||
821 | unsigned char mask; | 810 | unsigned char mask; |
822 | 811 | ||
823 | if (do_wake) | 812 | if (do_wake) |
@@ -828,10 +817,7 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) | |||
828 | CMOS_WRITE(tmp, RTC_CONTROL); | 817 | CMOS_WRITE(tmp, RTC_CONTROL); |
829 | hpet_mask_rtc_irq_bit(mask); | 818 | hpet_mask_rtc_irq_bit(mask); |
830 | 819 | ||
831 | irqstat = CMOS_READ(RTC_INTR_FLAGS); | 820 | cmos_checkintr(cmos, tmp); |
832 | irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF; | ||
833 | if (is_intr(irqstat)) | ||
834 | rtc_update_irq(cmos->rtc, 1, irqstat); | ||
835 | } | 821 | } |
836 | spin_unlock_irq(&rtc_lock); | 822 | spin_unlock_irq(&rtc_lock); |
837 | 823 | ||
@@ -875,7 +861,7 @@ static int cmos_resume(struct device *dev) | |||
875 | 861 | ||
876 | mask = CMOS_READ(RTC_INTR_FLAGS); | 862 | mask = CMOS_READ(RTC_INTR_FLAGS); |
877 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; | 863 | mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; |
878 | if (!is_intr(mask)) | 864 | if (!is_hpet_enabled() || !is_intr(mask)) |
879 | break; | 865 | break; |
880 | 866 | ||
881 | /* force one-shot behavior if HPET blocked | 867 | /* force one-shot behavior if HPET blocked |