diff options
| -rw-r--r-- | arch/x86/include/asm/hpet.h | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/hpet.c | 29 | ||||
| -rw-r--r-- | arch/x86/kernel/quirks.c | 8 |
3 files changed, 26 insertions, 12 deletions
diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h index 1d5c08a1bdfd..004e6e25e913 100644 --- a/arch/x86/include/asm/hpet.h +++ b/arch/x86/include/asm/hpet.h | |||
| @@ -68,6 +68,7 @@ extern unsigned long force_hpet_address; | |||
| 68 | extern u8 hpet_blockid; | 68 | extern u8 hpet_blockid; |
| 69 | extern int hpet_force_user; | 69 | extern int hpet_force_user; |
| 70 | extern u8 hpet_msi_disable; | 70 | extern u8 hpet_msi_disable; |
| 71 | extern u8 hpet_readback_cmp; | ||
| 71 | extern int is_hpet_enabled(void); | 72 | extern int is_hpet_enabled(void); |
| 72 | extern int hpet_enable(void); | 73 | extern int hpet_enable(void); |
| 73 | extern void hpet_disable(void); | 74 | extern void hpet_disable(void); |
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; |
diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index 12e9feaa2f7a..e72d3fc6547d 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c | |||
| @@ -495,10 +495,18 @@ void force_hpet_resume(void) | |||
| 495 | /* | 495 | /* |
| 496 | * HPET MSI on some boards (ATI SB700/SB800) has side effect on | 496 | * HPET MSI on some boards (ATI SB700/SB800) has side effect on |
| 497 | * floppy DMA. Disable HPET MSI on such platforms. | 497 | * floppy DMA. Disable HPET MSI on such platforms. |
| 498 | * See erratum #27 (Misinterpreted MSI Requests May Result in | ||
| 499 | * Corrupted LPC DMA Data) in AMD Publication #46837, | ||
| 500 | * "SB700 Family Product Errata", Rev. 1.0, March 2010. | ||
| 501 | * | ||
| 502 | * Also force the read back of the CMP register in hpet_next_event() | ||
| 503 | * to work around the problem that the CMP register write seems to be | ||
| 504 | * delayed. See hpet_next_event() for details. | ||
| 498 | */ | 505 | */ |
| 499 | static void force_disable_hpet_msi(struct pci_dev *unused) | 506 | static void force_disable_hpet_msi(struct pci_dev *unused) |
| 500 | { | 507 | { |
| 501 | hpet_msi_disable = 1; | 508 | hpet_msi_disable = 1; |
| 509 | hpet_readback_cmp = 1; | ||
| 502 | } | 510 | } |
| 503 | 511 | ||
| 504 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, | 512 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, |
