From: john stultz My reimplementation of pmtimer workaround from OGAWA Hirofumi. Signed-off-by: OGAWA Hirofumi Signed-off-by: John Stultz Signed-off-by: Andrew Morton --- drivers/clocksource/acpi_pm.c | 102 +++++++++++++++++++++++++++++++- 1 files changed, 99 insertions(+), 3 deletions(-) diff -puN drivers/clocksource/acpi_pm.c~time-i386-clocksource-drivers-pm-timer-doesnt-use-workaround-if-chipset-is-not-buggy drivers/clocksource/acpi_pm.c --- devel/drivers/clocksource/acpi_pm.c~time-i386-clocksource-drivers-pm-timer-doesnt-use-workaround-if-chipset-is-not-buggy 2006-03-25 15:24:24.000000000 -0800 +++ devel-akpm/drivers/clocksource/acpi_pm.c 2006-03-25 15:24:37.000000000 -0800 @@ -19,6 +19,7 @@ #include #include #include +#include #include /* Number of PMTMR ticks expected during calibration run */ @@ -30,6 +31,7 @@ * in arch/i386/acpi/boot.c */ u32 pmtmr_ioport; +static int pmtmr_need_workaround = 1; #define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ @@ -39,6 +41,26 @@ static inline u32 read_pmtmr(void) return inl(pmtmr_ioport) & ACPI_PM_MASK; } +static cycle_t acpi_pm_read_verified(void) +{ + u32 v1 = 0, v2 = 0, v3 = 0; + + /* + * It has been reported that because of various broken + * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock + * source is not latched, so you must read it multiple + * times to ensure a safe value is read: + */ + do { + v1 = read_pmtmr(); + v2 = read_pmtmr(); + v3 = read_pmtmr(); + } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) + || (v3 > v1 && v3 < v2)); + + return (cycle_t)v2; +} + static cycle_t acpi_pm_read(void) { return (cycle_t)read_pmtmr(); @@ -54,6 +76,73 @@ static struct clocksource clocksource_ac .is_continuous = 1, }; + +#ifdef CONFIG_PCI +/* + * PIIX4 Errata: + * + * The power management timer may return improper results when read. + * Although the timer value settles properly after incrementing, + * while incrementing there is a 3 ns window every 69.8 ns where the + * timer value is indeterminate (a 4.2% chance that the data will be + * incorrect when read). As a result, the ACPI free running count up + * timer specification is violated due to erroneous reads. + */ +static void __init pmtmr_bug_check(void) +{ + static struct pci_device_id gray_list[] __initdata = { + /* these chipsets may have bug. */ + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801DB_0) }, + { }, + }; + struct pci_dev *dev; + int pmtmr_has_bug = 0; + u8 rev; + + if (!pmtmr_need_workaround) + return; + + dev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + if (dev) { + pci_read_config_byte(dev, PCI_REVISION_ID, &rev); + /* the bug has been fixed in PIIX4M */ + if (rev < 3) { + printk(KERN_WARNING "* Found PM-Timer Bug on this" + " chipset. Due to workarounds for a bug,\n" + "* this time source is slow. Consider trying" + " other time sources (clock=)\n"); + pmtmr_has_bug = 1; + } + pci_dev_put(dev); + } + + if (pci_dev_present(gray_list)) { + printk(KERN_WARNING "* This chipset may have PM-Timer Bug." + " Due to workarounds for a bug,\n" + "* this time source is slow. If you are sure your" + " timer does not have\n" + "* this bug, please use \"pmtmr_good\" to disable?" + " the workaround\n"); + pmtmr_has_bug = 1; + } + + if (!pmtmr_has_bug) + pmtmr_need_workaround = 0; +} +#else +#define pmtmr_bug_check() +#endif + +static int __init pmtr_good_setup(char *__str) +{ + pmtmr_need_workaround = 0; + return 1; +} +__setup("pmtmr_good", pmtr_good_setup); + + static int __init init_acpi_pm_clocksource(void) { u32 value1, value2; @@ -75,14 +164,21 @@ static int __init init_acpi_pm_clocksour goto pm_good; if ((value2 < value1) && ((value2) < 0xFFF)) goto pm_good; - printk(KERN_INFO "PM-Timer had inconsistent results: 0x%#x, 0x%#x - aborting.\n", value1, value2); + printk(KERN_INFO "PM-Timer had inconsistent results:" + " 0x%#x, 0x%#x - aborting.\n", value1, value2); return -EINVAL; } - printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1); + printk(KERN_INFO "PM-Timer had no reasonable result:" + " 0x%#x - aborting.\n", value1); return -ENODEV; pm_good: - + pmtmr_bug_check(); + /* check to see if pmtmr is known buggy: */ + if (pmtmr_need_workaround) { + clocksource_acpi_pm.read = acpi_pm_read_verified; + clocksource_acpi_pm.rating = 110; + } return register_clocksource(&clocksource_acpi_pm); } _