|
| 1 | +/* |
| 2 | + * tsc_msr.c - MSR based TSC calibration on Intel Atom SoC platforms. |
| 3 | + * |
| 4 | + * TSC in Intel Atom SoC runs at a constant rate which can be figured |
| 5 | + * by this formula: |
| 6 | + * <maximum core-clock to bus-clock ratio> * <maximum resolved frequency> |
| 7 | + * See Intel 64 and IA-32 System Programming Guid section 16.12 and 30.11.5 |
| 8 | + * for details. |
| 9 | + * Especially some Intel Atom SoCs don't have PIT(i8254) or HPET, so MSR |
| 10 | + * based calibration is the only option. |
| 11 | + * |
| 12 | + * |
| 13 | + * Copyright (C) 2013 Intel Corporation |
| 14 | + * Author: Bin Gao <bin.gao@intel.com> |
| 15 | + * |
| 16 | + * This file is released under the GPLv2. |
| 17 | + */ |
| 18 | + |
| 19 | +#include <linux/kernel.h> |
| 20 | +#include <asm/processor.h> |
| 21 | +#include <asm/setup.h> |
| 22 | +#include <asm/apic.h> |
| 23 | +#include <asm/param.h> |
| 24 | + |
| 25 | +/* CPU reference clock frequency: in KHz */ |
| 26 | +#define FREQ_83 83200 |
| 27 | +#define FREQ_100 99840 |
| 28 | +#define FREQ_133 133200 |
| 29 | +#define FREQ_166 166400 |
| 30 | + |
| 31 | +#define MAX_NUM_FREQS 8 |
| 32 | + |
| 33 | +/* |
| 34 | + * According to Intel 64 and IA-32 System Programming Guide, |
| 35 | + * if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be |
| 36 | + * read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40]. |
| 37 | + * Unfortunately some Intel Atom SoCs aren't quite compliant to this, |
| 38 | + * so we need manually differentiate SoC families. This is what the |
| 39 | + * field msr_plat does. |
| 40 | + */ |
| 41 | +struct freq_desc { |
| 42 | + u8 x86_family; /* CPU family */ |
| 43 | + u8 x86_model; /* model */ |
| 44 | + u8 msr_plat; /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */ |
| 45 | + u32 freqs[MAX_NUM_FREQS]; |
| 46 | +}; |
| 47 | + |
| 48 | +static struct freq_desc freq_desc_tables[] = { |
| 49 | + /* PNW */ |
| 50 | + { 6, 0x27, 0, { 0, 0, 0, 0, 0, FREQ_100, 0, FREQ_83 } }, |
| 51 | + /* CLV+ */ |
| 52 | + { 6, 0x35, 0, { 0, FREQ_133, 0, 0, 0, FREQ_100, 0, FREQ_83 } }, |
| 53 | + /* TNG */ |
| 54 | + { 6, 0x4a, 1, { 0, FREQ_100, FREQ_133, 0, 0, 0, 0, 0 } }, |
| 55 | + /* VLV2 */ |
| 56 | + { 6, 0x37, 1, { 0, FREQ_100, FREQ_133, FREQ_166, 0, 0, 0, 0 } }, |
| 57 | + /* ANN */ |
| 58 | + { 6, 0x5a, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_100, 0, 0, 0, 0 } }, |
| 59 | +}; |
| 60 | + |
| 61 | +static int match_cpu(u8 family, u8 model) |
| 62 | +{ |
| 63 | + int i; |
| 64 | + |
| 65 | + for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) { |
| 66 | + if ((family == freq_desc_tables[i].x86_family) && |
| 67 | + (model == freq_desc_tables[i].x86_model)) |
| 68 | + return i; |
| 69 | + } |
| 70 | + |
| 71 | + return -1; |
| 72 | +} |
| 73 | + |
| 74 | +/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */ |
| 75 | +#define id_to_freq(cpu_index, freq_id) \ |
| 76 | + (freq_desc_tables[cpu_index].freqs[freq_id]) |
| 77 | + |
| 78 | +/* |
| 79 | + * Do MSR calibration only for known/supported CPUs. |
| 80 | + * Return values: |
| 81 | + * -1: CPU is unknown/unsupported for MSR based calibration |
| 82 | + * 0: CPU is known/supported, but calibration failed |
| 83 | + * 1: CPU is known/supported, and calibration succeeded |
| 84 | + */ |
| 85 | +int try_msr_calibrate_tsc(unsigned long *fast_calibrate) |
| 86 | +{ |
| 87 | + int cpu_index; |
| 88 | + u32 lo, hi, ratio, freq_id, freq; |
| 89 | + |
| 90 | + cpu_index = match_cpu(boot_cpu_data.x86, boot_cpu_data.x86_model); |
| 91 | + if (cpu_index < 0) |
| 92 | + return -1; |
| 93 | + |
| 94 | + *fast_calibrate = 0; |
| 95 | + |
| 96 | + if (freq_desc_tables[cpu_index].msr_plat) { |
| 97 | + rdmsr(MSR_PLATFORM_INFO, lo, hi); |
| 98 | + ratio = (lo >> 8) & 0x1f; |
| 99 | + } else { |
| 100 | + rdmsr(MSR_IA32_PERF_STATUS, lo, hi); |
| 101 | + ratio = (hi >> 8) & 0x1f; |
| 102 | + } |
| 103 | + pr_info("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio); |
| 104 | + |
| 105 | + if (!ratio) |
| 106 | + return 0; |
| 107 | + |
| 108 | + /* Get FSB FREQ ID */ |
| 109 | + rdmsr(MSR_FSB_FREQ, lo, hi); |
| 110 | + freq_id = lo & 0x7; |
| 111 | + freq = id_to_freq(cpu_index, freq_id); |
| 112 | + pr_info("Resolved frequency ID: %u, frequency: %u KHz\n", |
| 113 | + freq_id, freq); |
| 114 | + if (!freq) |
| 115 | + return 0; |
| 116 | + |
| 117 | + /* TSC frequency = maximum resolved freq * maximum resolved bus ratio */ |
| 118 | + *fast_calibrate = freq * ratio; |
| 119 | + pr_info("TSC runs at %lu KHz\n", *fast_calibrate); |
| 120 | + |
| 121 | + lapic_timer_frequency = (freq * 1000) / HZ; |
| 122 | + pr_info("lapic_timer_frequency = %d\n", lapic_timer_frequency); |
| 123 | + |
| 124 | + return 1; |
| 125 | +} |
0 commit comments