--- src/sys/platform/pc32/isa/clock.c 2003/06/17 02:54:47 1.1 +++ src/sys/platform/pc32/isa/clock.c 2004/01/08 08:11:12 1.10 @@ -35,6 +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$ */ /* @@ -48,8 +49,9 @@ * reintroduced and updated by Chris Stenton 8/10/94 */ +#include "use_apm.h" +#include "use_mca.h" #include "opt_clock.h" -#include "apm.h" #include #include @@ -79,29 +81,23 @@ #include #include -#include -#include +#include +#include #include #include -#include "mca.h" #if NMCA > 0 -#include +#include #endif -#ifdef SMP -#define disable_intr() CLOCK_DISABLE_INTR() -#define enable_intr() CLOCK_ENABLE_INTR() - #ifdef APIC_IO #include /* The interrupt triggered by the 8254 (timer) chip */ int apic_8254_intr; -static u_long read_intr_count __P((int vec)); -static void setup_8254_mixed_mode __P((void)); +static u_long read_intr_count (int vec); +static void setup_8254_mixed_mode (void); #endif -#endif /* SMP */ /* * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we @@ -110,7 +106,8 @@ static void setup_8254_mixed_mode __P((v #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)) +#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 @@ -139,6 +136,7 @@ u_int stat_imask = SWI_CLOCK_MASK; #endif u_int timer_freq = TIMER_FREQ; int timer0_max_count; +u_int timer0_frac_freq; u_int tsc_freq; int tsc_is_broken; int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ @@ -156,7 +154,7 @@ static int i8254_ticked; * timer0_state == 0 case. We should use inthand_add()/inthand_remove() * to switch between clkintr() and a slightly different timerintr(). */ -static void (*new_function) __P((struct clockframe *frame)); +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; @@ -170,11 +168,11 @@ static u_int timer0_prescaler_count; static u_char timer0_state; static u_char timer2_state; -static void (*timer_func) __P((struct clockframe *frame)) = hardclock; +static void (*timer_func) (struct clockframe *frame) = hardclock; static u_int tsc_present; -static unsigned i8254_get_timecount __P((struct timecounter *tc)); -static unsigned tsc_get_timecount __P((struct timecounter *tc)); +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 = { @@ -202,51 +200,92 @@ SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_ti static void clkintr(struct clockframe frame) { + int phase; + int delta; + if (timecounter->tc_get_timecount == i8254_get_timecount) { - disable_intr(); - if (i8254_ticked) + clock_lock(); + if (i8254_ticked) { i8254_ticked = 0; - else { + } 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; - enable_intr(); + clock_unlock(); + } + + /* + * 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. + */ + 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 + timer_func(&frame); - switch (timer0_state) { + switch (timer0_state) { case RELEASED: setdelayed(); break; - case ACQUIRED: - if ((timer0_prescaler_count += timer0_max_count) - >= hardclock_max_count) { + 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: - disable_intr(); + 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); - enable_intr(); + clock_unlock(); timer_func = new_function; timer0_state = ACQUIRED; setdelayed(); break; - case RELEASE_PENDING: - if ((timer0_prescaler_count += timer0_max_count) - >= hardclock_max_count) { - disable_intr(); + 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; @@ -254,7 +293,7 @@ clkintr(struct clockframe frame) TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); outb(TIMER_CNTR0, timer0_max_count & 0xff); outb(TIMER_CNTR0, timer0_max_count >> 8); - enable_intr(); + clock_unlock(); timer0_prescaler_count = 0; timer_func = hardclock; timer0_state = RELEASED; @@ -274,7 +313,7 @@ clkintr(struct clockframe frame) * The acquire and release functions must be called at ipl >= splclock(). */ int -acquire_timer0(int rate, void (*function) __P((struct clockframe *frame))) +acquire_timer0(int rate, void (*function) (struct clockframe *frame)) { static int old_rate; @@ -401,11 +440,9 @@ DB_SHOW_COMMAND(rtc, rtc) static int getit(void) { - u_long ef; int high, low; - ef = read_eflags(); - disable_intr(); + clock_lock(); /* Select timer0 and latch counter value. */ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); @@ -413,8 +450,7 @@ getit(void) low = inb(TIMER_CNTR0); high = inb(TIMER_CNTR0); - CLOCK_UNLOCK(); - write_eflags(ef); + clock_unlock(); return ((high << 8) | low); } @@ -528,10 +564,10 @@ sysbeep(int pitch, int period) splx(x); return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */ } - disable_intr(); + clock_lock(); outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); - enable_intr(); + clock_unlock(); if (!beeping) { /* enable counter2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); @@ -680,35 +716,29 @@ fail: static void set_timer_freq(u_int freq, int intr_freq) { - u_long ef; int new_timer0_max_count; - ef = read_eflags(); - disable_intr(); + 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(); - write_eflags(ef); + clock_unlock(); } static void i8254_restore(void) { - u_long ef; - - ef = read_eflags(); - disable_intr(); + 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); - CLOCK_UNLOCK(); - write_eflags(ef); + clock_unlock(); } static void @@ -1025,13 +1055,13 @@ cpu_initclocks() } clkdesc = inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, - NULL, &clk_imask, INTR_EXCL); + NULL, &clk_imask, INTR_EXCL | INTR_FAST); INTREN(1 << apic_8254_intr); #else /* APIC_IO */ inthand_add("clk", 0, (inthand2_t *)clkintr, NULL, &clk_imask, - INTR_EXCL); + INTR_EXCL | INTR_FAST); INTREN(IRQ0); #endif /* APIC_IO */ @@ -1053,7 +1083,7 @@ cpu_initclocks() #endif /* APIC_IO */ inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask, - INTR_EXCL); + INTR_EXCL | INTR_FAST); #ifdef APIC_IO INTREN(APIC_IRQ8); @@ -1098,7 +1128,7 @@ cpu_initclocks() setup_8254_mixed_mode(); inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, - NULL, &clk_imask, INTR_EXCL); + NULL, &clk_imask, INTR_EXCL | INTR_FAST); INTREN(1 << apic_8254_intr); } @@ -1211,14 +1241,18 @@ i8254_get_timecount(struct timecounter * u_int high, low; ef = read_eflags(); - disable_intr(); + clock_lock(); - /* Select timer0 and latch counter value. */ + /* + * 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 - ((high << 8) | low); + 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)) && @@ -1235,8 +1269,7 @@ i8254_get_timecount(struct timecounter * } i8254_lastcount = count; count += i8254_offset; - CLOCK_UNLOCK(); - write_eflags(ef); + clock_unlock(); return (count); } @@ -1263,5 +1296,5 @@ _TSTMP(u_int32_t x) i = 0; tsc[i] = 0; /* mark last entry */ } -#endif KERN_TIMESTAMP +#endif /* KERN_TIMESTAMP */