[PATCH] ppc: prep_setup (QEMU) does not correctly calculate decrementer value

Bruce Beare (bbeare) bbeare at cisco.com
Thu Sep 4 01:31:33 EST 2008


The problem:
  The system clock under QEMU (PREP architecture) increments at four
  times the correct wall clock rate.

Comments:
  prep_calibrate_decr() hardcoded the divisor for the timebase to be
  '4'. This value should be read from the residual data that is provided
  by PREP at boot.  QEMU for example provides '1000' for this value.
  The other constants in the calculation have been adjusted to correct
  account for the scaling (x1000).

  If the residual data is not correct, the problem can also be
  worked around by specifying 'noresidual' on the command line. This
  specification forces the code to use the real time clock to calculate
  the correct decrementer value.

  Also provided is the inclusion of some additional debug data (printk)
  and the correction of an unrelated printk to include KERN_CRIT.
  These printk statements are only executed on a PREP machine boot.

Tested on:
  QEMU
  This fix has not been tested on real PREP hardware. If it doesn't
appear
  to work on such hardware, please return the kernel boot printk's to
  the patch author for further work.

Test:
  # Watch the ticks... they should be 1/second. 
  while :
  do
    sleep 1
    echo -n .
  done

Note:
  This code is ppc (not powerpc) architure -- and is in neither -tip nor
  -mm tree.  The patch is directly useful for the 2.6.27 prepatch tree
  and for 2.6.26 and below.  It may be necessary to re-work the patch
  for the tip tree... once PREP is added to it.


Signed-off-by: Bruce Beare <bbeare at cisco.com>



--- linux-2.6.26/arch/ppc/platforms/prep_setup.orig.c	Wed Aug 13
15:29:04 2008
+++ linux-2.6.26/arch/ppc/platforms/prep_setup.c	Wed Aug 13
16:17:41 2008
@@ -847,15 +847,19 @@ static void __init
 prep_calibrate_decr(void)
 {
 	if (have_residual_data) {
-		unsigned long freq, divisor = 4;
+		unsigned long freq, divisor;
 
 		if ( res->VitalProductData.ProcessorBusHz ) {
+			divisor = res->VitalProductData.TimeBaseDivisor;
 			freq = res->VitalProductData.ProcessorBusHz;
-			printk("time_init: decrementer frequency =
%lu.%.6lu MHz\n",
-					(freq/divisor)/1000000,
-					(freq/divisor)%1000000);
-			tb_to_us = mulhwu_scale_factor(freq/divisor,
1000000);
-			tb_ticks_per_jiffy = freq / HZ / divisor;
+			tb_to_us = mulhwu_scale_factor(freq/divisor,
1000);
+			tb_ticks_per_jiffy = freq / HZ / divisor * 1000;
+
+			printk(KERN_CRIT "prep_calibrate_decr: "
+				"frequency = %lu.%.6lu MHz,"
+				" tb_to_us: %d, tb_ticks_per_jiffy:
%d\n",
+				(freq/divisor)/1000,
(freq/divisor)%1000,
+				tb_to_us, tb_ticks_per_jiffy);
 		}
 	}
 	else
@@ -914,7 +918,7 @@ smp_prep_kick_cpu(int nr)
 {
 	*(unsigned long *)KERNELBASE = nr;
 	asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory");
-	printk("CPU1 released, waiting\n");
+	printk(KERN_CRIT "CPU1 released, waiting\n");
 }
 
 static void __init
@@ -972,6 +976,13 @@ prep_init(unsigned long r3, unsigned lon
 		memcpy((void *)res,(void *)(r3+KERNELBASE),
 			 sizeof(RESIDUAL));
 	}
+	if (have_residual_data)
+	    printk(KERN_INFO "Residual data for model: %s, "
+		"TimeBaseDivisor: %lu, ProcHz: %lu, BusHz: %lu\n",
+		res->VitalProductData.PrintableModel,
+		res->VitalProductData.TimeBaseDivisor,
+		res->VitalProductData.ProcessorHz,
+		res->VitalProductData.ProcessorBusHz);
 #endif
 
 	isa_io_base = PREP_ISA_IO_BASE;
@@ -996,8 +1007,10 @@ prep_init(unsigned long r3, unsigned lon
 
 #ifdef CONFIG_PREP_RESIDUAL
 	/* Switch off all residual data processing if the user requests
it */
-	if (strstr(cmd_line, "noresidual") != NULL)
-			res = NULL;
+	if (strstr(cmd_line, "noresidual") != NULL) {
+		printk(KERN_INFO "Disabled PREP residual data\n");
+		res = NULL;
+	}
 #endif
 
 	/* Initialise progress early to get maximum benefit */



More information about the Linuxppc-dev mailing list