2005-04-17 02:20:36 +04:00
/*
* ( C ) 2002 - 2003 Dominik Brodowski < linux @ brodo . de >
*
* Licensed under the terms of the GNU GPL License version 2.
*
* Library for common functions for Intel SpeedStep v .1 and v .2 support
*
* BIG FAT DISCLAIMER : Work in progress code . Possibly * dangerous *
*/
# include <linux/kernel.h>
2006-02-28 08:43:23 +03:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
# include <linux/slab.h>
# include <asm/msr.h>
# include "speedstep-lib.h"
2009-01-18 07:55:22 +03:00
# define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
" speedstep-lib " , msg )
# define PFX "speedstep-lib: "
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
2009-01-18 07:55:22 +03:00
static int relaxed_check ;
2005-04-17 02:20:36 +04:00
# else
# define relaxed_check 0
# endif
/*********************************************************************
* GET PROCESSOR CORE SPEED IN KHZ *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-18 07:55:22 +03:00
static unsigned int pentium3_get_frequency ( unsigned int processor )
2005-04-17 02:20:36 +04:00
{
2009-01-18 07:55:22 +03:00
/* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
2005-04-17 02:20:36 +04:00
struct {
unsigned int ratio ; /* Frequency Multiplier (x10) */
2006-02-28 08:43:23 +03:00
u8 bitmap ; /* power on configuration bits
[ 27 , 25 : 22 ] ( in MSR 0x2a ) */
2009-01-18 07:55:22 +03:00
} msr_decode_mult [ ] = {
2005-04-17 02:20:36 +04:00
{ 30 , 0x01 } ,
{ 35 , 0x05 } ,
{ 40 , 0x02 } ,
{ 45 , 0x06 } ,
{ 50 , 0x00 } ,
{ 55 , 0x04 } ,
{ 60 , 0x0b } ,
{ 65 , 0x0f } ,
{ 70 , 0x09 } ,
{ 75 , 0x0d } ,
{ 80 , 0x0a } ,
{ 85 , 0x26 } ,
{ 90 , 0x20 } ,
{ 100 , 0x2b } ,
2009-01-18 07:55:22 +03:00
{ 0 , 0xff } /* error or unknown value */
2005-04-17 02:20:36 +04:00
} ;
/* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */
struct {
2006-02-28 08:43:23 +03:00
unsigned int value ; /* Front Side Bus speed in MHz */
u8 bitmap ; /* power on configuration bits [18: 19]
( in MSR 0x2a ) */
2009-01-18 07:55:22 +03:00
} msr_decode_fsb [ ] = {
2005-04-17 02:20:36 +04:00
{ 66 , 0x0 } ,
{ 100 , 0x2 } ,
{ 133 , 0x1 } ,
{ 0 , 0xff }
} ;
2006-02-28 08:43:23 +03:00
u32 msr_lo , msr_tmp ;
int i = 0 , j = 0 ;
2005-04-17 02:20:36 +04:00
/* read MSR 0x2a - we only need the low 32 bits */
rdmsr ( MSR_IA32_EBL_CR_POWERON , msr_lo , msr_tmp ) ;
dprintk ( " P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x \n " , msr_lo , msr_tmp ) ;
msr_tmp = msr_lo ;
/* decode the FSB */
msr_tmp & = 0x00c0000 ;
msr_tmp > > = 18 ;
while ( msr_tmp ! = msr_decode_fsb [ i ] . bitmap ) {
if ( msr_decode_fsb [ i ] . bitmap = = 0xff )
return 0 ;
i + + ;
}
/* decode the multiplier */
2009-01-18 07:55:22 +03:00
if ( processor = = SPEEDSTEP_CPU_PIII_C_EARLY ) {
2005-04-17 02:20:36 +04:00
dprintk ( " workaround for early PIIIs \n " ) ;
msr_lo & = 0x03c00000 ;
} else
msr_lo & = 0x0bc00000 ;
msr_lo > > = 22 ;
while ( msr_lo ! = msr_decode_mult [ j ] . bitmap ) {
if ( msr_decode_mult [ j ] . bitmap = = 0xff )
return 0 ;
j + + ;
}
2009-01-18 07:55:22 +03:00
dprintk ( " speed is %u \n " ,
( msr_decode_mult [ j ] . ratio * msr_decode_fsb [ i ] . value * 100 ) ) ;
2005-04-17 02:20:36 +04:00
2009-01-18 07:55:22 +03:00
return msr_decode_mult [ j ] . ratio * msr_decode_fsb [ i ] . value * 100 ;
2005-04-17 02:20:36 +04:00
}
static unsigned int pentiumM_get_frequency ( void )
{
2006-02-28 08:43:23 +03:00
u32 msr_lo , msr_tmp ;
2005-04-17 02:20:36 +04:00
rdmsr ( MSR_IA32_EBL_CR_POWERON , msr_lo , msr_tmp ) ;
dprintk ( " PM - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x \n " , msr_lo , msr_tmp ) ;
/* see table B-2 of 24547212.pdf */
if ( msr_lo & 0x00040000 ) {
2009-01-18 07:55:22 +03:00
printk ( KERN_DEBUG PFX " PM - invalid FSB: 0x%x 0x%x \n " ,
msr_lo , msr_tmp ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
msr_tmp = ( msr_lo > > 22 ) & 0x1f ;
2009-01-18 07:55:22 +03:00
dprintk ( " bits 22-26 are 0x%x, speed is %u \n " ,
msr_tmp , ( msr_tmp * 100 * 1000 ) ) ;
2005-04-17 02:20:36 +04:00
2009-01-18 07:55:22 +03:00
return msr_tmp * 100 * 1000 ;
2005-04-17 02:20:36 +04:00
}
2006-10-31 20:44:08 +03:00
static unsigned int pentium_core_get_frequency ( void )
{
u32 fsb = 0 ;
u32 msr_lo , msr_tmp ;
2009-01-18 07:55:22 +03:00
int ret ;
2006-10-31 20:44:08 +03:00
rdmsr ( MSR_FSB_FREQ , msr_lo , msr_tmp ) ;
2006-12-05 04:39:16 +03:00
/* see table B-2 of 25366920.pdf */
2006-10-31 20:44:08 +03:00
switch ( msr_lo & 0x07 ) {
case 5 :
2006-12-05 04:39:16 +03:00
fsb = 100000 ;
2006-10-31 20:44:08 +03:00
break ;
case 1 :
2006-12-05 04:39:16 +03:00
fsb = 133333 ;
2006-10-31 20:44:08 +03:00
break ;
case 3 :
2006-12-05 04:39:16 +03:00
fsb = 166667 ;
2006-10-31 20:44:08 +03:00
break ;
2008-11-15 22:02:46 +03:00
case 2 :
fsb = 200000 ;
break ;
case 0 :
fsb = 266667 ;
break ;
case 4 :
fsb = 333333 ;
break ;
2006-10-31 20:44:08 +03:00
default :
printk ( KERN_ERR " PCORE - MSR_FSB_FREQ undefined value " ) ;
}
rdmsr ( MSR_IA32_EBL_CR_POWERON , msr_lo , msr_tmp ) ;
2009-01-18 07:55:22 +03:00
dprintk ( " PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x \n " ,
msr_lo , msr_tmp ) ;
2006-10-31 20:44:08 +03:00
msr_tmp = ( msr_lo > > 22 ) & 0x1f ;
2009-01-18 07:55:22 +03:00
dprintk ( " bits 22-26 are 0x%x, speed is %u \n " ,
msr_tmp , ( msr_tmp * fsb ) ) ;
2006-10-31 20:44:08 +03:00
2009-01-18 07:55:22 +03:00
ret = ( msr_tmp * fsb ) ;
return ret ;
2006-10-31 20:44:08 +03:00
}
2006-12-05 04:39:16 +03:00
2005-04-17 02:20:36 +04:00
static unsigned int pentium4_get_frequency ( void )
{
struct cpuinfo_x86 * c = & boot_cpu_data ;
u32 msr_lo , msr_hi , mult ;
unsigned int fsb = 0 ;
2009-01-18 07:55:22 +03:00
unsigned int ret ;
2005-04-17 02:20:36 +04:00
rdmsr ( 0x2c , msr_lo , msr_hi ) ;
dprintk ( " P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x \n " , msr_lo , msr_hi ) ;
2006-02-28 08:43:23 +03:00
/* decode the FSB: see IA-32 Intel (C) Architecture Software
2005-04-17 02:20:36 +04:00
* Developer ' s Manual , Volume 3 : System Prgramming Guide ,
* revision # 12 in Table B - 1 : MSRs in the Pentium 4 and
* Intel Xeon Processors , on page B - 4 and B - 5.
*/
if ( c - > x86_model < 2 )
fsb = 100 * 1000 ;
else {
u8 fsb_code = ( msr_lo > > 16 ) & 0x7 ;
switch ( fsb_code ) {
case 0 :
fsb = 100 * 1000 ;
break ;
case 1 :
fsb = 13333 * 10 ;
break ;
case 2 :
fsb = 200 * 1000 ;
break ;
}
}
if ( ! fsb )
2009-01-18 07:55:22 +03:00
printk ( KERN_DEBUG PFX " couldn't detect FSB speed. "
" Please send an e-mail to <linux@brodo.de> \n " ) ;
2005-04-17 02:20:36 +04:00
/* Multiplier. */
2007-11-20 22:20:21 +03:00
mult = msr_lo > > 24 ;
2005-04-17 02:20:36 +04:00
2009-01-18 07:55:22 +03:00
dprintk ( " P4 - FSB %u kHz; Multiplier %u; Speed %u kHz \n " ,
fsb , mult , ( fsb * mult ) ) ;
2005-04-17 02:20:36 +04:00
2009-01-18 07:55:22 +03:00
ret = ( fsb * mult ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-02-28 08:43:23 +03:00
2009-01-18 07:55:22 +03:00
unsigned int speedstep_get_frequency ( unsigned int processor )
2005-04-17 02:20:36 +04:00
{
switch ( processor ) {
2009-01-18 07:55:22 +03:00
case SPEEDSTEP_CPU_PCORE :
2006-10-31 20:44:08 +03:00
return pentium_core_get_frequency ( ) ;
2009-01-18 07:55:22 +03:00
case SPEEDSTEP_CPU_PM :
2005-04-17 02:20:36 +04:00
return pentiumM_get_frequency ( ) ;
2009-01-18 07:55:22 +03:00
case SPEEDSTEP_CPU_P4D :
case SPEEDSTEP_CPU_P4M :
2005-04-17 02:20:36 +04:00
return pentium4_get_frequency ( ) ;
2009-01-18 07:55:22 +03:00
case SPEEDSTEP_CPU_PIII_T :
case SPEEDSTEP_CPU_PIII_C :
case SPEEDSTEP_CPU_PIII_C_EARLY :
2005-04-17 02:20:36 +04:00
return pentium3_get_frequency ( processor ) ;
default :
return 0 ;
} ;
return 0 ;
}
2009-01-18 07:55:22 +03:00
EXPORT_SYMBOL_GPL ( speedstep_get_frequency ) ;
2005-04-17 02:20:36 +04:00
/*********************************************************************
* DETECT SPEEDSTEP - CAPABLE PROCESSOR *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-18 07:55:22 +03:00
unsigned int speedstep_detect_processor ( void )
2005-04-17 02:20:36 +04:00
{
2007-10-19 22:35:04 +04:00
struct cpuinfo_x86 * c = & cpu_data ( 0 ) ;
2006-02-28 08:43:23 +03:00
u32 ebx , msr_lo , msr_hi ;
2005-04-17 02:20:36 +04:00
dprintk ( " x86: %x, model: %x \n " , c - > x86 , c - > x86_model ) ;
2006-02-28 08:43:23 +03:00
if ( ( c - > x86_vendor ! = X86_VENDOR_INTEL ) | |
2005-04-17 02:20:36 +04:00
( ( c - > x86 ! = 6 ) & & ( c - > x86 ! = 0xF ) ) )
return 0 ;
if ( c - > x86 = = 0xF ) {
/* Intel Mobile Pentium 4-M
* or Intel Mobile Pentium 4 with 533 MHz FSB */
if ( c - > x86_model ! = 2 )
return 0 ;
ebx = cpuid_ebx ( 0x00000001 ) ;
ebx & = 0x000000FF ;
dprintk ( " ebx value is %x, x86_mask is %x \n " , ebx , c - > x86_mask ) ;
switch ( c - > x86_mask ) {
2006-02-28 08:43:23 +03:00
case 4 :
2005-04-17 02:20:36 +04:00
/*
2006-02-28 08:43:23 +03:00
* B - stepping [ M - P4 - M ]
2005-04-17 02:20:36 +04:00
* sample has ebx = 0x0f , production has 0x0e .
*/
if ( ( ebx = = 0x0e ) | | ( ebx = = 0x0f ) )
2009-01-18 07:55:22 +03:00
return SPEEDSTEP_CPU_P4M ;
2005-04-17 02:20:36 +04:00
break ;
2006-02-28 08:43:23 +03:00
case 7 :
2005-04-17 02:20:36 +04:00
/*
* C - stepping [ M - P4 - M ]
* needs to have ebx = 0x0e , else it ' s a celeron :
* cf . 25130917. pdf / page 7 , footnote 5 even
* though 25072120. pdf / page 7 doesn ' t say
* samples are only of B - stepping . . .
*/
if ( ebx = = 0x0e )
2009-01-18 07:55:22 +03:00
return SPEEDSTEP_CPU_P4M ;
2005-04-17 02:20:36 +04:00
break ;
case 9 :
/*
* D - stepping [ M - P4 - M or M - P4 / 533 ]
*
* this is totally strange : CPUID 0x0F29 is
* used by M - P4 - M , M - P4 / 533 and ( ! ) Celeron CPUs .
* The latter need to be sorted out as they don ' t
* support speedstep .
* Celerons with CPUID 0x0F29 may have either
* ebx = 0x8 or 0xf - - 25130917. pdf doesn ' t say anything
* specific .
* M - P4 - Ms may have either ebx = 0xe or 0xf [ see above ]
* M - P4 / 533 have either ebx = 0xe or 0xf . [ 25317607. pdf ]
* also , M - P4M HTs have ebx = 0x8 , too
2009-01-18 07:55:22 +03:00
* For now , they are distinguished by the model_id
* string
2005-04-17 02:20:36 +04:00
*/
2009-01-18 07:55:22 +03:00
if ( ( ebx = = 0x0e ) | |
( strstr ( c - > x86_model_id ,
" Mobile Intel(R) Pentium(R) 4 " ) ! = NULL ) )
return SPEEDSTEP_CPU_P4M ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
return 0 ;
}
switch ( c - > x86_model ) {
case 0x0B : /* Intel PIII [Tualatin] */
2009-01-18 07:55:22 +03:00
/* cpuid_ebx(1) is 0x04 for desktop PIII,
* 0x06 for mobile PIII - M */
2005-04-17 02:20:36 +04:00
ebx = cpuid_ebx ( 0x00000001 ) ;
dprintk ( " ebx is %x \n " , ebx ) ;
ebx & = 0x000000FF ;
if ( ebx ! = 0x06 )
return 0 ;
/* So far all PIII-M processors support SpeedStep. See
2006-02-28 08:43:23 +03:00
* Intel ' s 24540640. pdf of June 2003
2005-04-17 02:20:36 +04:00
*/
2009-01-18 07:55:22 +03:00
return SPEEDSTEP_CPU_PIII_T ;
2005-04-17 02:20:36 +04:00
case 0x08 : /* Intel PIII [Coppermine] */
/* all mobile PIII Coppermines have FSB 100 MHz
* = = > sort out a few desktop PIIIs . */
rdmsr ( MSR_IA32_EBL_CR_POWERON , msr_lo , msr_hi ) ;
2009-01-18 07:55:22 +03:00
dprintk ( " Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x \n " ,
msr_lo , msr_hi ) ;
2005-04-17 02:20:36 +04:00
msr_lo & = 0x00c0000 ;
if ( msr_lo ! = 0x0080000 )
return 0 ;
/*
* If the processor is a mobile version ,
* platform ID has bit 50 set
* it has SpeedStep technology if either
* bit 56 or 57 is set
*/
rdmsr ( MSR_IA32_PLATFORM_ID , msr_lo , msr_hi ) ;
2009-01-18 07:55:22 +03:00
dprintk ( " Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x \n " ,
msr_lo , msr_hi ) ;
if ( ( msr_hi & ( 1 < < 18 ) ) & &
( relaxed_check ? 1 : ( msr_hi & ( 3 < < 24 ) ) ) ) {
2005-04-17 02:20:36 +04:00
if ( c - > x86_mask = = 0x01 ) {
dprintk ( " early PIII version \n " ) ;
2009-01-18 07:55:22 +03:00
return SPEEDSTEP_CPU_PIII_C_EARLY ;
2005-04-17 02:20:36 +04:00
} else
2009-01-18 07:55:22 +03:00
return SPEEDSTEP_CPU_PIII_C ;
2005-04-17 02:20:36 +04:00
}
default :
return 0 ;
}
}
EXPORT_SYMBOL_GPL ( speedstep_detect_processor ) ;
/*********************************************************************
* DETECT SPEEDSTEP SPEEDS *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
unsigned int speedstep_get_freqs ( unsigned int processor ,
unsigned int * low_speed ,
unsigned int * high_speed ,
2005-12-02 23:59:41 +03:00
unsigned int * transition_latency ,
2005-04-17 02:20:36 +04:00
void ( * set_state ) ( unsigned int state ) )
{
unsigned int prev_speed ;
unsigned int ret = 0 ;
unsigned long flags ;
2005-12-02 23:59:41 +03:00
struct timeval tv1 , tv2 ;
2005-04-17 02:20:36 +04:00
if ( ( ! processor ) | | ( ! low_speed ) | | ( ! high_speed ) | | ( ! set_state ) )
return - EINVAL ;
dprintk ( " trying to determine both speeds \n " ) ;
/* get current speed */
2009-01-18 07:55:22 +03:00
prev_speed = speedstep_get_frequency ( processor ) ;
2005-04-17 02:20:36 +04:00
if ( ! prev_speed )
return - EIO ;
2005-06-01 06:03:52 +04:00
dprintk ( " previous speed is %u \n " , prev_speed ) ;
2005-12-02 23:59:41 +03:00
2005-04-17 02:20:36 +04:00
local_irq_save ( flags ) ;
/* switch to low state */
set_state ( SPEEDSTEP_LOW ) ;
2009-01-18 07:55:22 +03:00
* low_speed = speedstep_get_frequency ( processor ) ;
2005-04-17 02:20:36 +04:00
if ( ! * low_speed ) {
ret = - EIO ;
goto out ;
}
2005-06-01 06:03:52 +04:00
dprintk ( " low speed is %u \n " , * low_speed ) ;
2005-04-17 02:20:36 +04:00
2005-12-02 23:59:41 +03:00
/* start latency measurement */
if ( transition_latency )
do_gettimeofday ( & tv1 ) ;
2005-04-17 02:20:36 +04:00
/* switch to high state */
set_state ( SPEEDSTEP_HIGH ) ;
2005-12-02 23:59:41 +03:00
/* end latency measurement */
if ( transition_latency )
do_gettimeofday ( & tv2 ) ;
2009-01-18 07:55:22 +03:00
* high_speed = speedstep_get_frequency ( processor ) ;
2005-04-17 02:20:36 +04:00
if ( ! * high_speed ) {
ret = - EIO ;
goto out ;
}
2005-06-01 06:03:52 +04:00
dprintk ( " high speed is %u \n " , * high_speed ) ;
2005-04-17 02:20:36 +04:00
if ( * low_speed = = * high_speed ) {
ret = - ENODEV ;
goto out ;
}
/* switch to previous state, if necessary */
if ( * high_speed ! = prev_speed )
set_state ( SPEEDSTEP_LOW ) ;
2005-12-02 23:59:41 +03:00
if ( transition_latency ) {
* transition_latency = ( tv2 . tv_sec - tv1 . tv_sec ) * USEC_PER_SEC +
tv2 . tv_usec - tv1 . tv_usec ;
dprintk ( " transition latency is %u uSec \n " , * transition_latency ) ;
/* convert uSec to nSec and add 20% for safety reasons */
* transition_latency * = 1200 ;
/* check if the latency measurement is too high or too low
* and set it to a safe value ( 500u Sec ) in that case
*/
2009-01-18 07:55:22 +03:00
if ( * transition_latency > 10000000 | |
* transition_latency < 50000 ) {
printk ( KERN_WARNING PFX " frequency transition "
" measured seems out of range (%u "
" nSec), falling back to a safe one of "
" %u nSec. \n " ,
2005-12-02 23:59:41 +03:00
* transition_latency , 500000 ) ;
* transition_latency = 500000 ;
}
}
2006-02-28 08:43:23 +03:00
out :
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
2009-01-18 07:55:22 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( speedstep_get_freqs ) ;
# ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
module_param ( relaxed_check , int , 0444 ) ;
2009-01-18 07:55:22 +03:00
MODULE_PARM_DESC ( relaxed_check ,
" Don't do all checks for speedstep capability. " ) ;
2005-04-17 02:20:36 +04:00
# endif
2009-01-18 07:55:22 +03:00
MODULE_AUTHOR ( " Dominik Brodowski <linux@brodo.de> " ) ;
MODULE_DESCRIPTION ( " Library for Intel SpeedStep 1 or 2 cpufreq drivers. " ) ;
MODULE_LICENSE ( " GPL " ) ;