Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Jul 2010 10:58:56 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r210290 - in head/sys: dev/acpica kern sys x86/isa x86/x86
Message-ID:  <201007201058.o6KAwuUE073270@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Tue Jul 20 10:58:56 2010
New Revision: 210290
URL: http://svn.freebsd.org/changeset/base/210290

Log:
  Extend timer driver API to report also minimal and maximal supported period
  lengths. Make MI wrapper code to validate periods in request. Make kernel
  clock management code to honor these hardware limitations while choosing hz,
  stathz and profhz values.

Modified:
  head/sys/dev/acpica/acpi_hpet.c
  head/sys/kern/kern_clocksource.c
  head/sys/kern/kern_et.c
  head/sys/sys/timeet.h
  head/sys/x86/isa/atrtc.c
  head/sys/x86/isa/clock.c
  head/sys/x86/x86/local_apic.c

Modified: head/sys/dev/acpica/acpi_hpet.c
==============================================================================
--- head/sys/dev/acpica/acpi_hpet.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/dev/acpica/acpi_hpet.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -154,15 +154,16 @@ hpet_start(struct eventtimer *et,
 		t->div = (sc->freq * (period->frac >> 32)) >> 32;
 		if (period->sec != 0)
 			t->div += sc->freq * period->sec;
-		if (first == NULL)
-			first = period;
 	} else {
 		t->mode = 2;
 		t->div = 0;
 	}
-	fdiv = (sc->freq * (first->frac >> 32)) >> 32;
-	if (first->sec != 0)
-		fdiv += sc->freq * first->sec;
+	if (first != NULL) {
+		fdiv = (sc->freq * (first->frac >> 32)) >> 32;
+		if (first->sec != 0)
+			fdiv += sc->freq * first->sec;
+	} else
+		fdiv = t->div;
 	t->last = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER);
 	if (t->mode == 1 && (t->caps & HPET_TCAP_PER_INT)) {
 		t->caps |= HPET_TCNF_TYPE;
@@ -583,6 +584,11 @@ hpet_attach(device_t dev)
 		if ((t->caps & HPET_TCAP_PER_INT) == 0)
 			t->et.et_quality -= 10;
 		t->et.et_frequency = sc->freq;
+		t->et.et_min_period.sec = 0;
+		t->et.et_min_period.frac = 0x00004000LL << 32;
+		t->et.et_max_period.sec = 0xffffffff / sc->freq;
+		t->et.et_max_period.frac =
+		    ((0xffffffffLL << 32) / sc->freq) << 32;
 		t->et.et_start = hpet_start;
 		t->et.et_stop = hpet_stop;
 		t->et.et_priv = &sc->t[i];

Modified: head/sys/kern/kern_clocksource.c
==============================================================================
--- head/sys/kern/kern_clocksource.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/kern/kern_clocksource.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -87,11 +87,9 @@ static DPCPU_DEFINE(tc, configtimer);
 	(bt)->sec = 0;							\
 	(bt)->frac = ((uint64_t)0x8000000000000000  / (freq)) << 1;	\
 }
-#define BT2FREQ(bt, freq)						\
-{									\
-	*(freq) = ((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) /	\
-		    ((bt)->frac >> 1);					\
-}
+#define BT2FREQ(bt)							\
+	(((uint64_t)0x8000000000000000 + ((bt)->frac >> 2)) /		\
+	    ((bt)->frac >> 1))
 
 /* Per-CPU timer1 handler. */
 static int
@@ -295,6 +293,26 @@ restart:
 #endif
 }
 
+static int
+round_freq(struct eventtimer *et, int freq)
+{
+	uint64_t div;
+
+	if (et->et_frequency != 0) {
+		div = (et->et_frequency + freq / 2) / freq;
+		if (et->et_flags & ET_FLAGS_POW2DIV)
+			div = 1 << (flsl(div + div / 2) - 1);
+		freq = (et->et_frequency + div / 2) / div;
+	}
+	if (et->et_min_period.sec > 0)
+		freq = 0;
+	else if (et->et_max_period.frac != 0)
+		freq = min(freq, BT2FREQ(&et->et_min_period));
+	if (et->et_max_period.sec == 0 && et->et_max_period.frac != 0)
+		freq = max(freq, BT2FREQ(&et->et_max_period));
+	return (freq);
+}
+
 /*
  * Configure and start event timers.
  */
@@ -327,21 +345,25 @@ cpu_initclocks_bsp(void)
 			singlemul = 4;
 	}
 	if (timer[1] == NULL) {
-		base = hz * singlemul;
-		if (base < 128)
+		base = round_freq(timer[0], hz * singlemul);
+		singlemul = max((base + hz / 2) / hz, 1);
+		hz = (base + singlemul / 2) / singlemul;
+		if (base <= 128)
 			stathz = base;
 		else {
 			div = base / 128;
-			if (div % 2 == 0)
+			if (div >= singlemul && (div % singlemul) == 0)
 				div++;
 			stathz = base / div;
 		}
 		profhz = stathz;
-		while ((profhz + stathz) <= 8192)
+		while ((profhz + stathz) <= 128 * 64)
 			profhz += stathz;
+		profhz = round_freq(timer[0], profhz);
 	} else {
-		stathz = 128;
-		profhz = stathz * 64;
+		hz = round_freq(timer[0], hz);
+		stathz = round_freq(timer[1], 127);
+		profhz = round_freq(timer[1], stathz * 64);
 	}
 	ET_LOCK();
 	cpu_restartclocks();
@@ -385,7 +407,9 @@ cpu_restartclocks(void)
 	} else {
 		timer1hz = hz;
 		timer2hz = profiling_on ? profhz : stathz;
+		timer2hz = round_freq(timer[1], timer2hz);
 	}
+	timer1hz = round_freq(timer[0], timer1hz);
 	printf("Starting kernel event timers: %s @ %dHz, %s @ %dHz\n",
 	    timer[0]->et_name, timer1hz,
 	    timer[1] ? timer[1]->et_name : "NONE", timer2hz);

Modified: head/sys/kern/kern_et.c
==============================================================================
--- head/sys/kern/kern_et.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/kern/kern_et.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -167,6 +167,26 @@ et_start(struct eventtimer *et,
 	if ((et->et_flags & ET_FLAGS_ONESHOT) == 0 &&
 	    period == NULL)
 		return (ENODEV);
+	if (first != NULL) {
+		if (first->sec < et->et_min_period.sec ||
+		    (first->sec == et->et_min_period.sec &&
+		     first->frac < et->et_min_period.frac))
+		        first = &et->et_min_period;
+		if (first->sec > et->et_max_period.sec ||
+		    (first->sec == et->et_max_period.sec &&
+		     first->frac > et->et_max_period.frac))
+		        first = &et->et_max_period;
+	}
+	if (period != NULL) {
+		if (period->sec < et->et_min_period.sec ||
+		    (period->sec == et->et_min_period.sec &&
+		     period->frac < et->et_min_period.frac))
+		        period = &et->et_min_period;
+		if (period->sec > et->et_max_period.sec ||
+		    (period->sec == et->et_max_period.sec &&
+		     period->frac > et->et_max_period.frac))
+		        period = &et->et_max_period;
+	}
 	if (et->et_start)
 		return (et->et_start(et, first, period));
 	return (0);

Modified: head/sys/sys/timeet.h
==============================================================================
--- head/sys/sys/timeet.h	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/sys/timeet.h	Tue Jul 20 10:58:56 2010	(r210290)
@@ -61,6 +61,7 @@ struct eventtimer {
 #define ET_FLAGS_ONESHOT	2
 #define ET_FLAGS_PERCPU		4
 #define ET_FLAGS_C3STOP		8
+#define ET_FLAGS_POW2DIV	16
 	int			et_quality;
 		/*
 		 * Used to determine if this timecounter is better than
@@ -69,6 +70,8 @@ struct eventtimer {
 	int			et_active;
 	u_int64_t		et_frequency;
 		/* Base frequency in Hz. */
+	struct bintime		et_min_period;
+	struct bintime		et_max_period;
 	et_start_t		*et_start;
 	et_stop_t		*et_stop;
 	et_event_cb_t		*et_event_cb;

Modified: head/sys/x86/isa/atrtc.c
==============================================================================
--- head/sys/x86/isa/atrtc.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/x86/isa/atrtc.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -276,9 +276,13 @@ atrtc_attach(device_t dev)
 			bus_bind_intr(dev, sc->intr_res, 0);
 		}
 		sc->et.et_name = "RTC";
-		sc->et.et_flags = ET_FLAGS_PERIODIC;
+		sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_POW2DIV;
 		sc->et.et_quality = 0;
 		sc->et.et_frequency = 32768;
+		sc->et.et_min_period.sec = 0;
+		sc->et.et_min_period.frac = 0x0008LL << 48;
+		sc->et.et_max_period.sec = 0;
+		sc->et.et_max_period.frac = 0x8000LL << 48;
 		sc->et.et_start = rtc_start;
 		sc->et.et_stop = rtc_stop;
 		sc->et.et_priv = dev;

Modified: head/sys/x86/isa/clock.c
==============================================================================
--- head/sys/x86/isa/clock.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/x86/isa/clock.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -665,6 +665,11 @@ attimer_attach(device_t dev)
 		sc->et.et_flags = ET_FLAGS_PERIODIC;
 		sc->et.et_quality = 100;
 		sc->et.et_frequency = i8254_freq;
+		sc->et.et_min_period.sec = 0;
+		sc->et.et_min_period.frac = ((1LL << 62) / i8254_freq) << 2;
+		sc->et.et_max_period.sec = 0xffff / i8254_freq;
+		sc->et.et_max_period.frac =
+		    ((0xffffLL << 48) / i8254_freq) << 16;
 		sc->et.et_start = attimer_start;
 		sc->et.et_stop = attimer_stop;
 		sc->et.et_priv = dev;

Modified: head/sys/x86/x86/local_apic.c
==============================================================================
--- head/sys/x86/x86/local_apic.c	Tue Jul 20 10:45:05 2010	(r210289)
+++ head/sys/x86/x86/local_apic.c	Tue Jul 20 10:58:56 2010	(r210290)
@@ -263,6 +263,11 @@ lapic_init(vm_paddr_t addr)
 			lapic_et.et_quality -= 100;
 		}
 		lapic_et.et_frequency = 0;
+		/* We don't know frequency yet, so trying to guess. */
+		lapic_et.et_min_period.sec = 0;
+		lapic_et.et_min_period.frac = 0x00001000LL << 32;
+		lapic_et.et_max_period.sec = 1;
+		lapic_et.et_max_period.frac = 0;
 		lapic_et.et_start = lapic_et_start;
 		lapic_et.et_stop = lapic_et_stop;
 		lapic_et.et_priv = NULL;
@@ -493,6 +498,12 @@ lapic_et_start(struct eventtimer *et,
 			printf("lapic: Divisor %lu, Frequency %lu Hz\n",
 			    lapic_timer_divisor, value);
 		et->et_frequency = value;
+		et->et_min_period.sec = 0;
+		et->et_min_period.frac =
+		    ((1LL << 63) / et->et_frequency) << 1;
+		et->et_max_period.sec = 0xffffffff / et->et_frequency;
+		et->et_max_period.frac =
+		    ((0xffffffffLL << 32) / et->et_frequency) << 32;
 	}
 	la = &lapics[lapic_id()];
 	/*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201007201058.o6KAwuUE073270>