mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
7.1 KiB
236 lines
7.1 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* TSC frequency enumeration via MSR |
|
* |
|
* Copyright (C) 2013, 2018 Intel Corporation |
|
* Author: Bin Gao <[email protected]> |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/thread_info.h> |
|
|
|
#include <asm/apic.h> |
|
#include <asm/cpu_device_id.h> |
|
#include <asm/intel-family.h> |
|
#include <asm/msr.h> |
|
#include <asm/param.h> |
|
#include <asm/tsc.h> |
|
|
|
#define MAX_NUM_FREQS 16 /* 4 bits to select the frequency */ |
|
|
|
/* |
|
* The frequency numbers in the SDM are e.g. 83.3 MHz, which does not contain a |
|
* lot of accuracy which leads to clock drift. As far as we know Bay Trail SoCs |
|
* use a 25 MHz crystal and Cherry Trail uses a 19.2 MHz crystal, the crystal |
|
* is the source clk for a root PLL which outputs 1600 and 100 MHz. It is |
|
* unclear if the root PLL outputs are used directly by the CPU clock PLL or |
|
* if there is another PLL in between. |
|
* This does not matter though, we can model the chain of PLLs as a single PLL |
|
* with a quotient equal to the quotients of all PLLs in the chain multiplied. |
|
* So we can create a simplified model of the CPU clock setup using a reference |
|
* clock of 100 MHz plus a quotient which gets us as close to the frequency |
|
* from the SDM as possible. |
|
* For the 83.3 MHz example from above this would give us 100 MHz * 5 / 6 = |
|
* 83 and 1/3 MHz, which matches exactly what has been measured on actual hw. |
|
*/ |
|
#define TSC_REFERENCE_KHZ 100000 |
|
|
|
struct muldiv { |
|
u32 multiplier; |
|
u32 divider; |
|
}; |
|
|
|
/* |
|
* 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 use_msr_plat does. |
|
*/ |
|
struct freq_desc { |
|
bool use_msr_plat; |
|
struct muldiv muldiv[MAX_NUM_FREQS]; |
|
/* |
|
* Some CPU frequencies in the SDM do not map to known PLL freqs, in |
|
* that case the muldiv array is empty and the freqs array is used. |
|
*/ |
|
u32 freqs[MAX_NUM_FREQS]; |
|
u32 mask; |
|
}; |
|
|
|
/* |
|
* Penwell and Clovertrail use spread spectrum clock, |
|
* so the freq number is not exactly the same as reported |
|
* by MSR based on SDM. |
|
*/ |
|
static const struct freq_desc freq_desc_pnw = { |
|
.use_msr_plat = false, |
|
.freqs = { 0, 0, 0, 0, 0, 99840, 0, 83200 }, |
|
.mask = 0x07, |
|
}; |
|
|
|
static const struct freq_desc freq_desc_clv = { |
|
.use_msr_plat = false, |
|
.freqs = { 0, 133200, 0, 0, 0, 99840, 0, 83200 }, |
|
.mask = 0x07, |
|
}; |
|
|
|
/* |
|
* Bay Trail SDM MSR_FSB_FREQ frequencies simplified PLL model: |
|
* 000: 100 * 5 / 6 = 83.3333 MHz |
|
* 001: 100 * 1 / 1 = 100.0000 MHz |
|
* 010: 100 * 4 / 3 = 133.3333 MHz |
|
* 011: 100 * 7 / 6 = 116.6667 MHz |
|
* 100: 100 * 4 / 5 = 80.0000 MHz |
|
*/ |
|
static const struct freq_desc freq_desc_byt = { |
|
.use_msr_plat = true, |
|
.muldiv = { { 5, 6 }, { 1, 1 }, { 4, 3 }, { 7, 6 }, |
|
{ 4, 5 } }, |
|
.mask = 0x07, |
|
}; |
|
|
|
/* |
|
* Cherry Trail SDM MSR_FSB_FREQ frequencies simplified PLL model: |
|
* 0000: 100 * 5 / 6 = 83.3333 MHz |
|
* 0001: 100 * 1 / 1 = 100.0000 MHz |
|
* 0010: 100 * 4 / 3 = 133.3333 MHz |
|
* 0011: 100 * 7 / 6 = 116.6667 MHz |
|
* 0100: 100 * 4 / 5 = 80.0000 MHz |
|
* 0101: 100 * 14 / 15 = 93.3333 MHz |
|
* 0110: 100 * 9 / 10 = 90.0000 MHz |
|
* 0111: 100 * 8 / 9 = 88.8889 MHz |
|
* 1000: 100 * 7 / 8 = 87.5000 MHz |
|
*/ |
|
static const struct freq_desc freq_desc_cht = { |
|
.use_msr_plat = true, |
|
.muldiv = { { 5, 6 }, { 1, 1 }, { 4, 3 }, { 7, 6 }, |
|
{ 4, 5 }, { 14, 15 }, { 9, 10 }, { 8, 9 }, |
|
{ 7, 8 } }, |
|
.mask = 0x0f, |
|
}; |
|
|
|
/* |
|
* Merriefield SDM MSR_FSB_FREQ frequencies simplified PLL model: |
|
* 0001: 100 * 1 / 1 = 100.0000 MHz |
|
* 0010: 100 * 4 / 3 = 133.3333 MHz |
|
*/ |
|
static const struct freq_desc freq_desc_tng = { |
|
.use_msr_plat = true, |
|
.muldiv = { { 0, 0 }, { 1, 1 }, { 4, 3 } }, |
|
.mask = 0x07, |
|
}; |
|
|
|
/* |
|
* Moorefield SDM MSR_FSB_FREQ frequencies simplified PLL model: |
|
* 0000: 100 * 5 / 6 = 83.3333 MHz |
|
* 0001: 100 * 1 / 1 = 100.0000 MHz |
|
* 0010: 100 * 4 / 3 = 133.3333 MHz |
|
* 0011: 100 * 1 / 1 = 100.0000 MHz |
|
*/ |
|
static const struct freq_desc freq_desc_ann = { |
|
.use_msr_plat = true, |
|
.muldiv = { { 5, 6 }, { 1, 1 }, { 4, 3 }, { 1, 1 } }, |
|
.mask = 0x0f, |
|
}; |
|
|
|
/* |
|
* 24 MHz crystal? : 24 * 13 / 4 = 78 MHz |
|
* Frequency step for Lightning Mountain SoC is fixed to 78 MHz, |
|
* so all the frequency entries are 78000. |
|
*/ |
|
static const struct freq_desc freq_desc_lgm = { |
|
.use_msr_plat = true, |
|
.freqs = { 78000, 78000, 78000, 78000, 78000, 78000, 78000, 78000, |
|
78000, 78000, 78000, 78000, 78000, 78000, 78000, 78000 }, |
|
.mask = 0x0f, |
|
}; |
|
|
|
static const struct x86_cpu_id tsc_msr_cpu_ids[] = { |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SALTWELL_MID, &freq_desc_pnw), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SALTWELL_TABLET,&freq_desc_clv), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, &freq_desc_byt), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &freq_desc_tng), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, &freq_desc_cht), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT_MID, &freq_desc_ann), |
|
X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT_NP, &freq_desc_lgm), |
|
{} |
|
}; |
|
|
|
/* |
|
* MSR-based CPU/TSC frequency discovery for certain CPUs. |
|
* |
|
* Set global "lapic_timer_period" to bus_clock_cycles/jiffy |
|
* Return processor base frequency in KHz, or 0 on failure. |
|
*/ |
|
unsigned long cpu_khz_from_msr(void) |
|
{ |
|
u32 lo, hi, ratio, freq, tscref; |
|
const struct freq_desc *freq_desc; |
|
const struct x86_cpu_id *id; |
|
const struct muldiv *md; |
|
unsigned long res; |
|
int index; |
|
|
|
id = x86_match_cpu(tsc_msr_cpu_ids); |
|
if (!id) |
|
return 0; |
|
|
|
freq_desc = (struct freq_desc *)id->driver_data; |
|
if (freq_desc->use_msr_plat) { |
|
rdmsr(MSR_PLATFORM_INFO, lo, hi); |
|
ratio = (lo >> 8) & 0xff; |
|
} else { |
|
rdmsr(MSR_IA32_PERF_STATUS, lo, hi); |
|
ratio = (hi >> 8) & 0x1f; |
|
} |
|
|
|
/* Get FSB FREQ ID */ |
|
rdmsr(MSR_FSB_FREQ, lo, hi); |
|
index = lo & freq_desc->mask; |
|
md = &freq_desc->muldiv[index]; |
|
|
|
/* |
|
* Note this also catches cases where the index points to an unpopulated |
|
* part of muldiv, in that case the else will set freq and res to 0. |
|
*/ |
|
if (md->divider) { |
|
tscref = TSC_REFERENCE_KHZ * md->multiplier; |
|
freq = DIV_ROUND_CLOSEST(tscref, md->divider); |
|
/* |
|
* Multiplying by ratio before the division has better |
|
* accuracy than just calculating freq * ratio. |
|
*/ |
|
res = DIV_ROUND_CLOSEST(tscref * ratio, md->divider); |
|
} else { |
|
freq = freq_desc->freqs[index]; |
|
res = freq * ratio; |
|
} |
|
|
|
if (freq == 0) |
|
pr_err("Error MSR_FSB_FREQ index %d is unknown\n", index); |
|
|
|
#ifdef CONFIG_X86_LOCAL_APIC |
|
lapic_timer_period = (freq * 1000) / HZ; |
|
#endif |
|
|
|
/* |
|
* TSC frequency determined by MSR is always considered "known" |
|
* because it is reported by HW. |
|
* Another fact is that on MSR capable platforms, PIT/HPET is |
|
* generally not available so calibration won't work at all. |
|
*/ |
|
setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); |
|
|
|
/* |
|
* Unfortunately there is no way for hardware to tell whether the |
|
* TSC is reliable. We were told by silicon design team that TSC |
|
* on Atom SoCs are always "reliable". TSC is also the only |
|
* reliable clocksource on these SoCs (HPET is either not present |
|
* or not functional) so mark TSC reliable which removes the |
|
* requirement for a watchdog clocksource. |
|
*/ |
|
setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE); |
|
|
|
return res; |
|
}
|
|
|