summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorjkim <jkim@FreeBSD.org>2011-03-14 22:05:59 +0000
committerjkim <jkim@FreeBSD.org>2011-03-14 22:05:59 +0000
commit36e15e1609c7110814c49d7ef7893cf4a66c719b (patch)
tree5a60ca33d66de9bf8dc9b38cd1b796fc7f15a76e /sys/x86
parent45a4292831f10bddd44d6ff7b7ac96253b63d03c (diff)
downloadFreeBSD-src-36e15e1609c7110814c49d7ef7893cf4a66c719b.zip
FreeBSD-src-36e15e1609c7110814c49d7ef7893cf4a66c719b.tar.gz
When TSC is unavailable, broken or disabled and the current timecounter has
better quality than i8254 timer, use it for DELAY(9).
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/isa/clock.c53
1 files changed, 43 insertions, 10 deletions
diff --git a/sys/x86/isa/clock.c b/sys/x86/isa/clock.c
index cb34eb4..f0016b2 100644
--- a/sys/x86/isa/clock.c
+++ b/sys/x86/isa/clock.c
@@ -245,6 +245,42 @@ getit(void)
return ((high << 8) | low);
}
+static __inline void
+delay_tsc(int n)
+{
+ uint64_t start, end, now;
+
+ sched_pin();
+ start = rdtsc();
+ end = start + (tsc_freq * n) / 1000000;
+ do {
+ cpu_spinwait();
+ now = rdtsc();
+ } while (now < end || (now > start && end < start));
+ sched_unpin();
+}
+
+static __inline void
+delay_timecounter(struct timecounter *tc, int n)
+{
+ uint64_t end, now;
+ u_int last, mask, u;
+
+ mask = tc->tc_counter_mask;
+ last = tc->tc_get_timecount(tc) & mask;
+ end = tc->tc_frequency * n / 1000000;
+ now = 0;
+ do {
+ cpu_spinwait();
+ u = tc->tc_get_timecount(tc) & mask;
+ if (u < last)
+ now += mask - last + u + 1;
+ else
+ now += u - last;
+ last = u;
+ } while (now < end);
+}
+
/*
* Wait "n" microseconds.
* Relies on timer 1 counting down from (i8254_freq / hz)
@@ -253,6 +289,7 @@ getit(void)
void
DELAY(int n)
{
+ struct timecounter *tc;
int delta, prev_tick, tick, ticks_left;
#ifdef DELAYDEBUG
@@ -262,16 +299,12 @@ DELAY(int n)
#endif
if (tsc_freq != 0) {
- uint64_t start, end, now;
-
- sched_pin();
- start = rdtsc();
- end = start + (tsc_freq * n) / 1000000;
- do {
- cpu_spinwait();
- now = rdtsc();
- } while (now < end || (now > start && end < start));
- sched_unpin();
+ delay_tsc(n);
+ return;
+ }
+ tc = timecounter;
+ if (tc->tc_quality > 0) {
+ delay_timecounter(tc, n);
return;
}
#ifdef DELAYDEBUG
OpenPOWER on IntegriCloud