summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/pc98/cbus/clock.c147
-rw-r--r--sys/pc98/cbus/pcrtc.c147
-rw-r--r--sys/pc98/pc98/clock.c147
3 files changed, 438 insertions, 3 deletions
diff --git a/sys/pc98/cbus/clock.c b/sys/pc98/cbus/clock.c
index bc4cf6b..3724de0 100644
--- a/sys/pc98/cbus/clock.c
+++ b/sys/pc98/cbus/clock.c
@@ -111,6 +111,23 @@ static void setup_8254_mixed_mode(void);
#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
+#ifndef BURN_BRIDGES
+/*
+ * 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
+
+/*
+ * 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
+#endif
int adjkerntz; /* local offset from GMT in seconds */
int clkintr_pending;
@@ -132,7 +149,18 @@ static u_int hardclock_max_count;
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
-
+#ifndef BURN_BRIDGES
+/*
+ * 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_int timer0_prescaler_count;
+static u_char timer0_state;
+#endif
/* Values for timerX_state: */
#define RELEASED 0
@@ -180,8 +208,99 @@ clkintr(struct clockframe frame)
if (timer_func == hardclock)
forward_hardclock();
#endif
+#ifndef BURN_BRIDGES
+ switch (timer0_state) {
+
+ case RELEASED:
+ break;
+
+ case ACQUIRED:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ timer0_prescaler_count -= hardclock_max_count;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+
+ case ACQUIRE_PENDING:
+ mtx_lock_spin(&clock_lock);
+ i8254_offset = i8254_get_timecount(NULL);
+ i8254_lastcount = 0;
+ timer0_max_count = TIMER_DIV(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);
+ mtx_unlock_spin(&clock_lock);
+ timer_func = new_function;
+ timer0_state = ACQUIRED;
+ break;
+
+ case RELEASE_PENDING:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ mtx_lock_spin(&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);
+ mtx_unlock_spin(&clock_lock);
+ timer0_prescaler_count = 0;
+ timer_func = hardclock;
+ timer0_state = RELEASED;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+ }
+#endif
}
+#ifndef BURN_BRIDGES
+/*
+ * The acquire and release functions must be called at ipl >= splclock().
+ */
+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);
+}
+#endif
+
int
acquire_timer1(int mode)
{
@@ -222,6 +341,28 @@ acquire_timer2(int mode)
return (0);
}
+#ifndef BURN_BRIDGES
+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_timer1()
{
@@ -937,6 +1078,10 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
freq = timer_freq;
error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
if (error == 0 && req->newptr != NULL) {
+#ifndef BURN_BRIDGES
+ if (timer0_state != RELEASED)
+ return (EBUSY); /* too much trouble to handle */
+#endif
set_timer_freq(freq, hz);
i8254_timecounter.tc_frequency = freq;
}
diff --git a/sys/pc98/cbus/pcrtc.c b/sys/pc98/cbus/pcrtc.c
index bc4cf6b..3724de0 100644
--- a/sys/pc98/cbus/pcrtc.c
+++ b/sys/pc98/cbus/pcrtc.c
@@ -111,6 +111,23 @@ static void setup_8254_mixed_mode(void);
#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
+#ifndef BURN_BRIDGES
+/*
+ * 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
+
+/*
+ * 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
+#endif
int adjkerntz; /* local offset from GMT in seconds */
int clkintr_pending;
@@ -132,7 +149,18 @@ static u_int hardclock_max_count;
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
-
+#ifndef BURN_BRIDGES
+/*
+ * 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_int timer0_prescaler_count;
+static u_char timer0_state;
+#endif
/* Values for timerX_state: */
#define RELEASED 0
@@ -180,8 +208,99 @@ clkintr(struct clockframe frame)
if (timer_func == hardclock)
forward_hardclock();
#endif
+#ifndef BURN_BRIDGES
+ switch (timer0_state) {
+
+ case RELEASED:
+ break;
+
+ case ACQUIRED:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ timer0_prescaler_count -= hardclock_max_count;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+
+ case ACQUIRE_PENDING:
+ mtx_lock_spin(&clock_lock);
+ i8254_offset = i8254_get_timecount(NULL);
+ i8254_lastcount = 0;
+ timer0_max_count = TIMER_DIV(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);
+ mtx_unlock_spin(&clock_lock);
+ timer_func = new_function;
+ timer0_state = ACQUIRED;
+ break;
+
+ case RELEASE_PENDING:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ mtx_lock_spin(&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);
+ mtx_unlock_spin(&clock_lock);
+ timer0_prescaler_count = 0;
+ timer_func = hardclock;
+ timer0_state = RELEASED;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+ }
+#endif
}
+#ifndef BURN_BRIDGES
+/*
+ * The acquire and release functions must be called at ipl >= splclock().
+ */
+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);
+}
+#endif
+
int
acquire_timer1(int mode)
{
@@ -222,6 +341,28 @@ acquire_timer2(int mode)
return (0);
}
+#ifndef BURN_BRIDGES
+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_timer1()
{
@@ -937,6 +1078,10 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
freq = timer_freq;
error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
if (error == 0 && req->newptr != NULL) {
+#ifndef BURN_BRIDGES
+ if (timer0_state != RELEASED)
+ return (EBUSY); /* too much trouble to handle */
+#endif
set_timer_freq(freq, hz);
i8254_timecounter.tc_frequency = freq;
}
diff --git a/sys/pc98/pc98/clock.c b/sys/pc98/pc98/clock.c
index bc4cf6b..3724de0 100644
--- a/sys/pc98/pc98/clock.c
+++ b/sys/pc98/pc98/clock.c
@@ -111,6 +111,23 @@ static void setup_8254_mixed_mode(void);
#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
+#ifndef BURN_BRIDGES
+/*
+ * 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
+
+/*
+ * 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
+#endif
int adjkerntz; /* local offset from GMT in seconds */
int clkintr_pending;
@@ -132,7 +149,18 @@ static u_int hardclock_max_count;
static u_int32_t i8254_lastcount;
static u_int32_t i8254_offset;
static int i8254_ticked;
-
+#ifndef BURN_BRIDGES
+/*
+ * 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_int timer0_prescaler_count;
+static u_char timer0_state;
+#endif
/* Values for timerX_state: */
#define RELEASED 0
@@ -180,8 +208,99 @@ clkintr(struct clockframe frame)
if (timer_func == hardclock)
forward_hardclock();
#endif
+#ifndef BURN_BRIDGES
+ switch (timer0_state) {
+
+ case RELEASED:
+ break;
+
+ case ACQUIRED:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ timer0_prescaler_count -= hardclock_max_count;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+
+ case ACQUIRE_PENDING:
+ mtx_lock_spin(&clock_lock);
+ i8254_offset = i8254_get_timecount(NULL);
+ i8254_lastcount = 0;
+ timer0_max_count = TIMER_DIV(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);
+ mtx_unlock_spin(&clock_lock);
+ timer_func = new_function;
+ timer0_state = ACQUIRED;
+ break;
+
+ case RELEASE_PENDING:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ mtx_lock_spin(&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);
+ mtx_unlock_spin(&clock_lock);
+ timer0_prescaler_count = 0;
+ timer_func = hardclock;
+ timer0_state = RELEASED;
+ hardclock(&frame);
+#ifdef SMP
+ forward_hardclock();
+#endif
+ }
+ break;
+ }
+#endif
}
+#ifndef BURN_BRIDGES
+/*
+ * The acquire and release functions must be called at ipl >= splclock().
+ */
+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);
+}
+#endif
+
int
acquire_timer1(int mode)
{
@@ -222,6 +341,28 @@ acquire_timer2(int mode)
return (0);
}
+#ifndef BURN_BRIDGES
+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_timer1()
{
@@ -937,6 +1078,10 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
freq = timer_freq;
error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
if (error == 0 && req->newptr != NULL) {
+#ifndef BURN_BRIDGES
+ if (timer0_state != RELEASED)
+ return (EBUSY); /* too much trouble to handle */
+#endif
set_timer_freq(freq, hz);
i8254_timecounter.tc_frequency = freq;
}
OpenPOWER on IntegriCloud