2018-06-29 22:31:11 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-10-21 09:16:33 -07:00
/*
2018-06-29 22:31:11 +03:00
* TSC frequency enumeration via MSR
2013-10-21 09:16:33 -07:00
*
2018-06-29 22:31:11 +03:00
* Copyright ( C ) 2013 , 2018 Intel Corporation
2013-10-21 09:16:33 -07:00
* Author : Bin Gao < bin . gao @ intel . com >
*/
# include <linux/kernel.h>
2018-06-29 22:31:09 +03:00
2013-10-21 09:16:33 -07:00
# include <asm/apic.h>
2018-06-29 22:31:09 +03:00
# include <asm/cpu_device_id.h>
# include <asm/intel-family.h>
# include <asm/msr.h>
2013-10-21 09:16:33 -07:00
# include <asm/param.h>
2018-06-29 22:31:10 +03:00
# include <asm/tsc.h>
2013-10-21 09:16:33 -07:00
2016-06-17 01:22:48 -04:00
# define MAX_NUM_FREQS 9
2013-10-21 09:16:33 -07:00
/*
2016-06-17 01:22:46 -04:00
* If MSR_PERF_STAT [ 31 ] is set , the maximum resolved bus ratio can be
2013-10-21 09:16:33 -07:00
* 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 msr_plat ; /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
u32 freqs [ MAX_NUM_FREQS ] ;
} ;
2018-06-29 22:31:12 +03:00
/*
* Penwell and Clovertrail use spread spectrum clock ,
* so the freq number is not exactly the same as reported
* by MSR based on SDM .
*/
2018-06-29 22:31:09 +03:00
static const struct freq_desc freq_desc_pnw = {
0 , { 0 , 0 , 0 , 0 , 0 , 99840 , 0 , 83200 }
2013-10-21 09:16:33 -07:00
} ;
2018-06-29 22:31:09 +03:00
static const struct freq_desc freq_desc_clv = {
0 , { 0 , 133200 , 0 , 0 , 0 , 99840 , 0 , 83200 }
} ;
2013-10-21 09:16:33 -07:00
2018-06-29 22:31:09 +03:00
static const struct freq_desc freq_desc_byt = {
1 , { 83300 , 100000 , 133300 , 116700 , 80000 , 0 , 0 , 0 }
} ;
2013-10-21 09:16:33 -07:00
2018-06-29 22:31:09 +03:00
static const struct freq_desc freq_desc_cht = {
1 , { 83300 , 100000 , 133300 , 116700 , 80000 , 93300 , 90000 , 88900 , 87500 }
} ;
2013-10-21 09:16:33 -07:00
2018-06-29 22:31:09 +03:00
static const struct freq_desc freq_desc_tng = {
1 , { 0 , 100000 , 133300 , 0 , 0 , 0 , 0 , 0 }
} ;
static const struct freq_desc freq_desc_ann = {
1 , { 83300 , 100000 , 133300 , 100000 , 0 , 0 , 0 , 0 }
} ;
2019-09-05 12:30:20 -07:00
static const struct freq_desc freq_desc_lgm = {
1 , { 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 , 78000 }
} ;
2018-06-29 22:31:09 +03:00
static const struct x86_cpu_id tsc_msr_cpu_ids [ ] = {
2018-08-07 10:17:27 -07:00
INTEL_CPU_FAM6 ( ATOM_SALTWELL_MID , freq_desc_pnw ) ,
INTEL_CPU_FAM6 ( ATOM_SALTWELL_TABLET , freq_desc_clv ) ,
INTEL_CPU_FAM6 ( ATOM_SILVERMONT , freq_desc_byt ) ,
INTEL_CPU_FAM6 ( ATOM_SILVERMONT_MID , freq_desc_tng ) ,
2018-06-29 22:31:09 +03:00
INTEL_CPU_FAM6 ( ATOM_AIRMONT , freq_desc_cht ) ,
2018-08-07 10:17:27 -07:00
INTEL_CPU_FAM6 ( ATOM_AIRMONT_MID , freq_desc_ann ) ,
2019-09-05 12:30:20 -07:00
INTEL_CPU_FAM6 ( ATOM_AIRMONT_NP , freq_desc_lgm ) ,
2018-06-29 22:31:09 +03:00
{ }
} ;
2013-10-21 09:16:33 -07:00
/*
2016-06-17 01:22:45 -04:00
* MSR - based CPU / TSC frequency discovery for certain CPUs .
2014-02-19 13:52:29 +02:00
*
2019-05-09 13:54:16 +08:00
* Set global " lapic_timer_period " to bus_clock_cycles / jiffy
2016-06-17 01:22:45 -04:00
* Return processor base frequency in KHz , or 0 on failure .
2013-10-21 09:16:33 -07:00
*/
2016-06-17 01:22:50 -04:00
unsigned long cpu_khz_from_msr ( void )
2013-10-21 09:16:33 -07:00
{
2018-06-29 22:31:09 +03:00
u32 lo , hi , ratio , freq ;
const struct freq_desc * freq_desc ;
const struct x86_cpu_id * id ;
2014-02-19 13:52:29 +02:00
unsigned long res ;
2013-10-21 09:16:33 -07:00
2018-06-29 22:31:09 +03:00
id = x86_match_cpu ( tsc_msr_cpu_ids ) ;
if ( ! id )
2016-06-17 01:22:44 -04:00
return 0 ;
2018-06-29 22:31:09 +03:00
freq_desc = ( struct freq_desc * ) id - > driver_data ;
if ( freq_desc - > msr_plat ) {
2013-10-21 09:16:33 -07:00
rdmsr ( MSR_PLATFORM_INFO , lo , hi ) ;
2016-05-06 11:33:39 +08:00
ratio = ( lo > > 8 ) & 0xff ;
2013-10-21 09:16:33 -07:00
} else {
rdmsr ( MSR_IA32_PERF_STATUS , lo , hi ) ;
ratio = ( hi > > 8 ) & 0x1f ;
}
/* Get FSB FREQ ID */
rdmsr ( MSR_FSB_FREQ , lo , hi ) ;
2018-06-29 22:31:09 +03:00
/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
freq = freq_desc - > freqs [ lo & 0x7 ] ;
2013-10-21 09:16:33 -07:00
/* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
2014-02-19 13:52:29 +02:00
res = freq * ratio ;
2013-10-21 09:16:33 -07:00
2014-01-16 13:00:21 -08:00
# ifdef CONFIG_X86_LOCAL_APIC
2019-05-09 13:54:16 +08:00
lapic_timer_period = ( freq * 1000 ) / HZ ;
2014-01-16 13:00:21 -08:00
# endif
2016-11-15 12:27:24 -08:00
/*
* 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 ) ;
2014-02-19 13:52:29 +02:00
return res ;
2013-10-21 09:16:33 -07:00
}