summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2010-07-20 10:58:56 +0000
committermav <mav@FreeBSD.org>2010-07-20 10:58:56 +0000
commit1021ed9c1fb242e77973084e90e83ad8f307080a (patch)
treea2ade1a810689c7741fce55a1e8c698c5abacbdc /sys
parent19bed51cff8dcac07d39993556ad270818a248cd (diff)
downloadFreeBSD-src-1021ed9c1fb242e77973084e90e83ad8f307080a.zip
FreeBSD-src-1021ed9c1fb242e77973084e90e83ad8f307080a.tar.gz
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.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/acpica/acpi_hpet.c16
-rw-r--r--sys/kern/kern_clocksource.c46
-rw-r--r--sys/kern/kern_et.c20
-rw-r--r--sys/sys/timeet.h3
-rw-r--r--sys/x86/isa/atrtc.c6
-rw-r--r--sys/x86/isa/clock.c5
-rw-r--r--sys/x86/x86/local_apic.c11
7 files changed, 90 insertions, 17 deletions
diff --git a/sys/dev/acpica/acpi_hpet.c b/sys/dev/acpica/acpi_hpet.c
index 95f2b32..a789418 100644
--- a/sys/dev/acpica/acpi_hpet.c
+++ b/sys/dev/acpica/acpi_hpet.c
@@ -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];
diff --git a/sys/kern/kern_clocksource.c b/sys/kern/kern_clocksource.c
index 697275a..9656db6 100644
--- a/sys/kern/kern_clocksource.c
+++ b/sys/kern/kern_clocksource.c
@@ -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);
diff --git a/sys/kern/kern_et.c b/sys/kern/kern_et.c
index 52fb705..61a2500 100644
--- a/sys/kern/kern_et.c
+++ b/sys/kern/kern_et.c
@@ -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);
diff --git a/sys/sys/timeet.h b/sys/sys/timeet.h
index bafa55a..bc713d6 100644
--- a/sys/sys/timeet.h
+++ b/sys/sys/timeet.h
@@ -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;
diff --git a/sys/x86/isa/atrtc.c b/sys/x86/isa/atrtc.c
index 5a5ba63..b01e933 100644
--- a/sys/x86/isa/atrtc.c
+++ b/sys/x86/isa/atrtc.c
@@ -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;
diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c
index 6b5fede..62869a6 100644
--- a/sys/x86/isa/clock.c
+++ b/sys/x86/isa/clock.c
@@ -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;
diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c
index 4364fb1..ffa34a5 100644
--- a/sys/x86/x86/local_apic.c
+++ b/sys/x86/x86/local_apic.c
@@ -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()];
/*
OpenPOWER on IntegriCloud