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, |
