|
@@ -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,
|