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:
Alexander Motin 2010-07-20 10:58:56 +00:00
parent 2833a73148
commit 51636352b6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=210290
7 changed files with 90 additions and 17 deletions

View File

@ -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];

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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()];
/*