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 <bowman@math.ualberta.ca>: allow the values
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/