浏览代码

x86/mrst: Add support for Penwell clock calibration

Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Dirk Brandewie 13 年之前
父节点
当前提交
0a9153261d
共有 2 个文件被更改,包括 35 次插入5 次删除
  1. 7 0
      arch/x86/include/asm/mrst.h
  2. 28 5
      arch/x86/platform/mrst/mrst.c

+ 7 - 0
arch/x86/include/asm/mrst.h

@@ -44,6 +44,13 @@ enum mrst_timer_options {
 
 
 extern enum mrst_timer_options mrst_timer_options;
 extern enum mrst_timer_options mrst_timer_options;
 
 
+/*
+ * Penwell uses spread spectrum clock, so the freq number is not exactly
+ * the same as reported by MSR based on SDM.
+ */
+#define PENWELL_FSB_FREQ_83SKU         83200
+#define PENWELL_FSB_FREQ_100SKU        99840
+
 #define SFI_MTMR_MAX_NUM 8
 #define SFI_MTMR_MAX_NUM 8
 #define SFI_MRTC_MAX	8
 #define SFI_MRTC_MAX	8
 
 

+ 28 - 5
arch/x86/platform/mrst/mrst.c

@@ -187,11 +187,34 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table)
 static unsigned long __init mrst_calibrate_tsc(void)
 static unsigned long __init mrst_calibrate_tsc(void)
 {
 {
 	unsigned long flags, fast_calibrate;
 	unsigned long flags, fast_calibrate;
-
-	local_irq_save(flags);
-	fast_calibrate = apbt_quick_calibrate();
-	local_irq_restore(flags);
-
+	if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) {
+		u32 lo, hi, ratio, fsb;
+
+		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+		pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi);
+		ratio = (hi >> 8) & 0x1f;
+		pr_debug("ratio is %d\n", ratio);
+		if (!ratio) {
+			pr_err("read a zero ratio, should be incorrect!\n");
+			pr_err("force tsc ratio to 16 ...\n");
+			ratio = 16;
+		}
+		rdmsr(MSR_FSB_FREQ, lo, hi);
+		if ((lo & 0x7) == 0x7)
+			fsb = PENWELL_FSB_FREQ_83SKU;
+		else
+			fsb = PENWELL_FSB_FREQ_100SKU;
+		fast_calibrate = ratio * fsb;
+		pr_debug("read penwell tsc %lu khz\n", fast_calibrate);
+		lapic_timer_frequency = fsb * 1000 / HZ;
+		/* mark tsc clocksource as reliable */
+		set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE);
+	} else {
+		local_irq_save(flags);
+		fast_calibrate = apbt_quick_calibrate();
+		local_irq_restore(flags);
+	}
+	
 	if (fast_calibrate)
 	if (fast_calibrate)
 		return fast_calibrate;
 		return fast_calibrate;