summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorjkim <jkim@FreeBSD.org>2011-04-12 21:08:34 +0000
committerjkim <jkim@FreeBSD.org>2011-04-12 21:08:34 +0000
commit76647eca4db3af964b009762417db77486718a6c (patch)
treee21e442df5036b6cd8f1089e553934aa1ef08eb5 /sys/x86
parent93b6f3ee5d39b3d66ed1d08b60349a8e3094dc0e (diff)
downloadFreeBSD-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.c109
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;
OpenPOWER on IntegriCloud