summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>1996-05-01 08:39:02 +0000
committerbde <bde@FreeBSD.org>1996-05-01 08:39:02 +0000
commitca8106a4ee34cb61e26e28ea80d9f6589caa51a6 (patch)
tree95750958823ac0fa9fa014498439a90f6aa982ee
parent00b2015fb5a605103d9ba4381b6f2884ee87d5f3 (diff)
downloadFreeBSD-src-ca8106a4ee34cb61e26e28ea80d9f6589caa51a6.zip
FreeBSD-src-ca8106a4ee34cb61e26e28ea80d9f6589caa51a6.tar.gz
Added calibration the i8254 and the i586 clocks agains the RTC at boot
time. The results are currently ignored unless certain temporary options are used. Added sysctls to support reading and writing the clock frequency variables (not the frequencies themselves). Writing is supposed to atomically adjust all related variables. machdep.c: Fixed spelling of a function name in a comment so that I can log this message which should have been with the previous commit. Initialize `cpu_class' earlier so that it can be used in startrtclock() instead of in calibrate_cyclecounter() (which no longer exists). Removed range checking of `cpu'. It is always initialized to CPU_XXX so it is less likely to be out of bounds than most variables. clock.h: Removed I586_CYCLECTR(). Use rdtsc() instead. clock.c: TIMER_FREQ is now a variable timer_freq that defaults to the old value of TIMER_FREQ. #define'ing TIMER_FREQ should still work and may be the best way of setting the frequency. Calibration involves counting cycles while watching the RTC for one second. This gives values correct to within (a few ppm) + (the innaccuracy of the RTC) on my systems.
-rw-r--r--sys/amd64/amd64/machdep.c4
-rw-r--r--sys/amd64/amd64/tsc.c292
-rw-r--r--sys/amd64/include/clock.h13
-rw-r--r--sys/amd64/isa/clock.c292
-rw-r--r--sys/i386/i386/machdep.c4
-rw-r--r--sys/i386/i386/tsc.c292
-rw-r--r--sys/i386/include/clock.h13
-rw-r--r--sys/i386/isa/clock.c292
-rw-r--r--sys/isa/atrtc.c292
9 files changed, 1257 insertions, 237 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 03cf864..b20ad93 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -35,7 +35,7 @@
* SUCH DAMAGE.
*
* from: @(#)machdep.c 7.4 (Berkeley) 6/3/91
- * $Id: machdep.c,v 1.184 1996/04/26 13:47:39 phk Exp $
+ * $Id: machdep.c,v 1.185 1996/05/01 08:31:21 bde Exp $
*/
#include "npx.h"
@@ -234,7 +234,7 @@ cpu_startup(dummy)
* Initialize error message buffer (at end of core).
*/
- /* avail_end was pre-decremented in init_386() to compensate */
+ /* avail_end was pre-decremented in init386() to compensate */
for (i = 0; i < btoc(sizeof (struct msgbuf)); i++)
pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp,
avail_end + i * NBPG,
diff --git a/sys/amd64/amd64/tsc.c b/sys/amd64/amd64/tsc.c
index d26b30c..ca9e405 100644
--- a/sys/amd64/amd64/tsc.c
+++ b/sys/amd64/amd64/tsc.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.56 1996/04/05 18:56:10 ache Exp $
+ * $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
*/
/*
@@ -53,8 +53,15 @@
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
#include <machine/frame.h>
+
#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -68,11 +75,7 @@
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
-/* X-tals being what they are, it's nice to be able to fudge this one... */
-#ifndef TIMER_FREQ
-#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
-#endif
-#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
/*
* Time in timer cycles that it takes for microtime() to disable interrupts
@@ -98,6 +101,7 @@ int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
u_int idelayed;
#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
unsigned i586_ctr_rate;
long long i586_ctr_bias;
long long i586_last_tick;
@@ -125,6 +129,11 @@ static void (*new_function) __P((struct clockframe *frame));
static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+static u_int timer_freq = 1193182;
+#endif
static char timer0_state = 0;
static char timer2_state = 0;
static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
@@ -287,34 +296,9 @@ getit(void)
return ((high << 8) | low);
}
-#if defined(I586_CPU) || defined(I686_CPU)
-/*
- * Figure out how fast the cyclecounter runs. This must be run with
- * clock interrupts disabled, but with the timer/counter programmed
- * and running.
- */
-void
-calibrate_cyclecounter(void)
-{
- /*
- * Don't need volatile; should always use unsigned if 2's
- * complement arithmetic is desired.
- */
- unsigned long long count;
-
-#define howlong 131072UL
- __asm __volatile(".byte 0x0f, 0x30" : : "A"(0LL), "c" (0x10));
- DELAY(howlong);
- __asm __volatile(".byte 0xf,0x31" : "=A" (count));
-
- i586_ctr_rate = (count << I586_CTR_RATE_SHIFT) / howlong;
-#undef howlong
-}
-#endif
-
/*
* Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Relies on timer 1 counting down from (timer_freq / hz)
* Note: timer had better have been programmed before this is first used!
*/
void
@@ -346,15 +330,17 @@ DELAY(int n)
prev_tick = getit();
n -= 20;
/*
- * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
* and without any avoidable overflows.
*/
sec = n / 1000000;
usec = n - sec * 1000000;
- ticks_left = sec * TIMER_FREQ
- + usec * (TIMER_FREQ / 1000000)
- + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
- + usec * (TIMER_FREQ % 1000) / 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
while (ticks_left > 0) {
tick = getit();
@@ -430,6 +416,116 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
@@ -437,11 +533,75 @@ readrtc(int port)
void
startrtclock()
{
- timer0_max_count = hardclock_max_count = TIMER_DIV(hz);
- timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
+ u_int delta, freq;
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
}
/*
@@ -597,7 +757,7 @@ cpu_initclocks()
* Finish setting up anti-jitter measures.
*/
if (i586_ctr_rate) {
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
i586_ctr_bias = i586_last_tick;
}
#endif
@@ -628,3 +788,49 @@ setstatclockrate(int newhz)
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
}
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
diff --git a/sys/amd64/include/clock.h b/sys/amd64/include/clock.h
index b624a61..1467e4e 100644
--- a/sys/amd64/include/clock.h
+++ b/sys/amd64/include/clock.h
@@ -3,7 +3,7 @@
* Garrett Wollman, September 1994.
* This file is in the public domain.
*
- * $Id: clock.h,v 1.11 1996/04/05 03:36:20 ache Exp $
+ * $Id: clock.h,v 1.12 1996/04/22 19:40:27 nate Exp $
*/
#ifndef _MACHINE_CLOCK_H_
@@ -11,9 +11,6 @@
#if defined(I586_CPU) || defined(I686_CPU)
-#define I586_CYCLECTR(x) \
- __asm __volatile(".byte 0x0f, 0x31" : "=A" (x))
-
/*
* When we update the clock, we also update this bias value which is
* automatically subtracted in microtime(). We assume that CPU_THISTICKLEN()
@@ -58,6 +55,7 @@ extern int statclock_disable;
extern int wall_cmos_clock;
#if defined(I586_CPU) || defined(I686_CPU)
+extern unsigned i586_ctr_freq;
extern unsigned i586_ctr_rate; /* fixed point */
extern long long i586_last_tick;
extern long long i586_ctr_bias;
@@ -67,9 +65,6 @@ extern int timer0_max_count;
extern u_int timer0_overflow_threshold;
extern u_int timer0_prescaler_count;
-#if defined(I586_CPU) || defined(I686_CPU)
-void calibrate_cyclecounter __P((void));
-#endif
#if defined(I586_CPU) || defined(I686_CPU)
static __inline u_long
@@ -77,10 +72,10 @@ cpu_thisticklen(u_long dflt)
{
long long old;
long len;
-
+
if (i586_ctr_rate) {
old = i586_last_tick;
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
len = ((i586_last_tick - old) << I586_CTR_RATE_SHIFT)
/ i586_ctr_rate;
i586_avg_tick = i586_avg_tick * 15 / 16 + len / 16;
diff --git a/sys/amd64/isa/clock.c b/sys/amd64/isa/clock.c
index d26b30c..ca9e405 100644
--- a/sys/amd64/isa/clock.c
+++ b/sys/amd64/isa/clock.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.56 1996/04/05 18:56:10 ache Exp $
+ * $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
*/
/*
@@ -53,8 +53,15 @@
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
#include <machine/frame.h>
+
#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -68,11 +75,7 @@
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
-/* X-tals being what they are, it's nice to be able to fudge this one... */
-#ifndef TIMER_FREQ
-#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
-#endif
-#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
/*
* Time in timer cycles that it takes for microtime() to disable interrupts
@@ -98,6 +101,7 @@ int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
u_int idelayed;
#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
unsigned i586_ctr_rate;
long long i586_ctr_bias;
long long i586_last_tick;
@@ -125,6 +129,11 @@ static void (*new_function) __P((struct clockframe *frame));
static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+static u_int timer_freq = 1193182;
+#endif
static char timer0_state = 0;
static char timer2_state = 0;
static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
@@ -287,34 +296,9 @@ getit(void)
return ((high << 8) | low);
}
-#if defined(I586_CPU) || defined(I686_CPU)
-/*
- * Figure out how fast the cyclecounter runs. This must be run with
- * clock interrupts disabled, but with the timer/counter programmed
- * and running.
- */
-void
-calibrate_cyclecounter(void)
-{
- /*
- * Don't need volatile; should always use unsigned if 2's
- * complement arithmetic is desired.
- */
- unsigned long long count;
-
-#define howlong 131072UL
- __asm __volatile(".byte 0x0f, 0x30" : : "A"(0LL), "c" (0x10));
- DELAY(howlong);
- __asm __volatile(".byte 0xf,0x31" : "=A" (count));
-
- i586_ctr_rate = (count << I586_CTR_RATE_SHIFT) / howlong;
-#undef howlong
-}
-#endif
-
/*
* Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Relies on timer 1 counting down from (timer_freq / hz)
* Note: timer had better have been programmed before this is first used!
*/
void
@@ -346,15 +330,17 @@ DELAY(int n)
prev_tick = getit();
n -= 20;
/*
- * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
* and without any avoidable overflows.
*/
sec = n / 1000000;
usec = n - sec * 1000000;
- ticks_left = sec * TIMER_FREQ
- + usec * (TIMER_FREQ / 1000000)
- + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
- + usec * (TIMER_FREQ % 1000) / 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
while (ticks_left > 0) {
tick = getit();
@@ -430,6 +416,116 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
@@ -437,11 +533,75 @@ readrtc(int port)
void
startrtclock()
{
- timer0_max_count = hardclock_max_count = TIMER_DIV(hz);
- timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
+ u_int delta, freq;
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
}
/*
@@ -597,7 +757,7 @@ cpu_initclocks()
* Finish setting up anti-jitter measures.
*/
if (i586_ctr_rate) {
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
i586_ctr_bias = i586_last_tick;
}
#endif
@@ -628,3 +788,49 @@ setstatclockrate(int newhz)
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
}
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
index 03cf864..b20ad93 100644
--- a/sys/i386/i386/machdep.c
+++ b/sys/i386/i386/machdep.c
@@ -35,7 +35,7 @@
* SUCH DAMAGE.
*
* from: @(#)machdep.c 7.4 (Berkeley) 6/3/91
- * $Id: machdep.c,v 1.184 1996/04/26 13:47:39 phk Exp $
+ * $Id: machdep.c,v 1.185 1996/05/01 08:31:21 bde Exp $
*/
#include "npx.h"
@@ -234,7 +234,7 @@ cpu_startup(dummy)
* Initialize error message buffer (at end of core).
*/
- /* avail_end was pre-decremented in init_386() to compensate */
+ /* avail_end was pre-decremented in init386() to compensate */
for (i = 0; i < btoc(sizeof (struct msgbuf)); i++)
pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp,
avail_end + i * NBPG,
diff --git a/sys/i386/i386/tsc.c b/sys/i386/i386/tsc.c
index d26b30c..ca9e405 100644
--- a/sys/i386/i386/tsc.c
+++ b/sys/i386/i386/tsc.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.56 1996/04/05 18:56:10 ache Exp $
+ * $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
*/
/*
@@ -53,8 +53,15 @@
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
#include <machine/frame.h>
+
#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -68,11 +75,7 @@
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
-/* X-tals being what they are, it's nice to be able to fudge this one... */
-#ifndef TIMER_FREQ
-#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
-#endif
-#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
/*
* Time in timer cycles that it takes for microtime() to disable interrupts
@@ -98,6 +101,7 @@ int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
u_int idelayed;
#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
unsigned i586_ctr_rate;
long long i586_ctr_bias;
long long i586_last_tick;
@@ -125,6 +129,11 @@ static void (*new_function) __P((struct clockframe *frame));
static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+static u_int timer_freq = 1193182;
+#endif
static char timer0_state = 0;
static char timer2_state = 0;
static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
@@ -287,34 +296,9 @@ getit(void)
return ((high << 8) | low);
}
-#if defined(I586_CPU) || defined(I686_CPU)
-/*
- * Figure out how fast the cyclecounter runs. This must be run with
- * clock interrupts disabled, but with the timer/counter programmed
- * and running.
- */
-void
-calibrate_cyclecounter(void)
-{
- /*
- * Don't need volatile; should always use unsigned if 2's
- * complement arithmetic is desired.
- */
- unsigned long long count;
-
-#define howlong 131072UL
- __asm __volatile(".byte 0x0f, 0x30" : : "A"(0LL), "c" (0x10));
- DELAY(howlong);
- __asm __volatile(".byte 0xf,0x31" : "=A" (count));
-
- i586_ctr_rate = (count << I586_CTR_RATE_SHIFT) / howlong;
-#undef howlong
-}
-#endif
-
/*
* Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Relies on timer 1 counting down from (timer_freq / hz)
* Note: timer had better have been programmed before this is first used!
*/
void
@@ -346,15 +330,17 @@ DELAY(int n)
prev_tick = getit();
n -= 20;
/*
- * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
* and without any avoidable overflows.
*/
sec = n / 1000000;
usec = n - sec * 1000000;
- ticks_left = sec * TIMER_FREQ
- + usec * (TIMER_FREQ / 1000000)
- + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
- + usec * (TIMER_FREQ % 1000) / 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
while (ticks_left > 0) {
tick = getit();
@@ -430,6 +416,116 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
@@ -437,11 +533,75 @@ readrtc(int port)
void
startrtclock()
{
- timer0_max_count = hardclock_max_count = TIMER_DIV(hz);
- timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
+ u_int delta, freq;
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
}
/*
@@ -597,7 +757,7 @@ cpu_initclocks()
* Finish setting up anti-jitter measures.
*/
if (i586_ctr_rate) {
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
i586_ctr_bias = i586_last_tick;
}
#endif
@@ -628,3 +788,49 @@ setstatclockrate(int newhz)
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
}
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
diff --git a/sys/i386/include/clock.h b/sys/i386/include/clock.h
index b624a61..1467e4e 100644
--- a/sys/i386/include/clock.h
+++ b/sys/i386/include/clock.h
@@ -3,7 +3,7 @@
* Garrett Wollman, September 1994.
* This file is in the public domain.
*
- * $Id: clock.h,v 1.11 1996/04/05 03:36:20 ache Exp $
+ * $Id: clock.h,v 1.12 1996/04/22 19:40:27 nate Exp $
*/
#ifndef _MACHINE_CLOCK_H_
@@ -11,9 +11,6 @@
#if defined(I586_CPU) || defined(I686_CPU)
-#define I586_CYCLECTR(x) \
- __asm __volatile(".byte 0x0f, 0x31" : "=A" (x))
-
/*
* When we update the clock, we also update this bias value which is
* automatically subtracted in microtime(). We assume that CPU_THISTICKLEN()
@@ -58,6 +55,7 @@ extern int statclock_disable;
extern int wall_cmos_clock;
#if defined(I586_CPU) || defined(I686_CPU)
+extern unsigned i586_ctr_freq;
extern unsigned i586_ctr_rate; /* fixed point */
extern long long i586_last_tick;
extern long long i586_ctr_bias;
@@ -67,9 +65,6 @@ extern int timer0_max_count;
extern u_int timer0_overflow_threshold;
extern u_int timer0_prescaler_count;
-#if defined(I586_CPU) || defined(I686_CPU)
-void calibrate_cyclecounter __P((void));
-#endif
#if defined(I586_CPU) || defined(I686_CPU)
static __inline u_long
@@ -77,10 +72,10 @@ cpu_thisticklen(u_long dflt)
{
long long old;
long len;
-
+
if (i586_ctr_rate) {
old = i586_last_tick;
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
len = ((i586_last_tick - old) << I586_CTR_RATE_SHIFT)
/ i586_ctr_rate;
i586_avg_tick = i586_avg_tick * 15 / 16 + len / 16;
diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c
index d26b30c..ca9e405 100644
--- a/sys/i386/isa/clock.c
+++ b/sys/i386/isa/clock.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.56 1996/04/05 18:56:10 ache Exp $
+ * $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
*/
/*
@@ -53,8 +53,15 @@
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
#include <machine/frame.h>
+
#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -68,11 +75,7 @@
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
-/* X-tals being what they are, it's nice to be able to fudge this one... */
-#ifndef TIMER_FREQ
-#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
-#endif
-#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
/*
* Time in timer cycles that it takes for microtime() to disable interrupts
@@ -98,6 +101,7 @@ int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
u_int idelayed;
#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
unsigned i586_ctr_rate;
long long i586_ctr_bias;
long long i586_last_tick;
@@ -125,6 +129,11 @@ static void (*new_function) __P((struct clockframe *frame));
static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+static u_int timer_freq = 1193182;
+#endif
static char timer0_state = 0;
static char timer2_state = 0;
static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
@@ -287,34 +296,9 @@ getit(void)
return ((high << 8) | low);
}
-#if defined(I586_CPU) || defined(I686_CPU)
-/*
- * Figure out how fast the cyclecounter runs. This must be run with
- * clock interrupts disabled, but with the timer/counter programmed
- * and running.
- */
-void
-calibrate_cyclecounter(void)
-{
- /*
- * Don't need volatile; should always use unsigned if 2's
- * complement arithmetic is desired.
- */
- unsigned long long count;
-
-#define howlong 131072UL
- __asm __volatile(".byte 0x0f, 0x30" : : "A"(0LL), "c" (0x10));
- DELAY(howlong);
- __asm __volatile(".byte 0xf,0x31" : "=A" (count));
-
- i586_ctr_rate = (count << I586_CTR_RATE_SHIFT) / howlong;
-#undef howlong
-}
-#endif
-
/*
* Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Relies on timer 1 counting down from (timer_freq / hz)
* Note: timer had better have been programmed before this is first used!
*/
void
@@ -346,15 +330,17 @@ DELAY(int n)
prev_tick = getit();
n -= 20;
/*
- * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
* and without any avoidable overflows.
*/
sec = n / 1000000;
usec = n - sec * 1000000;
- ticks_left = sec * TIMER_FREQ
- + usec * (TIMER_FREQ / 1000000)
- + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
- + usec * (TIMER_FREQ % 1000) / 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
while (ticks_left > 0) {
tick = getit();
@@ -430,6 +416,116 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
@@ -437,11 +533,75 @@ readrtc(int port)
void
startrtclock()
{
- timer0_max_count = hardclock_max_count = TIMER_DIV(hz);
- timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
+ u_int delta, freq;
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
}
/*
@@ -597,7 +757,7 @@ cpu_initclocks()
* Finish setting up anti-jitter measures.
*/
if (i586_ctr_rate) {
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
i586_ctr_bias = i586_last_tick;
}
#endif
@@ -628,3 +788,49 @@ setstatclockrate(int newhz)
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
}
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
diff --git a/sys/isa/atrtc.c b/sys/isa/atrtc.c
index d26b30c..ca9e405 100644
--- a/sys/isa/atrtc.c
+++ b/sys/isa/atrtc.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
- * $Id: clock.c,v 1.56 1996/04/05 18:56:10 ache Exp $
+ * $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
*/
/*
@@ -53,8 +53,15 @@
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
#include <machine/frame.h>
+
#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -68,11 +75,7 @@
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
-/* X-tals being what they are, it's nice to be able to fudge this one... */
-#ifndef TIMER_FREQ
-#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
-#endif
-#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x))
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
/*
* Time in timer cycles that it takes for microtime() to disable interrupts
@@ -98,6 +101,7 @@ int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
u_int idelayed;
#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
unsigned i586_ctr_rate;
long long i586_ctr_bias;
long long i586_last_tick;
@@ -125,6 +129,11 @@ static void (*new_function) __P((struct clockframe *frame));
static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+static u_int timer_freq = 1193182;
+#endif
static char timer0_state = 0;
static char timer2_state = 0;
static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
@@ -287,34 +296,9 @@ getit(void)
return ((high << 8) | low);
}
-#if defined(I586_CPU) || defined(I686_CPU)
-/*
- * Figure out how fast the cyclecounter runs. This must be run with
- * clock interrupts disabled, but with the timer/counter programmed
- * and running.
- */
-void
-calibrate_cyclecounter(void)
-{
- /*
- * Don't need volatile; should always use unsigned if 2's
- * complement arithmetic is desired.
- */
- unsigned long long count;
-
-#define howlong 131072UL
- __asm __volatile(".byte 0x0f, 0x30" : : "A"(0LL), "c" (0x10));
- DELAY(howlong);
- __asm __volatile(".byte 0xf,0x31" : "=A" (count));
-
- i586_ctr_rate = (count << I586_CTR_RATE_SHIFT) / howlong;
-#undef howlong
-}
-#endif
-
/*
* Wait "n" microseconds.
- * Relies on timer 1 counting down from (TIMER_FREQ / hz)
+ * Relies on timer 1 counting down from (timer_freq / hz)
* Note: timer had better have been programmed before this is first used!
*/
void
@@ -346,15 +330,17 @@ DELAY(int n)
prev_tick = getit();
n -= 20;
/*
- * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
* and without any avoidable overflows.
*/
sec = n / 1000000;
usec = n - sec * 1000000;
- ticks_left = sec * TIMER_FREQ
- + usec * (TIMER_FREQ / 1000000)
- + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000
- + usec * (TIMER_FREQ % 1000) / 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
while (ticks_left > 0) {
tick = getit();
@@ -430,6 +416,116 @@ readrtc(int port)
return(bcd2bin(rtcin(port)));
}
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
@@ -437,11 +533,75 @@ readrtc(int port)
void
startrtclock()
{
- timer0_max_count = hardclock_max_count = TIMER_DIV(hz);
- timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, timer0_max_count & 0xff);
- outb(TIMER_CNTR0, timer0_max_count >> 8);
+ u_int delta, freq;
+
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
}
/*
@@ -597,7 +757,7 @@ cpu_initclocks()
* Finish setting up anti-jitter measures.
*/
if (i586_ctr_rate) {
- I586_CYCLECTR(i586_last_tick);
+ i586_last_tick = rdtsc();
i586_ctr_bias = i586_last_tick;
}
#endif
@@ -628,3 +788,49 @@ setstatclockrate(int newhz)
rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
writertc(RTC_STATUSA, rtc_statusa);
}
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
OpenPOWER on IntegriCloud