Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / i386 / kernel / timers / timer_hpet.c
index 15035a5..17a6fe7 100644 (file)
@@ -18,8 +18,8 @@
 #include "mach_timer.h"
 #include <asm/hpet.h>
 
-static unsigned long hpet_usec_quotient;       /* convert hpet clks to usec */
-static unsigned long tsc_hpet_quotient;                /* convert tsc to hpet clks */
+static unsigned long hpet_usec_quotient __read_mostly; /* convert hpet clks to usec */
+static unsigned long tsc_hpet_quotient __read_mostly;  /* convert tsc to hpet clks */
 static unsigned long hpet_last;        /* hpet counter value at last tick*/
 static unsigned long last_tsc_low;     /* lsb 32 bits of Time Stamp Counter */
 static unsigned long last_tsc_high;    /* msb 32 bits of Time Stamp Counter */
@@ -30,23 +30,28 @@ static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
  *  basic equation:
  *             ns = cycles / (freq / ns_per_sec)
  *             ns = cycles * (ns_per_sec / freq)
- *             ns = cycles * (10^9 / (cpu_mhz * 10^6))
- *             ns = cycles * (10^3 / cpu_mhz)
+ *             ns = cycles * (10^9 / (cpu_khz * 10^3))
+ *             ns = cycles * (10^6 / cpu_khz)
  *
  *     Then we use scaling math (suggested by george@mvista.com) to get:
- *             ns = cycles * (10^3 * SC / cpu_mhz) / SC
+ *             ns = cycles * (10^6 * SC / cpu_khz) / SC
  *             ns = cycles * cyc2ns_scale / SC
  *
  *     And since SC is a constant power of two, we can convert the div
  *  into a shift.
+ *
+ *  We can use khz divisor instead of mhz to keep a better percision, since
+ *  cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
+ *  (mathieu.desnoyers@polymtl.ca)
+ *
  *                     -johnstul@us.ibm.com "math is hard, lets go shopping!"
  */
-static unsigned long cyc2ns_scale;
+static unsigned long cyc2ns_scale __read_mostly;
 #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
 
-static inline void set_cyc2ns_scale(unsigned long cpu_mhz)
+static inline void set_cyc2ns_scale(unsigned long cpu_khz)
 {
-       cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz;
+       cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
 }
 
 static inline unsigned long long cycles_2_ns(unsigned long long cyc)
@@ -79,7 +84,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 +110,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;
@@ -118,7 +126,7 @@ static void mark_offset_hpet(void)
        write_sequnlock(&monotonic_lock);
 }
 
-void delay_hpet(unsigned long loops)
+static void delay_hpet(unsigned long loops)
 {
        unsigned long hpet_start, hpet_end;
        unsigned long eax;
@@ -133,6 +141,8 @@ void delay_hpet(unsigned long loops)
        } while ((hpet_end - hpet_start) < (loops));
 }
 
+static struct timer_opts timer_hpet;
+
 static int __init init_hpet(char* override)
 {
        unsigned long result, remain;
@@ -155,11 +165,13 @@ static int __init init_hpet(char* override)
                        {       unsigned long eax=0, edx=1000;
                                ASM_DIV64_REG(cpu_khz, edx, tsc_quotient,
                                                eax, edx);
-                               printk("Detected %lu.%03lu MHz processor.\n",
+                               printk("Detected %u.%03u MHz processor.\n",
                                        cpu_khz / 1000, cpu_khz % 1000);
                        }
-                       set_cyc2ns_scale(cpu_khz/1000);
+                       set_cyc2ns_scale(cpu_khz);
                }
+               /* set this only when cpu_has_tsc */
+               timer_hpet.read_timer = read_timer_tsc;
        }
 
        /*
@@ -174,14 +186,32 @@ static int __init init_hpet(char* override)
        return 0;
 }
 
+static int hpet_resume(void)
+{
+       write_seqlock(&monotonic_lock);
+       /* Assume this is the last mark offset time */
+       rdtsc(last_tsc_low, last_tsc_high);
+
+       if (hpet_use_timer)
+               hpet_last = hpet_readl(HPET_T0_CMP) - hpet_tick;
+       else
+               hpet_last = hpet_readl(HPET_COUNTER);
+       write_sequnlock(&monotonic_lock);
+       return 0;
+}
 /************************************************************/
 
 /* tsc timer_opts struct */
-struct timer_opts timer_hpet = {
+static struct timer_opts timer_hpet __read_mostly = {
        .name =                 "hpet",
-       .init =                 init_hpet,
        .mark_offset =          mark_offset_hpet,
        .get_offset =           get_offset_hpet,
        .monotonic_clock =      monotonic_clock_hpet,
        .delay =                delay_hpet,
+       .resume =               hpet_resume,
+};
+
+struct init_timer_opts __initdata timer_hpet_init = {
+       .init = init_hpet,
+       .opts = &timer_hpet,
 };