Merge branch 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* '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
This commit is contained in:
Коммит
211baf4ffc
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче