[PATCH] i386: fix hpet for systems that don't support legacy replacement
authorjohn stultz <johnstul@us.ibm.com>
Sun, 1 May 2005 15:58:50 +0000 (08:58 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sun, 1 May 2005 15:58:50 +0000 (08:58 -0700)
Currently the i386 HPET code assumes the entire HPET implementation from
the spec is present.  This breaks on boxes that do not implement the
optional legacy timer replacement functionality portion of the spec.

This patch, which is very similar to my x86-64 patch for the same issue,
fixes the problem allowing i386 systems that cannot use the HPET for the
timer interrupt and RTC to still use the HPET as a time source.  I've
tested this patch on a system systems without HPET, with HPET but without
legacy timer replacement, as well as HPET with legacy timer replacement.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/kernel/time.c
arch/i386/kernel/time_hpet.c
arch/i386/kernel/timers/timer_hpet.c
arch/i386/kernel/timers/timer_tsc.c
include/asm-i386/hpet.h

index 4d75b373f90eaf3fd01bf229b39f7ec49a98d719..a0dcb7c87c3083c75216e53108042c1bce645275 100644 (file)
@@ -441,7 +441,7 @@ static void __init hpet_time_init(void)
        set_normalized_timespec(&wall_to_monotonic,
                -xtime.tv_sec, -xtime.tv_nsec);
 
-       if (hpet_enable() >= 0) {
+       if ((hpet_enable() >= 0) && hpet_use_timer) {
                printk("Using HPET for base-timer\n");
        }
 
index 244a31b04be703a4e46cc3234d72df2de4bd2603..10a0cbb88e754e81cb01604bca90e989c5b9d50c 100644 (file)
@@ -26,6 +26,7 @@
 static unsigned long hpet_period;      /* fsecs / HPET clock */
 unsigned long hpet_tick;               /* hpet clks count per tick */
 unsigned long hpet_address;            /* hpet memory map physical address */
+int hpet_use_timer;
 
 static int use_hpet;           /* can be used for runtime check of hpet */
 static int boot_hpet_disable;  /* boottime override for HPET timer */
@@ -73,27 +74,30 @@ static int hpet_timer_stop_set_go(unsigned long tick)
        hpet_writel(0, HPET_COUNTER);
        hpet_writel(0, HPET_COUNTER + 4);
 
-       /*
-        * Set up timer 0, as periodic with first interrupt to happen at
-        * hpet_tick, and period also hpet_tick.
-        */
-       cfg = hpet_readl(HPET_T0_CFG);
-       cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
-              HPET_TN_SETVAL | HPET_TN_32BIT;
-       hpet_writel(cfg, HPET_T0_CFG);
-
-       /*
-        * The first write after writing TN_SETVAL to the config register sets
-        * the counter value, the second write sets the threshold.
-        */
-       hpet_writel(tick, HPET_T0_CMP);
-       hpet_writel(tick, HPET_T0_CMP);
+       if (hpet_use_timer) {
+               /*
+                * Set up timer 0, as periodic with first interrupt to happen at
+                * hpet_tick, and period also hpet_tick.
+                */
+               cfg = hpet_readl(HPET_T0_CFG);
+               cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
+                      HPET_TN_SETVAL | HPET_TN_32BIT;
+               hpet_writel(cfg, HPET_T0_CFG);
 
+               /*
+                * The first write after writing TN_SETVAL to the config register sets
+                * the counter value, the second write sets the threshold.
+                */
+               hpet_writel(tick, HPET_T0_CMP);
+               hpet_writel(tick, HPET_T0_CMP);
+       }
        /*
         * Go!
         */
        cfg = hpet_readl(HPET_CFG);
-       cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
+       if (hpet_use_timer)
+               cfg |= HPET_CFG_LEGACY;
+       cfg |= HPET_CFG_ENABLE;
        hpet_writel(cfg, HPET_CFG);
 
        return 0;
@@ -128,12 +132,11 @@ int __init hpet_enable(void)
         * However, we can do with one timer otherwise using the
         * the single HPET timer for system time.
         */
-       if (
 #ifdef CONFIG_HPET_EMULATE_RTC
-               !(id & HPET_ID_NUMBER) ||
-#endif
-           !(id & HPET_ID_LEGSUP))
+       if (!(id & HPET_ID_NUMBER))
                return -1;
+#endif
+
 
        hpet_period = hpet_readl(HPET_PERIOD);
        if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
@@ -152,6 +155,8 @@ int __init hpet_enable(void)
        if (hpet_tick_rem > (hpet_period >> 1))
                hpet_tick++; /* rounding the result */
 
+       hpet_use_timer = id & HPET_ID_LEGSUP;
+
        if (hpet_timer_stop_set_go(hpet_tick))
                return -1;
 
@@ -202,7 +207,8 @@ int __init hpet_enable(void)
 #endif
 
 #ifdef CONFIG_X86_LOCAL_APIC
-       wait_timer_tick = wait_hpet_tick;
+       if (hpet_use_timer)
+               wait_timer_tick = wait_hpet_tick;
 #endif
        return 0;
 }
index 713134e718448271c6299c6340c665720f4cbe1d..f778f471a09ab4934063225b4df1c7e7e8163c5e 100644 (file)
@@ -79,7 +79,7 @@ static unsigned long get_offset_hpet(void)
 
        eax = hpet_readl(HPET_COUNTER);
        eax -= hpet_last;       /* hpet delta */
-
+       eax = min(hpet_tick, eax);
        /*
          * Time offset = (hpet delta) * ( usecs per HPET clock )
         *             = (hpet delta) * ( usecs per tick / HPET clocks per tick)
@@ -105,9 +105,12 @@ static void mark_offset_hpet(void)
        last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
        rdtsc(last_tsc_low, last_tsc_high);
 
-       offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
-       if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) {
-               int lost_ticks = (offset - hpet_last) / hpet_tick;
+       if (hpet_use_timer)
+               offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
+       else
+               offset = hpet_readl(HPET_COUNTER);
+       if (unlikely(((offset - hpet_last) >= (2*hpet_tick)) && (hpet_last != 0))) {
+               int lost_ticks = ((offset - hpet_last) / hpet_tick) - 1;
                jiffies_64 += lost_ticks;
        }
        hpet_last = offset;
index a685994e5c8e2316068ec8ecf44aff98952ef8d6..7926d967be00ddf89718a4c43c4e866b17f09cfc 100644 (file)
@@ -477,7 +477,7 @@ static int __init init_tsc(char* override)
        if (cpu_has_tsc) {
                unsigned long tsc_quotient;
 #ifdef CONFIG_HPET_TIMER
-               if (is_hpet_enabled()){
+               if (is_hpet_enabled() && hpet_use_timer) {
                        unsigned long result, remain;
                        printk("Using TSC for gettimeofday\n");
                        tsc_quotient = calibrate_tsc_hpet(NULL);
index 6e20b079f1d3bc73fa79fc3a4815f273f0362d3d..16ef9f996e3f41606974816a6ac4a36851ad6e76 100644 (file)
@@ -92,6 +92,7 @@
 
 extern unsigned long hpet_tick;        /* hpet clks count per tick */
 extern unsigned long hpet_address;     /* hpet memory map physical address */
+extern int hpet_use_timer;
 
 extern int hpet_rtc_timer_init(void);
 extern int hpet_enable(void);