2005-04-17 02:20:36 +04:00
/*
* AMD K7 Powernow driver .
2008-10-20 21:31:45 +04:00
* ( C ) 2003 Dave Jones on behalf of SuSE Labs .
2005-04-17 02:20:36 +04:00
* ( C ) 2003 - 2004 Dave Jones < davej @ redhat . com >
*
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD .
*
2009-01-18 08:32:26 +03:00
* Errata 5 :
* CPU may fail to execute a FID / VID change in presence of interrupt .
* - We cli / sti on stepping A0 CPUs around the FID / VID transition .
* Errata 15 :
* CPU with half frequency multipliers may hang upon wakeup from disconnect .
* - We disable half multipliers if ACPI is used on A0 stepping CPUs .
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/dmi.h>
2009-01-18 08:32:26 +03:00
# include <linux/timex.h>
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
2009-01-18 08:32:26 +03:00
# include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
2005-04-17 02:20:36 +04:00
# include <asm/msr.h>
2012-01-26 03:09:12 +04:00
# include <asm/cpu_device_id.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
# include <linux/acpi.h>
# include <acpi/processor.h>
# endif
# include "powernow-k7.h"
# define PFX "powernow: "
struct psb_s {
u8 signature [ 10 ] ;
u8 tableversion ;
u8 flags ;
u16 settlingtime ;
u8 reserved1 ;
u8 numpst ;
} ;
struct pst_s {
u32 cpuid ;
u8 fsbspeed ;
u8 maxfid ;
u8 startvid ;
u8 numpstates ;
} ;
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
union powernow_acpi_control_t {
struct {
unsigned long fid : 5 ,
2009-01-18 08:32:26 +03:00
vid : 5 ,
sgtc : 20 ,
res1 : 2 ;
2005-04-17 02:20:36 +04:00
} bits ;
unsigned long val ;
} ;
# endif
/* divide by 1000 to get VCore voltage in V. */
2007-02-23 03:11:16 +03:00
static const int mobile_vid_table [ 32 ] = {
2005-04-17 02:20:36 +04:00
2000 , 1950 , 1900 , 1850 , 1800 , 1750 , 1700 , 1650 ,
1600 , 1550 , 1500 , 1450 , 1400 , 1350 , 1300 , 0 ,
1275 , 1250 , 1225 , 1200 , 1175 , 1150 , 1125 , 1100 ,
1075 , 1050 , 1025 , 1000 , 975 , 950 , 925 , 0 ,
} ;
/* divide by 10 to get FID. */
2007-02-23 03:11:16 +03:00
static const int fid_codes [ 32 ] = {
2005-04-17 02:20:36 +04:00
110 , 115 , 120 , 125 , 50 , 55 , 60 , 65 ,
70 , 75 , 80 , 85 , 90 , 95 , 100 , 105 ,
30 , 190 , 40 , 200 , 130 , 135 , 140 , 210 ,
150 , 225 , 160 , 165 , 170 , 180 , - 1 , - 1 ,
} ;
/* This parameter is used in order to force ACPI instead of legacy method for
* configuration purpose .
*/
static int acpi_force ;
static struct cpufreq_frequency_table * powernow_table ;
static unsigned int can_scale_bus ;
static unsigned int can_scale_vid ;
2009-01-18 06:28:42 +03:00
static unsigned int minimum_speed = - 1 ;
2005-04-17 02:20:36 +04:00
static unsigned int maximum_speed ;
static unsigned int number_scales ;
static unsigned int fsb ;
static unsigned int latency ;
static char have_a0 ;
static int check_fsb ( unsigned int fsbspeed )
{
int delta ;
unsigned int f = fsb / 1000 ;
delta = ( fsbspeed > f ) ? fsbspeed - f : f - fsbspeed ;
2009-01-18 08:32:26 +03:00
return delta < 5 ;
2005-04-17 02:20:36 +04:00
}
2012-01-26 03:09:12 +04:00
static const struct x86_cpu_id powernow_k7_cpuids [ ] = {
2012-02-12 02:58:14 +04:00
{ X86_VENDOR_AMD , 6 , } ,
2012-01-26 03:09:12 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , powernow_k7_cpuids ) ;
2005-04-17 02:20:36 +04:00
static int check_powernow ( void )
{
2007-10-19 22:35:04 +04:00
struct cpuinfo_x86 * c = & cpu_data ( 0 ) ;
2005-04-17 02:20:36 +04:00
unsigned int maxei , eax , ebx , ecx , edx ;
2012-01-26 03:09:12 +04:00
if ( ! x86_match_cpu ( powernow_k7_cpuids ) )
2005-04-17 02:20:36 +04:00
return 0 ;
/* Get maximum capabilities */
2009-01-18 08:32:26 +03:00
maxei = cpuid_eax ( 0x80000000 ) ;
2005-04-17 02:20:36 +04:00
if ( maxei < 0x80000007 ) { /* Any powernow info ? */
# ifdef MODULE
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " No powernow capabilities detected \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
}
if ( ( c - > x86_model = = 6 ) & & ( c - > x86_mask = = 0 ) ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " K7 660[A0] core detected, "
" enabling errata workarounds \n " ) ;
2005-04-17 02:20:36 +04:00
have_a0 = 1 ;
}
cpuid ( 0x80000007 , & eax , & ebx , & ecx , & edx ) ;
/* Check we can actually do something before we say anything.*/
if ( ! ( edx & ( 1 < < 1 | 1 < < 2 ) ) )
return 0 ;
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " PowerNOW! Technology present. Can scale: " ) ;
2005-04-17 02:20:36 +04:00
if ( edx & 1 < < 1 ) {
2009-01-18 08:32:26 +03:00
printk ( " frequency " ) ;
can_scale_bus = 1 ;
2005-04-17 02:20:36 +04:00
}
if ( ( edx & ( 1 < < 1 | 1 < < 2 ) ) = = 0x6 )
2009-01-18 08:32:26 +03:00
printk ( " and " ) ;
2005-04-17 02:20:36 +04:00
if ( edx & 1 < < 2 ) {
2009-01-18 08:32:26 +03:00
printk ( " voltage " ) ;
can_scale_vid = 1 ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 08:32:26 +03:00
printk ( " . \n " ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2009-04-23 21:36:12 +04:00
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
2009-01-18 08:32:26 +03:00
static void invalidate_entry ( unsigned int entry )
{
powernow_table [ entry ] . frequency = CPUFREQ_ENTRY_INVALID ;
}
2009-04-23 21:36:12 +04:00
# endif
2005-04-17 02:20:36 +04:00
2009-01-18 08:32:26 +03:00
static int get_ranges ( unsigned char * pst )
2005-04-17 02:20:36 +04:00
{
unsigned int j ;
unsigned int speed ;
u8 fid , vid ;
2013-08-06 21:23:06 +04:00
powernow_table = kzalloc ( ( sizeof ( * powernow_table ) *
2009-01-18 08:32:26 +03:00
( number_scales + 1 ) ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! powernow_table )
return - ENOMEM ;
2009-01-18 08:32:26 +03:00
for ( j = 0 ; j < number_scales ; j + + ) {
2005-04-17 02:20:36 +04:00
fid = * pst + + ;
powernow_table [ j ] . frequency = ( fsb * fid_codes [ fid ] ) / 10 ;
2013-03-30 14:55:15 +04:00
powernow_table [ j ] . driver_data = fid ; /* lower 8 bits */
2005-04-17 02:20:36 +04:00
speed = powernow_table [ j ] . frequency ;
2009-01-18 08:32:26 +03:00
if ( ( fid_codes [ fid ] % 10 ) = = 5 ) {
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
if ( have_a0 = = 1 )
2009-01-18 08:32:26 +03:00
invalidate_entry ( j ) ;
2005-04-17 02:20:36 +04:00
# endif
}
if ( speed < minimum_speed )
minimum_speed = speed ;
if ( speed > maximum_speed )
maximum_speed = speed ;
vid = * pst + + ;
2013-03-30 14:55:15 +04:00
powernow_table [ j ] . driver_data | = ( vid < < 8 ) ; /* upper 8 bits */
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " FID: 0x%x (%d.%dx [%dMHz]) "
2006-02-28 08:43:23 +03:00
" VID: 0x%x (%d.%03dV) \n " , fid , fid_codes [ fid ] / 10 ,
fid_codes [ fid ] % 10 , speed / 1000 , vid ,
2005-04-17 02:20:36 +04:00
mobile_vid_table [ vid ] / 1000 ,
mobile_vid_table [ vid ] % 1000 ) ;
}
powernow_table [ number_scales ] . frequency = CPUFREQ_TABLE_END ;
2013-03-30 14:55:15 +04:00
powernow_table [ number_scales ] . driver_data = 0 ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void change_FID ( int fid )
{
union msr_fidvidctl fidvidctl ;
2009-01-18 08:32:26 +03:00
rdmsrl ( MSR_K7_FID_VID_CTL , fidvidctl . val ) ;
2005-04-17 02:20:36 +04:00
if ( fidvidctl . bits . FID ! = fid ) {
fidvidctl . bits . SGTC = latency ;
fidvidctl . bits . FID = fid ;
fidvidctl . bits . VIDC = 0 ;
fidvidctl . bits . FIDC = 1 ;
2009-01-18 08:32:26 +03:00
wrmsrl ( MSR_K7_FID_VID_CTL , fidvidctl . val ) ;
2005-04-17 02:20:36 +04:00
}
}
static void change_VID ( int vid )
{
union msr_fidvidctl fidvidctl ;
2009-01-18 08:32:26 +03:00
rdmsrl ( MSR_K7_FID_VID_CTL , fidvidctl . val ) ;
2005-04-17 02:20:36 +04:00
if ( fidvidctl . bits . VID ! = vid ) {
fidvidctl . bits . SGTC = latency ;
fidvidctl . bits . VID = vid ;
fidvidctl . bits . FIDC = 0 ;
fidvidctl . bits . VIDC = 1 ;
2009-01-18 08:32:26 +03:00
wrmsrl ( MSR_K7_FID_VID_CTL , fidvidctl . val ) ;
2005-04-17 02:20:36 +04:00
}
}
2013-10-25 18:15:48 +04:00
static int powernow_target ( struct cpufreq_policy * policy , unsigned int index )
2005-04-17 02:20:36 +04:00
{
u8 fid , vid ;
struct cpufreq_freqs freqs ;
union msr_fidvidstatus fidvidstatus ;
int cfid ;
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in powernow_decode_bios ,
* vid are the upper 8 bits .
*/
2013-03-30 14:55:15 +04:00
fid = powernow_table [ index ] . driver_data & 0xFF ;
vid = ( powernow_table [ index ] . driver_data & 0xFF00 ) > > 8 ;
2005-04-17 02:20:36 +04:00
2009-01-18 08:32:26 +03:00
rdmsrl ( MSR_K7_FID_VID_STATUS , fidvidstatus . val ) ;
2005-04-17 02:20:36 +04:00
cfid = fidvidstatus . bits . CFID ;
freqs . old = fsb * fid_codes [ cfid ] / 10 ;
freqs . new = powernow_table [ index ] . frequency ;
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_PRECHANGE ) ;
2005-04-17 02:20:36 +04:00
/* Now do the magic poking into the MSRs. */
if ( have_a0 = = 1 ) /* A0 errata 5 */
local_irq_disable ( ) ;
if ( freqs . old > freqs . new ) {
/* Going down, so change FID first */
change_FID ( fid ) ;
change_VID ( vid ) ;
} else {
/* Going up, so change VID first */
change_VID ( vid ) ;
change_FID ( fid ) ;
}
if ( have_a0 = = 1 )
local_irq_enable ( ) ;
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_POSTCHANGE ) ;
2013-10-25 18:15:48 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
static struct acpi_processor_performance * acpi_processor_perf ;
static int powernow_acpi_init ( void )
{
int i ;
int retval = 0 ;
union powernow_acpi_control_t pc ;
if ( acpi_processor_perf ! = NULL & & powernow_table ! = NULL ) {
retval = - EINVAL ;
goto err0 ;
}
2013-08-06 21:23:06 +04:00
acpi_processor_perf = kzalloc ( sizeof ( * acpi_processor_perf ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_processor_perf ) {
retval = - ENOMEM ;
goto err0 ;
}
2009-06-07 01:51:36 +04:00
if ( ! zalloc_cpumask_var ( & acpi_processor_perf - > shared_cpu_map ,
2009-01-01 05:08:47 +03:00
GFP_KERNEL ) ) {
retval = - ENOMEM ;
goto err05 ;
}
2005-04-17 02:20:36 +04:00
if ( acpi_processor_register_performance ( acpi_processor_perf , 0 ) ) {
retval = - EIO ;
goto err1 ;
}
2009-01-18 08:32:26 +03:00
if ( acpi_processor_perf - > control_register . space_id ! =
ACPI_ADR_SPACE_FIXED_HARDWARE ) {
2005-04-17 02:20:36 +04:00
retval = - ENODEV ;
goto err2 ;
}
2009-01-18 08:32:26 +03:00
if ( acpi_processor_perf - > status_register . space_id ! =
ACPI_ADR_SPACE_FIXED_HARDWARE ) {
2005-04-17 02:20:36 +04:00
retval = - ENODEV ;
goto err2 ;
}
number_scales = acpi_processor_perf - > state_count ;
if ( number_scales < 2 ) {
retval = - ENODEV ;
goto err2 ;
}
2013-08-06 21:23:06 +04:00
powernow_table = kzalloc ( ( sizeof ( * powernow_table ) *
2009-01-18 08:32:26 +03:00
( number_scales + 1 ) ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! powernow_table ) {
retval = - ENOMEM ;
goto err2 ;
}
pc . val = ( unsigned long ) acpi_processor_perf - > states [ 0 ] . control ;
for ( i = 0 ; i < number_scales ; i + + ) {
u8 fid , vid ;
2007-05-03 02:19:05 +04:00
struct acpi_processor_px * state =
& acpi_processor_perf - > states [ i ] ;
unsigned int speed , speed_mhz ;
2005-04-17 02:20:36 +04:00
2007-05-03 02:19:05 +04:00
pc . val = ( unsigned long ) state - > control ;
2011-03-27 17:04:46 +04:00
pr_debug ( " acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d \n " ,
2005-04-17 02:20:36 +04:00
i ,
2007-05-03 02:19:05 +04:00
( u32 ) state - > core_frequency ,
( u32 ) state - > power ,
( u32 ) state - > transition_latency ,
( u32 ) state - > control ,
2005-04-17 02:20:36 +04:00
pc . bits . sgtc ) ;
vid = pc . bits . vid ;
fid = pc . bits . fid ;
powernow_table [ i ] . frequency = fsb * fid_codes [ fid ] / 10 ;
2013-03-30 14:55:15 +04:00
powernow_table [ i ] . driver_data = fid ; /* lower 8 bits */
powernow_table [ i ] . driver_data | = ( vid < < 8 ) ; /* upper 8 bits */
2005-04-17 02:20:36 +04:00
speed = powernow_table [ i ] . frequency ;
2007-05-03 02:19:05 +04:00
speed_mhz = speed / 1000 ;
/* processor_perflib will multiply the MHz value by 1000 to
* get a KHz value ( e . g . 1266000 ) . However , powernow - k7 works
* with true KHz values ( e . g . 1266768 ) . To ensure that all
* powernow frequencies are available , we must ensure that
* ACPI doesn ' t restrict them , so we round up the MHz value
* to ensure that perflib ' s computed KHz value is greater than
* or equal to powernow ' s KHz value .
*/
if ( speed % 1000 > 0 )
speed_mhz + + ;
2005-04-17 02:20:36 +04:00
2009-01-18 08:32:26 +03:00
if ( ( fid_codes [ fid ] % 10 ) = = 5 ) {
2005-04-17 02:20:36 +04:00
if ( have_a0 = = 1 )
2009-01-18 08:32:26 +03:00
invalidate_entry ( i ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-27 17:04:46 +04:00
pr_debug ( " FID: 0x%x (%d.%dx [%dMHz]) "
2006-02-28 08:43:23 +03:00
" VID: 0x%x (%d.%03dV) \n " , fid , fid_codes [ fid ] / 10 ,
2007-05-03 02:19:05 +04:00
fid_codes [ fid ] % 10 , speed_mhz , vid ,
2005-04-17 02:20:36 +04:00
mobile_vid_table [ vid ] / 1000 ,
mobile_vid_table [ vid ] % 1000 ) ;
2007-05-03 02:19:05 +04:00
if ( state - > core_frequency ! = speed_mhz ) {
state - > core_frequency = speed_mhz ;
2011-03-27 17:04:46 +04:00
pr_debug ( " Corrected ACPI frequency to %d \n " ,
2007-05-03 02:19:05 +04:00
speed_mhz ) ;
}
2005-04-17 02:20:36 +04:00
if ( latency < pc . bits . sgtc )
latency = pc . bits . sgtc ;
if ( speed < minimum_speed )
minimum_speed = speed ;
if ( speed > maximum_speed )
maximum_speed = speed ;
}
powernow_table [ i ] . frequency = CPUFREQ_TABLE_END ;
2013-03-30 14:55:15 +04:00
powernow_table [ i ] . driver_data = 0 ;
2005-04-17 02:20:36 +04:00
/* notify BIOS that we exist */
acpi_processor_notify_smm ( THIS_MODULE ) ;
return 0 ;
err2 :
acpi_processor_unregister_performance ( acpi_processor_perf , 0 ) ;
err1 :
2009-01-01 05:08:47 +03:00
free_cpumask_var ( acpi_processor_perf - > shared_cpu_map ) ;
err05 :
2005-04-17 02:20:36 +04:00
kfree ( acpi_processor_perf ) ;
err0 :
2009-01-18 08:32:26 +03:00
printk ( KERN_WARNING PFX " ACPI perflib can not be used on "
" this platform \n " ) ;
2005-04-17 02:20:36 +04:00
acpi_processor_perf = NULL ;
return retval ;
}
# else
static int powernow_acpi_init ( void )
{
printk ( KERN_INFO PFX " no support for ACPI processor found. "
" Please recompile your kernel with ACPI processor \n " ) ;
return - EINVAL ;
}
# endif
2009-01-18 08:32:26 +03:00
static void print_pst_entry ( struct pst_s * pst , unsigned int j )
{
2011-03-27 17:04:46 +04:00
pr_debug ( " PST:%d (@%p) \n " , j , pst ) ;
pr_debug ( " cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x \n " ,
2009-01-18 08:32:26 +03:00
pst - > cpuid , pst - > fsbspeed , pst - > maxfid , pst - > startvid ) ;
}
static int powernow_decode_bios ( int maxfid , int startvid )
2005-04-17 02:20:36 +04:00
{
struct psb_s * psb ;
struct pst_s * pst ;
unsigned int i , j ;
unsigned char * p ;
unsigned int etuple ;
unsigned int ret ;
etuple = cpuid_eax ( 0x80000001 ) ;
2009-01-18 08:32:26 +03:00
for ( i = 0xC0000 ; i < 0xffff0 ; i + = 16 ) {
2005-04-17 02:20:36 +04:00
p = phys_to_virt ( i ) ;
2009-01-18 08:32:26 +03:00
if ( memcmp ( p , " AMDK7PNOW! " , 10 ) = = 0 ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " Found PSB header at %p \n " , p ) ;
2005-04-17 02:20:36 +04:00
psb = ( struct psb_s * ) p ;
2011-03-27 17:04:46 +04:00
pr_debug ( " Table version: 0x%x \n " , psb - > tableversion ) ;
2005-04-17 02:20:36 +04:00
if ( psb - > tableversion ! = 0x12 ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " Sorry, only v1.2 tables "
" supported right now \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " Flags: 0x%x \n " , psb - > flags ) ;
2009-01-18 08:32:26 +03:00
if ( ( psb - > flags & 1 ) = = 0 )
2011-03-27 17:04:46 +04:00
pr_debug ( " Mobile voltage regulator \n " ) ;
2009-01-18 08:32:26 +03:00
else
2011-03-27 17:04:46 +04:00
pr_debug ( " Desktop voltage regulator \n " ) ;
2005-04-17 02:20:36 +04:00
latency = psb - > settlingtime ;
if ( latency < 100 ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " BIOS set settling time "
" to %d microseconds. "
" Should be at least 100. "
" Correcting. \n " , latency ) ;
2005-04-17 02:20:36 +04:00
latency = 100 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " Settling Time: %d microseconds. \n " ,
2009-01-18 08:32:26 +03:00
psb - > settlingtime ) ;
2011-03-27 17:04:46 +04:00
pr_debug ( " Has %d PST tables. (Only dumping ones "
2009-01-18 08:32:26 +03:00
" relevant to this CPU). \n " ,
psb - > numpst ) ;
2005-04-17 02:20:36 +04:00
2013-08-06 21:23:06 +04:00
p + = sizeof ( * psb ) ;
2005-04-17 02:20:36 +04:00
pst = ( struct pst_s * ) p ;
2009-01-18 08:32:26 +03:00
for ( j = 0 ; j < psb - > numpst ; j + + ) {
2005-04-17 02:20:36 +04:00
pst = ( struct pst_s * ) p ;
number_scales = pst - > numpstates ;
2009-01-18 08:32:26 +03:00
if ( ( etuple = = pst - > cpuid ) & &
check_fsb ( pst - > fsbspeed ) & &
( maxfid = = pst - > maxfid ) & &
( startvid = = pst - > startvid ) ) {
print_pst_entry ( pst , j ) ;
2013-08-06 21:23:06 +04:00
p = ( char * ) pst + sizeof ( * pst ) ;
2009-01-18 08:32:26 +03:00
ret = get_ranges ( p ) ;
2005-04-17 02:20:36 +04:00
return ret ;
} else {
2006-05-31 01:26:08 +04:00
unsigned int k ;
2013-08-06 21:23:06 +04:00
p = ( char * ) pst + sizeof ( * pst ) ;
2009-01-18 08:32:26 +03:00
for ( k = 0 ; k < number_scales ; k + + )
p + = 2 ;
2005-04-17 02:20:36 +04:00
}
}
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " No PST tables match this cpuid "
" (0x%x) \n " , etuple ) ;
printk ( KERN_INFO PFX " This is indicative of a broken "
" BIOS. \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
p + + ;
}
return - ENODEV ;
}
/*
* We use the fact that the bus frequency is somehow
* a multiple of 100000 / 3 khz , then we compute sgtc according
* to this multiple .
* That way , we match more how AMD thinks all of that work .
* We will then get the same kind of behaviour already tested under
* the " well-known " other OS .
*/
2013-06-19 21:54:04 +04:00
static int fixup_sgtc ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int sgtc ;
unsigned int m ;
m = fsb / 3333 ;
if ( ( m % 10 ) > = 5 )
m + = 5 ;
m / = 10 ;
sgtc = 100 * m * latency ;
sgtc = sgtc / 3 ;
if ( sgtc > 0xfffff ) {
printk ( KERN_WARNING PFX " SGTC too large %d \n " , sgtc ) ;
sgtc = 0xfffff ;
}
return sgtc ;
}
static unsigned int powernow_get ( unsigned int cpu )
{
union msr_fidvidstatus fidvidstatus ;
unsigned int cfid ;
if ( cpu )
return 0 ;
2009-01-18 08:32:26 +03:00
rdmsrl ( MSR_K7_FID_VID_STATUS , fidvidstatus . val ) ;
2005-04-17 02:20:36 +04:00
cfid = fidvidstatus . bits . CFID ;
2009-01-18 08:32:26 +03:00
return fsb * fid_codes [ cfid ] / 10 ;
2005-04-17 02:20:36 +04:00
}
2013-06-19 21:54:04 +04:00
static int acer_cpufreq_pst ( const struct dmi_system_id * d )
2005-04-17 02:20:36 +04:00
{
2009-01-18 08:32:26 +03:00
printk ( KERN_WARNING PFX
" %s laptop with broken PST tables in BIOS detected. \n " ,
d - > ident ) ;
printk ( KERN_WARNING PFX
" You need to downgrade to 3A21 (09/09/2002), or try a newer "
" BIOS than 3A71 (01/20/2003) \n " ) ;
printk ( KERN_WARNING PFX
" cpufreq scaling has been disabled as a result of this. \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Some Athlon laptops have really fucked PST tables .
* A BIOS update is all that can save them .
* Mention this , and disable cpufreq .
*/
2013-06-19 21:54:04 +04:00
static struct dmi_system_id powernow_dmi_table [ ] = {
2005-04-17 02:20:36 +04:00
{
. callback = acer_cpufreq_pst ,
. ident = " Acer Aspire " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Insyde Software " ) ,
DMI_MATCH ( DMI_BIOS_VERSION , " 3A71 " ) ,
} ,
} ,
{ }
} ;
2013-06-19 21:54:04 +04:00
static int powernow_cpu_init ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
union msr_fidvidstatus fidvidstatus ;
int result ;
if ( policy - > cpu ! = 0 )
return - ENODEV ;
2009-01-18 08:32:26 +03:00
rdmsrl ( MSR_K7_FID_VID_STATUS , fidvidstatus . val ) ;
2005-04-17 02:20:36 +04:00
2006-06-05 22:03:50 +04:00
recalibrate_cpu_khz ( ) ;
2005-06-01 06:03:45 +04:00
fsb = ( 10 * cpu_khz ) / fid_codes [ fidvidstatus . bits . CFID ] ;
2005-04-17 02:20:36 +04:00
if ( ! fsb ) {
printk ( KERN_WARNING PFX " can not determine bus frequency \n " ) ;
return - EINVAL ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " FSB: %3dMHz \n " , fsb / 1000 ) ;
2005-04-17 02:20:36 +04:00
if ( dmi_check_system ( powernow_dmi_table ) | | acpi_force ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " PSB/PST known to be broken. "
" Trying ACPI instead \n " ) ;
2005-04-17 02:20:36 +04:00
result = powernow_acpi_init ( ) ;
} else {
2009-01-18 08:32:26 +03:00
result = powernow_decode_bios ( fidvidstatus . bits . MFID ,
fidvidstatus . bits . SVID ) ;
2005-04-17 02:20:36 +04:00
if ( result ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " Trying ACPI perflib \n " ) ;
2005-04-17 02:20:36 +04:00
maximum_speed = 0 ;
minimum_speed = - 1 ;
latency = 0 ;
result = powernow_acpi_init ( ) ;
if ( result ) {
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX
" ACPI and legacy methods failed \n " ) ;
2005-04-17 02:20:36 +04:00
}
} else {
/* SGTC use the bus clock as timer */
latency = fixup_sgtc ( ) ;
printk ( KERN_INFO PFX " SGTC: %d \n " , latency ) ;
}
}
if ( result )
return result ;
2009-01-18 08:32:26 +03:00
printk ( KERN_INFO PFX " Minimum speed %d MHz. Maximum speed %d MHz. \n " ,
2005-04-17 02:20:36 +04:00
minimum_speed / 1000 , maximum_speed / 1000 ) ;
2009-01-18 08:32:26 +03:00
policy - > cpuinfo . transition_latency =
cpufreq_scale ( 2000000UL , fsb , latency ) ;
2005-04-17 02:20:36 +04:00
2013-09-16 17:26:27 +04:00
return cpufreq_table_validate_and_show ( policy , powernow_table ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-18 08:32:26 +03:00
static int powernow_cpu_exit ( struct cpufreq_policy * policy )
{
2005-04-17 02:20:36 +04:00
cpufreq_frequency_table_put_attr ( policy - > cpu ) ;
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
if ( acpi_processor_perf ) {
acpi_processor_unregister_performance ( acpi_processor_perf , 0 ) ;
2009-01-01 05:08:47 +03:00
free_cpumask_var ( acpi_processor_perf - > shared_cpu_map ) ;
2005-04-17 02:20:36 +04:00
kfree ( acpi_processor_perf ) ;
}
# endif
2005-06-26 01:58:48 +04:00
kfree ( powernow_table ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-27 01:55:48 +03:00
static struct cpufreq_driver powernow_driver = {
2013-10-03 18:58:17 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = powernow_target ,
2009-11-19 14:31:01 +03:00
. get = powernow_get ,
# ifdef CONFIG_X86_POWERNOW_K7_ACPI
. bios_limit = acpi_processor_get_bios_limit ,
# endif
. init = powernow_cpu_init ,
. exit = powernow_cpu_exit ,
. name = " powernow-k7 " ,
2013-10-03 18:58:17 +04:00
. attr = cpufreq_generic_attr ,
2005-04-17 02:20:36 +04:00
} ;
2009-01-18 08:32:26 +03:00
static int __init powernow_init ( void )
2005-04-17 02:20:36 +04:00
{
2009-01-18 08:32:26 +03:00
if ( check_powernow ( ) = = 0 )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
return cpufreq_register_driver ( & powernow_driver ) ;
}
2009-01-18 08:32:26 +03:00
static void __exit powernow_exit ( void )
2005-04-17 02:20:36 +04:00
{
cpufreq_unregister_driver ( & powernow_driver ) ;
}
module_param ( acpi_force , int , 0444 ) ;
MODULE_PARM_DESC ( acpi_force , " Force ACPI to be used. " ) ;
2009-01-18 08:32:26 +03:00
MODULE_AUTHOR ( " Dave Jones <davej@redhat.com> " ) ;
MODULE_DESCRIPTION ( " Powernow driver for AMD K7 processors. " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
late_initcall ( powernow_init ) ;
module_exit ( powernow_exit ) ;