--- src/sys/i386/isa/Attic/clock.c 2003/11/15 21:05:43 1.7 +++ src/sys/i386/isa/Attic/clock.c 2004/01/08 08:11:12 1.10 @@ -106,7 +106,8 @@ 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) / 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 @@ -135,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 */ @@ -198,38 +200,80 @@ 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) { clock_lock(); - if (i8254_ticked) + 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; 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: 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); @@ -238,10 +282,9 @@ clkintr(struct clockframe frame) timer0_state = ACQUIRED; setdelayed(); break; - case RELEASE_PENDING: - if ((timer0_prescaler_count += timer0_max_count) - >= hardclock_max_count) { + timer0_prescaler_count += timer0_max_count; + if (timer0_prescaler_count >= hardclock_max_count) { clock_lock(); i8254_offset = i8254_get_timecount(NULL); i8254_lastcount = 0; @@ -678,6 +721,7 @@ set_timer_freq(u_int freq, int intr_freq 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); @@ -1199,12 +1243,16 @@ i8254_get_timecount(struct timecounter * ef = read_eflags(); 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)) &&