mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-12-29 15:10:57 +01:00
2dafbfcbab
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.
837 lines
21 KiB
C
837 lines
21 KiB
C
/*-
|
|
* Copyright (c) 1990 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* William Jolitz and Don Ahn.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* from: @(#)clock.c 7.2 (Berkeley) 5/12/91
|
|
* $Id: clock.c,v 1.57 1996/04/22 19:40:28 nate Exp $
|
|
*/
|
|
|
|
/*
|
|
* inittodr, settodr and support routines written
|
|
* by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>
|
|
*
|
|
* reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94
|
|
*/
|
|
|
|
/*
|
|
* Primitive clock interrupt routines.
|
|
*/
|
|
#include "opt_ddb.h"
|
|
|
|
#include <sys/param.h>
|
|
#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>
|
|
#include <i386/isa/rtc.h>
|
|
#include <i386/isa/timerreg.h>
|
|
|
|
/*
|
|
* 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
|
|
* can use a simple formula for leap years.
|
|
*/
|
|
#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
|
|
#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
|
|
|
|
#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
|
|
|
|
/*
|
|
* Time in timer cycles that it takes for microtime() to disable interrupts
|
|
* and latch the count. microtime() currently uses "cli; outb ..." so it
|
|
* normally takes less than 2 timer cycles. Add a few for cache misses.
|
|
* Add a few more to allow for latency in bogus calls to microtime() with
|
|
* interrupts already disabled.
|
|
*/
|
|
#define TIMER0_LATCH_COUNT 20
|
|
|
|
/*
|
|
* Minimum maximum count that we are willing to program into timer0.
|
|
* Must be large enough to guarantee that the timer interrupt handler
|
|
* returns before the next timer interrupt. Must be larger than
|
|
* TIMER0_LATCH_COUNT so that we don't have to worry about underflow in
|
|
* the calculation of timer0_overflow_threshold.
|
|
*/
|
|
#define TIMER0_MIN_MAX_COUNT TIMER_DIV(20000)
|
|
|
|
int adjkerntz; /* local offset from GMT in seconds */
|
|
int disable_rtc_set; /* disable resettodr() if != 0 */
|
|
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;
|
|
unsigned long i586_avg_tick;
|
|
#endif
|
|
int statclock_disable;
|
|
u_int stat_imask = SWI_CLOCK_MASK;
|
|
int timer0_max_count;
|
|
u_int timer0_overflow_threshold;
|
|
u_int timer0_prescaler_count;
|
|
|
|
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;
|
|
/*
|
|
* XXX new_function and timer_func should not handle clockframes, but
|
|
* timer_func currently needs to hold hardclock to handle the
|
|
* timer0_state == 0 case. We should use register_intr()/unregister_intr()
|
|
* to switch between clkintr() and a slightly different timerintr().
|
|
* This will require locking when acquiring and releasing timer0 - the
|
|
* current (nonexistent) locking doesn't seem to be adequate even now.
|
|
*/
|
|
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;
|
|
|
|
#if 0
|
|
void
|
|
clkintr(struct clockframe frame)
|
|
{
|
|
hardclock(&frame);
|
|
setdelayed();
|
|
}
|
|
#else
|
|
static void
|
|
clkintr(struct clockframe frame)
|
|
{
|
|
timer_func(&frame);
|
|
switch (timer0_state) {
|
|
case 0:
|
|
setdelayed();
|
|
break;
|
|
case 1:
|
|
if ((timer0_prescaler_count += timer0_max_count)
|
|
>= hardclock_max_count) {
|
|
hardclock(&frame);
|
|
setdelayed();
|
|
timer0_prescaler_count -= hardclock_max_count;
|
|
}
|
|
break;
|
|
case 2:
|
|
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);
|
|
outb(TIMER_CNTR0, timer0_max_count >> 8);
|
|
enable_intr();
|
|
timer0_prescaler_count = 0;
|
|
timer_func = new_function;
|
|
timer0_state = 1;
|
|
break;
|
|
case 3:
|
|
if ((timer0_prescaler_count += timer0_max_count)
|
|
>= hardclock_max_count) {
|
|
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);
|
|
outb(TIMER_CNTR0, timer0_max_count & 0xff);
|
|
outb(TIMER_CNTR0, timer0_max_count >> 8);
|
|
enable_intr();
|
|
/*
|
|
* See microtime.s for this magic.
|
|
*/
|
|
time.tv_usec += (27645 *
|
|
(timer0_prescaler_count - hardclock_max_count))
|
|
>> 15;
|
|
if (time.tv_usec >= 1000000)
|
|
time.tv_usec -= 1000000;
|
|
timer0_prescaler_count = 0;
|
|
timer_func = hardclock;;
|
|
timer0_state = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int
|
|
acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
|
|
{
|
|
if (timer0_state || TIMER_DIV(rate) < TIMER0_MIN_MAX_COUNT ||
|
|
!function)
|
|
return -1;
|
|
new_function = function;
|
|
new_rate = rate;
|
|
timer0_state = 2;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
acquire_timer2(int mode)
|
|
{
|
|
if (timer2_state)
|
|
return -1;
|
|
timer2_state = 1;
|
|
outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
release_timer0()
|
|
{
|
|
if (!timer0_state)
|
|
return -1;
|
|
timer0_state = 3;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
release_timer2()
|
|
{
|
|
if (!timer2_state)
|
|
return -1;
|
|
timer2_state = 0;
|
|
outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This routine receives statistical clock interrupts from the RTC.
|
|
* As explained above, these occur at 128 interrupts per second.
|
|
* When profiling, we receive interrupts at a rate of 1024 Hz.
|
|
*
|
|
* This does not actually add as much overhead as it sounds, because
|
|
* when the statistical clock is active, the hardclock driver no longer
|
|
* needs to keep (inaccurate) statistics on its own. This decouples
|
|
* statistics gathering from scheduling interrupts.
|
|
*
|
|
* The RTC chip requires that we read status register C (RTC_INTR)
|
|
* to acknowledge an interrupt, before it will generate the next one.
|
|
*/
|
|
static void
|
|
rtcintr(struct clockframe frame)
|
|
{
|
|
u_char stat;
|
|
stat = rtcin(RTC_INTR);
|
|
if(stat & RTCIR_PERIOD) {
|
|
statclock(&frame);
|
|
}
|
|
}
|
|
|
|
#ifdef DDB
|
|
static void
|
|
DDB_printrtc(void)
|
|
{
|
|
printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n",
|
|
rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY),
|
|
rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC),
|
|
rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR));
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
getit(void)
|
|
{
|
|
int high, low;
|
|
|
|
disable_intr();
|
|
/* select timer0 and latch counter value */
|
|
outb(TIMER_MODE, TIMER_SEL0);
|
|
low = inb(TIMER_CNTR0);
|
|
high = inb(TIMER_CNTR0);
|
|
enable_intr();
|
|
return ((high << 8) | low);
|
|
}
|
|
|
|
/*
|
|
* Wait "n" microseconds.
|
|
* Relies on timer 1 counting down from (timer_freq / hz)
|
|
* Note: timer had better have been programmed before this is first used!
|
|
*/
|
|
void
|
|
DELAY(int n)
|
|
{
|
|
int prev_tick, tick, ticks_left, sec, usec;
|
|
|
|
#ifdef DELAYDEBUG
|
|
int getit_calls = 1;
|
|
int n1;
|
|
static int state = 0;
|
|
|
|
if (state == 0) {
|
|
state = 1;
|
|
for (n1 = 1; n1 <= 10000000; n1 *= 10)
|
|
DELAY(n1);
|
|
state = 2;
|
|
}
|
|
if (state == 1)
|
|
printf("DELAY(%d)...", n);
|
|
#endif
|
|
/*
|
|
* Read the counter first, so that the rest of the setup overhead is
|
|
* counted. Guess the initial overhead is 20 usec (on most systems it
|
|
* takes about 1.5 usec for each of the i/o's in getit(). The loop
|
|
* takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
|
|
* multiplications and divisions to scale the count take a while).
|
|
*/
|
|
prev_tick = getit();
|
|
n -= 20;
|
|
/*
|
|
* 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;
|
|
if (n < 0)
|
|
ticks_left = 0; /* XXX timer_freq is unsigned */
|
|
|
|
while (ticks_left > 0) {
|
|
tick = getit();
|
|
#ifdef DELAYDEBUG
|
|
++getit_calls;
|
|
#endif
|
|
if (tick > prev_tick)
|
|
ticks_left -= prev_tick - (tick - timer0_max_count);
|
|
else
|
|
ticks_left -= prev_tick - tick;
|
|
prev_tick = tick;
|
|
}
|
|
#ifdef DELAYDEBUG
|
|
if (state == 1)
|
|
printf(" %d calls to getit() at %d usec each\n",
|
|
getit_calls, (n + 5) / getit_calls);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sysbeepstop(void *chan)
|
|
{
|
|
outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
|
|
release_timer2();
|
|
beeping = 0;
|
|
}
|
|
|
|
int
|
|
sysbeep(int pitch, int period)
|
|
{
|
|
|
|
if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
|
|
return -1;
|
|
disable_intr();
|
|
outb(TIMER_CNTR2, pitch);
|
|
outb(TIMER_CNTR2, (pitch>>8));
|
|
enable_intr();
|
|
if (!beeping) {
|
|
outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
|
|
beeping = period;
|
|
timeout(sysbeepstop, (void *)NULL, period);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* RTC support routines
|
|
*/
|
|
|
|
int
|
|
rtcin(reg)
|
|
int reg;
|
|
{
|
|
u_char val;
|
|
|
|
outb(IO_RTC, reg);
|
|
inb(0x84);
|
|
val = inb(IO_RTC + 1);
|
|
inb(0x84);
|
|
return (val);
|
|
}
|
|
|
|
static __inline void
|
|
writertc(u_char reg, u_char val)
|
|
{
|
|
outb(IO_RTC, reg);
|
|
outb(IO_RTC + 1, val);
|
|
}
|
|
|
|
static __inline int
|
|
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.
|
|
*/
|
|
void
|
|
startrtclock()
|
|
{
|
|
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
|
|
}
|
|
|
|
/*
|
|
* Initialize the time of day register, based on the time base which is, e.g.
|
|
* from a filesystem.
|
|
*/
|
|
void
|
|
inittodr(time_t base)
|
|
{
|
|
unsigned long sec, days;
|
|
int yd;
|
|
int year, month;
|
|
int y, m, s;
|
|
|
|
s = splclock();
|
|
time.tv_sec = base;
|
|
time.tv_usec = 0;
|
|
splx(s);
|
|
|
|
/* Look if we have a RTC present and the time is valid */
|
|
if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
|
|
goto wrong_time;
|
|
|
|
/* wait for time update to complete */
|
|
/* If RTCSA_TUP is zero, we have at least 244us before next update */
|
|
while (rtcin(RTC_STATUSA) & RTCSA_TUP);
|
|
|
|
days = 0;
|
|
#ifdef USE_RTC_CENTURY
|
|
year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100;
|
|
#else
|
|
year = readrtc(RTC_YEAR) + 1900;
|
|
if (year < 1970)
|
|
year += 100;
|
|
#endif
|
|
if (year < 1970)
|
|
goto wrong_time;
|
|
month = readrtc(RTC_MONTH);
|
|
for (m = 1; m < month; m++)
|
|
days += daysinmonth[m-1];
|
|
if ((month > 2) && LEAPYEAR(year))
|
|
days ++;
|
|
days += readrtc(RTC_DAY) - 1;
|
|
yd = days;
|
|
for (y = 1970; y < year; y++)
|
|
days += DAYSPERYEAR + LEAPYEAR(y);
|
|
sec = ((( days * 24 +
|
|
readrtc(RTC_HRS)) * 60 +
|
|
readrtc(RTC_MIN)) * 60 +
|
|
readrtc(RTC_SEC));
|
|
/* sec now contains the number of seconds, since Jan 1 1970,
|
|
in the local time zone */
|
|
|
|
sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
s = splclock();
|
|
time.tv_sec = sec;
|
|
splx(s);
|
|
return;
|
|
|
|
wrong_time:
|
|
printf("Invalid time in real time clock.\n");
|
|
printf("Check and reset the date immediately!\n");
|
|
}
|
|
|
|
/*
|
|
* Write system time back to RTC
|
|
*/
|
|
void
|
|
resettodr()
|
|
{
|
|
unsigned long tm;
|
|
int y, m, s;
|
|
|
|
if (disable_rtc_set)
|
|
return;
|
|
|
|
s = splclock();
|
|
tm = time.tv_sec;
|
|
splx(s);
|
|
|
|
/* Disable RTC updates and interrupts. */
|
|
writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
|
|
|
|
/* Calculate local time to put in RTC */
|
|
|
|
tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
|
|
|
|
writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
|
|
writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
|
|
writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
|
|
|
|
/* We have now the days since 01-01-1970 in tm */
|
|
writertc(RTC_WDAY, (tm+4)%7); /* Write back Weekday */
|
|
for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
|
|
tm >= m;
|
|
y++, m = DAYSPERYEAR + LEAPYEAR(y))
|
|
tm -= m;
|
|
|
|
/* Now we have the years in y and the day-of-the-year in tm */
|
|
writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */
|
|
#ifdef USE_RTC_CENTURY
|
|
writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */
|
|
#endif
|
|
for (m = 0; ; m++) {
|
|
int ml;
|
|
|
|
ml = daysinmonth[m];
|
|
if (m == 1 && LEAPYEAR(y))
|
|
ml++;
|
|
if (tm < ml)
|
|
break;
|
|
tm -= ml;
|
|
}
|
|
|
|
writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */
|
|
writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */
|
|
|
|
/* Reenable RTC updates and interrupts. */
|
|
writertc(RTC_STATUSB, rtc_statusb);
|
|
}
|
|
|
|
/*
|
|
* Start both clocks running.
|
|
*/
|
|
void
|
|
cpu_initclocks()
|
|
{
|
|
int diag;
|
|
|
|
if (statclock_disable) {
|
|
/*
|
|
* The stat interrupt mask is different without the
|
|
* statistics clock. Also, don't set the interrupt
|
|
* flag which would normally cause the RTC to generate
|
|
* interrupts.
|
|
*/
|
|
stat_imask = HWI_MASK | SWI_MASK;
|
|
rtc_statusb = RTCSB_24HR;
|
|
} else {
|
|
/* Setting stathz to nonzero early helps avoid races. */
|
|
stathz = RTC_NOPROFRATE;
|
|
profhz = RTC_PROFRATE;
|
|
}
|
|
|
|
/* Finish initializing 8253 timer 0. */
|
|
register_intr(/* irq */ 0, /* XXX id */ 0, /* flags */ 0,
|
|
/* XXX */ (inthand2_t *)clkintr, &clk_imask,
|
|
/* unit */ 0);
|
|
INTREN(IRQ0);
|
|
#if defined(I586_CPU) || defined(I686_CPU)
|
|
/*
|
|
* Finish setting up anti-jitter measures.
|
|
*/
|
|
if (i586_ctr_rate) {
|
|
i586_last_tick = rdtsc();
|
|
i586_ctr_bias = i586_last_tick;
|
|
}
|
|
#endif
|
|
|
|
/* Initialize RTC. */
|
|
writertc(RTC_STATUSA, rtc_statusa);
|
|
writertc(RTC_STATUSB, RTCSB_24HR);
|
|
|
|
/* Don't bother enabling the statistics clock. */
|
|
if (statclock_disable)
|
|
return;
|
|
diag = rtcin(RTC_DIAG);
|
|
if (diag != 0)
|
|
printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
|
|
register_intr(/* irq */ 8, /* XXX id */ 1, /* flags */ 0,
|
|
/* XXX */ (inthand2_t *)rtcintr, &stat_imask,
|
|
/* unit */ 0);
|
|
INTREN(IRQ8);
|
|
writertc(RTC_STATUSB, rtc_statusb);
|
|
}
|
|
|
|
void
|
|
setstatclockrate(int newhz)
|
|
{
|
|
if (newhz == RTC_PROFRATE)
|
|
rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF;
|
|
else
|
|
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) */
|