X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/b505872fe545ea24ba3d57c0f5c8a459d9eaecc4..0284027ea11f2fa480ade46bf23d4769748f91f3:/sys/i386/isa/clock.c diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index d68f7b2022..ed7145050f 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -35,7 +35,7 @@ * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 * $FreeBSD: src/sys/i386/isa/clock.c,v 1.149.2.6 2002/11/02 04:41:50 iwasaki Exp $ - * $DragonFly: src/sys/i386/isa/Attic/clock.c,v 1.10 2004/01/08 08:11:12 dillon Exp $ + * $DragonFly: src/sys/i386/isa/Attic/clock.c,v 1.16 2004/08/02 23:20:30 dillon Exp $ */ /* @@ -57,11 +57,16 @@ #include #include #include +#include #ifndef SMP #include #endif #include #include +#include +#include +#include +#include #include #ifdef CLK_CALIBRATION_LOOP @@ -98,6 +103,7 @@ int apic_8254_intr; static u_long read_intr_count (int vec); static void setup_8254_mixed_mode (void); #endif +static void i8254_restore(void); /* * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we @@ -106,201 +112,73 @@ static void setup_8254_mixed_mode (void); #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)) -#define FRAC_ADJUST(x) (timer_freq - ((timer_freq / (x)) * (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 +#ifndef TIMER_FREQ +#define TIMER_FREQ 1193182 +#endif -/* - * 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. - */ -#define TIMER0_MAX_FREQ 20000 +#define TIMER_SELX TIMER_SEL2 +#define TIMER_CNTRX TIMER_CNTR2 int adjkerntz; /* local offset from GMT in seconds */ -int clkintr_pending; int disable_rtc_set; /* disable resettodr() if != 0 */ volatile u_int idelayed; -int statclock_disable; +int statclock_disable = 1; /* we don't use the statclock right now */ u_int stat_imask = SWI_CLOCK_MASK; -#ifndef TIMER_FREQ -#define TIMER_FREQ 1193182 +u_int cputimer_freq = TIMER_FREQ; +#if 0 +int64_t cputimer_freq64_usec = ((int64_t)TIMER_FREQ << 32) / 1000000; +int64_t cputimer_freq64_nsec = ((int64_t)TIMER_FREQ << 32) / 1000000000LL; #endif -u_int timer_freq = TIMER_FREQ; -int timer0_max_count; -u_int timer0_frac_freq; +int64_t cputimer_freq64_usec = (1000000LL << 32) / TIMER_FREQ; +int64_t cputimer_freq64_nsec = (1000000000LL << 32) / TIMER_FREQ; u_int tsc_freq; int tsc_is_broken; int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ +int timer0_running; +enum tstate { RELEASED, ACQUIRED }; +enum tstate timer0_state; +enum tstate timer1_state; +enum tstate timer2_state; 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; -static u_int32_t i8254_lastcount; -static u_int32_t i8254_offset; -static int i8254_ticked; -/* - * 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 inthand_add()/inthand_remove() - * to switch between clkintr() and a slightly different timerintr(). - */ -static void (*new_function) (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; -static u_int timer0_prescaler_count; - -/* Values for timerX_state: */ -#define RELEASED 0 -#define RELEASE_PENDING 1 -#define ACQUIRED 2 -#define ACQUIRE_PENDING 3 - -static u_char timer0_state; -static u_char timer2_state; -static void (*timer_func) (struct clockframe *frame) = hardclock; static u_int tsc_present; -static unsigned i8254_get_timecount (struct timecounter *tc); -static unsigned tsc_get_timecount (struct timecounter *tc); -static void set_timer_freq(u_int freq, int intr_freq); - -static struct timecounter tsc_timecounter = { - tsc_get_timecount, /* get_timecount */ - 0, /* no poll_pps */ - ~0u, /* 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 = { - i8254_get_timecount, /* get_timecount */ - 0, /* no poll_pps */ - ~0u, /* counter_mask */ - 0, /* frequency */ - "i8254" /* name */ -}; - -SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD, - &i8254_timecounter, sizeof(i8254_timecounter), "S,timecounter", ""); - +/* + * timer0 clock interrupt. Timer0 is in one-shot mode and has stopped + * counting as of this interrupt. We use timer1 in free-running mode (not + * generating any interrupts) as our main counter. Each cpu has timeouts + * pending. + */ static void -clkintr(struct clockframe frame) +clkintr(struct intrframe frame) { - int phase; - int delta; - - if (timecounter->tc_get_timecount == i8254_get_timecount) { - clock_lock(); - if (i8254_ticked) { - i8254_ticked = 0; - } else { - i8254_offset += timer0_max_count; - i8254_lastcount = 0; - } - /* - * Lets say we are running at 100Hz. Our counter load will - * be 1193182 / 100 = 11931.82, which is really only 11931. - * The fractional code accounts for the .82 count. When it - * exceeds 1.00 count we adjust the reload register by + 1 - * to compensate for the error. We must also adjust - * i8254_offset. - * - * If we did not do this a high frequency would cause the - * actual interrupt rate to seriously diverge from 'hz'. - */ - clkintr_pending = 0; - clock_unlock(); - } + static sysclock_t timer1_count; + struct globaldata *gd = mycpu; + struct globaldata *gscan; + int n; /* - * Use the previously synchronized timecounter value to phase-sync - * our hz clock interrupt. We do this by reloading the initial count - * register of timer0, which takes effect the next time it reloads. + * SWSTROBE mode is a one-shot, the timer is no longer running */ - phase = 1000000 / timer0_frac_freq; - delta = timecounter->tc_microtime.tv_usec % phase; -#if 1 - clock_lock(); - if (delta < (phase >> 1)) { - /* - * Current time is a bit past what we expect, speed up the - * clock interrupt. - */ - outb(TIMER_CNTR0, timer0_max_count & 0xff); - outb(TIMER_CNTR0, timer0_max_count >> 8); - } else { - /* - * Current time is a bit before what we expect, slow down - * the clock interrupt. - */ - outb(TIMER_CNTR0, (timer0_max_count + 1) & 0xff); - outb(TIMER_CNTR0, (timer0_max_count + 1) >> 8); - ++i8254_offset; /* take into account extra count for tc==8254*/ - } - clock_unlock(); -#endif + timer0_running = 0; - timer_func(&frame); - - switch (timer0_state) { - case RELEASED: - setdelayed(); - break; - case ACQUIRED: - timer0_prescaler_count += timer0_max_count; - if (timer0_prescaler_count >= hardclock_max_count) { - timer0_prescaler_count -= hardclock_max_count; - hardclock(&frame); - setdelayed(); - } - break; - case ACQUIRE_PENDING: - clock_lock(); - i8254_offset = i8254_get_timecount(NULL); - i8254_lastcount = 0; - timer0_max_count = TIMER_DIV(new_rate); - timer0_frac_freq = new_rate; - outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); - outb(TIMER_CNTR0, timer0_max_count & 0xff); - outb(TIMER_CNTR0, timer0_max_count >> 8); - clock_unlock(); - timer_func = new_function; - timer0_state = ACQUIRED; - setdelayed(); - break; - case RELEASE_PENDING: - timer0_prescaler_count += timer0_max_count; - if (timer0_prescaler_count >= hardclock_max_count) { - clock_lock(); - i8254_offset = i8254_get_timecount(NULL); - i8254_lastcount = 0; - timer0_max_count = hardclock_max_count; - outb(TIMER_MODE, - TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); - outb(TIMER_CNTR0, timer0_max_count & 0xff); - outb(TIMER_CNTR0, timer0_max_count >> 8); - clock_unlock(); - timer0_prescaler_count = 0; - timer_func = hardclock; - timer0_state = RELEASED; - hardclock(&frame); - setdelayed(); - } - break; + /* + * XXX this could be done more efficiently by using a bitmask? + */ + timer1_count = cputimer_count(); + for (n = 0; n < ncpus; ++n) { + gscan = globaldata_find(n); + if (gscan->gd_nextclock == 0) + continue; + if (gscan != gd) { + lwkt_send_ipiq(gscan, (ipifunc_t)systimer_intr, &timer1_count); + } else { + systimer_intr(&timer1_count, &frame); + } } #if NMCA > 0 /* Reset clock interrupt by asserting bit 7 of port 0x61 */ @@ -309,46 +187,16 @@ clkintr(struct clockframe frame) #endif } + /* - * The acquire and release functions must be called at ipl >= splclock(). + * NOTE! not MP safe. */ -int -acquire_timer0(int rate, void (*function) (struct clockframe *frame)) -{ - static int old_rate; - - if (rate <= 0 || rate > TIMER0_MAX_FREQ) - return (-1); - switch (timer0_state) { - - case RELEASED: - timer0_state = ACQUIRE_PENDING; - break; - - case RELEASE_PENDING: - if (rate != old_rate) - return (-1); - /* - * The timer has been released recently, but is being - * re-acquired before the release completed. In this - * case, we simply reclaim it as if it had not been - * released at all. - */ - timer0_state = ACQUIRED; - break; - - default: - return (-1); /* busy */ - } - new_function = function; - old_rate = new_rate = rate; - return (0); -} - int acquire_timer2(int mode) { - + /* Timer2 is being used for time count operation */ + return(-1); +#if 0 if (timer2_state != RELEASED) return (-1); timer2_state = ACQUIRED; @@ -356,43 +204,20 @@ acquire_timer2(int mode) /* * This access to the timer registers is as atomic as possible * because it is a single instruction. We could do better if we - * knew the rate. Use of splclock() limits glitches to 10-100us, - * and this is probably good enough for timer2, so we aren't as - * careful with it as with timer0. + * knew the rate. */ outb(TIMER_MODE, TIMER_SEL2 | (mode & 0x3f)); - - return (0); -} - -int -release_timer0() -{ - switch (timer0_state) { - - case ACQUIRED: - timer0_state = RELEASE_PENDING; - break; - - case ACQUIRE_PENDING: - /* Nothing happened yet, release quickly. */ - timer0_state = RELEASED; - break; - - default: - return (-1); - } return (0); +#endif } int release_timer2() { - if (timer2_state != ACQUIRED) return (-1); - timer2_state = RELEASED; outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT); + timer2_state = RELEASED; return (0); } @@ -418,10 +243,11 @@ release_timer2() * in the statistics, but the stat clock will no longer stop. */ static void -rtcintr(struct clockframe frame) +rtcintr(struct intrframe frame) { while (rtcin(RTC_INTR) & RTCIR_PERIOD) - statclock(&frame); + ; + /* statclock(&frame); no longer used */ } #include "opt_ddb.h" @@ -437,26 +263,82 @@ DB_SHOW_COMMAND(rtc, rtc) } #endif /* DDB */ -static int -getit(void) +/* + * Convert a frequency to a cpu timer count. + */ +sysclock_t +cputimer_fromhz(int freq) { - int high, low; - - clock_lock(); + return(cputimer_freq / freq + 1); +} - /* Select timer0 and latch counter value. */ - outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); +sysclock_t +cputimer_fromus(int us) +{ + return((int64_t)cputimer_freq * us / 1000000); +} - low = inb(TIMER_CNTR0); - high = inb(TIMER_CNTR0); +/* + * Return the current cpu timer count as a 32 bit integer. + */ +sysclock_t +cputimer_count(void) +{ + static sysclock_t cputimer_base; + static __uint16_t cputimer_last; + __uint16_t count; + sysclock_t ret; + clock_lock(); + outb(TIMER_MODE, TIMER_SELX | TIMER_LATCH); + count = (__uint8_t)inb(TIMER_CNTRX); /* get countdown */ + count |= ((__uint8_t)inb(TIMER_CNTRX) << 8); + count = -count; /* -> countup */ + if (count < cputimer_last) /* rollover */ + cputimer_base += 0x00010000; + ret = cputimer_base | count; + cputimer_last = count; clock_unlock(); - return ((high << 8) | low); + return(ret); +} + +/* + * Reload for the next timeout. It is possible for the reload value + * to be 0 or negative, indicating that an immediate timer interrupt + * is desired. For now make the minimum 2 ticks. + */ +void +cputimer_intr_reload(sysclock_t reload) +{ + __uint16_t count; + + if ((int)reload < 2) + reload = 2; + + clock_lock(); + if (timer0_running) { + outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); /* count-down timer */ + count = (__uint8_t)inb(TIMER_CNTR0); /* lsb */ + count |= ((__uint8_t)inb(TIMER_CNTR0) << 8); /* msb */ + if (reload < count) { + outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); + outb(TIMER_CNTR0, (__uint8_t)reload); /* lsb */ + outb(TIMER_CNTR0, (__uint8_t)(reload >> 8)); /* msb */ + } + } else { + timer0_running = 1; + if (reload > 0xFFFF) + reload = 0; /* full count */ + outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); + outb(TIMER_CNTR0, (__uint8_t)reload); /* lsb */ + outb(TIMER_CNTR0, (__uint8_t)(reload >> 8)); /* msb */ + } + clock_unlock(); } /* * Wait "n" microseconds. - * Relies on timer 1 counting down from (timer_freq / hz) + * Relies on timer 1 counting down from (cputimer_freq / hz) * Note: timer had better have been programmed before this is first used! */ void @@ -482,8 +364,8 @@ DELAY(int n) * Guard against the timer being uninitialized if we are called * early for console i/o. */ - if (timer0_max_count == 0) - set_timer_freq(timer_freq, hz); + if (timer0_state == RELEASED) + i8254_restore(); /* * Read the counter first, so that the rest of the setup overhead is @@ -492,15 +374,15 @@ DELAY(int n) * 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(); + prev_tick = cputimer_count(); n -= 0; /* XXX actually guess no initial overhead */ /* - * Calculate (n * (timer_freq / 1e6)) without using floating point + * Calculate (n * (cputimer_freq / 1e6)) without using floating point * and without any avoidable overflows. */ - if (n <= 0) + if (n <= 0) { ticks_left = 0; - else if (n < 256) + } else if (n < 256) { /* * Use fixed point to avoid a slow division by 1000000. * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest. @@ -508,34 +390,26 @@ DELAY(int n) * for n between 0 and 256. */ ticks_left = ((u_int)n * 39099 + (1 << 15) - 1) >> 15; - else + } else { /* * Don't bother using fixed point, although gcc-2.7.2 * generates particularly poor code for the long long * division, since even the slow way will complete long * before the delay is up (unless we're interrupted). */ - ticks_left = ((u_int)n * (long long)timer_freq + 999999) + ticks_left = ((u_int)n * (long long)cputimer_freq + 999999) / 1000000; + } while (ticks_left > 0) { - tick = getit(); + tick = cputimer_count(); #ifdef DELAYDEBUG ++getit_calls; #endif - delta = prev_tick - tick; + delta = tick - prev_tick; prev_tick = tick; - if (delta < 0) { - delta += timer0_max_count; - /* - * Guard against timer0_max_count being wrong. - * This shouldn't happen in normal operation, - * but it may happen if set_timer_freq() is - * traced. - */ - if (delta < 0) - delta = 0; - } + if (delta < 0) + delta = 0; ticks_left -= delta; } #ifdef DELAYDEBUG @@ -549,32 +423,26 @@ static void sysbeepstop(void *chan) { outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */ - release_timer2(); beeping = 0; + release_timer2(); } int sysbeep(int pitch, int period) { - int x = splclock(); - if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) - if (!beeping) { - /* Something else owns it. */ - splx(x); - return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */ - } - clock_lock(); + return(-1); + /* + * Nobody else is using timer2, we do not need the clock lock + */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); - clock_unlock(); if (!beeping) { /* enable counter2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); beeping = period; timeout(sysbeepstop, (void *)NULL, period); } - splx(x); return (0); } @@ -654,9 +522,7 @@ calibrate_clocks(void) } /* Start keeping track of the i8254 counter. */ - prev_count = getit(); - if (prev_count == 0 || prev_count > timer0_max_count) - goto fail; + prev_count = cputimer_count(); tot_count = 0; if (tsc_present) @@ -678,13 +544,8 @@ calibrate_clocks(void) 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; + count = cputimer_count(); + tot_count += (int)(count - prev_count); prev_count = count; if (sec != start_sec) break; @@ -699,52 +560,35 @@ calibrate_clocks(void) if (tsc_present) tsc_freq = rdtsc() - old_tsc; - if (bootverbose) { - if (tsc_present) - printf("TSC clock: %u Hz, ", tsc_freq); - printf("i8254 clock: %u Hz\n", tot_count); - } + if (tsc_present) + printf("TSC clock: %u Hz, ", tsc_freq); + printf("i8254 clock: %u Hz\n", tot_count); return (tot_count); fail: - if (bootverbose) - 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) -{ - int new_timer0_max_count; - - clock_lock(); - timer_freq = freq; - new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq); - timer0_frac_freq = intr_freq; - if (new_timer0_max_count != timer0_max_count) { - timer0_max_count = new_timer0_max_count; - outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); - outb(TIMER_CNTR0, timer0_max_count & 0xff); - outb(TIMER_CNTR0, timer0_max_count >> 8); - } - clock_unlock(); + printf("failed, using default i8254 clock of %u Hz\n", cputimer_freq); + return (cputimer_freq); } static void i8254_restore(void) { + timer0_state = ACQUIRED; + timer1_state = ACQUIRED; clock_lock(); - outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); - outb(TIMER_CNTR0, timer0_max_count & 0xff); - outb(TIMER_CNTR0, timer0_max_count >> 8); + outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); + outb(TIMER_CNTR0, 2); /* lsb */ + outb(TIMER_CNTR0, 0); /* msb */ + outb(TIMER_MODE, TIMER_SELX | TIMER_RATEGEN | TIMER_16BIT); + outb(TIMER_CNTRX, 0); /* lsb */ + outb(TIMER_CNTRX, 0); /* msb */ + outb(IO_PPI, inb(IO_PPI) | 1); /* bit 0: enable gate, bit 1: spkr */ clock_unlock(); } static void rtc_restore(void) { - /* Restore all of the RTC's "status" (actually, control) registers. */ writertc(RTC_STATUSB, RTCSB_24HR); writertc(RTC_STATUSA, rtc_statusa); @@ -752,38 +596,54 @@ rtc_restore(void) } /* - * Restore all the timers non-atomically (XXX: should be atomically). + * Restore all the timers. * - * This function is called from apm_default_resume() to restore all the timers. - * This should not be necessary, but there are broken laptops that do not - * restore all the timers on resume. + * This function is called from apm_default_resume() / pmtimer to restore + * all the timers. We also have to restore our timebases, especially on + * MP systems, because cputimer_count() counter's delta may have grown + * too large for nanouptime() and friends to handle. */ void timer_restore(void) { - + crit_enter(); i8254_restore(); /* restore timer_freq and hz */ rtc_restore(); /* reenable RTC interrupts */ + restoreclocks(); + crit_exit(); } /* * Initialize 8254 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; + /* + * Can we use the TSC? + */ if (cpu_feature & CPUID_TSC) tsc_present = 1; else tsc_present = 0; + /* + * Initial RTC state, don't do anything unexpected + */ writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, RTCSB_24HR); - set_timer_freq(timer_freq, hz); + /* + * Set the 8254 timer0 in TIMER_SWSTROBE mode and cause it to + * generate an interrupt, which we will ignore for now. + * + * Set the 8254 timer1 in TIMER_RATEGEN mode and load 0x0000 + * (so it counts a full 2^16 and repeats). We will use this timer + * for our counting. + */ + i8254_restore(); freq = calibrate_clocks(); #ifdef CLK_CALIBRATION_LOOP if (bootverbose) { @@ -799,27 +659,26 @@ startrtclock() * 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) { + delta = freq > cputimer_freq ? + freq - cputimer_freq : cputimer_freq - freq; + if (delta < cputimer_freq / 100) { #ifndef CLK_USE_I8254_CALIBRATION if (bootverbose) printf( "CLK_USE_I8254_CALIBRATION not specified - using default frequency\n"); - freq = timer_freq; + freq = cputimer_freq; #endif - timer_freq = freq; + cputimer_freq = freq; + cputimer_freq64_usec = (1000000LL << 32) / freq; + cputimer_freq64_nsec = (1000000000LL << 32) / freq; } else { if (bootverbose) printf( "%d Hz differs from default of %d Hz by more than 1%%\n", - freq, timer_freq); + freq, cputimer_freq); tsc_freq = 0; } - set_timer_freq(timer_freq, hz); - i8254_timecounter.tc_frequency = timer_freq; - init_timecounter(&i8254_timecounter); - #ifndef CLK_USE_TSC_CALIBRATION if (tsc_freq != 0) { if (bootverbose) @@ -866,11 +725,6 @@ startrtclock() return; #endif /* NAPM > 0 */ - if (tsc_present && tsc_freq != 0 && !tsc_is_broken) { - tsc_timecounter.tc_frequency = tsc_freq; - init_timecounter(&tsc_timecounter); - } - #endif /* !defined(SMP) */ } @@ -884,15 +738,13 @@ inittodr(time_t base) unsigned long sec, days; int yd; int year, month; - int y, m, s; + int y, m; struct timespec ts; if (base) { - s = splclock(); ts.tv_sec = base; ts.tv_nsec = 0; - set_timecounter(&ts); - splx(s); + set_timeofday(&ts); } /* Look if we have a RTC present and the time is valid */ @@ -901,10 +753,10 @@ inittodr(time_t base) /* wait for time update to complete */ /* If RTCSA_TUP is zero, we have at least 244us before next update */ - s = splhigh(); + crit_enter(); while (rtcin(RTC_STATUSA) & RTCSA_TUP) { - splx(s); - s = splhigh(); + crit_exit(); + crit_enter(); } days = 0; @@ -916,7 +768,7 @@ inittodr(time_t base) year += 100; #endif if (year < 1970) { - splx(s); + crit_exit(); goto wrong_time; } month = readrtc(RTC_MONTH); @@ -942,9 +794,9 @@ inittodr(time_t base) /* badly off, adjust it */ ts.tv_sec = sec; ts.tv_nsec = 0; - set_timecounter(&ts); + set_timeofday(&ts); } - splx(s); + crit_exit(); return; wrong_time: @@ -958,16 +810,18 @@ wrong_time: void resettodr() { - unsigned long tm; - int y, m, s; + struct timeval tv; + unsigned long tm; + int m; + int y; if (disable_rtc_set) return; - s = splclock(); - tm = time_second; - splx(s); + microtime(&tv); + tm = tv.tv_sec; + crit_enter(); /* Disable RTC updates and interrupts. */ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); @@ -1007,11 +861,14 @@ resettodr() /* Reenable RTC updates and interrupts. */ writertc(RTC_STATUSB, rtc_statusb); + crit_exit(); } /* - * Start both clocks running. + * Start both clocks running. DragonFly note: the stat clock is no longer + * used. Instead, 8254 based systimers are used for all major clock + * interrupts. statclock_disable is set by default. */ void cpu_initclocks() @@ -1070,36 +927,44 @@ cpu_initclocks() 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); + if (statclock_disable == 0) { + diag = rtcin(RTC_DIAG); + if (diag != 0) + printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS); #ifdef APIC_IO - if (isa_apic_irq(8) != 8) - panic("APIC RTC != 8"); + if (isa_apic_irq(8) != 8) + panic("APIC RTC != 8"); #endif /* APIC_IO */ - inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask, - INTR_EXCL | INTR_FAST); + inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask, + INTR_EXCL | INTR_FAST); #ifdef APIC_IO - INTREN(APIC_IRQ8); + INTREN(APIC_IRQ8); #else - INTREN(IRQ8); + INTREN(IRQ8); #endif /* APIC_IO */ - writertc(RTC_STATUSB, rtc_statusb); + writertc(RTC_STATUSB, rtc_statusb); + } #ifdef APIC_IO if (apic_8254_trial) { - + sysclock_t base; + int lastcnt = read_intr_count(apic_8254_intr); + + /* + * XXX this assumes the 8254 is the cpu timer. Force an + * 8254 Timer0 interrupt and wait 1/100s for it to happen, + * then see if we got it. + */ printf("APIC_IO: Testing 8254 interrupt delivery\n"); - while (read_intr_count(8) < 6) + cputimer_intr_reload(2); /* XXX assumes 8254 */ + base = cputimer_count(); + while (cputimer_count() - base < cputimer_freq / 100) ; /* nothing */ - if (read_intr_count(apic_8254_intr) < 3) { + if (read_intr_count(apic_8254_intr) - lastcnt == 0) { /* * The MP table is broken. * The 8254 was not connected to the specified pin @@ -1135,13 +1000,14 @@ cpu_initclocks() } if (apic_int_type(0, 0) != 3 || int_to_apicintpin[apic_8254_intr].ioapic != 0 || - int_to_apicintpin[apic_8254_intr].int_pin != 0) + int_to_apicintpin[apic_8254_intr].int_pin != 0) { printf("APIC_IO: routing 8254 via IOAPIC #%d intpin %d\n", int_to_apicintpin[apic_8254_intr].ioapic, int_to_apicintpin[apic_8254_intr].int_pin); - else + } else { printf("APIC_IO: " "routing 8254 via 8259 and IOAPIC #0 intpin 0\n"); + } #endif } @@ -1187,97 +1053,13 @@ setstatclockrate(int newhz) 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_int(oidp, &freq, sizeof(freq), req); - if (error == 0 && req->newptr != NULL) { - if (timer0_state != RELEASED) - return (EBUSY); /* too much trouble to handle */ - set_timer_freq(freq, hz); - i8254_timecounter.tc_frequency = freq; - update_timecounter(&i8254_timecounter); - } - return (error); -} - -SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", ""); - -static int -sysctl_machdep_tsc_freq(SYSCTL_HANDLER_ARGS) -{ - int error; - u_int freq; - - if (tsc_timecounter.tc_frequency == 0) - return (EOPNOTSUPP); - freq = tsc_freq; - error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); - if (error == 0 && req->newptr != NULL) { - tsc_freq = freq; - tsc_timecounter.tc_frequency = tsc_freq; - update_timecounter(&tsc_timecounter); - } - return (error); -} - -SYSCTL_PROC(_machdep, OID_AUTO, tsc_freq, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(u_int), sysctl_machdep_tsc_freq, "IU", ""); - -static unsigned -i8254_get_timecount(struct timecounter *tc) -{ - u_int count; - u_long ef; - u_int high, low; - - ef = read_eflags(); - clock_lock(); - - /* - * Select timer0 and latch counter value. Because we may reload - * the counter with timer0_max_count + 1 to correct the frequency - * our delta count calculation must use timer0_max_count + 1. - */ - outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); - - low = inb(TIMER_CNTR0); - high = inb(TIMER_CNTR0); - count = timer0_max_count + 1 - ((high << 8) | low); - if (count < i8254_lastcount || - (!i8254_ticked && (clkintr_pending || - ((count < 20 || (!(ef & PSL_I) && count < timer0_max_count / 2u)) && -#ifdef APIC_IO -#define lapic_irr1 ((volatile u_int *)&lapic)[0x210 / 4] /* XXX XXX */ - /* XXX this assumes that apic_8254_intr is < 24. */ - (lapic_irr1 & (1 << apic_8254_intr)))) -#else - (inb(IO_ICU1) & 1))) -#endif - )) { - i8254_ticked = 1; - i8254_offset += timer0_max_count; - } - i8254_lastcount = count; - count += i8254_offset; - clock_unlock(); - return (count); -} - +#if 0 static unsigned tsc_get_timecount(struct timecounter *tc) { return (rdtsc()); } +#endif #ifdef KERN_TIMESTAMP #define KERN_TIMESTAMP_SIZE 16384 @@ -1298,3 +1080,30 @@ _TSTMP(u_int32_t x) } #endif /* KERN_TIMESTAMP */ +/* + * + */ + +static int +hw_i8254_timestamp(SYSCTL_HANDLER_ARGS) +{ + sysclock_t count; + __uint64_t tscval; + char buf[32]; + + crit_enter(); + count = cputimer_count(); + if (tsc_present) + tscval = rdtsc(); + else + tscval = 0; + crit_exit(); + snprintf(buf, sizeof(buf), "%08x %016llx", count, (long long)tscval); + return(SYSCTL_OUT(req, buf, strlen(buf) + 1)); +} + +SYSCTL_NODE(_hw, OID_AUTO, i8254, CTLFLAG_RW, 0, "I8254"); +SYSCTL_UINT(_hw_i8254, OID_AUTO, freq, CTLFLAG_RD, &cputimer_freq, 0, ""); +SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD, + 0, 0, hw_i8254_timestamp, "A", ""); +