x86: tsc: Add support for native calibration of TSC freq

Add native tsc calibration function. Calibrate the tsc timer the same
way as linux does in arch/x86/kernel/tsc.c.

Fixes booting for Apollo Lake processors.

Signed-off-by: Bernhard Messerklinger <bernhard.messerklinger@br-automation.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/drivers/timer/tsc_timer.c b/drivers/timer/tsc_timer.c
index ba940eb..919caba 100644
--- a/drivers/timer/tsc_timer.c
+++ b/drivers/timer/tsc_timer.c
@@ -19,8 +19,59 @@
 
 #define MAX_NUM_FREQS	9
 
+#define INTEL_FAM6_SKYLAKE_MOBILE	0x4E
+#define INTEL_FAM6_ATOM_GOLDMONT	0x5C /* Apollo Lake */
+#define INTEL_FAM6_SKYLAKE_DESKTOP	0x5E
+#define INTEL_FAM6_ATOM_GOLDMONT_X	0x5F /* Denverton */
+#define INTEL_FAM6_KABYLAKE_MOBILE	0x8E
+#define INTEL_FAM6_KABYLAKE_DESKTOP	0x9E
+
 DECLARE_GLOBAL_DATA_PTR;
 
+/*
+ * native_calibrate_tsc
+ * Determine TSC frequency via CPUID, else return 0.
+ */
+static unsigned long native_calibrate_tsc(void)
+{
+	struct cpuid_result tsc_info;
+	unsigned int crystal_freq;
+
+	if (gd->arch.x86_vendor != X86_VENDOR_INTEL)
+		return 0;
+
+	if (cpuid_eax(0) < 0x15)
+		return 0;
+
+	tsc_info = cpuid(0x15);
+
+	if (tsc_info.ebx == 0 || tsc_info.eax == 0)
+		return 0;
+
+	crystal_freq = tsc_info.ecx / 1000;
+
+	if (!crystal_freq) {
+		switch (gd->arch.x86_model) {
+		case INTEL_FAM6_SKYLAKE_MOBILE:
+		case INTEL_FAM6_SKYLAKE_DESKTOP:
+		case INTEL_FAM6_KABYLAKE_MOBILE:
+		case INTEL_FAM6_KABYLAKE_DESKTOP:
+			crystal_freq = 24000;	/* 24.0 MHz */
+			break;
+		case INTEL_FAM6_ATOM_GOLDMONT_X:
+			crystal_freq = 25000;	/* 25.0 MHz */
+			break;
+		case INTEL_FAM6_ATOM_GOLDMONT:
+			crystal_freq = 19200;	/* 19.2 MHz */
+			break;
+		default:
+			return 0;
+		}
+	}
+
+	return (crystal_freq * tsc_info.ebx / tsc_info.eax) / 1000;
+}
+
 static unsigned long cpu_mhz_from_cpuid(void)
 {
 	if (gd->arch.x86_vendor != X86_VENDOR_INTEL)
@@ -350,6 +401,10 @@
 	if (!gd->arch.clock_rate) {
 		unsigned long fast_calibrate;
 
+		fast_calibrate = native_calibrate_tsc();
+		if (fast_calibrate)
+			goto done;
+
 		fast_calibrate = cpu_mhz_from_cpuid();
 		if (fast_calibrate)
 			goto done;