clock/tsc: Detect invariant TSC
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 19 Jun 2013 08:37:55 +0000 (16:37 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 19 Jun 2013 08:44:38 +0000 (16:44 +0800)
According to Intel's description:
"The invariant TSC will run at a constant rate in all ACPI P-, C-. "
 and T-states. ..."

The difference between invariant TSC and constant TSC is that
invariant TSC is not affected by frequency changes and deep ACPI
C-state.

Constant TSC could be detected based on the CPU model (Intel has
the model list, while there is no information from AMD's document);
constant TSC is not detected yet.

sys/platform/pc32/include/clock.h
sys/platform/pc32/isa/clock.c
sys/platform/pc64/include/clock.h
sys/platform/pc64/isa/clock.c
sys/platform/vkernel/include/clock.h
sys/platform/vkernel/platform/init.c
sys/platform/vkernel/platform/systimer.c
sys/platform/vkernel64/include/clock.h
sys/platform/vkernel64/platform/init.c
sys/platform/vkernel64/platform/systimer.c

index e3d4586..7144a38 100644 (file)
@@ -34,6 +34,7 @@ extern int    disable_rtc_set;
 extern u_int   timer_freq;
 extern int     timer0_max_count;
 extern int     tsc_present;
+extern int     tsc_invariant;
 extern int64_t tsc_frequency;
 extern int     tsc_is_broken;
 extern int     wall_cmos_clock;
index 066731c..1aab043 100644 (file)
@@ -105,6 +105,7 @@ static uint16_t i8254_walltimer_cntr;
 int    adjkerntz;              /* local offset from GMT in seconds */
 int    disable_rtc_set;        /* disable resettodr() if != 0 */
 int    tsc_present;
+int    tsc_invariant;
 int64_t        tsc_frequency;
 int    tsc_is_broken;
 int    wall_cmos_clock;        /* wall CMOS clock assumed if != 0 */
@@ -528,7 +529,7 @@ calibrate_clocks(void)
        int sec, start_sec, timeout;
 
        if (bootverbose)
-               kprintf("Calibrating clock(s) ... ");
+               kprintf("Calibrating clock(s) ...\n");
        if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
                goto fail;
        timeout = 100000000;
@@ -595,8 +596,11 @@ calibrate_clocks(void)
                tsc_frequency = rdtsc() - old_tsc;
        }
 
-       if (tsc_present)
-               kprintf("TSC clock: %llu Hz, ", tsc_frequency);
+       if (tsc_present) {
+               kprintf("TSC%s clock: %llu Hz, ",
+                   tsc_invariant ? " invariant" : "",
+                   (long long)tsc_frequency);
+       }
        kprintf("i8254 clock: %u Hz\n", tot_count);
        return (tot_count);
 
@@ -729,10 +733,20 @@ startrtclock(void)
        /* 
         * Can we use the TSC?
         */
-       if (cpu_feature & CPUID_TSC)
+       if (cpu_feature & CPUID_TSC) {
                tsc_present = 1;
-       else
+               if ((cpu_vendor_id == CPU_VENDOR_INTEL ||
+                    cpu_vendor_id == CPU_VENDOR_AMD) &&
+                   cpu_exthigh >= 0x80000007) {
+                       u_int regs[4];
+
+                       do_cpuid(0x80000007, regs);
+                       if (regs[3] & 0x100)
+                               tsc_invariant = 1;
+               }
+       } else {
                tsc_present = 0;
+       }
 
        /*
         * Initial RTC state, don't do anything unexpected
@@ -1160,6 +1174,7 @@ SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD,
 
 SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD,
            &tsc_present, 0, "TSC Available");
+SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD,
+           &tsc_invariant, 0, "Invariant TSC");
 SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD,
            &tsc_frequency, 0, "TSC Frequency");
-
index e3d4586..7144a38 100644 (file)
@@ -34,6 +34,7 @@ extern int    disable_rtc_set;
 extern u_int   timer_freq;
 extern int     timer0_max_count;
 extern int     tsc_present;
+extern int     tsc_invariant;
 extern int64_t tsc_frequency;
 extern int     tsc_is_broken;
 extern int     wall_cmos_clock;
index 9d7b750..933962d 100644 (file)
@@ -107,6 +107,7 @@ static uint16_t i8254_walltimer_cntr;
 int    adjkerntz;              /* local offset from GMT in seconds */
 int    disable_rtc_set;        /* disable resettodr() if != 0 */
 int    tsc_present;
+int    tsc_invariant;
 int64_t        tsc_frequency;
 int    tsc_is_broken;
 int    wall_cmos_clock;        /* wall CMOS clock assumed if != 0 */
@@ -535,7 +536,7 @@ calibrate_clocks(void)
        int sec, start_sec, timeout;
 
        if (bootverbose)
-               kprintf("Calibrating clock(s) ... ");
+               kprintf("Calibrating clock(s) ...\n");
        if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
                goto fail;
        timeout = 100000000;
@@ -602,8 +603,11 @@ calibrate_clocks(void)
                tsc_frequency = rdtsc() - old_tsc;
        }
 
-       if (tsc_present)
-               kprintf("TSC clock: %llu Hz, ", (long long)tsc_frequency);
+       if (tsc_present) {
+               kprintf("TSC%s clock: %llu Hz, ",
+                   tsc_invariant ? " invariant" : "",
+                   (long long)tsc_frequency);
+       }
        kprintf("i8254 clock: %u Hz\n", tot_count);
        return (tot_count);
 
@@ -736,10 +740,20 @@ startrtclock(void)
        /* 
         * Can we use the TSC?
         */
-       if (cpu_feature & CPUID_TSC)
+       if (cpu_feature & CPUID_TSC) {
                tsc_present = 1;
-       else
+               if ((cpu_vendor_id == CPU_VENDOR_INTEL ||
+                    cpu_vendor_id == CPU_VENDOR_AMD) &&
+                   cpu_exthigh >= 0x80000007) {
+                       u_int regs[4];
+
+                       do_cpuid(0x80000007, regs);
+                       if (regs[3] & 0x100)
+                               tsc_invariant = 1;
+               }
+       } else {
                tsc_present = 0;
+       }
 
        /*
         * Initial RTC state, don't do anything unexpected
@@ -1167,6 +1181,7 @@ SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD,
 
 SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD,
            &tsc_present, 0, "TSC Available");
+SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD,
+           &tsc_invariant, 0, "Invariant TSC");
 SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD,
            &tsc_frequency, 0, "TSC Frequency");
-
index 4f461ac..56f85c9 100644 (file)
@@ -24,6 +24,7 @@ extern int    disable_rtc_set;
 extern u_int   timer_freq;
 extern int     timer0_max_count;
 extern int     tsc_present;
+extern int     tsc_invariant;
 extern int64_t tsc_frequency;
 extern int     tsc_is_broken;
 extern int     wall_cmos_clock;
index dffee02..4a59a3d 100644 (file)
@@ -106,6 +106,7 @@ vpte_t      *KernelPTD;
 vpte_t *KernelPTA;     /* Warning: Offset for direct VA translation */
 u_int cpu_feature;     /* XXX */
 int tsc_present;
+int tsc_invariant;
 int64_t tsc_frequency;
 int optcpus;           /* number of cpus - see mp_start() */
 int lwp_cpu_lock;      /* if/how to lock virtual CPUs to real CPUs */
@@ -368,6 +369,8 @@ main(int ac, char **av)
         */
        vsize = sizeof(tsc_present);
        sysctlbyname("hw.tsc_present", &tsc_present, &vsize, NULL, 0);
+       vsize = sizeof(tsc_invariant);
+       sysctlbyname("hw.tsc_invariant", &tsc_invariant, &vsize, NULL, 0);
        vsize = sizeof(tsc_frequency);
        sysctlbyname("hw.tsc_frequency", &tsc_frequency, &vsize, NULL, 0);
        if (tsc_present)
index f40fb7c..3e81fb0 100644 (file)
@@ -60,6 +60,8 @@ SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
           CTLFLAG_RW, &disable_rtc_set, 0, "");
 SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD,
             &tsc_present, 0, "TSC Available");
+SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD,
+            &tsc_invariant, 0, "Invariant TSC");
 SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD,
            &tsc_frequency, 0, "TSC Frequency");
 
index 4f461ac..56f85c9 100644 (file)
@@ -24,6 +24,7 @@ extern int    disable_rtc_set;
 extern u_int   timer_freq;
 extern int     timer0_max_count;
 extern int     tsc_present;
+extern int     tsc_invariant;
 extern int64_t tsc_frequency;
 extern int     tsc_is_broken;
 extern int     wall_cmos_clock;
index c4085c1..95ecab9 100644 (file)
@@ -108,6 +108,7 @@ vpte_t      *KernelPTA;     /* Warning: Offset for direct VA translation */
 void *dmap_min_address;
 u_int cpu_feature;     /* XXX */
 int tsc_present;
+int tsc_invariant;
 int64_t tsc_frequency;
 int optcpus;           /* number of cpus - see mp_start() */
 int lwp_cpu_lock;      /* if/how to lock virtual CPUs to real CPUs */
@@ -367,6 +368,8 @@ main(int ac, char **av)
         */
        vsize = sizeof(tsc_present);
        sysctlbyname("hw.tsc_present", &tsc_present, &vsize, NULL, 0);
+       vsize = sizeof(tsc_invariant);
+       sysctlbyname("hw.tsc_invariant", &tsc_invariant, &vsize, NULL, 0);
        vsize = sizeof(tsc_frequency);
        sysctlbyname("hw.tsc_frequency", &tsc_frequency, &vsize, NULL, 0);
        if (tsc_present)
index e641c62..49b57b5 100644 (file)
@@ -61,6 +61,8 @@ SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
           CTLFLAG_RW, &disable_rtc_set, 0, "");
 SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD,
             &tsc_present, 0, "TSC Available");
+SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD,
+            &tsc_invariant, 0, "Invariant TSC");
 SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD,
            &tsc_frequency, 0, "TSC Frequency");