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.
This commit is contained in:
parent
2833a73148
commit
51636352b6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=210290
@ -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];
|
||||
|
@ -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 @@ configtimer(int i)
|
||||
#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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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()];
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user