diff -purN -X /home/mbligh/.diff.exclude 111-ppc64_reloc_hide/arch/ppc64/kernel/time.c 112-ppc64_sched_clock_fix/arch/ppc64/kernel/time.c
--- 111-ppc64_reloc_hide/arch/ppc64/kernel/time.c	2003-10-14 15:50:14.000000000 -0700
+++ 112-ppc64_sched_clock_fix/arch/ppc64/kernel/time.c	2003-12-11 17:05:26.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