]> git.hungrycats.org Git - linux/commitdiff
[PATCH] ppc64: fix sched_clock, from Paul Mackerras:
authorAndrew Morton <akpm@osdl.org>
Mon, 19 Jan 2004 02:17:03 +0000 (18:17 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Mon, 19 Jan 2004 02:17:03 +0000 (18:17 -0800)
From: Anton Blanchard <anton@samba.org>

Currently the sched_clock implementation for PPC64 is bogus.  It just reads
the timebase register, which counts at some fixed rate, typically around
100MHz.  This patch adds code to calculate a suitable multiplier from the
timebase frequency, and use that in sched_clock().

arch/ppc64/kernel/time.c

index 3f652e5e6a3ee0fb125d46673cd654563db72cd1..cd9745c3748f5db6c409c9e26d9e8d8f8a183074 100644 (file)
@@ -91,6 +91,9 @@ unsigned      tb_to_us;
 unsigned long processor_freq;
 spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 
+unsigned long tb_to_ns_scale;
+unsigned long tb_to_ns_shift;
+
 struct gettimeofday_struct do_gtod;
 
 extern unsigned long wall_jiffies;
@@ -313,11 +316,13 @@ int timer_interrupt(struct pt_regs * regs)
 /*
  * Scheduler clock - returns current time in nanosec units.
  *
- * This is wrong, but my CPUs run at 1GHz, so nyer nyer.
+ * Note: mulhdu(a, b) (multiply high double unsigned) returns
+ * the high 64 bits of a * b, i.e. (a * b) >> 64, where a and b
+ * are 64-bit unsigned numbers.
  */
 unsigned long long sched_clock(void)
 {
-       return get_tb();
+       return mulhdu(get_tb(), tb_to_ns_scale) << tb_to_ns_shift;
 }
 
 /*
@@ -473,9 +478,30 @@ void __init time_init(void)
        /* This function is only called on the boot processor */
        unsigned long flags;
        struct rtc_time tm;
+       struct div_result res;
+       unsigned long scale, shift;
 
        ppc_md.calibrate_decr();
 
+       /*
+        * Compute scale factor for sched_clock.
+        * The calibrate_decr() function has set tb_ticks_per_sec,
+        * which is the timebase frequency.
+        * We compute 1e9 * 2^64 / tb_ticks_per_sec and interpret
+        * the 128-bit result as a 64.64 fixed-point number.
+        * We then shift that number right until it is less than 1.0,
+        * giving us the scale factor and shift count to use in
+        * sched_clock().
+        */
+       div128_by_32(1000000000, 0, tb_ticks_per_sec, &res);
+       scale = res.result_low;
+       for (shift = 0; res.result_high != 0; ++shift) {
+               scale = (scale >> 1) | (res.result_high << 63);
+               res.result_high >>= 1;
+       }
+       tb_to_ns_scale = scale;
+       tb_to_ns_shift = shift;
+
 #ifdef CONFIG_PPC_ISERIES
        if (!piranha_simulator)
 #endif