// SPDX-License-Identifier: GPL-2.0
/*
* TSC frequency enumeration via MSR
*
* Copyright ( C ) 2013 , 2018 Intel Corporation
* Author : Bin Gao < bin . gao @ intel . com >
*/
#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 = 0 x07,
};
static const struct freq_desc freq_desc_clv = {
.use_msr_plat = false ,
.freqs = { 0 , 133200 , 0 , 0 , 0 , 99840 , 0 , 83200 },
.mask = 0 x07,
};
/*
* 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 = 0 x07,
};
/*
* 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 = 0 x0f,
};
/*
* 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 = 0 x07,
};
/*
* 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 = 0 x0f,
};
/*
* 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 = 0 x0f,
};
static const struct x86_cpu_id tsc_msr_cpu_ids[] = {
X86_MATCH_VFM(INTEL_ATOM_SALTWELL_MID, &freq_desc_pnw),
X86_MATCH_VFM(INTEL_ATOM_SALTWELL_TABLET, &freq_desc_clv),
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT, &freq_desc_byt),
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID, &freq_desc_tng),
X86_MATCH_VFM(INTEL_ATOM_AIRMONT, &freq_desc_cht),
X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID2, &freq_desc_ann),
X86_MATCH_VFM(INTEL_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 ) & 0 xff;
} else {
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
ratio = (hi >> 8 ) & 0 x1f;
}
/* 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;
}
Messung V0.5 in Prozent C=96 H=99 G=97
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland