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_tsc.c
index 180444d..f1187dd 100644 (file)
@@ -24,6 +24,7 @@
 #include "mach_timer.h"
 
 #include <asm/hpet.h>
+#include <asm/i8253.h>
 
 #ifdef CONFIG_HPET_TIMER
 static unsigned long hpet_usec_quotient;
@@ -33,9 +34,7 @@ static struct timer_opts timer_tsc;
 
 static inline void cpufreq_delayed_get(void);
 
-int tsc_disable __initdata = 0;
-
-extern spinlock_t i8253_lock;
+int tsc_disable __devinitdata = 0;
 
 static int use_tsc;
 /* Number of usecs that the last interrupt was delayed */
@@ -46,27 +45,41 @@ static unsigned long last_tsc_high; /* msb 32 bits of Time Stamp Counter */
 static unsigned long long monotonic_base;
 static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
 
+/* Avoid compensating for lost ticks before TSCs are synched */
+static int detect_lost_ticks;
+static int __init start_lost_tick_compensation(void)
+{
+       detect_lost_ticks = 1;
+       return 0;
+}
+late_initcall(start_lost_tick_compensation);
+
 /* convert from cycles(64bits) => nanoseconds (64bits)
  *  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.   
+ *  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)
@@ -192,7 +205,8 @@ static void mark_offset_tsc_hpet(void)
 
        /* lost tick compensation */
        offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
-       if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) {
+       if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))
+                                       && detect_lost_ticks) {
                int lost_ticks = (offset - hpet_last) / hpet_tick;
                jiffies_64 += lost_ticks;
        }
@@ -256,7 +270,7 @@ static unsigned long loops_per_jiffy_ref = 0;
 
 #ifndef CONFIG_SMP
 static unsigned long fast_gettimeoffset_ref = 0;
-static unsigned long cpu_khz_ref = 0;
+static unsigned int cpu_khz_ref = 0;
 #endif
 
 static int
@@ -265,9 +279,13 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
 {
        struct cpufreq_freqs *freq = data;
 
-       if (val != CPUFREQ_RESUMECHANGE)
+       if (val != CPUFREQ_RESUMECHANGE && val != CPUFREQ_SUSPENDCHANGE)
                write_seqlock_irq(&xtime_lock);
        if (!ref_freq) {
+               if (!freq->old){
+                       ref_freq = freq->new;
+                       goto end;
+               }
                ref_freq = freq->old;
                loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
 #ifndef CONFIG_SMP
@@ -287,13 +305,14 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
                if (use_tsc) {
                        if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
                                fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
-                               set_cyc2ns_scale(cpu_khz/1000);
+                               set_cyc2ns_scale(cpu_khz);
                        }
                }
 #endif
        }
 
-       if (val != CPUFREQ_RESUMECHANGE)
+end:
+       if (val != CPUFREQ_RESUMECHANGE && val != CPUFREQ_SUSPENDCHANGE)
                write_sequnlock_irq(&xtime_lock);
 
        return 0;
@@ -323,10 +342,12 @@ static inline void cpufreq_delayed_get(void) { return; }
 int recalibrate_cpu_khz(void)
 {
 #ifndef CONFIG_SMP
-       unsigned long cpu_khz_old = cpu_khz;
+       unsigned int cpu_khz_old = cpu_khz;
 
        if (cpu_has_tsc) {
+               local_irq_disable();
                init_cpu_khz();
+               local_irq_enable();
                cpu_data[0].loops_per_jiffy =
                    cpufreq_scale(cpu_data[0].loops_per_jiffy,
                                  cpu_khz_old,
@@ -415,7 +436,7 @@ static void mark_offset_tsc(void)
        delta += delay_at_last_interrupt;
        lost = delta/(1000000/HZ);
        delay = delta%(1000000/HZ);
-       if (lost >= 2) {
+       if (lost >= 2 && detect_lost_ticks) {
                jiffies_64 += lost-1;
 
                /* sanity check to ensure we're not always losing ticks */
@@ -534,15 +555,29 @@ static int __init init_tsc(char* override)
                                :"=a" (cpu_khz), "=d" (edx)
                                :"r" (tsc_quotient),
                                "0" (eax), "1" (edx));
-                               printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
+                               printk("Detected %u.%03u MHz processor.\n",
+                                       cpu_khz / 1000, cpu_khz % 1000);
                        }
-                       set_cyc2ns_scale(cpu_khz/1000);
+                       set_cyc2ns_scale(cpu_khz);
                        return 0;
                }
        }
        return -ENODEV;
 }
 
+static int tsc_resume(void)
+{
+       write_seqlock(&monotonic_lock);
+       /* Assume this is the last mark offset time */
+       rdtsc(last_tsc_low, last_tsc_high);
+#ifdef CONFIG_HPET_TIMER
+       if (is_hpet_enabled() && hpet_use_timer)
+               hpet_last = hpet_readl(HPET_COUNTER);
+#endif
+       write_sequnlock(&monotonic_lock);
+       return 0;
+}
+
 #ifndef CONFIG_X86_TSC
 /* disable flag for tsc.  Takes effect by clearing the TSC cpu flag
  * in cpu/common.c */
@@ -572,6 +607,8 @@ static struct timer_opts timer_tsc = {
        .get_offset = get_offset_tsc,
        .monotonic_clock = monotonic_clock_tsc,
        .delay = delay_tsc,
+       .read_timer = read_timer_tsc,
+       .resume = tsc_resume,
 };
 
 struct init_timer_opts __initdata timer_tsc_init = {