Subject: alpha 2.4.13: cycle detection sanity checks
From: Richard Henderson <[email-masked]>
To: [email-masked], [email-masked]
Cc: [email-masked]
Date: Fri, 26 Oct 2001 01:41:33 -0700

Ivan Kokshaysky and others recently put in code to fix some cycle
counter detection problems on UP2000 systems.  It turns out that
PC164 and LX164 systems have different broken hardware, and the
new code turns up completely bogus numbers like 1.9GHz on those
systems.

The following adds some knowledge about what sort of hardware
actually exists, so that we can determine that the computed
value is completely out of line.

Worst case (say if someone overclocked their system) we'll fail
to believe both of our computations and accept (with a warning)
the value given by the PROM (which is sometimes wrong, which is
why we're trying to compute the value in the first place).

r~

--- linux/arch/alpha/kernel/time.c	Thu Oct  4 18:47:08 2001
+++ 2.4.13/arch/alpha/kernel/time.c	Thu Oct 25 13:37:24 2001
@@ -169,6 +169,50 @@ common_init_rtc(void)
 	init_rtc_irq();
 }
 
+
+/* Validate a computed cycle counter result against the known bounds for
+   the given processor core.  There's too much brokenness in the way of
+   timing hardware for any one method to work everywhere.  :-(
+
+   Return 0 if the result cannot be trusted, otherwise return the argument.  */
+
+static unsigned long __init
+validate_cc_value(unsigned long cc)
+{
+	static struct bounds {
+		unsigned int min, max;
+	} cpu_hz[] __initdata = {
+		[EV3_CPU]   = {  50000000, 200000000 },		/* guess */
+		[EV4_CPU]   = { 150000000, 300000000 },
+		[LCA4_CPU]  = { 150000000, 300000000 },		/* guess */
+		[EV45_CPU]  = { 200000000, 300000000 },
+		[EV5_CPU]   = { 266000000, 333333333 },
+		[EV56_CPU]  = { 366000000, 667000000 },
+		[PCA56_CPU] = { 400000000, 600000000 },		/* guess */
+		[PCA57_CPU] = { 500000000, 600000000 },		/* guess */
+		[EV6_CPU]   = { 466000000, 600000000 },
+		[EV67_CPU]  = { 600000000, 833333333 },		/* guess */
+		/* ??? EV68 is shipping.  No hwrpb value?
+		   Christopher C. Chimelis sez 667-833MHz.  */
+	};
+
+	struct percpu_struct *cpu;
+	unsigned int index;
+
+	cpu = (struct percpu_struct *)((char*)hwrpb + hwrpb->processor_offset);
+	index = cpu->type & 0xffffffff;
+
+	/* If index out of bounds, no way to validate.  */
+	if (index >= sizeof(cpu_hz)/sizeof(cpu_hz[0]))
+		return cc;
+
+	if (cc < cpu_hz[index].min || cc > cpu_hz[index].max)
+		return 0;
+
+	return cc;
+}
+
+
 /*
  * Calibrate CPU clock using legacy 8254 timer/counter. Stolen from
  * arch/i386/time.c.
@@ -180,8 +224,7 @@ common_init_rtc(void)
 static unsigned long __init
 calibrate_cc_with_pic(void)
 {
-	int cc;
-	unsigned long count = 0;
+	int cc, count = 0;
 
 	/* Set the Gate high, disable speaker */
 	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
@@ -200,30 +243,18 @@ calibrate_cc_with_pic(void)
 	cc = rpcc();
 	do {
 		count++;
-	} while ((inb(0x61) & 0x20) == 0);
+	} while ((inb(0x61) & 0x20) == 0 && count > 0);
 	cc = rpcc() - cc;
 
-	/* Error: ECTCNEVERSET */
+	/* Error: ECTCNEVERSET or ECPUTOOFAST.  */
 	if (count <= 1)
-		goto bad_ctc;
+		return 0;
 
-	/* Error: ECPUTOOFAST */
-	if (count >> 32)
-		goto bad_ctc;
-
-	/* Error: ECPUTOOSLOW */
+	/* Error: ECPUTOOSLOW.  */
 	if (cc <= CALIBRATE_TIME)
-		goto bad_ctc;
-
-	return ((long)cc * 1000000) / CALIBRATE_TIME;
+		return 0;
 
-	/*
-	 * The CTC wasn't reliable: we got a hit on the very first read,
-	 * or the CPU was so fast/slow that the quotient wouldn't fit in
-	 * 32 bits..
-	 */
- bad_ctc:
-	return 0;
+	return (cc * 1000000UL) / CALIBRATE_TIME;
 }
 
 /* The Linux interpretation of the CMOS clock register contents:
@@ -249,31 +280,35 @@ time_init(void)
 
 	/* Calibrate CPU clock -- attempt #1.  */
 	if (!est_cycle_freq)
-		est_cycle_freq = calibrate_cc_with_pic();
+		est_cycle_freq = validate_cc_value(calibrate_cc_with_pic());
 
 	cc1 = rpcc_after_update_in_progress();
 
 	/* Calibrate CPU clock -- attempt #2.  */
 	if (!est_cycle_freq) {
 		cc2 = rpcc_after_update_in_progress();
-		est_cycle_freq = cc2 - cc1;
+		est_cycle_freq = validate_cc_value(cc2 - cc1);
 		cc1 = cc2;
 	}
 
-	/* If the given value is within 1% of what we calculated, 
-	   accept it.  Otherwise, use what we found.  */
 	cycle_freq = hwrpb->cycle_freq;
-	one_percent = cycle_freq / 100;
-	diff = cycle_freq - est_cycle_freq;
-	if (diff < 0)
-		diff = -diff;
-	if (diff > one_percent) {
-		cycle_freq = est_cycle_freq;
-		printk("HWRPB cycle frequency bogus.  Estimated %lu Hz\n",
-		       cycle_freq);
-	}
-	else {
-		est_cycle_freq = 0;
+	if (est_cycle_freq) {
+		/* If the given value is within 1% of what we calculated, 
+		   accept it.  Otherwise, use what we found.  */
+		one_percent = cycle_freq / 100;
+		diff = cycle_freq - est_cycle_freq;
+		if (diff < 0)
+			diff = -diff;
+		if (diff > one_percent) {
+			cycle_freq = est_cycle_freq;
+			printk("HWRPB cycle frequency bogus.  "
+			       "Estimated %lu Hz\n", cycle_freq);
+		} else {
+			est_cycle_freq = 0;
+		}
+	} else if (! validate_cc_value (cycle_freq)) {
+		printk("HWRPB cycle frequency bogus, "
+		       "and unable to estimate a proper value!\n");
 	}
 
 	/* From John Bowman <[email-masked]>: allow the values
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email-masked]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/