diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2010-04-13 09:31:36 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-04-28 21:14:29 -0400 |
commit | 30a564be9d9554c168a654eddc2165869cc0d7bf (patch) | |
tree | 734de780e0f84a189f66ca3cbf611e6f2bc563e9 /arch/x86/kernel/hpet.c | |
parent | 1d16b0f2f3edf05f12a9e3960588e0d4854157bb (diff) |
x86, hpet: Restrict read back to affected ATI chipsets
After programming the HPET, we do a readback as a workaround for
ATI/SBx00 chipsets as a synchronization. Unfortunately this triggers
an erratum in newer ICH chipsets (ICH9+) where reading the comparator
immediately after the write returns the old value. Furthermore, as
always, I/O reads are bad for performance.
Therefore, restrict the readback to the chipsets that need it, or, for
debugging purposes, when we are running with hpet=verbose.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Venkatesh Pallipadi <venki@google.com>
LKML-Reference: <20100225185348.GA9674@linux-os.sc.intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r-- | arch/x86/kernel/hpet.c | 29 |
1 files changed, 17 insertions, 12 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 23b4ecdffa9b..a198b7c87a12 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -36,6 +36,7 @@ | |||
36 | unsigned long hpet_address; | 36 | unsigned long hpet_address; |
37 | u8 hpet_blockid; /* OS timer block num */ | 37 | u8 hpet_blockid; /* OS timer block num */ |
38 | u8 hpet_msi_disable; | 38 | u8 hpet_msi_disable; |
39 | u8 hpet_readback_cmp; | ||
39 | 40 | ||
40 | #ifdef CONFIG_PCI_MSI | 41 | #ifdef CONFIG_PCI_MSI |
41 | static unsigned long hpet_num_timers; | 42 | static unsigned long hpet_num_timers; |
@@ -395,19 +396,23 @@ static int hpet_next_event(unsigned long delta, | |||
395 | * at that point and we would wait for the next hpet interrupt | 396 | * at that point and we would wait for the next hpet interrupt |
396 | * forever. We found out that reading the CMP register back | 397 | * forever. We found out that reading the CMP register back |
397 | * forces the transfer so we can rely on the comparison with | 398 | * forces the transfer so we can rely on the comparison with |
398 | * the counter register below. If the read back from the | 399 | * the counter register below. |
399 | * compare register does not match the value we programmed | 400 | * |
400 | * then we might have a real hardware problem. We can not do | 401 | * That works fine on those ATI chipsets, but on newer Intel |
401 | * much about it here, but at least alert the user/admin with | 402 | * chipsets (ICH9...) this triggers due to an erratum: Reading |
402 | * a prominent warning. | 403 | * the comparator immediately following a write is returning |
403 | * An erratum on some chipsets (ICH9,..), results in comparator read | 404 | * the old value. |
404 | * immediately following a write returning old value. Workaround | 405 | * |
405 | * for this is to read this value second time, when first | 406 | * We restrict the read back to the affected ATI chipsets (set |
406 | * read returns old value. | 407 | * by quirks) and also run it with hpet=verbose for debugging |
408 | * purposes. | ||
407 | */ | 409 | */ |
408 | if (unlikely((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt)) { | 410 | if (hpet_readback_cmp || hpet_verbose) { |
409 | WARN_ONCE(hpet_readl(HPET_Tn_CMP(timer)) != cnt, | 411 | u32 cmp = hpet_readl(HPET_Tn_CMP(timer)); |
410 | KERN_WARNING "hpet: compare register read back failed.\n"); | 412 | |
413 | if (cmp != cnt) | ||
414 | printk_once(KERN_WARNING | ||
415 | "hpet: compare register read back failed.\n"); | ||
411 | } | 416 | } |
412 | 417 | ||
413 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | 418 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; |