x86, hpet: Restrict read back to affected ATI chipsets
[sfrench/cifs-2.6.git] / arch / x86 / kernel / hpet.c
index ee4fa1bfcb33354b96069b4728dd5cf367e74e12..a198b7c87a123d2f80c6313261e9b80e8346cad1 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/sysdev.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/slab.h>
 #include <linux/hpet.h>
 #include <linux/init.h>
 #include <linux/cpu.h>
@@ -35,6 +36,7 @@
 unsigned long                          hpet_address;
 u8                                     hpet_blockid; /* OS timer block num */
 u8                                     hpet_msi_disable;
+u8                                     hpet_readback_cmp;
 
 #ifdef CONFIG_PCI_MSI
 static unsigned long                   hpet_num_timers;
@@ -394,14 +396,24 @@ static int hpet_next_event(unsigned long delta,
         * at that point and we would wait for the next hpet interrupt
         * forever. We found out that reading the CMP register back
         * forces the transfer so we can rely on the comparison with
-        * the counter register below. If the read back from the
-        * compare register does not match the value we programmed
-        * then we might have a real hardware problem. We can not do
-        * much about it here, but at least alert the user/admin with
-        * a prominent warning.
+        * the counter register below.
+        *
+        * That works fine on those ATI chipsets, but on newer Intel
+        * chipsets (ICH9...) this triggers due to an erratum: Reading
+        * the comparator immediately following a write is returning
+        * the old value.
+        *
+        * We restrict the read back to the affected ATI chipsets (set
+        * by quirks) and also run it with hpet=verbose for debugging
+        * purposes.
         */
-       WARN_ONCE(hpet_readl(HPET_Tn_CMP(timer)) != cnt,
-                 KERN_WARNING "hpet: compare register read back failed.\n");
+       if (hpet_readback_cmp || hpet_verbose) {
+               u32 cmp = hpet_readl(HPET_Tn_CMP(timer));
+
+               if (cmp != cnt)
+                       printk_once(KERN_WARNING
+                           "hpet: compare register read back failed.\n");
+       }
 
        return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0;
 }
@@ -1143,6 +1155,7 @@ int hpet_set_periodic_freq(unsigned long freq)
                do_div(clc, freq);
                clc >>= hpet_clockevent.shift;
                hpet_pie_delta = clc;
+               hpet_pie_limit = 0;
        }
        return 1;
 }