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.
This commit is contained in:
Bruce Evans 1996-05-01 08:39:02 +00:00
parent 0f99c703ec
commit 2dafbfcbab
9 changed files with 1257 additions and 237 deletions

View file

@ -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,

View file

@ -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) */

View file

@ -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;

View file

@ -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) */

View file

@ -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,

View file

@ -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) */

View file

@ -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;

View file

@ -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) */

View file

@ -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) */