summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2010-09-17 04:48:50 +0000
committermav <mav@FreeBSD.org>2010-09-17 04:48:50 +0000
commit0f4b390682ca45c998f8e307bb037c7b092c6e84 (patch)
tree7dc5422462e044dedf1dd67e156d345dd0dbe837 /sys/x86
parentf2848f933e7bedfa20b56e0a413cf93b75480123 (diff)
downloadFreeBSD-src-0f4b390682ca45c998f8e307bb037c7b092c6e84.zip
FreeBSD-src-0f4b390682ca45c998f8e307bb037c7b092c6e84.tar.gz
Add one-shot mode support to attimer (i8254) event timer.
Unluckily, using one-shot mode is impossible, when same hardware used for time counting. Introduce new tunable hint.attimer.0.timecounter, setting which to 0 disables i8254 time counter and allows one-shot mode. Note, that on some systems there may be no other reliable enough time counters, so this tunable should be used with understanding.
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/isa/clock.c115
1 files changed, 71 insertions, 44 deletions
diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c
index 2c0e788..4fb41d8 100644
--- a/sys/x86/isa/clock.c
+++ b/sys/x86/isa/clock.c
@@ -95,7 +95,7 @@ int clkintr_pending;
u_int i8254_freq = TIMER_FREQ;
TUNABLE_INT("hw.i8254.freq", &i8254_freq);
int i8254_max_count;
-static int i8254_real_max_count;
+static int i8254_timecounter = 1;
struct mtx clock_lock;
static struct intsrc *i8254_intsrc;
@@ -116,7 +116,11 @@ struct attimer_softc {
void *intr_handler;
struct timecounter tc;
struct eventtimer et;
- uint32_t intr_period;
+ int mode;
+#define MODE_STOP 0
+#define MODE_PERIODIC 1
+#define MODE_ONESHOT 2
+ uint32_t period;
};
static struct attimer_softc *attimer_sc = NULL;
@@ -129,14 +133,14 @@ static struct attimer_softc *attimer_sc = NULL;
static u_char timer2_state;
static unsigned i8254_get_timecount(struct timecounter *tc);
-static void set_i8254_freq(u_int freq, uint32_t intr_period);
+static void set_i8254_freq(int mode, uint32_t period);
static int
clkintr(void *arg)
{
struct attimer_softc *sc = (struct attimer_softc *)arg;
- if (sc->intr_period != 0) {
+ if (i8254_timecounter && sc->period != 0) {
mtx_lock_spin(&clock_lock);
if (i8254_ticked)
i8254_ticked = 0;
@@ -148,7 +152,7 @@ clkintr(void *arg)
mtx_unlock_spin(&clock_lock);
}
- if (sc && sc->et.et_active)
+ if (sc && sc->et.et_active && sc->mode != MODE_STOP)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
#ifdef DEV_MCA
@@ -361,27 +365,37 @@ DELAY(int n)
}
static void
-set_i8254_freq(u_int freq, uint32_t intr_period)
+set_i8254_freq(int mode, uint32_t period)
{
- int new_i8254_real_max_count;
+ int val;
mtx_lock_spin(&clock_lock);
- i8254_freq = freq;
- if (intr_period == 0)
- new_i8254_real_max_count = 0x10000;
- else {
- new_i8254_real_max_count =
- min(((uint64_t)i8254_freq * intr_period) >> 32, 0x10000);
- }
- if (new_i8254_real_max_count != i8254_real_max_count) {
- i8254_real_max_count = new_i8254_real_max_count;
- if (i8254_real_max_count == 0x10000)
- i8254_max_count = 0xffff;
- else
- i8254_max_count = i8254_real_max_count;
+ if (period == 0)
+ val = 0x10000;
+ else
+ val = min(((uint64_t)i8254_freq * period) >> 32, 0x10000);
+ if (val == 0x10000)
+ i8254_max_count = 0xffff;
+ else
+ i8254_max_count = val;
+ if (mode == MODE_STOP && i8254_timecounter)
+ mode = MODE_PERIODIC;
+ switch (mode) {
+ case MODE_STOP:
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_INTTC | TIMER_16BIT);
+ outb(TIMER_CNTR0, 0xff);
+ outb(TIMER_CNTR0, 0xff);
+ break;
+ case MODE_PERIODIC:
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, i8254_real_max_count & 0xff);
- outb(TIMER_CNTR0, i8254_real_max_count >> 8);
+ outb(TIMER_CNTR0, val & 0xff);
+ outb(TIMER_CNTR0, val >> 8);
+ break;
+ case MODE_ONESHOT:
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_INTTC | TIMER_16BIT);
+ outb(TIMER_CNTR0, val & 0xff);
+ outb(TIMER_CNTR0, val >> 8);
+ break;
}
mtx_unlock_spin(&clock_lock);
}
@@ -390,11 +404,10 @@ static void
i8254_restore(void)
{
- mtx_lock_spin(&clock_lock);
- outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR0, i8254_real_max_count & 0xff);
- outb(TIMER_CNTR0, i8254_real_max_count >> 8);
- mtx_unlock_spin(&clock_lock);
+ if (attimer_sc)
+ set_i8254_freq(attimer_sc->mode, attimer_sc->period);
+ else
+ set_i8254_freq(0, 0);
}
#ifndef __amd64__
@@ -428,7 +441,7 @@ i8254_init(void)
if (pc98_machine_type & M_8M)
i8254_freq = 1996800L; /* 1.9968 MHz */
#endif
- set_i8254_freq(i8254_freq, 0);
+ set_i8254_freq(0, 0);
}
void
@@ -459,11 +472,12 @@ sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS)
freq = i8254_freq;
error = sysctl_handle_int(oidp, &freq, 0, req);
if (error == 0 && req->newptr != NULL) {
+ i8254_freq = freq;
if (attimer_sc) {
- set_i8254_freq(freq, attimer_sc->intr_period);
+ set_i8254_freq(attimer_sc->mode, attimer_sc->period);
attimer_sc->tc.tc_frequency = freq;
} else {
- set_i8254_freq(freq, 0);
+ set_i8254_freq(0, 0);
}
}
return (error);
@@ -481,7 +495,7 @@ i8254_get_timecount(struct timecounter *tc)
uint16_t count;
u_int high, low;
- if (sc->intr_period == 0)
+ if (sc->period == 0)
return (i8254_max_count - getit());
#ifdef __amd64__
@@ -517,13 +531,19 @@ attimer_start(struct eventtimer *et,
{
device_t dev = (device_t)et->et_priv;
struct attimer_softc *sc = device_get_softc(dev);
-
- sc->intr_period = period->frac >> 32;
- set_i8254_freq(i8254_freq, sc->intr_period);
+
+ if (period != NULL) {
+ sc->mode = MODE_PERIODIC;
+ sc->period = period->frac >> 32;
+ } else {
+ sc->mode = MODE_ONESHOT;
+ sc->period = first->frac >> 32;
+ }
if (!sc->intr_en) {
i8254_intsrc->is_pic->pic_enable_source(i8254_intsrc);
sc->intr_en = 1;
}
+ set_i8254_freq(sc->mode, sc->period);
return (0);
}
@@ -533,8 +553,9 @@ attimer_stop(struct eventtimer *et)
device_t dev = (device_t)et->et_priv;
struct attimer_softc *sc = device_get_softc(dev);
- sc->intr_period = 0;
- set_i8254_freq(i8254_freq, sc->intr_period);
+ sc->mode = MODE_STOP;
+ sc->period = 0;
+ set_i8254_freq(sc->mode, sc->period);
return (0);
}
@@ -630,14 +651,18 @@ attimer_attach(device_t dev)
i8254_intsrc = intr_lookup_source(0);
if (i8254_intsrc != NULL)
i8254_pending = i8254_intsrc->is_pic->pic_source_pending;
- set_i8254_freq(i8254_freq, 0);
- sc->tc.tc_get_timecount = i8254_get_timecount;
- sc->tc.tc_counter_mask = 0xffff;
- sc->tc.tc_frequency = i8254_freq;
- sc->tc.tc_name = "i8254";
- sc->tc.tc_quality = 0;
- sc->tc.tc_priv = dev;
- tc_init(&sc->tc);
+ resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "timecounter", &i8254_timecounter);
+ set_i8254_freq(0, 0);
+ if (i8254_timecounter) {
+ sc->tc.tc_get_timecount = i8254_get_timecount;
+ sc->tc.tc_counter_mask = 0xffff;
+ sc->tc.tc_frequency = i8254_freq;
+ sc->tc.tc_name = "i8254";
+ sc->tc.tc_quality = 0;
+ sc->tc.tc_priv = dev;
+ tc_init(&sc->tc);
+ }
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
"clock", &i) != 0 || i != 0) {
sc->intr_rid = 0;
@@ -663,6 +688,8 @@ attimer_attach(device_t dev)
i8254_intsrc->is_pic->pic_enable_intr(i8254_intsrc);
sc->et.et_name = "i8254";
sc->et.et_flags = ET_FLAGS_PERIODIC;
+ if (!i8254_timecounter)
+ sc->et.et_flags |= ET_FLAGS_ONESHOT;
sc->et.et_quality = 100;
sc->et.et_frequency = i8254_freq;
sc->et.et_min_period.sec = 0;
OpenPOWER on IntegriCloud