]> err.no Git - linux-2.6/commitdiff
[PATCH] x86_64: Fix drift with HPET timer enabled
authorJordan Hargrave <jordan_hargrave@dell.com>
Fri, 7 Apr 2006 17:50:18 +0000 (19:50 +0200)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 9 Apr 2006 18:53:53 +0000 (11:53 -0700)
If the HPET timer is enabled, the clock can drift by ~3 seconds a day.
This is due to the HPET timer not being initialized with the correct
setting (still using PIT count).

If HZ changes, this drift can become even more pronounced.

HPET patch initializes tick_nsec with correct tick_nsec settings for
HPET timer.

Vojtech comments:

  "It's not entirely correct (it assumes the HPET ticks totally
   exactly), but it's significantly better than assuming the PIT error
   there."

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/x86_64/kernel/time.c
include/asm-i386/hpet.h
include/asm-x86_64/hpet.h
include/linux/jiffies.h
kernel/timer.c

index 2eeaa95edff062afb16c94bd93c9c6d6b56b2f9d..7392570f975dc705b2d29c98c2571366d8e9666d 100644 (file)
@@ -917,6 +917,8 @@ void __init time_init(void)
                vxtime.hpet_address = 0;
 
        if (hpet_use_timer) {
+               /* set tick_nsec to use the proper rate for HPET */
+               tick_nsec = TICK_NSEC_HPET;
                cpu_khz = hpet_calibrate_tsc();
                timename = "HPET";
 #ifdef CONFIG_X86_PM_TIMER
index 16ef9f996e3f41606974816a6ac4a36851ad6e76..7f1a8a6ee32fa41dfcb86683f6f5aaae4f9d3d52 100644 (file)
@@ -89,6 +89,7 @@
  * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
  */
 #define HPET_MIN_PERIOD (100000UL)
+#define HPET_TICK_RATE  (HZ * 100000UL)
 
 extern unsigned long hpet_tick;        /* hpet clks count per tick */
 extern unsigned long hpet_address;     /* hpet memory map physical address */
index 08b75c15269ae1fcd01cb2457cddaca1477b239a..18ff7ee9e774c7506ce3fe596a2945ea425c91df 100644 (file)
@@ -51,6 +51,8 @@
 
 #define HPET_TN_ROUTE_SHIFT    9
 
+#define HPET_TICK_RATE (HZ * 100000UL)
+
 extern int is_hpet_enabled(void);
 extern int hpet_rtc_timer_init(void);
 extern int oem_force_hpet_timer(void);
index 99905e180532e094c2efb0fc8c3ce6c66c63d92b..043376920f51af0aaa317459a8172b00c814dab0 100644 (file)
@@ -36,6 +36,8 @@
 /* LATCH is used in the interval timer and ftape setup. */
 #define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
 
+#define LATCH_HPET ((HPET_TICK_RATE + HZ/2) / HZ)
+
 /* Suppose we want to devide two numbers NOM and DEN: NOM/DEN, the we can
  * improve accuracy by shifting LSH bits, hence calculating:
  *     (NOM << LSH) / DEN
 /* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
 #define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8))
 
+#define ACTHZ_HPET (SH_DIV (HPET_TICK_RATE, LATCH_HPET, 8))
+
 /* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */
 #define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
 
+#define TICK_NSEC_HPET (SH_DIV(1000000UL * 1000, ACTHZ_HPET, 8))
+
 /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */
 #define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ)
 
index c3a874f1393cd95d859267ed783a9edde8a8ff71..471ab8710b8fdcbf11816f233ad9ee91fa56877d 100644 (file)
@@ -1455,7 +1455,7 @@ static void time_interpolator_update(long delta_nsec)
         */
        if (jiffies % INTERPOLATOR_ADJUST == 0)
        {
-               if (time_interpolator->skips == 0 && time_interpolator->offset > TICK_NSEC)
+               if (time_interpolator->skips == 0 && time_interpolator->offset > tick_nsec)
                        time_interpolator->nsec_per_cyc--;
                if (time_interpolator->ns_skipped > INTERPOLATOR_MAX_SKIP && time_interpolator->offset == 0)
                        time_interpolator->nsec_per_cyc++;