Merge branches 'softirq-for-linus', 'x86-debug-for-linus', 'x86-numa-for-linus',...
[pandora-kernel.git] / arch / x86 / kernel / hpet.c
index efaf906..aff0b3c 100644 (file)
@@ -380,44 +380,35 @@ static int hpet_next_event(unsigned long delta,
                           struct clock_event_device *evt, int timer)
 {
        u32 cnt;
+       s32 res;
 
        cnt = hpet_readl(HPET_COUNTER);
        cnt += (u32) delta;
        hpet_writel(cnt, HPET_Tn_CMP(timer));
 
        /*
-        * We need to read back the CMP register on certain HPET
-        * implementations (ATI chipsets) which seem to delay the
-        * transfer of the compare register into the internal compare
-        * logic. With small deltas this might actually be too late as
-        * the counter could already be higher than the compare value
-        * 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.
-        *
-        * An erratum on some chipsets (ICH9,..), results in
-        * comparator read immediately following a write returning old
-        * value. Workaround for this is to read this value second
-        * time, when first read returns old value.
-        *
-        * In fact the write to the comparator register is delayed up
-        * to two HPET cycles so the workaround we tried to restrict
-        * the readback to those known to be borked ATI chipsets
-        * failed miserably. So we give up on optimizations forever
-        * and penalize all HPET incarnations unconditionally.
+        * HPETs are a complete disaster. The compare register is
+        * based on a equal comparison and neither provides a less
+        * than or equal functionality (which would require to take
+        * the wraparound into account) nor a simple count down event
+        * mode. Further the write to the comparator register is
+        * delayed internally up to two HPET clock cycles in certain
+        * chipsets (ATI, ICH9,10). We worked around that by reading
+        * back the compare register, but that required another
+        * workaround for ICH9,10 chips where the first readout after
+        * write can return the old stale value. We already have a
+        * minimum delta of 5us enforced, but a NMI or SMI hitting
+        * between the counter readout and the comparator write can
+        * move us behind that point easily. Now instead of reading
+        * the compare register back several times, we make the ETIME
+        * decision based on the following: Return ETIME if the
+        * counter value after the write is less than 8 HPET cycles
+        * away from the event or if the counter is already ahead of
+        * the event.
         */
-       if (unlikely((u32)hpet_readl(HPET_Tn_CMP(timer)) != cnt)) {
-               if (hpet_readl(HPET_Tn_CMP(timer)) != cnt)
-                       printk_once(KERN_WARNING
-                               "hpet: compare register read back failed.\n");
-       }
+       res = (s32)(cnt - hpet_readl(HPET_COUNTER));
 
-       return (s32)(hpet_readl(HPET_COUNTER) - cnt) >= 0 ? -ETIME : 0;
+       return res < 8 ? -ETIME : 0;
 }
 
 static void hpet_legacy_set_mode(enum clock_event_mode mode,