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

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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@chromium.org --- arch/x86/lib/tsc_timer.c | 116 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 7 deletions(-)
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; + return fast_calibrate; }
unsigned long get_tbclk(void)

Hi Bin,
On 9 November 2014 07:19, Bin Meng bmeng.cn@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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@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.
Regards, Simon

Hi Simon,
On Tue, Nov 11, 2014 at 2:54 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 9 November 2014 07:19, Bin Meng bmeng.cn@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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@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

Hi Bin,
On 11 November 2014 01:25, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Nov 11, 2014 at 2:54 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 9 November 2014 07:19, Bin Meng bmeng.cn@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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@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.
I can add exceptions, but I wonder why this fails on my platform? Is your method supposed to work on any platform?
Regards, Simon

Hi Simon,
On Wed, Nov 12, 2014 at 12:10 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 11 November 2014 01:25, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Nov 11, 2014 at 2:54 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 9 November 2014 07:19, Bin Meng bmeng.cn@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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@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.
I can add exceptions, but I wonder why this fails on my platform? Is your method supposed to work on any platform?
Which one fails? Do you mean the quick_pit_calibrate() fails? Is that because your platform does not has the legacy 8254 integrated?
Regards, Bin

Hi Bin,
On 11 November 2014 18:16, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Wed, Nov 12, 2014 at 12:10 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 11 November 2014 01:25, Bin Meng bmeng.cn@gmail.com wrote:
Hi Simon,
On Tue, Nov 11, 2014 at 2:54 AM, Simon Glass sjg@chromium.org wrote:
Hi Bin,
On 9 November 2014 07:19, Bin Meng bmeng.cn@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@gmail.com Acked-by: Simon Glass sjg@chromium.org Tested-by: Simon Glass sjg@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.
I can add exceptions, but I wonder why this fails on my platform? Is your method supposed to work on any platform?
Which one fails? Do you mean the quick_pit_calibrate() fails? Is that because your platform does not has the legacy 8254 integrated?
I'll have to have another look at this. We dropped some of the old timer stuff a while ago and it would be a shame to bring it back when newer platforms apparently don't need it. But I may be talking about a different timer.
Regards, Simon
participants (2)
-
Bin Meng
-
Simon Glass