Merge branch 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Oct 2010 15:47:45 +0000 (08:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 22 Oct 2010 15:47:45 +0000 (08:47 -0700)
* 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  x86: Hpet: Avoid the comparator readback penalty

1  2 
arch/x86/kernel/hpet.c

diff --combined arch/x86/kernel/hpet.c
@@@ -380,44 -380,35 +380,35 @@@ static int hpet_next_event(unsigned lon
                           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,
@@@ -440,9 -431,9 +431,9 @@@ static int hpet_legacy_next_event(unsig
  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 */
        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));
        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;
@@@ -502,7 -497,7 +493,7 @@@ static int hpet_assign_irq(struct hpet_
  {
        unsigned int irq;
  
 -      irq = create_irq();
 +      irq = create_irq_nr(0, -1);
        if (!irq)
                return -EINVAL;