patch-2.4.14 linux/arch/alpha/kernel/time.c

Next file: linux/arch/alpha/kernel/traps.c
Previous file: linux/arch/alpha/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.13/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
@@ -169,6 +169,63 @@
 	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,  750000000 },
+		[EV68AL_CPU] = {  750000000,  940000000 },
+		[EV68CB_CPU] = { 1000000000, 1333333333 },
+		/* None of the following are shipping as of 2001-11-01.  */
+		[EV68CX_CPU] = { 1000000000, 1700000000 },	/* guess */
+		[EV69_CPU]   = { 1000000000, 1700000000 },	/* guess */
+		[EV7_CPU]    = {  800000000, 1400000000 },	/* guess */
+		[EV79_CPU]   = { 1000000000, 2000000000 },	/* guess */
+	};
+
+	/* Allow for some drift in the crystal.  10MHz is more than enough.  */
+	const unsigned int deviation = 10000000;
+
+	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 index contains no data, no way to validate.  */
+	if (cpu_hz[index].max == 0)
+		return cc;
+
+	if (cc < cpu_hz[index].min - deviation
+	    || cc > cpu_hz[index].max + deviation)
+		return 0;
+
+	return cc;
+}
+
+
 /*
  * Calibrate CPU clock using legacy 8254 timer/counter. Stolen from
  * arch/i386/time.c.
@@ -180,8 +237,7 @@
 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 +256,18 @@
 	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 +293,35 @@
 
 	/* 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 <bowman@math.ualberta.ca>: allow the values

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)