]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/x86/kernel/hpet.c
Merge branch 'master' into tk71
[mv-sheeva.git] / arch / x86 / kernel / hpet.c
index 7494999141b3ae9c9ab67302481a2e6c15e969e6..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,
@@ -440,9 +437,9 @@ static int hpet_legacy_next_event(unsigned long delta,
 static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev);
 static struct hpet_dev *hpet_devs;
 
-void hpet_msi_unmask(unsigned int irq)
+void hpet_msi_unmask(struct irq_data *data)
 {
-       struct hpet_dev *hdev = get_irq_data(irq);
+       struct hpet_dev *hdev = data->handler_data;
        unsigned int cfg;
 
        /* unmask it */
@@ -451,10 +448,10 @@ void hpet_msi_unmask(unsigned int irq)
        hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
 }
 
-void hpet_msi_mask(unsigned int irq)
+void hpet_msi_mask(struct irq_data *data)
 {
+       struct hpet_dev *hdev = data->handler_data;
        unsigned int cfg;
-       struct hpet_dev *hdev = get_irq_data(irq);
 
        /* mask it */
        cfg = hpet_readl(HPET_Tn_CFG(hdev->num));
@@ -462,18 +459,14 @@ void hpet_msi_mask(unsigned int irq)
        hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
 }
 
-void hpet_msi_write(unsigned int irq, struct msi_msg *msg)
+void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg)
 {
-       struct hpet_dev *hdev = get_irq_data(irq);
-
        hpet_writel(msg->data, HPET_Tn_ROUTE(hdev->num));
        hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hdev->num) + 4);
 }
 
-void hpet_msi_read(unsigned int irq, struct msi_msg *msg)
+void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg)
 {
-       struct hpet_dev *hdev = get_irq_data(irq);
-
        msg->data = hpet_readl(HPET_Tn_ROUTE(hdev->num));
        msg->address_lo = hpet_readl(HPET_Tn_ROUTE(hdev->num) + 4);
        msg->address_hi = 0;
@@ -726,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);