[U-Boot] [PATCH v2 2/4] x86: Do TSC MSR calibration only for known/supported CPUs

Bin Meng bmeng.cn at gmail.com
Tue Nov 11 09:25:53 CET 2014


Hi Simon,

On Tue, Nov 11, 2014 at 2:54 AM, Simon Glass <sjg at chromium.org> wrote:
> Hi Bin,
>
> On 9 November 2014 07:19, Bin Meng <bmeng.cn at gmail.com> wrote:
>> Using MSR_PLATFORM_INFO (0xCE) to calibrate TSR will cause #GP on
>> processors which do not have this MSR. Instead only doing the MSR
>> calibration for known/supported CPUs.
>>
>> Signed-off-by: Bin Meng <bmeng.cn at gmail.com>
>> Acked-by: Simon Glass <sjg at chromium.org>
>> Tested-by: Simon Glass <sjg at chromium.org>
>> ---
>>  arch/x86/lib/tsc_timer.c | 116 ++++++++++++++++++++++++++++++++++++++++++++---
>>  1 file changed, 109 insertions(+), 7 deletions(-)
>
> Applied to u-boot-x86/master, thanks!
>
> (Please see note below)
>
>>
>> diff --git a/arch/x86/lib/tsc_timer.c b/arch/x86/lib/tsc_timer.c
>> index 8b38702..fafbbfc 100644
>> --- a/arch/x86/lib/tsc_timer.c
>> +++ b/arch/x86/lib/tsc_timer.c
>> @@ -1,6 +1,9 @@
>>  /*
>>   * Copyright (c) 2012 The Chromium OS Authors.
>>   *
>> + * TSC calibration codes are adapted from Linux kernel
>> + * arch/x86/kernel/tsc_msr.c and arch/x86/kernel/tsc.c
>> + *
>>   * SPDX-License-Identifier:    GPL-2.0+
>>   */
>>
>> @@ -12,8 +15,108 @@
>>  #include <asm/msr.h>
>>  #include <asm/u-boot-x86.h>
>>
>> +/* CPU reference clock frequency: in KHz */
>> +#define FREQ_83                83200
>> +#define FREQ_100       99840
>> +#define FREQ_133       133200
>> +#define FREQ_166       166400
>> +
>> +#define MAX_NUM_FREQS  8
>> +
>>  DECLARE_GLOBAL_DATA_PTR;
>>
>> +/*
>> + * According to Intel 64 and IA-32 System Programming Guide,
>> + * if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
>> + * read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
>> + * Unfortunately some Intel Atom SoCs aren't quite compliant to this,
>> + * so we need manually differentiate SoC families. This is what the
>> + * field msr_plat does.
>> + */
>> +struct freq_desc {
>> +       u8 x86_family;  /* CPU family */
>> +       u8 x86_model;   /* model */
>> +       u8 msr_plat;    /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
>> +       u32 freqs[MAX_NUM_FREQS];
>> +};
>> +
>> +static struct freq_desc freq_desc_tables[] = {
>> +       /* PNW */
>> +       { 6, 0x27, 0, { 0, 0, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
>> +       /* CLV+ */
>> +       { 6, 0x35, 0, { 0, FREQ_133, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
>> +       /* TNG */
>> +       { 6, 0x4a, 1, { 0, FREQ_100, FREQ_133, 0, 0, 0, 0, 0 } },
>> +       /* VLV2 */
>> +       { 6, 0x37, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_166, 0, 0, 0, 0 } },
>> +       /* ANN */
>> +       { 6, 0x5a, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_100, 0, 0, 0, 0 } },
>> +};
>> +
>> +static int match_cpu(u8 family, u8 model)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) {
>> +               if ((family == freq_desc_tables[i].x86_family) &&
>> +                       (model == freq_desc_tables[i].x86_model))
>> +                       return i;
>> +       }
>> +
>> +       return -1;
>> +}
>> +
>> +/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
>> +#define id_to_freq(cpu_index, freq_id) \
>> +       (freq_desc_tables[cpu_index].freqs[freq_id])
>> +
>> +/*
>> + * Do MSR calibration only for known/supported CPUs.
>> + *
>> + * Returns the calibration value or 0 if MSR calibration failed.
>> + */
>> +static unsigned long try_msr_calibrate_tsc(void)
>> +{
>> +       u32 lo, hi, ratio, freq_id, freq;
>> +       unsigned long res;
>> +       int cpu_index;
>> +
>> +       cpu_index = match_cpu(gd->arch.x86, gd->arch.x86_model);
>> +       if (cpu_index < 0)
>> +               return 0;
>> +
>> +       if (freq_desc_tables[cpu_index].msr_plat) {
>> +               rdmsr(MSR_PLATFORM_INFO, lo, hi);
>> +               ratio = (lo >> 8) & 0x1f;
>> +       } else {
>> +               rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
>> +               ratio = (hi >> 8) & 0x1f;
>> +       }
>> +       debug("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio);
>> +
>> +       if (!ratio)
>> +               goto fail;
>> +
>> +       /* Get FSB FREQ ID */
>> +       rdmsr(MSR_FSB_FREQ, lo, hi);
>> +       freq_id = lo & 0x7;
>> +       freq = id_to_freq(cpu_index, freq_id);
>> +       debug("Resolved frequency ID: %u, frequency: %u KHz\n",
>> +                               freq_id, freq);
>> +       if (!freq)
>> +               goto fail;
>> +
>> +       /* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
>> +       res = freq * ratio / 1000;
>> +       debug("TSC runs at %lu MHz\n", res);
>> +
>> +       return res;
>> +
>> +fail:
>> +       debug("Fast TSC calibration using MSR failed\n");
>> +       return 0;
>> +}
>> +
>>  void timer_set_base(u64 base)
>>  {
>>         gd->arch.tsc_base = base;
>> @@ -34,17 +137,16 @@ u64 __attribute__((no_instrument_function)) get_ticks(void)
>>         return now_tick - gd->arch.tsc_base;
>>  }
>>
>> -#define PLATFORM_INFO_MSR 0xce
>> -
>>  /* Get the speed of the TSC timer in MHz */
>>  unsigned __attribute__((no_instrument_function)) long get_tbclk_mhz(void)
>>  {
>> -       u32 ratio;
>> -       u64 platform_info = native_read_msr(PLATFORM_INFO_MSR);
>> +       unsigned long fast_calibrate;
>> +
>> +       fast_calibrate = try_msr_calibrate_tsc();
>> +       if (!fast_calibrate)
>> +               panic("TSC frequency is ZERO");
>>
>> -       /* 100MHz times Max Non Turbo ratio */
>> -       ratio = (platform_info >> 8) & 0xff;
>> -       return 100 * ratio;
>
> What happens for platforms that have this MSR? Do we ignore it now?
> For my current platform this code is needed.

In Linux the MSR calibration is only available on several Atom
platforms. If that fails, it falls back to use fast PIT calibration.
However, I think you can add an entry in the freq_desc_tables[] for
your platform if you want to use MSR calibration.

Regards,
Bin


More information about the U-Boot mailing list