One of the newer methods to determine TSC frequency, is to use one of cpuid
extensions to get TSC/Crystal ratio. This method is preferred on CPUs that
implements it. This patch adds a new function calibrate_tsc_early() that
can be called early in boot to determine the TSC by using this method.

Signed-off-by: Pavel Tatashin <pasha.tatas...@oracle.com>
---
 arch/x86/kernel/tsc.c |   39 ++++++++++++++++++++++++++-------------
 1 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 5add503..1c9fc23 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -26,6 +26,9 @@
 #include <asm/apic.h>
 #include <asm/intel-family.h>
 
+/* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */
+#define CPUID_TSC_LEAF         0x15
+
 unsigned int __read_mostly cpu_khz;    /* TSC clocks / usec, not used here */
 EXPORT_SYMBOL(cpu_khz);
 
@@ -671,24 +674,16 @@ static unsigned long quick_pit_calibrate(bool early_boot)
 }
 
 /**
- * native_calibrate_tsc
- * Determine TSC frequency via CPUID, else return 0.
+ * The caller already checked that TSC leaf capability can be read from cpuid
  */
-unsigned long native_calibrate_tsc(void)
+static unsigned long calibrate_tsc_early(int model)
 {
        unsigned int eax_denominator, ebx_numerator, ecx_hz, edx;
        unsigned int crystal_khz;
 
-       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
-               return 0;
-
-       if (boot_cpu_data.cpuid_level < 0x15)
-               return 0;
-
        eax_denominator = ebx_numerator = ecx_hz = edx = 0;
 
-       /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */
-       cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_hz, &edx);
+       cpuid(CPUID_TSC_LEAF, &eax_denominator, &ebx_numerator, &ecx_hz, &edx);
 
        if (ebx_numerator == 0 || eax_denominator == 0)
                return 0;
@@ -696,7 +691,7 @@ unsigned long native_calibrate_tsc(void)
        crystal_khz = ecx_hz / 1000;
 
        if (crystal_khz == 0) {
-               switch (boot_cpu_data.x86_model) {
+               switch (model) {
                case INTEL_FAM6_SKYLAKE_MOBILE:
                case INTEL_FAM6_SKYLAKE_DESKTOP:
                case INTEL_FAM6_KABYLAKE_MOBILE:
@@ -713,6 +708,24 @@ unsigned long native_calibrate_tsc(void)
                }
        }
 
+       return crystal_khz * ebx_numerator / eax_denominator;
+}
+
+/**
+ * native_calibrate_tsc
+ * Determine TSC frequency via CPUID, else return 0.
+ */
+unsigned long native_calibrate_tsc(void)
+{
+       unsigned int tsc_khz;
+
+       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+               return 0;
+
+       if (boot_cpu_data.cpuid_level < CPUID_TSC_LEAF)
+               return 0;
+
+       tsc_khz = calibrate_tsc_early(boot_cpu_data.x86_model);
        /*
         * TSC frequency determined by CPUID is a "hardware reported"
         * frequency and is the most accurate one so far we have. This
@@ -727,7 +740,7 @@ unsigned long native_calibrate_tsc(void)
        if (boot_cpu_data.x86_model == INTEL_FAM6_ATOM_GOLDMONT)
                setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
 
-       return crystal_khz * ebx_numerator / eax_denominator;
+       return tsc_khz;
 }
 
 static unsigned long cpu_khz_from_cpuid(void)
-- 
1.7.1

Reply via email to