summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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