Alexandru Gagniuc (mr.nuke.me@gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13391
-gerrit
commit a0db0015a3223863de7c078030e325e430725265 Author: Alexandru Gagniuc mr.nuke.me@gmail.com Date: Tue Nov 24 16:40:10 2015 -0800
libpayload: x86/timer: Implement constant TSC based calibration
On certain SOCs, such as Apollolake, the 8254 timer is not present or not operational. On such systems, attempting to calibrate the TSC against the 8254 will hang the payload. The next best approach is to identify processors with a constant TSC rate, and determine the actual speed by reading model-specific registers.
With this patch, filo can now succesfully load the filo shell on Appololalke RVP2 board.
Change-Id: I863acbc015cf072fa007584ab1d4f4531f0a6d4f Signed-off-by: Alexandru Gagniuc alexandrux.gagniuc@intel.com Signed-off-by: Andrey Petrov andrey.petrov@intel.com --- payloads/libpayload/arch/x86/timer.c | 99 ++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)
diff --git a/payloads/libpayload/arch/x86/timer.c b/payloads/libpayload/arch/x86/timer.c index bf0c30a..eb222b2 100644 --- a/payloads/libpayload/arch/x86/timer.c +++ b/payloads/libpayload/arch/x86/timer.c @@ -33,20 +33,93 @@ */
#include <libpayload.h> +#include <arch/cpuid.h> #include <arch/rdtsc.h> +#include <arch/msr.h>
+#define MSR_PLATFORM_INFO 0xce /** * @ingroup arch * Global variable containing the speed of the processor in KHz. */ uint32_t cpu_khz;
-/** - * Calculate the speed of the processor for use in delays. +static const char intel_cpuid_mfg_string[] = "GenuineIntel"; + +/* + * Certain Atom SOCs don't either don't have an 8254 timer (PIT), or coreboot + * disables the PIT. In these cases TSC calibration by PIT will hang. * - * @return The CPU speed in kHz. + * NOTE: This table and its associated helper functions can be extended to + * support a larger range of CPUs and access methods. This only includes tested + * CPUs. */ -unsigned int get_cpu_speed(void) +static const struct fsb_freq_descriptor { + uint8_t cpu_family; + uint8_t cpu_model; + unsigned int base_clock_khz; +} intel_freq_table[] = { + { 6, 0x5c, 100000 }, /* Apollolake (Broxton A0) */ +}; + +static const struct fsb_freq_descriptor *get_cpu_freq_info(void) +{ + size_t i; + struct cpuid_fms fms = cpuid_get_fms(); + + for (i = 0; i < ARRAY_SIZE(intel_freq_table); i++) { + if ((intel_freq_table[i].cpu_family == fms.family) && + (intel_freq_table[i].cpu_model == fms.model)) { + return &intel_freq_table[i]; + } + } + + return NULL; +} + +static int is_intel_cpu(void) +{ + char id_string[12]; + struct cpuid_result res; + + /* Get manufacturer's ID string */ + res = cpuid(0); + memcpy(id_string + 0, &res.ebx, 4); + memcpy(id_string + 4, &res.edx, 4); + memcpy(id_string + 8, &res.ecx, 4); + + return !memcmp(intel_cpuid_mfg_string, id_string, 12); +} + +/* + * Get the speed of the processor's timestamp counter on supported CPUs + */ +static unsigned int get_cpu_speed_by_const_tsc(void) +{ + uint64_t msr; + uint32_t tsc_multiplier; + const struct fsb_freq_descriptor *freq_info; + + if (!is_intel_cpu()) + return 0; + + freq_info = get_cpu_freq_info(); + if (!freq_info) + return 0; + + msr = _rdmsr(MSR_PLATFORM_INFO); + tsc_multiplier = (msr >> 8) & 0xff; + + cpu_khz = (freq_info->base_clock_khz * tsc_multiplier); + return cpu_khz; +} + +/* + * Get the speed of the processor's timestamp counter by calibrating it + * against the 8254 programmable interval timer. + * This function waits 2 ms to get an accurate calibration. + */ +static unsigned int get_cpu_speed_by_8254_timer(void) { unsigned long long start, end; const uint32_t clock_rate = 1193182; // 1.193182 MHz @@ -76,3 +149,21 @@ unsigned int get_cpu_speed(void)
return cpu_khz; } + +/** + * Calculate the speed of the processor for use in delays. + * + * @return The CPU speed in kHz. + */ +unsigned int get_cpu_speed(void) +{ + uint32_t tsc_rate_khz; + + tsc_rate_khz = get_cpu_speed_by_const_tsc(); + if (tsc_rate_khz) + return tsc_rate_khz; + + tsc_rate_khz = get_cpu_speed_by_8254_timer(); + + return tsc_rate_khz; +}