svn commit: r212778 - head/sys/x86/isa

Alexander Motin mav at FreeBSD.org
Fri Sep 17 04:48:51 UTC 2010


Author: mav
Date: Fri Sep 17 04:48:50 2010
New Revision: 212778
URL: http://svn.freebsd.org/changeset/base/212778

Log:
  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.

Modified:
  head/sys/x86/isa/clock.c

Modified: head/sys/x86/isa/clock.c
==============================================================================
--- head/sys/x86/isa/clock.c	Fri Sep 17 02:20:12 2010	(r212777)
+++ head/sys/x86/isa/clock.c	Fri Sep 17 04:48:50 2010	(r212778)
@@ -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 
 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
 	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 *
 	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;


More information about the svn-src-head mailing list