diff options
Diffstat (limited to 'arch/x86/kernel/hpet.c')
| -rw-r--r-- | arch/x86/kernel/hpet.c | 31 |
1 files changed, 17 insertions, 14 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 351f9c0fea1f..410fdb3f1939 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
| @@ -35,7 +35,6 @@ | |||
| 35 | unsigned long hpet_address; | 35 | unsigned long hpet_address; |
| 36 | u8 hpet_blockid; /* OS timer block num */ | 36 | u8 hpet_blockid; /* OS timer block num */ |
| 37 | u8 hpet_msi_disable; | 37 | u8 hpet_msi_disable; |
| 38 | u8 hpet_readback_cmp; | ||
| 39 | 38 | ||
| 40 | #ifdef CONFIG_PCI_MSI | 39 | #ifdef CONFIG_PCI_MSI |
| 41 | static unsigned long hpet_num_timers; | 40 | static unsigned long hpet_num_timers; |
| @@ -395,23 +394,27 @@ static int hpet_next_event(unsigned long delta, | |||
| 395 | * at that point and we would wait for the next hpet interrupt | 394 | * at that point and we would wait for the next hpet interrupt |
| 396 | * forever. We found out that reading the CMP register back | 395 | * forever. We found out that reading the CMP register back |
| 397 | * forces the transfer so we can rely on the comparison with | 396 | * forces the transfer so we can rely on the comparison with |
| 398 | * the counter register below. | 397 | * the counter register below. If the read back from the |
| 398 | * compare register does not match the value we programmed | ||
| 399 | * then we might have a real hardware problem. We can not do | ||
| 400 | * much about it here, but at least alert the user/admin with | ||
| 401 | * a prominent warning. | ||
| 399 | * | 402 | * |
| 400 | * That works fine on those ATI chipsets, but on newer Intel | 403 | * An erratum on some chipsets (ICH9,..), results in |
| 401 | * chipsets (ICH9...) this triggers due to an erratum: Reading | 404 | * comparator read immediately following a write returning old |
| 402 | * the comparator immediately following a write is returning | 405 | * value. Workaround for this is to read this value second |
| 403 | * the old value. | 406 | * time, when first read returns old value. |
| 404 | * | 407 | * |
| 405 | * We restrict the read back to the affected ATI chipsets (set | 408 | * In fact the write to the comparator register is delayed up |
| 406 | * by quirks) and also run it with hpet=verbose for debugging | 409 | * to two HPET cycles so the workaround we tried to restrict |
| 407 | * purposes. | 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. | ||
| 408 | */ | 413 | */ |
| 409 | if (hpet_readback_cmp || hpet_verbose) { | 414 | if (unlikely((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt)) { |
| 410 | u32 cmp = hpet_readl(HPET_Tn_CMP(timer)); | 415 | if (hpet_readl(HPET_Tn_CMP(timer)) != cnt) |
| 411 | |||
| 412 | if (cmp != cnt) | ||
| 413 | printk_once(KERN_WARNING | 416 | printk_once(KERN_WARNING |
| 414 | "hpet: compare register read back failed.\n"); | 417 | "hpet: compare register read back failed.\n"); |
| 415 | } | 418 | } |
| 416 | 419 | ||
| 417 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; | 420 | return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0; |
