Sync with sys/i386/isa/clock.c revision 1.111.

This commit is contained in:
KATO Takenori 1998-02-21 15:52:40 +00:00
parent 36c12c2f2e
commit c2515e4e62
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=33713
3 changed files with 363 additions and 213 deletions

View file

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
* $Id: clock.c,v 1.41 1998/02/09 15:05:42 kato Exp $
* $Id: clock.c,v 1.42 1998/02/13 09:32:17 kato Exp $
*/
/*
@ -119,9 +119,7 @@
/*
* Maximum frequency that we are willing to allow for timer0. Must be
* low enough to guarantee that the timer interrupt handler returns
* before the next timer interrupt. Must result in a lower TIMER_DIV
* value than TIMER0_LATCH_COUNT so that we don't have to worry about
* underflow in the calculation of timer0_overflow_threshold.
* before the next timer interrupt.
*/
#define TIMER0_MAX_FREQ 20000
@ -130,37 +128,35 @@ int disable_rtc_set; /* disable resettodr() if != 0 */
u_int idelayed;
int statclock_disable;
u_int stat_imask = SWI_CLOCK_MASK;
#ifdef TIMER_FREQ
u_int timer_freq = TIMER_FREQ;
#else
#ifndef TIMER_FREQ
#ifdef PC98
#ifndef AUTO_CLOCK
#ifndef PC98_8M
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#else /* !PC98_8M */
u_int timer_freq = 1996800;
#define TIMER_FREQ 1996800;
#endif /* PC98_8M */
#else /* AUTO_CLOCK */
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#endif /* AUTO_CLOCK */
#else /* IBM-PC */
u_int timer_freq = 1193182;
#define TIMER_FREQ 1193182;
#endif /* PC98 */
#endif
u_int timer_freq = TIMER_FREQ;
int timer0_max_count;
u_int timer0_overflow_threshold;
u_int timer0_prescaler_count;
u_int tsc_bias;
u_int tsc_comultiplier;
u_int tsc_freq;
u_int tsc_multiplier;
static u_int tsc_present;
int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
static int beeping = 0;
static u_int clk_imask = HWI_MASK | SWI_MASK;
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static u_int hardclock_max_count;
#ifndef PC98
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
#endif
/*
* XXX new_function and timer_func should not handle clockframes, but
* timer_func currently needs to hold hardclock to handle the
@ -173,6 +169,7 @@ static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
#endif
static u_int timer0_prescaler_count;
/* Values for timerX_state: */
#define RELEASED 0
@ -192,13 +189,44 @@ static void rtc_serialcom __P((int));
static int rtc_inb __P((void));
static void rtc_outb __P((int));
#endif
static u_int tsc_present;
static void set_tsc_freq(u_int tsc_count, u_int i8254_freq);
#ifndef PC98
static u_int64_t i8254_get_timecount __P((void));
#endif
static void set_timer_freq(u_int freq, int intr_freq);
static u_int64_t tsc_get_timecount __P((void));
static u_int32_t tsc_get_timedelta __P((struct timecounter *tc));
static struct timecounter tsc_timecounter[3] = {
tsc_get_timedelta, /* get_timedelta */
tsc_get_timecount, /* get_timecount */
~0, /* counter_mask */
0, /* frequency */
"TSC" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, tsc_timecounter, CTLFLAG_RD,
tsc_timecounter, sizeof(tsc_timecounter), "S,timecounter", "");
static struct timecounter i8254_timecounter[3] = {
0, /* get_timedelta */
i8254_get_timecount, /* get_timecount */
(1ULL << 32) - 1, /* counter_mask */
0, /* frequency */
"i8254" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD,
i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", "");
static void
clkintr(struct clockframe frame)
{
if (!i8254_ticked)
i8254_offset += timer0_max_count;
else
i8254_ticked = 0;
timer_func(&frame);
switch (timer0_state) {
@ -218,8 +246,6 @@ clkintr(struct clockframe frame)
case ACQUIRE_PENDING:
setdelayed();
timer0_max_count = TIMER_DIV(new_rate);
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
@ -260,8 +286,6 @@ clkintr(struct clockframe frame)
hardclock(&frame);
setdelayed();
timer0_max_count = hardclock_max_count;
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE,
TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
@ -286,6 +310,8 @@ acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
if (rate <= 0 || rate > TIMER0_MAX_FREQ)
return (-1);
if (strcmp(timecounter->name, "i8254") == 0)
return (-1);
switch (timer0_state) {
case RELEASED:
@ -739,14 +765,14 @@ calibrate_clocks(void)
* Read the cpu cycle counter. The timing considerations are
* similar to those for the i8254 clock.
*/
if (tsc_present) {
set_tsc_freq((u_int)rdtsc(), tot_count);
if (bootverbose)
if (tsc_present)
tsc_freq = rdtsc();
if (bootverbose) {
printf("i8254 clock: %u Hz\n", tot_count);
if (tsc_present)
printf("TSC clock: %u Hz, ", tsc_freq);
}
if (bootverbose)
printf("i8254 clock: %u Hz\n", tot_count);
return (tot_count);
fail:
@ -769,8 +795,6 @@ set_timer_freq(u_int freq, int intr_freq)
new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
if (new_timer0_max_count != timer0_max_count) {
timer0_max_count = new_timer0_max_count;
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);
@ -780,7 +804,7 @@ set_timer_freq(u_int freq, int intr_freq)
}
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* Initialize 8254 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
*/
void
@ -858,6 +882,8 @@ startrtclock()
#endif
set_timer_freq(timer_freq, hz);
i8254_timecounter[0].frequency = timer_freq;
init_timecounter(i8254_timecounter);
#ifndef CLK_USE_TSC_CALIBRATION
if (tsc_freq != 0) {
@ -875,12 +901,16 @@ startrtclock()
*/
wrmsr(0x10, 0LL); /* XXX */
DELAY(1000000);
set_tsc_freq((u_int)rdtsc(), timer_freq);
tsc_freq = rdtsc();
#ifdef CLK_USE_TSC_CALIBRATION
if (bootverbose)
printf("TSC clock: %u Hz\n", tsc_freq);
printf("TSC clock: %u Hz (Method B)\n", tsc_freq);
#endif
}
if (tsc_present && tsc_freq != 0) {
tsc_timecounter[0].frequency = tsc_freq;
init_timecounter(tsc_timecounter);
}
}
#ifdef PC98
@ -960,8 +990,9 @@ inittodr(time_t base)
if (base) {
s = splclock();
time.tv_sec = base;
time.tv_usec = 0;
ts.tv_sec = base;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
@ -1032,9 +1063,15 @@ inittodr(time_t base)
sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
s = splclock();
time.tv_sec = sec;
splx(s);
y = time.tv_sec - sec;
if (y <= -2 || y >= 2) {
/* badly off, adjust it */
s = splclock();
ts.tv_sec = sec;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
return;
wrong_time:
@ -1225,12 +1262,6 @@ cpu_initclocks()
#endif /* APIC_IO */
/*
* Finish setting up anti-jitter measures.
*/
if (tsc_freq != 0)
tsc_bias = rdtsc();
#ifndef PC98
/* Initialize RTC. */
writertc(RTC_STATUSA, rtc_statusa);
@ -1287,11 +1318,10 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
freq = timer_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL) {
if (timer0_state != 0)
if (timer0_state != RELEASED)
return (EBUSY); /* too much trouble to handle */
set_timer_freq(freq, hz);
if (tsc_present)
set_tsc_freq(tsc_freq, timer_freq);
i8254_timecounter[0].frequency = freq;
}
return (error);
}
@ -1299,28 +1329,6 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
static void
set_tsc_freq(u_int tsc_count, u_int i8254_freq)
{
u_int comultiplier, multiplier;
u_long ef;
if (tsc_count == 0) {
tsc_freq = tsc_count;
return;
}
comultiplier = ((unsigned long long)tsc_count
<< TSC_COMULTIPLIER_SHIFT) / i8254_freq;
multiplier = (1000000LL << TSC_MULTIPLIER_SHIFT) / tsc_count;
ef = read_eflags();
disable_intr();
tsc_freq = tsc_count;
tsc_comultiplier = comultiplier;
tsc_multiplier = multiplier;
CLOCK_UNLOCK();
write_eflags(ef);
}
static int
sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
{
@ -1331,10 +1339,52 @@ sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
return (EOPNOTSUPP);
freq = tsc_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL)
set_tsc_freq(freq, timer_freq);
if (error == 0 && req->newptr != NULL) {
tsc_freq = freq;
tsc_timecounter[0].frequency = tsc_freq;
}
return (error);
}
SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_tsc_freq, "I", "");
static u_int64_t
i8254_get_timecount(void)
{
u_int32_t count;
u_long ef;
u_int high, low;
ef = read_eflags();
disable_intr();
/* Select timer0 and latch counter value. */
outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
low = inb(TIMER_CNTR0);
high = inb(TIMER_CNTR0);
count = hardclock_max_count - ((high << 8) | low);
if (count < i8254_lastcount) {
i8254_ticked = 1;
i8254_offset += hardclock_max_count;
}
i8254_lastcount = count;
count += i8254_offset;
write_eflags(ef);
return (count);
}
static u_int64_t
tsc_get_timecount(void)
{
return ((u_int64_t)rdtsc());
}
static u_int32_t
tsc_get_timedelta(struct timecounter *tc)
{
return ((u_int64_t)rdtsc() - tc->offset_count);
}

View file

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
* $Id: clock.c,v 1.41 1998/02/09 15:05:42 kato Exp $
* $Id: clock.c,v 1.42 1998/02/13 09:32:17 kato Exp $
*/
/*
@ -119,9 +119,7 @@
/*
* Maximum frequency that we are willing to allow for timer0. Must be
* low enough to guarantee that the timer interrupt handler returns
* before the next timer interrupt. Must result in a lower TIMER_DIV
* value than TIMER0_LATCH_COUNT so that we don't have to worry about
* underflow in the calculation of timer0_overflow_threshold.
* before the next timer interrupt.
*/
#define TIMER0_MAX_FREQ 20000
@ -130,37 +128,35 @@ int disable_rtc_set; /* disable resettodr() if != 0 */
u_int idelayed;
int statclock_disable;
u_int stat_imask = SWI_CLOCK_MASK;
#ifdef TIMER_FREQ
u_int timer_freq = TIMER_FREQ;
#else
#ifndef TIMER_FREQ
#ifdef PC98
#ifndef AUTO_CLOCK
#ifndef PC98_8M
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#else /* !PC98_8M */
u_int timer_freq = 1996800;
#define TIMER_FREQ 1996800;
#endif /* PC98_8M */
#else /* AUTO_CLOCK */
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#endif /* AUTO_CLOCK */
#else /* IBM-PC */
u_int timer_freq = 1193182;
#define TIMER_FREQ 1193182;
#endif /* PC98 */
#endif
u_int timer_freq = TIMER_FREQ;
int timer0_max_count;
u_int timer0_overflow_threshold;
u_int timer0_prescaler_count;
u_int tsc_bias;
u_int tsc_comultiplier;
u_int tsc_freq;
u_int tsc_multiplier;
static u_int tsc_present;
int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
static int beeping = 0;
static u_int clk_imask = HWI_MASK | SWI_MASK;
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static u_int hardclock_max_count;
#ifndef PC98
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
#endif
/*
* XXX new_function and timer_func should not handle clockframes, but
* timer_func currently needs to hold hardclock to handle the
@ -173,6 +169,7 @@ static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
#endif
static u_int timer0_prescaler_count;
/* Values for timerX_state: */
#define RELEASED 0
@ -192,13 +189,44 @@ static void rtc_serialcom __P((int));
static int rtc_inb __P((void));
static void rtc_outb __P((int));
#endif
static u_int tsc_present;
static void set_tsc_freq(u_int tsc_count, u_int i8254_freq);
#ifndef PC98
static u_int64_t i8254_get_timecount __P((void));
#endif
static void set_timer_freq(u_int freq, int intr_freq);
static u_int64_t tsc_get_timecount __P((void));
static u_int32_t tsc_get_timedelta __P((struct timecounter *tc));
static struct timecounter tsc_timecounter[3] = {
tsc_get_timedelta, /* get_timedelta */
tsc_get_timecount, /* get_timecount */
~0, /* counter_mask */
0, /* frequency */
"TSC" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, tsc_timecounter, CTLFLAG_RD,
tsc_timecounter, sizeof(tsc_timecounter), "S,timecounter", "");
static struct timecounter i8254_timecounter[3] = {
0, /* get_timedelta */
i8254_get_timecount, /* get_timecount */
(1ULL << 32) - 1, /* counter_mask */
0, /* frequency */
"i8254" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD,
i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", "");
static void
clkintr(struct clockframe frame)
{
if (!i8254_ticked)
i8254_offset += timer0_max_count;
else
i8254_ticked = 0;
timer_func(&frame);
switch (timer0_state) {
@ -218,8 +246,6 @@ clkintr(struct clockframe frame)
case ACQUIRE_PENDING:
setdelayed();
timer0_max_count = TIMER_DIV(new_rate);
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
@ -260,8 +286,6 @@ clkintr(struct clockframe frame)
hardclock(&frame);
setdelayed();
timer0_max_count = hardclock_max_count;
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE,
TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
@ -286,6 +310,8 @@ acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
if (rate <= 0 || rate > TIMER0_MAX_FREQ)
return (-1);
if (strcmp(timecounter->name, "i8254") == 0)
return (-1);
switch (timer0_state) {
case RELEASED:
@ -739,14 +765,14 @@ calibrate_clocks(void)
* Read the cpu cycle counter. The timing considerations are
* similar to those for the i8254 clock.
*/
if (tsc_present) {
set_tsc_freq((u_int)rdtsc(), tot_count);
if (bootverbose)
if (tsc_present)
tsc_freq = rdtsc();
if (bootverbose) {
printf("i8254 clock: %u Hz\n", tot_count);
if (tsc_present)
printf("TSC clock: %u Hz, ", tsc_freq);
}
if (bootverbose)
printf("i8254 clock: %u Hz\n", tot_count);
return (tot_count);
fail:
@ -769,8 +795,6 @@ set_timer_freq(u_int freq, int intr_freq)
new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
if (new_timer0_max_count != timer0_max_count) {
timer0_max_count = new_timer0_max_count;
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);
@ -780,7 +804,7 @@ set_timer_freq(u_int freq, int intr_freq)
}
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* Initialize 8254 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
*/
void
@ -858,6 +882,8 @@ startrtclock()
#endif
set_timer_freq(timer_freq, hz);
i8254_timecounter[0].frequency = timer_freq;
init_timecounter(i8254_timecounter);
#ifndef CLK_USE_TSC_CALIBRATION
if (tsc_freq != 0) {
@ -875,12 +901,16 @@ startrtclock()
*/
wrmsr(0x10, 0LL); /* XXX */
DELAY(1000000);
set_tsc_freq((u_int)rdtsc(), timer_freq);
tsc_freq = rdtsc();
#ifdef CLK_USE_TSC_CALIBRATION
if (bootverbose)
printf("TSC clock: %u Hz\n", tsc_freq);
printf("TSC clock: %u Hz (Method B)\n", tsc_freq);
#endif
}
if (tsc_present && tsc_freq != 0) {
tsc_timecounter[0].frequency = tsc_freq;
init_timecounter(tsc_timecounter);
}
}
#ifdef PC98
@ -960,8 +990,9 @@ inittodr(time_t base)
if (base) {
s = splclock();
time.tv_sec = base;
time.tv_usec = 0;
ts.tv_sec = base;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
@ -1032,9 +1063,15 @@ inittodr(time_t base)
sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
s = splclock();
time.tv_sec = sec;
splx(s);
y = time.tv_sec - sec;
if (y <= -2 || y >= 2) {
/* badly off, adjust it */
s = splclock();
ts.tv_sec = sec;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
return;
wrong_time:
@ -1225,12 +1262,6 @@ cpu_initclocks()
#endif /* APIC_IO */
/*
* Finish setting up anti-jitter measures.
*/
if (tsc_freq != 0)
tsc_bias = rdtsc();
#ifndef PC98
/* Initialize RTC. */
writertc(RTC_STATUSA, rtc_statusa);
@ -1287,11 +1318,10 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
freq = timer_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL) {
if (timer0_state != 0)
if (timer0_state != RELEASED)
return (EBUSY); /* too much trouble to handle */
set_timer_freq(freq, hz);
if (tsc_present)
set_tsc_freq(tsc_freq, timer_freq);
i8254_timecounter[0].frequency = freq;
}
return (error);
}
@ -1299,28 +1329,6 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
static void
set_tsc_freq(u_int tsc_count, u_int i8254_freq)
{
u_int comultiplier, multiplier;
u_long ef;
if (tsc_count == 0) {
tsc_freq = tsc_count;
return;
}
comultiplier = ((unsigned long long)tsc_count
<< TSC_COMULTIPLIER_SHIFT) / i8254_freq;
multiplier = (1000000LL << TSC_MULTIPLIER_SHIFT) / tsc_count;
ef = read_eflags();
disable_intr();
tsc_freq = tsc_count;
tsc_comultiplier = comultiplier;
tsc_multiplier = multiplier;
CLOCK_UNLOCK();
write_eflags(ef);
}
static int
sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
{
@ -1331,10 +1339,52 @@ sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
return (EOPNOTSUPP);
freq = tsc_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL)
set_tsc_freq(freq, timer_freq);
if (error == 0 && req->newptr != NULL) {
tsc_freq = freq;
tsc_timecounter[0].frequency = tsc_freq;
}
return (error);
}
SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_tsc_freq, "I", "");
static u_int64_t
i8254_get_timecount(void)
{
u_int32_t count;
u_long ef;
u_int high, low;
ef = read_eflags();
disable_intr();
/* Select timer0 and latch counter value. */
outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
low = inb(TIMER_CNTR0);
high = inb(TIMER_CNTR0);
count = hardclock_max_count - ((high << 8) | low);
if (count < i8254_lastcount) {
i8254_ticked = 1;
i8254_offset += hardclock_max_count;
}
i8254_lastcount = count;
count += i8254_offset;
write_eflags(ef);
return (count);
}
static u_int64_t
tsc_get_timecount(void)
{
return ((u_int64_t)rdtsc());
}
static u_int32_t
tsc_get_timedelta(struct timecounter *tc)
{
return ((u_int64_t)rdtsc() - tc->offset_count);
}

View file

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
* $Id: clock.c,v 1.41 1998/02/09 15:05:42 kato Exp $
* $Id: clock.c,v 1.42 1998/02/13 09:32:17 kato Exp $
*/
/*
@ -119,9 +119,7 @@
/*
* Maximum frequency that we are willing to allow for timer0. Must be
* low enough to guarantee that the timer interrupt handler returns
* before the next timer interrupt. Must result in a lower TIMER_DIV
* value than TIMER0_LATCH_COUNT so that we don't have to worry about
* underflow in the calculation of timer0_overflow_threshold.
* before the next timer interrupt.
*/
#define TIMER0_MAX_FREQ 20000
@ -130,37 +128,35 @@ int disable_rtc_set; /* disable resettodr() if != 0 */
u_int idelayed;
int statclock_disable;
u_int stat_imask = SWI_CLOCK_MASK;
#ifdef TIMER_FREQ
u_int timer_freq = TIMER_FREQ;
#else
#ifndef TIMER_FREQ
#ifdef PC98
#ifndef AUTO_CLOCK
#ifndef PC98_8M
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#else /* !PC98_8M */
u_int timer_freq = 1996800;
#define TIMER_FREQ 1996800;
#endif /* PC98_8M */
#else /* AUTO_CLOCK */
u_int timer_freq = 2457600;
#define TIMER_FREQ 2457600;
#endif /* AUTO_CLOCK */
#else /* IBM-PC */
u_int timer_freq = 1193182;
#define TIMER_FREQ 1193182;
#endif /* PC98 */
#endif
u_int timer_freq = TIMER_FREQ;
int timer0_max_count;
u_int timer0_overflow_threshold;
u_int timer0_prescaler_count;
u_int tsc_bias;
u_int tsc_comultiplier;
u_int tsc_freq;
u_int tsc_multiplier;
static u_int tsc_present;
int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
static int beeping = 0;
static u_int clk_imask = HWI_MASK | SWI_MASK;
static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static u_int hardclock_max_count;
#ifndef PC98
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
#endif
/*
* XXX new_function and timer_func should not handle clockframes, but
* timer_func currently needs to hold hardclock to handle the
@ -173,6 +169,7 @@ static u_int new_rate;
static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
#endif
static u_int timer0_prescaler_count;
/* Values for timerX_state: */
#define RELEASED 0
@ -192,13 +189,44 @@ static void rtc_serialcom __P((int));
static int rtc_inb __P((void));
static void rtc_outb __P((int));
#endif
static u_int tsc_present;
static void set_tsc_freq(u_int tsc_count, u_int i8254_freq);
#ifndef PC98
static u_int64_t i8254_get_timecount __P((void));
#endif
static void set_timer_freq(u_int freq, int intr_freq);
static u_int64_t tsc_get_timecount __P((void));
static u_int32_t tsc_get_timedelta __P((struct timecounter *tc));
static struct timecounter tsc_timecounter[3] = {
tsc_get_timedelta, /* get_timedelta */
tsc_get_timecount, /* get_timecount */
~0, /* counter_mask */
0, /* frequency */
"TSC" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, tsc_timecounter, CTLFLAG_RD,
tsc_timecounter, sizeof(tsc_timecounter), "S,timecounter", "");
static struct timecounter i8254_timecounter[3] = {
0, /* get_timedelta */
i8254_get_timecount, /* get_timecount */
(1ULL << 32) - 1, /* counter_mask */
0, /* frequency */
"i8254" /* name */
};
SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD,
i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", "");
static void
clkintr(struct clockframe frame)
{
if (!i8254_ticked)
i8254_offset += timer0_max_count;
else
i8254_ticked = 0;
timer_func(&frame);
switch (timer0_state) {
@ -218,8 +246,6 @@ clkintr(struct clockframe frame)
case ACQUIRE_PENDING:
setdelayed();
timer0_max_count = TIMER_DIV(new_rate);
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
outb(TIMER_CNTR0, timer0_max_count & 0xff);
@ -260,8 +286,6 @@ clkintr(struct clockframe frame)
hardclock(&frame);
setdelayed();
timer0_max_count = hardclock_max_count;
timer0_overflow_threshold =
timer0_max_count - TIMER0_LATCH_COUNT;
disable_intr();
outb(TIMER_MODE,
TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
@ -286,6 +310,8 @@ acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
if (rate <= 0 || rate > TIMER0_MAX_FREQ)
return (-1);
if (strcmp(timecounter->name, "i8254") == 0)
return (-1);
switch (timer0_state) {
case RELEASED:
@ -739,14 +765,14 @@ calibrate_clocks(void)
* Read the cpu cycle counter. The timing considerations are
* similar to those for the i8254 clock.
*/
if (tsc_present) {
set_tsc_freq((u_int)rdtsc(), tot_count);
if (bootverbose)
if (tsc_present)
tsc_freq = rdtsc();
if (bootverbose) {
printf("i8254 clock: %u Hz\n", tot_count);
if (tsc_present)
printf("TSC clock: %u Hz, ", tsc_freq);
}
if (bootverbose)
printf("i8254 clock: %u Hz\n", tot_count);
return (tot_count);
fail:
@ -769,8 +795,6 @@ set_timer_freq(u_int freq, int intr_freq)
new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
if (new_timer0_max_count != timer0_max_count) {
timer0_max_count = new_timer0_max_count;
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);
@ -780,7 +804,7 @@ set_timer_freq(u_int freq, int intr_freq)
}
/*
* Initialize 8253 timer 0 early so that it can be used in DELAY().
* Initialize 8254 timer 0 early so that it can be used in DELAY().
* XXX initialization of other timers is unintentionally left blank.
*/
void
@ -858,6 +882,8 @@ startrtclock()
#endif
set_timer_freq(timer_freq, hz);
i8254_timecounter[0].frequency = timer_freq;
init_timecounter(i8254_timecounter);
#ifndef CLK_USE_TSC_CALIBRATION
if (tsc_freq != 0) {
@ -875,12 +901,16 @@ startrtclock()
*/
wrmsr(0x10, 0LL); /* XXX */
DELAY(1000000);
set_tsc_freq((u_int)rdtsc(), timer_freq);
tsc_freq = rdtsc();
#ifdef CLK_USE_TSC_CALIBRATION
if (bootverbose)
printf("TSC clock: %u Hz\n", tsc_freq);
printf("TSC clock: %u Hz (Method B)\n", tsc_freq);
#endif
}
if (tsc_present && tsc_freq != 0) {
tsc_timecounter[0].frequency = tsc_freq;
init_timecounter(tsc_timecounter);
}
}
#ifdef PC98
@ -960,8 +990,9 @@ inittodr(time_t base)
if (base) {
s = splclock();
time.tv_sec = base;
time.tv_usec = 0;
ts.tv_sec = base;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
@ -1032,9 +1063,15 @@ inittodr(time_t base)
sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
s = splclock();
time.tv_sec = sec;
splx(s);
y = time.tv_sec - sec;
if (y <= -2 || y >= 2) {
/* badly off, adjust it */
s = splclock();
ts.tv_sec = sec;
ts.tv_nsec = 0;
set_timecounter(&ts);
splx(s);
}
return;
wrong_time:
@ -1225,12 +1262,6 @@ cpu_initclocks()
#endif /* APIC_IO */
/*
* Finish setting up anti-jitter measures.
*/
if (tsc_freq != 0)
tsc_bias = rdtsc();
#ifndef PC98
/* Initialize RTC. */
writertc(RTC_STATUSA, rtc_statusa);
@ -1287,11 +1318,10 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
freq = timer_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL) {
if (timer0_state != 0)
if (timer0_state != RELEASED)
return (EBUSY); /* too much trouble to handle */
set_timer_freq(freq, hz);
if (tsc_present)
set_tsc_freq(tsc_freq, timer_freq);
i8254_timecounter[0].frequency = freq;
}
return (error);
}
@ -1299,28 +1329,6 @@ sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
static void
set_tsc_freq(u_int tsc_count, u_int i8254_freq)
{
u_int comultiplier, multiplier;
u_long ef;
if (tsc_count == 0) {
tsc_freq = tsc_count;
return;
}
comultiplier = ((unsigned long long)tsc_count
<< TSC_COMULTIPLIER_SHIFT) / i8254_freq;
multiplier = (1000000LL << TSC_MULTIPLIER_SHIFT) / tsc_count;
ef = read_eflags();
disable_intr();
tsc_freq = tsc_count;
tsc_comultiplier = comultiplier;
tsc_multiplier = multiplier;
CLOCK_UNLOCK();
write_eflags(ef);
}
static int
sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
{
@ -1331,10 +1339,52 @@ sysctl_machdep_tsc_freq SYSCTL_HANDLER_ARGS
return (EOPNOTSUPP);
freq = tsc_freq;
error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
if (error == 0 && req->newptr != NULL)
set_tsc_freq(freq, timer_freq);
if (error == 0 && req->newptr != NULL) {
tsc_freq = freq;
tsc_timecounter[0].frequency = tsc_freq;
}
return (error);
}
SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_INT | CTLFLAG_RW,
0, sizeof(u_int), sysctl_machdep_tsc_freq, "I", "");
static u_int64_t
i8254_get_timecount(void)
{
u_int32_t count;
u_long ef;
u_int high, low;
ef = read_eflags();
disable_intr();
/* Select timer0 and latch counter value. */
outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
low = inb(TIMER_CNTR0);
high = inb(TIMER_CNTR0);
count = hardclock_max_count - ((high << 8) | low);
if (count < i8254_lastcount) {
i8254_ticked = 1;
i8254_offset += hardclock_max_count;
}
i8254_lastcount = count;
count += i8254_offset;
write_eflags(ef);
return (count);
}
static u_int64_t
tsc_get_timecount(void)
{
return ((u_int64_t)rdtsc());
}
static u_int32_t
tsc_get_timedelta(struct timecounter *tc)
{
return ((u_int64_t)rdtsc() - tc->offset_count);
}