]> Pileus Git - ~andy/linux/blobdiff - arch/x86/kernel/hpet.c
Merge branches 'x86-fixes-for-linus' and 'perf-fixes-for-linus' of git://git.kernel...
[~andy/linux] / arch / x86 / kernel / hpet.c
index efaf906daf93ff4dcca7385d26047c2db8691fbe..4ff5968f12d295ac00a55ecbbae06dd97675a6c9 100644 (file)
@@ -27,6 +27,9 @@
 #define HPET_DEV_FSB_CAP               0x1000
 #define HPET_DEV_PERI_CAP              0x2000
 
+#define HPET_MIN_CYCLES                        128
+#define HPET_MIN_PROG_DELTA            (HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1))
+
 #define EVT_TO_HPET_DEV(evt) container_of(evt, struct hpet_dev, evt)
 
 /*
@@ -299,8 +302,9 @@ static void hpet_legacy_clockevent_register(void)
        /* Calculate the min / max delta */
        hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
                                                           &hpet_clockevent);
-       /* 5 usec minimum reprogramming delta. */
-       hpet_clockevent.min_delta_ns = 5000;
+       /* Setup minimum reprogramming delta. */
+       hpet_clockevent.min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA,
+                                                          &hpet_clockevent);
 
        /*
         * Start hpet with the boot cpu mask and make it
@@ -380,44 +384,37 @@ 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). Some newer AMD chipsets have even
+        * longer delays. 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 had a minimum
+        * programming 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 HPET_MIN_CYCLES
+        * away from the event or if the counter is already ahead of
+        * the event. The minimum programming delta for the generic
+        * clockevents code is set to 1.5 * HPET_MIN_CYCLES.
         */
-       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 < HPET_MIN_CYCLES ? -ETIME : 0;
 }
 
 static void hpet_legacy_set_mode(enum clock_event_mode mode,
@@ -722,7 +719,7 @@ static int hpet_cpuhp_notify(struct notifier_block *n,
 
        switch (action & 0xf) {
        case CPU_ONLINE:
-               INIT_DELAYED_WORK_ON_STACK(&work.work, hpet_work);
+               INIT_DELAYED_WORK_ONSTACK(&work.work, hpet_work);
                init_completion(&work.complete);
                /* FIXME: add schedule_work_on() */
                schedule_delayed_work_on(cpu, &work.work, 0);