diff options
-rw-r--r-- | arch/x86/kernel/hpet.c | 51 |
1 files changed, 21 insertions, 30 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 410fdb3f1939..0b568b30a4d8 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -380,44 +380,35 @@ static int hpet_next_event(unsigned long delta, | |||
380 | struct clock_event_device *evt, int timer) | 380 | struct clock_event_device *evt, int timer) |
381 | { | 381 | { |
382 | u32 cnt; | 382 | u32 cnt; |
383 | s32 res; | ||
383 | 384 | ||
384 | cnt = hpet_readl(HPET_COUNTER); | 385 | cnt = hpet_readl(HPET_COUNTER); |
385 | cnt += (u32) delta; | 386 | cnt += (u32) delta; |
386 | hpet_writel(cnt, HPET_Tn_CMP(timer)); | 387 | hpet_writel(cnt, HPET_Tn_CMP(timer)); |
387 | 388 | ||
388 | /* | 389 | /* |
389 | * We need to read back the CMP register on certain HPET | 390 | * HPETs are a complete disaster. The compare register is |
390 | * implementations (ATI chipsets) which seem to delay the | 391 | * based on a equal comparison and neither provides a less |
391 | * transfer of the compare register into the internal compare | 392 | * than or equal functionality (which would require to take |
392 | * logic. With small deltas this might actually be too late as | 393 | * the wraparound into account) nor a simple count down event |
393 | * the counter could already be higher than the compare value | 394 | * mode. Further the write to the comparator register is |
394 | * at that point and we would wait for the next hpet interrupt | 395 | * delayed internally up to two HPET clock cycles in certain |
395 | * forever. We found out that reading the CMP register back | 396 | * chipsets (ATI, ICH9,10). We worked around that by reading |
396 | * forces the transfer so we can rely on the comparison with | 397 | * back the compare register, but that required another |
397 | * the counter register below. If the read back from the | 398 | * workaround for ICH9,10 chips where the first readout after |
398 | * compare register does not match the value we programmed | 399 | * write can return the old stale value. We already have a |
399 | * then we might have a real hardware problem. We can not do | 400 | * minimum delta of 5us enforced, but a NMI or SMI hitting |
400 | * much about it here, but at least alert the user/admin with | 401 | * between the counter readout and the comparator write can |
401 | * a prominent warning. | 402 | * move us behind that point easily. Now instead of reading |
402 | * | 403 | * the compare register back several times, we make the ETIME |
403 | * An erratum on some chipsets (ICH9,..), results in | 404 | * decision based on the following: Return ETIME if the |
404 | * comparator read immediately following a write returning old | 405 | * counter value after the write is less than 8 HPET cycles |
405 | * value. Workaround for this is to read this value second | 406 | * away from the event or if the counter is already ahead of |
406 | * time, when first read returns old value. | 407 | * the event. |
407 | * | ||
408 | * In fact the write to the comparator register is delayed up | ||
409 | * to two HPET cycles so the workaround we tried to restrict | ||
410 | * the readback to those known to be borked ATI chipsets | ||
411 | * failed miserably. So we give up on optimizations forever | ||
412 | * and penalize all HPET incarnations unconditionally. | ||
413 | */ | 408 | */ |
414 | if (unlikely((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt)) { | 409 | res = (s32)(cnt - hpet_readl(HPET_COUNTER)); |
415 | if (hpet_readl(HPET_Tn_CMP(timer)) != cnt) | ||
416 | printk_once(KERN_WARNING | ||
417 | "hpet: compare register read back failed.\n"); | ||
418 | } | ||
419 | 410 | ||
420 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | 411 | return res < 8 ? -ETIME : 0; |
421 | } | 412 | } |
422 | 413 | ||
423 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | 414 | static void hpet_legacy_set_mode(enum clock_event_mode mode, |