summaryrefslogtreecommitdiffstats
path: root/sys/pc98/cbus/pcrtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/pc98/cbus/pcrtc.c')
-rw-r--r--sys/pc98/cbus/pcrtc.c264
1 files changed, 170 insertions, 94 deletions
diff --git a/sys/pc98/cbus/pcrtc.c b/sys/pc98/cbus/pcrtc.c
index b2befbf..071650c 100644
--- a/sys/pc98/cbus/pcrtc.c
+++ b/sys/pc98/cbus/pcrtc.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
}
OpenPOWER on IntegriCloud