diff options
Diffstat (limited to 'sys/pc98/cbus/clock.c')
-rw-r--r-- | sys/pc98/cbus/clock.c | 264 |
1 files changed, 170 insertions, 94 deletions
diff --git a/sys/pc98/cbus/clock.c b/sys/pc98/cbus/clock.c index b2befbf..071650c 100644 --- a/sys/pc98/cbus/clock.c +++ b/sys/pc98/cbus/clock.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 - * $Id: clock.c,v 1.58 1996/05/01 08:39:02 bde Exp $ + * $Id: clock.c,v 1.1.1.1 1996/06/14 10:04:42 asami Exp $ */ /* @@ -46,13 +46,14 @@ /* * modified for PC98 - * $Id: clock.c,v 1.7 1994/03/26 22:56:13 kakefuda Exp kakefuda $ + * $Id: clock.c,v 1.1.1.1 1996/06/14 10:04:42 asami Exp $ */ /* * Primitive clock interrupt routines. */ #include "opt_ddb.h" +#include "opt_clock.h" #include <sys/param.h> #include <sys/systm.h> @@ -100,13 +101,13 @@ #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. + * 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. */ -#define TIMER0_MIN_MAX_COUNT TIMER_DIV(20000) +#define TIMER0_MAX_FREQ 20000 int adjkerntz; /* local offset from GMT in seconds */ int disable_rtc_set; /* disable resettodr() if != 0 */ @@ -122,6 +123,23 @@ unsigned long i586_avg_tick; #endif int statclock_disable; u_int stat_imask = SWI_CLOCK_MASK; +#ifdef TIMER_FREQ +static u_int timer_freq = TIMER_FREQ; +#else +#ifdef PC98 +#ifndef AUTO_CLOCK +#ifndef PC98_8M +static u_int timer_freq = 2457600; +#else /* !PC98_8M */ +static u_int timer_freq = 1996800; +#endif /* PC98_8M */ +#else /* AUTO_CLOCK */ +static u_int timer_freq = 2457600; +#endif /* AUTO_CLOCK */ +#else /* IBM-PC */ +static u_int timer_freq = 1193182; +#endif /* PC98 */ +#endif int timer0_max_count; u_int timer0_overflow_threshold; u_int timer0_prescaler_count; @@ -135,8 +153,6 @@ static u_int hardclock_max_count; * 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; @@ -144,48 +160,32 @@ static u_int new_rate; static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR; #endif -#ifdef TIMER_FREQ -static u_int timer_freq = TIMER_FREQ; -#else -#ifdef PC98 -#ifndef AUTO_CLOCK -#ifndef PC98_8M -static u_int timer_freq = 2457600; -#else /* !PC98_8M */ -static u_int timer_freq = 1996800; -#endif /* PC98_8M */ -#else /* AUTO_CLOCK */ -static u_int timer_freq = 2457600; -#endif /* AUTO_CLOCK */ -#else /* IBM-PC */ -static u_int timer_freq = 1193182; -#endif /* PC98 */ -#endif -static char timer0_state = 0; + +/* Values for timerX_state: */ +#define RELEASED 0 +#define RELEASE_PENDING 1 +#define ACQUIRED 2 +#define ACQUIRE_PENDING 3 + +static u_char timer0_state; #ifdef PC98 -static char timer1_state = 0; +static u_char timer1_state; #endif -static char timer2_state = 0; +static u_char timer2_state; static void (*timer_func) __P((struct clockframe *frame)) = hardclock; int rtc_inb __P((void)); -#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: + + case RELEASED: setdelayed(); break; - case 1: + + case ACQUIRED: if ((timer0_prescaler_count += timer0_max_count) >= hardclock_max_count) { hardclock(&frame); @@ -193,7 +193,8 @@ clkintr(struct clockframe frame) timer0_prescaler_count -= hardclock_max_count; } break; - case 2: + + case ACQUIRE_PENDING: setdelayed(); timer0_max_count = TIMER_DIV(new_rate); timer0_overflow_threshold = @@ -205,9 +206,10 @@ clkintr(struct clockframe frame) enable_intr(); timer0_prescaler_count = 0; timer_func = new_function; - timer0_state = 1; + timer0_state = ACQUIRED; break; - case 3: + + case RELEASE_PENDING: if ((timer0_prescaler_count += timer0_max_count) >= hardclock_max_count) { hardclock(&frame); @@ -248,84 +250,140 @@ clkintr(struct clockframe frame) } #endif /* AUTO_CLOCK */ #else /* IBM-PC */ - time.tv_usec += (27645 * + time.tv_usec += (27465 * (timer0_prescaler_count - hardclock_max_count)) >> 15; #endif /* PC98 */ if (time.tv_usec >= 1000000) time.tv_usec -= 1000000; timer0_prescaler_count = 0; - timer_func = hardclock;; - timer0_state = 0; + timer_func = hardclock; + timer0_state = RELEASED; } break; } } -#endif +/* + * The acquire and release functions must be called at ipl >= splclock(). + */ int acquire_timer0(int rate, void (*function) __P((struct clockframe *frame))) { - if (timer0_state || TIMER_DIV(rate) < TIMER0_MIN_MAX_COUNT || - !function) - return -1; + 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; - new_rate = rate; - timer0_state = 2; - return 0; + old_rate = new_rate = rate; + return (0); } #ifdef PC98 int acquire_timer1(int mode) { - if (timer1_state) - return -1; - timer1_state = 1; - outb(TIMER_MODE, TIMER_SEL1 | (mode &0x3f)); - return 0; + + if (timer1_state != RELEASED) + return (-1); + timer1_state = ACQUIRED; + + /* + * 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. + */ + outb(TIMER_MODE, TIMER_SEL1 | (mode & 0x3f)); + + return (0); } #endif int acquire_timer2(int mode) { - if (timer2_state) - return -1; - timer2_state = 1; - outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f)); - return 0; + + if (timer2_state != RELEASED) + return (-1); + timer2_state = ACQUIRED; + + /* + * 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. + */ + outb(TIMER_MODE, TIMER_SEL2 | (mode & 0x3f)); + + return (0); } int release_timer0() { - if (!timer0_state) - return -1; - timer0_state = 3; - return 0; + 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); } #ifdef PC98 int release_timer1() { - if (!timer1_state) - return -1; - timer1_state = 0; - outb(TIMER_MODE, TIMER_SEL1|TIMER_SQWAVE|TIMER_16BIT); - return 0; + + if (timer1_state != ACQUIRED) + return (-1); + timer1_state = RELEASED; + outb(TIMER_MODE, TIMER_SEL1 | TIMER_SQWAVE | TIMER_16BIT); + return (0); } #endif int release_timer2() { - if (!timer2_state) - return -1; - timer2_state = 0; - outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT); - return 0; + + if (timer2_state != ACQUIRED) + return (-1); + timer2_state = RELEASED; + outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT); + return (0); } #ifndef PC98 @@ -367,14 +425,19 @@ DDB_printrtc(void) static int getit(void) { + u_long ef; int high, low; + ef = read_eflags(); disable_intr(); - /* select timer0 and latch counter value */ + + /* Select timer0 and latch counter value. */ outb(TIMER_MODE, TIMER_SEL0); + low = inb(TIMER_CNTR0); high = inb(TIMER_CNTR0); - enable_intr(); + + write_eflags(ef); return ((high << 8) | low); } @@ -459,33 +522,45 @@ sysbeepstop(void *chan) int sysbeep(int pitch, int period) { + int x = splclock(); + #ifdef PC98 if (acquire_timer1(TIMER_SQWAVE|TIMER_16BIT)) - return -1; + if (!beeping) { + /* Something else owns it. */ + splx(x); + return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */ + } disable_intr(); outb(0x3fdb, pitch); outb(0x3fdb, (pitch>>8)); enable_intr(); if (!beeping) { - outb(IO_PPI, (inb(IO_PPI) & 0xf7)); /* enable counter1 output to speaker */ + /* enable counter1 output to speaker */ + outb(IO_PPI, (inb(IO_PPI) & 0xf7)); beeping = period; timeout(sysbeepstop, (void *)NULL, period); } #else - if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) - return -1; + if (!beeping) { + /* Something else owns it. */ + splx(x); + return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */ + } 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 */ + /* enable counter2 output to speaker */ + outb(IO_PPI, inb(IO_PPI) | 3); beeping = period; timeout(sysbeepstop, (void *)NULL, period); } #endif - return 0; + splx(x); + return (0); } #ifndef PC98 @@ -546,7 +621,7 @@ calibrate_clocks(void) u_int count, prev_count, tot_count; int sec, start_sec, timeout; - printf("Calibrating clock(s) relative to mc146818A clock ... "); + printf("Calibrating clock(s) relative to mc146818A clock...\n"); if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) goto fail; timeout = 100000000; @@ -639,9 +714,10 @@ fail: static void set_timer_freq(u_int freq, int intr_freq) { - u_long ef; + u_long ef; ef = read_eflags(); + disable_intr(); timer_freq = freq; timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq); timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT; @@ -688,11 +764,7 @@ startrtclock() #endif #ifndef PC98 - /* - * 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); + set_timer_freq(timer_freq, hz); freq = calibrate_clocks(); #ifdef CLK_CALIBRATION_LOOP if (bootverbose) { @@ -711,7 +783,8 @@ startrtclock() delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq; if (delta < timer_freq / 100) { #ifndef CLK_USE_I8254_CALIBRATION - printf( + if (bootverbose) + printf( "CLK_USE_I8254_CALIBRATION not specified - using default frequency\n"); freq = timer_freq; #endif @@ -731,7 +804,8 @@ startrtclock() #if defined(I586_CPU) || defined(I686_CPU) #ifndef CLK_USE_I586_CALIBRATION if (i586_ctr_rate != 0) { - printf( + if (bootverbose) + printf( "CLK_USE_I586_CALIBRATION not specified - using old calibration method\n"); i586_ctr_freq = 0; i586_ctr_rate = 0; @@ -750,7 +824,9 @@ startrtclock() DELAY(1000000); i586_count = rdtsc(); i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000; +#ifdef CLK_USE_I586_CALIBRATION printf("i586 clock: %u Hz\n", i586_ctr_freq); +#endif } #endif } |