aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/hpet.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2010-09-15 09:11:57 -0400
committerThomas Gleixner <tglx@linutronix.de>2010-09-18 06:09:13 -0400
commit995bd3bb5c78f3ff71339803c0b8337ed36d64fb (patch)
treeca1bc2f52c20cac52b044a3664ac6e4b06f1fad4 /arch/x86/kernel/hpet.c
parent151b6a5f1d4c547c92ec67a5a6fedc16f435956e (diff)
x86: Hpet: Avoid the comparator readback penalty
Due to the overly intelligent design of HPETs, we need to workaround the problem that the compare value which we write is already behind the actual counter value at the point where the value hits the real compare register. This happens for two reasons: 1) We read out the counter, add the delta and write the result to the compare register. When a NMI or SMI hits between the read out and the write then the counter can be ahead of the event already 2) The write to the compare register is delayed by up to two HPET cycles in certain chipsets. We worked around this by reading back the compare register to make sure that the written value has hit the hardware. For certain ICH9+ chipsets this can require two readouts, as the first one can return the previous compare register value. That's bad performance wise for the normal case where the event is far enough in the future. As we already know that the write can be delayed by up to two cycles we can avoid the read back of the compare register completely if we make the decision whether the delta has elapsed already or not based on the following calculation: cmp = event - actual_count; If cmp is less than 8 HPET clock cycles, then we decide that the event has happened already and return -ETIME. That covers the above #1 and #2 problems which would cause a wait for HPET wraparound (~306 seconds). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Nix <nix@esperi.org.uk> Tested-by: Artur Skawina <art.08.09@gmail.com> Cc: Damien Wyart <damien.wyart@free.fr> Tested-by: John Drescher <drescherjm@gmail.com> Cc: Venkatesh Pallipadi <venki@google.com> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: Andreas Herrmann <andreas.herrmann3@amd.com> Tested-by: Borislav Petkov <borislav.petkov@amd.com> Cc: Suresh Siddha <suresh.b.siddha@intel.com> LKML-Reference: <alpine.LFD.2.00.1009151500060.2416@localhost6.localdomain6>
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r--arch/x86/kernel/hpet.c51
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
423static void hpet_legacy_set_mode(enum clock_event_mode mode, 414static void hpet_legacy_set_mode(enum clock_event_mode mode,