diff options
author | jkim <jkim@FreeBSD.org> | 2011-04-12 21:08:34 +0000 |
---|---|---|
committer | jkim <jkim@FreeBSD.org> | 2011-04-12 21:08:34 +0000 |
commit | 76647eca4db3af964b009762417db77486718a6c (patch) | |
tree | e21e442df5036b6cd8f1089e553934aa1ef08eb5 /sys/x86 | |
parent | 93b6f3ee5d39b3d66ed1d08b60349a8e3094dc0e (diff) | |
download | FreeBSD-src-76647eca4db3af964b009762417db77486718a6c.zip FreeBSD-src-76647eca4db3af964b009762417db77486718a6c.tar.gz |
Add a new tunable 'machdep.disable_tsc_calibration' to allow skipping TSC
frequency calibration. For Intel processors, if brand string from CPUID
contains its nominal frequency, this frequency is used instead.
Diffstat (limited to 'sys/x86')
-rw-r--r-- | sys/x86/x86/tsc.c | 109 |
1 files changed, 93 insertions, 16 deletions
diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c index 2b1c89a..be691f9e 100644 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -67,6 +67,11 @@ SYSCTL_INT(_machdep, OID_AUTO, disable_tsc, CTLFLAG_RDTUN, &tsc_disabled, 0, "Disable x86 Time Stamp Counter"); TUNABLE_INT("machdep.disable_tsc", &tsc_disabled); +static int tsc_skip_calibration; +SYSCTL_INT(_machdep, OID_AUTO, disable_tsc_calibration, CTLFLAG_RDTUN, + &tsc_skip_calibration, 0, "Disable TSC frequency calibration"); +TUNABLE_INT("machdep.disable_tsc_calibration", &tsc_skip_calibration); + static void tsc_freq_changed(void *arg, const struct cf_level *level, int status); static void tsc_freq_changing(void *arg, const struct cf_level *level, @@ -83,24 +88,70 @@ static struct timecounter tsc_timecounter = { 800, /* quality (adjusted in code) */ }; -void -init_TSC(void) +static void +tsc_freq_intel(void) { - u_int64_t tscval[2]; - - if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) - return; - - if (bootverbose) - printf("Calibrating TSC clock ... "); + char brand[48]; + u_int regs[4]; + uint64_t freq; + char *p; + u_int i; - tscval[0] = rdtsc(); - DELAY(1000000); - tscval[1] = rdtsc(); + /* + * Intel Processor Identification and the CPUID Instruction + * Application Note 485. + * + * http://www.intel.com/assets/pdf/appnote/241618.pdf + */ + if (cpu_exthigh >= 0x80000004) { + p = brand; + for (i = 0x80000002; i < 0x80000005; i++) { + do_cpuid(i, regs); + memcpy(p, regs, sizeof(regs)); + p += sizeof(regs); + } + p = NULL; + for (i = 0; i < sizeof(brand) - 1; i++) + if (brand[i] == 'H' && brand[i + 1] == 'z') + p = brand + i; + if (p != NULL) { + p -= 5; + switch (p[4]) { + case 'M': + i = 1; + break; + case 'G': + i = 1000; + break; + case 'T': + i = 1000000; + break; + default: + return; + } +#define C2D(c) ((c) - '0') + if (p[1] == '.') { + freq = C2D(p[0]) * 1000; + freq += C2D(p[2]) * 100; + freq += C2D(p[3]) * 10; + freq *= i * 1000; + } else { + freq = C2D(p[0]) * 1000; + freq += C2D(p[1]) * 100; + freq += C2D(p[2]) * 10; + freq += C2D(p[3]); + freq *= i * 1000000; + } +#undef C2D + tsc_freq = freq; + } + } +} - tsc_freq = tscval[1] - tscval[0]; - if (bootverbose) - printf("TSC clock: %ju Hz\n", (intmax_t)tsc_freq); +static void +probe_tsc_freq(void) +{ + uint64_t tsc1, tsc2; switch (cpu_vendor_id) { case CPU_VENDOR_AMD: @@ -127,12 +178,38 @@ init_TSC(void) break; } + if (tsc_skip_calibration) { + if (cpu_vendor_id == CPU_VENDOR_INTEL) + tsc_freq_intel(); + return; + } + + if (bootverbose) + printf("Calibrating TSC clock ... "); + tsc1 = rdtsc(); + DELAY(1000000); + tsc2 = rdtsc(); + tsc_freq = tsc2 - tsc1; + if (bootverbose) + printf("TSC clock: %ju Hz\n", (intmax_t)tsc_freq); +} + +void +init_TSC(void) +{ + + if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) + return; + + probe_tsc_freq(); + /* * Inform CPU accounting about our boot-time clock rate. This will * be updated if someone loads a cpufreq driver after boot that * discovers a new max frequency. */ - set_cputicker(rdtsc, tsc_freq, 1); + if (tsc_freq != 0) + set_cputicker(rdtsc, tsc_freq, 1); if (tsc_is_invariant) return; |