From: Anton Blanchard From: Paul Mackerras 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 | 30 ++++++++++++++++++++++++++++-- 1 files changed, 28 insertions(+), 2 deletions(-) diff -puN arch/ppc64/kernel/time.c~ppc64-sched_clock-2 arch/ppc64/kernel/time.c --- 25/arch/ppc64/kernel/time.c~ppc64-sched_clock-2 2003-12-31 01:42:23.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/time.c 2003-12-31 01:42:23.000000000 -0800 @@ -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 * reg /* * 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 _