2009-04-23 22:45:04 +04:00
2005-04-17 02:20:36 +04:00
/*
2006-06-05 03:37:58 +04:00
* ( c ) 2003 - 2006 Advanced Micro Devices , Inc .
2005-04-17 02:20:36 +04:00
* Your use of this code is subject to the terms and conditions of the
* GNU general public license version 2. See " COPYING " or
* http : //www.gnu.org/licenses/gpl.html
*
2005-06-01 06:03:46 +04:00
* Support : mark . langsdorf @ amd . com
2005-04-17 02:20:36 +04:00
*
* Based on the powernow - k7 . c module written by Dave Jones .
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 ) 2004 Dominik Brodowski < linux @ brodo . de >
* ( C ) 2004 Pavel Machek < pavel @ suse . cz >
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD .
*
* Valuable input gratefully received from Dave Jones , Pavel Machek ,
2006-06-05 03:37:58 +04:00
* Dominik Brodowski , Jacob Shin , and others .
2005-06-01 06:03:46 +04:00
* Originally developed by Paul Devriendt .
2005-04-17 02:20:36 +04:00
* Processor information obtained from Chapter 9 ( Power and Thermal Management )
* of the " BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors " available for download from www.amd.com
*
2006-05-31 01:25:14 +04:00
* Tables for specific CPUs can be inferred from
2005-06-01 06:03:46 +04:00
* http : //www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
2005-04-17 02:20:36 +04:00
*/
# include <linux/kernel.h>
# include <linux/smp.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
# include <linux/slab.h>
# include <linux/string.h>
2005-06-01 06:03:46 +04:00
# include <linux/cpumask.h>
2005-10-31 02:03:48 +03:00
# include <linux/sched.h> /* for current / set_cpus_allowed() */
2009-02-04 22:37:50 +03:00
# include <linux/io.h>
# include <linux/delay.h>
2005-04-17 02:20:36 +04:00
# include <asm/msr.h>
# include <linux/acpi.h>
2006-03-26 13:37:14 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <acpi/processor.h>
# define PFX "powernow-k8: "
2007-10-18 01:52:08 +04:00
# define VERSION "version 2.20.00"
2005-04-17 02:20:36 +04:00
# include "powernow-k8.h"
/* serialize freq changes */
2006-03-26 13:37:14 +04:00
static DEFINE_MUTEX ( fidvid_mutex ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:33:11 +03:00
static DEFINE_PER_CPU ( struct powernow_k8_data * , powernow_data ) ;
2005-04-17 02:20:36 +04:00
2006-06-05 03:37:58 +04:00
static int cpu_family = CPU_OPTERON ;
2005-06-01 06:03:46 +04:00
# ifndef CONFIG_SMP
2009-03-13 07:19:50 +03:00
static inline const struct cpumask * cpu_core_mask ( int cpu )
{
return cpumask_of ( 0 ) ;
}
2005-06-01 06:03:46 +04:00
# endif
2005-04-17 02:20:36 +04:00
/* Return a frequency in MHz, given an input fid */
static u32 find_freq_from_fid ( u32 fid )
{
return 800 + ( fid * 100 ) ;
}
/* Return a frequency in KHz, given an input fid */
static u32 find_khz_freq_from_fid ( u32 fid )
{
return 1000 * find_freq_from_fid ( fid ) ;
}
2009-02-04 22:37:50 +03:00
static u32 find_khz_freq_from_pstate ( struct cpufreq_frequency_table * data ,
u32 pstate )
2006-06-05 03:37:58 +04:00
{
2007-10-18 01:52:08 +04:00
return data [ pstate ] . frequency ;
2006-06-05 03:37:58 +04:00
}
2005-04-17 02:20:36 +04:00
/* Return the vco fid for an input fid
*
* Each " low " fid has corresponding " high " fid , and you can get to " low " fids
* only from corresponding high fids . This returns " high " fid corresponding to
* " low " one .
*/
static u32 convert_fid_to_vco_fid ( u32 fid )
{
2006-02-28 08:43:23 +03:00
if ( fid < HI_FID_TABLE_BOTTOM )
2005-04-17 02:20:36 +04:00
return 8 + ( 2 * fid ) ;
2006-02-28 08:43:23 +03:00
else
2005-04-17 02:20:36 +04:00
return fid ;
}
/*
* Return 1 if the pending bit is set . Unless we just instructed the processor
* to transition to a new state , seeing this bit set is really bad news .
*/
static int pending_bit_stuck ( void )
{
u32 lo , hi ;
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2006-06-05 03:37:58 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
rdmsr ( MSR_FIDVID_STATUS , lo , hi ) ;
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0 ;
}
/*
* Update the global current fid / vid values from the status msr .
* Returns 1 on error .
*/
static int query_current_values_with_pending_wait ( struct powernow_k8_data * data )
{
u32 lo , hi ;
u32 i = 0 ;
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE ) {
2009-06-11 19:26:48 +04:00
rdmsr ( MSR_PSTATE_STATUS , lo , hi ) ;
i = lo & HW_PSTATE_MASK ;
data - > currpstate = i ;
/*
* a workaround for family 11 h erratum 311 might cause
* an " out-of-range Pstate if the core is in Pstate-0
*/
if ( ( boot_cpu_data . x86 = = 0x11 ) & & ( i > = data - > numps ) )
data - > currpstate = HW_PSTATE_0 ;
2006-06-05 03:37:58 +04:00
return 0 ;
}
2005-07-28 20:45:10 +04:00
do {
2005-10-22 01:21:03 +04:00
if ( i + + > 10000 ) {
dprintk ( " detected change pending stuck \n " ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
rdmsr ( MSR_FIDVID_STATUS , lo , hi ) ;
2005-07-28 20:45:10 +04:00
} while ( lo & MSR_S_LO_CHANGE_PENDING ) ;
2005-04-17 02:20:36 +04:00
data - > currvid = hi & MSR_S_HI_CURRENT_VID ;
data - > currfid = lo & MSR_S_LO_CURRENT_FID ;
return 0 ;
}
/* the isochronous relief time */
static void count_off_irt ( struct powernow_k8_data * data )
{
udelay ( ( 1 < < data - > irt ) * 10 ) ;
return ;
}
2007-10-20 03:13:56 +04:00
/* the voltage stabilization time */
2005-04-17 02:20:36 +04:00
static void count_off_vst ( struct powernow_k8_data * data )
{
udelay ( data - > vstable * VST_UNITS_20US ) ;
return ;
}
/* need to init the control msr to a safe value (for each cpu) */
static void fidvid_msr_init ( void )
{
u32 lo , hi ;
u8 fid , vid ;
rdmsr ( MSR_FIDVID_STATUS , lo , hi ) ;
vid = hi & MSR_S_HI_CURRENT_VID ;
fid = lo & MSR_S_LO_CURRENT_FID ;
lo = fid | ( vid < < MSR_C_LO_VID_SHIFT ) ;
hi = MSR_C_HI_STP_GNT_BENIGN ;
dprintk ( " cpu%d, init lo 0x%x, hi 0x%x \n " , smp_processor_id ( ) , lo , hi ) ;
wrmsr ( MSR_FIDVID_CTL , lo , hi ) ;
}
/* write the new fid value along with the other control fields to the msr */
static int write_new_fid ( struct powernow_k8_data * data , u32 fid )
{
u32 lo ;
u32 savevid = data - > currvid ;
2005-10-22 01:21:03 +04:00
u32 i = 0 ;
2005-04-17 02:20:36 +04:00
if ( ( fid & INVALID_FID_MASK ) | | ( data - > currvid & INVALID_VID_MASK ) ) {
printk ( KERN_ERR PFX " internal error - overflow on fid write \n " ) ;
return 1 ;
}
2009-02-04 22:37:50 +03:00
lo = fid ;
lo | = ( data - > currvid < < MSR_C_LO_VID_SHIFT ) ;
lo | = MSR_C_LO_INIT_FID_VID ;
2005-04-17 02:20:36 +04:00
dprintk ( " writing fid 0x%x, lo 0x%x, hi 0x%x \n " ,
fid , lo , data - > plllock * PLL_LOCK_CONVERSION ) ;
2005-10-22 01:21:03 +04:00
do {
wrmsr ( MSR_FIDVID_CTL , lo , data - > plllock * PLL_LOCK_CONVERSION ) ;
if ( i + + > 100 ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX
" Hardware error - pending bit very stuck - "
" no further pstate changes possible \n " ) ;
2005-10-22 03:56:08 +04:00
return 1 ;
2006-02-28 08:43:23 +03:00
}
2005-10-22 01:21:03 +04:00
} while ( query_current_values_with_pending_wait ( data ) ) ;
2005-04-17 02:20:36 +04:00
count_off_irt ( data ) ;
if ( savevid ! = data - > currvid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX
" vid change on fid trans, old 0x%x, new 0x%x \n " ,
savevid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
if ( fid ! = data - > currfid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX
" fid trans failed, fid 0x%x, curr 0x%x \n " , fid ,
data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
return 0 ;
}
/* Write a new vid to the hardware */
static int write_new_vid ( struct powernow_k8_data * data , u32 vid )
{
u32 lo ;
u32 savefid = data - > currfid ;
2005-10-22 01:21:03 +04:00
int i = 0 ;
2005-04-17 02:20:36 +04:00
if ( ( data - > currfid & INVALID_FID_MASK ) | | ( vid & INVALID_VID_MASK ) ) {
printk ( KERN_ERR PFX " internal error - overflow on vid write \n " ) ;
return 1 ;
}
2009-02-04 22:37:50 +03:00
lo = data - > currfid ;
lo | = ( vid < < MSR_C_LO_VID_SHIFT ) ;
lo | = MSR_C_LO_INIT_FID_VID ;
2005-04-17 02:20:36 +04:00
dprintk ( " writing vid 0x%x, lo 0x%x, hi 0x%x \n " ,
vid , lo , STOP_GRANT_5NS ) ;
2005-10-22 01:21:03 +04:00
do {
wrmsr ( MSR_FIDVID_CTL , lo , STOP_GRANT_5NS ) ;
2005-12-01 00:33:30 +03:00
if ( i + + > 100 ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " internal error - pending bit "
" very stuck - no further pstate "
" changes possible \n " ) ;
2005-12-01 00:33:30 +03:00
return 1 ;
}
2005-10-22 01:21:03 +04:00
} while ( query_current_values_with_pending_wait ( data ) ) ;
2005-04-17 02:20:36 +04:00
if ( savefid ! = data - > currfid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " fid changed on vid trans, old "
" 0x%x new 0x%x \n " ,
2005-04-17 02:20:36 +04:00
savefid , data - > currfid ) ;
return 1 ;
}
if ( vid ! = data - > currvid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " vid trans failed, vid 0x%x, "
" curr 0x%x \n " ,
vid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
return 0 ;
}
/*
* Reduce the vid by the max of step or reqvid .
* Decreasing vid codes represent increasing voltages :
2005-07-28 20:40:04 +04:00
* vid of 0 is 1.550 V , vid of 0x1e is 0.800 V , vid of VID_OFF is off .
2005-04-17 02:20:36 +04:00
*/
2009-02-04 22:37:50 +03:00
static int decrease_vid_code_by_step ( struct powernow_k8_data * data ,
u32 reqvid , u32 step )
2005-04-17 02:20:36 +04:00
{
if ( ( data - > currvid - reqvid ) > step )
reqvid = data - > currvid - step ;
if ( write_new_vid ( data , reqvid ) )
return 1 ;
count_off_vst ( data ) ;
return 0 ;
}
2006-06-05 03:37:58 +04:00
/* Change hardware pstate by single MSR write */
static int transition_pstate ( struct powernow_k8_data * data , u32 pstate )
{
wrmsr ( MSR_PSTATE_CTRL , pstate , 0 ) ;
2007-10-18 01:52:08 +04:00
data - > currpstate = pstate ;
2006-06-05 03:37:58 +04:00
return 0 ;
}
/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
2009-02-04 22:37:50 +03:00
static int transition_fid_vid ( struct powernow_k8_data * data ,
u32 reqfid , u32 reqvid )
2005-04-17 02:20:36 +04:00
{
2009-07-26 19:55:25 +04:00
if ( core_voltage_pre_transition ( data , reqvid , reqfid ) )
2005-04-17 02:20:36 +04:00
return 1 ;
if ( core_frequency_transition ( data , reqfid ) )
return 1 ;
if ( core_voltage_post_transition ( data , reqvid ) )
return 1 ;
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( ( reqfid ! = data - > currfid ) | | ( reqvid ! = data - > currvid ) ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " failed (cpu%d): req 0x%x 0x%x, "
" curr 0x%x 0x%x \n " ,
2005-04-17 02:20:36 +04:00
smp_processor_id ( ) ,
reqfid , reqvid , data - > currfid , data - > currvid ) ;
return 1 ;
}
dprintk ( " transitioned (cpu%d): new fid 0x%x, vid 0x%x \n " ,
smp_processor_id ( ) , data - > currfid , data - > currvid ) ;
return 0 ;
}
/* Phase 1 - core voltage transition ... setup voltage */
2009-02-04 22:37:50 +03:00
static int core_voltage_pre_transition ( struct powernow_k8_data * data ,
2009-07-26 19:55:25 +04:00
u32 reqvid , u32 reqfid )
2005-04-17 02:20:36 +04:00
{
u32 rvosteps = data - > rvo ;
u32 savefid = data - > currfid ;
2009-07-26 19:55:25 +04:00
u32 maxvid , lo , rvomult = 1 ;
2005-04-17 02:20:36 +04:00
2009-02-04 22:37:50 +03:00
dprintk ( " ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, "
" reqvid 0x%x, rvo 0x%x \n " ,
2005-04-17 02:20:36 +04:00
smp_processor_id ( ) ,
data - > currfid , data - > currvid , reqvid , data - > rvo ) ;
2009-07-26 19:55:25 +04:00
if ( ( savefid < LO_FID_TABLE_TOP ) & & ( reqfid < LO_FID_TABLE_TOP ) )
rvomult = 2 ;
rvosteps * = rvomult ;
2005-06-01 06:03:46 +04:00
rdmsr ( MSR_FIDVID_STATUS , lo , maxvid ) ;
maxvid = 0x1f & ( maxvid > > 16 ) ;
dprintk ( " ph1 maxvid=0x%x \n " , maxvid ) ;
if ( reqvid < maxvid ) /* lower numbers are higher voltages */
reqvid = maxvid ;
2005-04-17 02:20:36 +04:00
while ( data - > currvid > reqvid ) {
dprintk ( " ph1: curr 0x%x, req vid 0x%x \n " ,
data - > currvid , reqvid ) ;
if ( decrease_vid_code_by_step ( data , reqvid , data - > vidmvs ) )
return 1 ;
}
2009-07-26 19:55:25 +04:00
while ( ( rvosteps > 0 ) & &
( ( rvomult * data - > rvo + data - > currvid ) > reqvid ) ) {
2005-06-01 06:03:46 +04:00
if ( data - > currvid = = maxvid ) {
2005-04-17 02:20:36 +04:00
rvosteps = 0 ;
} else {
dprintk ( " ph1: changing vid for rvo, req 0x%x \n " ,
data - > currvid - 1 ) ;
2009-02-04 22:37:50 +03:00
if ( decrease_vid_code_by_step ( data , data - > currvid - 1 , 1 ) )
2005-04-17 02:20:36 +04:00
return 1 ;
rvosteps - - ;
}
}
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( savefid ! = data - > currfid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " ph1 err, currfid changed 0x%x \n " ,
data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
dprintk ( " ph1 complete, currfid 0x%x, currvid 0x%x \n " ,
data - > currfid , data - > currvid ) ;
return 0 ;
}
/* Phase 2 - core frequency transition */
static int core_frequency_transition ( struct powernow_k8_data * data , u32 reqfid )
{
2009-02-04 22:37:50 +03:00
u32 vcoreqfid , vcocurrfid , vcofiddiff ;
u32 fid_interval , savevid = data - > currvid ;
2005-04-17 02:20:36 +04:00
if ( data - > currfid = = reqfid ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " ph2 null fid transition 0x%x \n " ,
data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-02-04 22:37:50 +03:00
dprintk ( " ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, "
" reqfid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
smp_processor_id ( ) ,
data - > currfid , data - > currvid , reqfid ) ;
vcoreqfid = convert_fid_to_vco_fid ( reqfid ) ;
vcocurrfid = convert_fid_to_vco_fid ( data - > currfid ) ;
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid ;
2009-07-26 19:55:25 +04:00
if ( ( reqfid < = LO_FID_TABLE_TOP ) & & ( data - > currfid < = LO_FID_TABLE_TOP ) )
vcofiddiff = 0 ;
2005-04-17 02:20:36 +04:00
while ( vcofiddiff > 2 ) {
2005-11-29 23:18:03 +03:00
( data - > currfid & 1 ) ? ( fid_interval = 1 ) : ( fid_interval = 2 ) ;
2005-04-17 02:20:36 +04:00
if ( reqfid > data - > currfid ) {
if ( data - > currfid > LO_FID_TABLE_TOP ) {
2009-02-04 22:37:50 +03:00
if ( write_new_fid ( data ,
data - > currfid + fid_interval ) )
2005-04-17 02:20:36 +04:00
return 1 ;
} else {
if ( write_new_fid
2009-02-04 22:37:50 +03:00
( data ,
2 + convert_fid_to_vco_fid ( data - > currfid ) ) )
2005-04-17 02:20:36 +04:00
return 1 ;
}
} else {
2005-11-29 23:18:03 +03:00
if ( write_new_fid ( data , data - > currfid - fid_interval ) )
2005-04-17 02:20:36 +04:00
return 1 ;
}
vcocurrfid = convert_fid_to_vco_fid ( data - > currfid ) ;
vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
: vcoreqfid - vcocurrfid ;
}
if ( write_new_fid ( data , reqfid ) )
return 1 ;
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( data - > currfid ! = reqfid ) {
printk ( KERN_ERR PFX
2009-02-04 22:37:50 +03:00
" ph2: mismatch, failed fid transition, "
" curr 0x%x, req 0x%x \n " ,
2005-04-17 02:20:36 +04:00
data - > currfid , reqfid ) ;
return 1 ;
}
if ( savevid ! = data - > currvid ) {
printk ( KERN_ERR PFX " ph2: vid changed, save 0x%x, curr 0x%x \n " ,
savevid , data - > currvid ) ;
return 1 ;
}
dprintk ( " ph2 complete, currfid 0x%x, currvid 0x%x \n " ,
data - > currfid , data - > currvid ) ;
return 0 ;
}
/* Phase 3 - core voltage transition flow ... jump to the final vid. */
2009-02-04 22:37:50 +03:00
static int core_voltage_post_transition ( struct powernow_k8_data * data ,
u32 reqvid )
2005-04-17 02:20:36 +04:00
{
u32 savefid = data - > currfid ;
u32 savereqvid = reqvid ;
dprintk ( " ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x \n " ,
smp_processor_id ( ) ,
data - > currfid , data - > currvid ) ;
if ( reqvid ! = data - > currvid ) {
if ( write_new_vid ( data , reqvid ) )
return 1 ;
if ( savefid ! = data - > currfid ) {
printk ( KERN_ERR PFX
" ph3: bad fid change, save 0x%x, curr 0x%x \n " ,
savefid , data - > currfid ) ;
return 1 ;
}
if ( data - > currvid ! = reqvid ) {
printk ( KERN_ERR PFX
2009-02-04 22:37:50 +03:00
" ph3: failed vid transition \n , "
" req 0x%x, curr 0x%x " ,
2005-04-17 02:20:36 +04:00
reqvid , data - > currvid ) ;
return 1 ;
}
}
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( savereqvid ! = data - > currvid ) {
dprintk ( " ph3 failed, currvid 0x%x \n " , data - > currvid ) ;
return 1 ;
}
if ( savefid ! = data - > currfid ) {
dprintk ( " ph3 failed, currfid changed 0x%x \n " ,
data - > currfid ) ;
return 1 ;
}
dprintk ( " ph3 complete, currfid 0x%x, currvid 0x%x \n " ,
data - > currfid , data - > currvid ) ;
return 0 ;
}
2009-06-12 15:25:37 +04:00
static void check_supported_cpu ( void * _rc )
2005-04-17 02:20:36 +04:00
{
u32 eax , ebx , ecx , edx ;
2009-06-12 15:25:37 +04:00
int * rc = _rc ;
2005-04-17 02:20:36 +04:00
2009-06-12 15:25:37 +04:00
* rc = - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( current_cpu_data . x86_vendor ! = X86_VENDOR_AMD )
2009-06-12 15:25:37 +04:00
return ;
2005-04-17 02:20:36 +04:00
eax = cpuid_eax ( CPUID_PROCESSOR_SIGNATURE ) ;
2006-06-05 03:37:58 +04:00
if ( ( ( eax & CPUID_XFAM ) ! = CPUID_XFAM_K8 ) & &
( ( eax & CPUID_XFAM ) < CPUID_XFAM_10H ) )
2009-06-12 15:25:37 +04:00
return ;
2006-02-28 08:36:32 +03:00
2006-06-05 03:37:58 +04:00
if ( ( eax & CPUID_XFAM ) = = CPUID_XFAM_K8 ) {
if ( ( ( eax & CPUID_USE_XFAM_XMOD ) ! = CPUID_USE_XFAM_XMOD ) | |
2007-05-15 02:27:29 +04:00
( ( eax & CPUID_XMOD ) > CPUID_XMOD_REV_MASK ) ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_INFO PFX
" Processor cpuid %x not supported \n " , eax ) ;
2009-06-12 15:25:37 +04:00
return ;
2006-06-05 03:37:58 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-05 03:37:58 +04:00
eax = cpuid_eax ( CPUID_GET_MAX_CAPABILITIES ) ;
if ( eax < CPUID_FREQ_VOLT_CAPABILITIES ) {
printk ( KERN_INFO PFX
" No frequency change capabilities detected \n " ) ;
2009-06-12 15:25:37 +04:00
return ;
2006-06-05 03:37:58 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-05 03:37:58 +04:00
cpuid ( CPUID_FREQ_VOLT_CAPABILITIES , & eax , & ebx , & ecx , & edx ) ;
2009-02-04 22:37:50 +03:00
if ( ( edx & P_STATE_TRANSITION_CAPABLE )
! = P_STATE_TRANSITION_CAPABLE ) {
printk ( KERN_INFO PFX
" Power state transitions not supported \n " ) ;
2009-06-12 15:25:37 +04:00
return ;
2006-06-05 03:37:58 +04:00
}
} else { /* must be a HW Pstate capable processor */
cpuid ( CPUID_FREQ_VOLT_CAPABILITIES , & eax , & ebx , & ecx , & edx ) ;
if ( ( edx & USE_HW_PSTATE ) = = USE_HW_PSTATE )
cpu_family = CPU_HW_PSTATE ;
else
2009-06-12 15:25:37 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2009-06-12 15:25:37 +04:00
* rc = 0 ;
2005-04-17 02:20:36 +04:00
}
2009-02-04 22:37:50 +03:00
static int check_pst_table ( struct powernow_k8_data * data , struct pst_s * pst ,
u8 maxvid )
2005-04-17 02:20:36 +04:00
{
unsigned int j ;
u8 lastfid = 0xff ;
for ( j = 0 ; j < data - > numps ; j + + ) {
if ( pst [ j ] . vid > LEAST_VID ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " vid %d invalid : 0x%x \n " ,
j , pst [ j ] . vid ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2009-02-04 22:37:50 +03:00
if ( pst [ j ] . vid < data - > rvo ) {
/* vid + rvo >= 0 */
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " 0 vid exceeded with pstate "
" %d \n " , j ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2009-02-04 22:37:50 +03:00
if ( pst [ j ] . vid < maxvid + data - > rvo ) {
/* vid + rvo >= maxvid */
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " maxvid exceeded with pstate "
" %d \n " , j ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2005-11-21 18:23:08 +03:00
if ( pst [ j ] . fid > MAX_FID ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " maxfid exceeded with pstate "
" %d \n " , j ) ;
2005-11-21 18:23:08 +03:00
return - ENODEV ;
}
if ( j & & ( pst [ j ] . fid < HI_FID_TABLE_BOTTOM ) ) {
2005-04-17 02:20:36 +04:00
/* Only first fid is allowed to be in "low" range */
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " two low fids - %d : "
" 0x%x \n " , j , pst [ j ] . fid ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( pst [ j ] . fid < lastfid )
lastfid = pst [ j ] . fid ;
}
if ( lastfid & 1 ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " lastfid invalid \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( lastfid > LO_FID_TABLE_TOP )
2009-02-04 22:37:50 +03:00
printk ( KERN_INFO FW_BUG PFX
" first fid not from lo freq table \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-09-16 19:09:32 +04:00
static void invalidate_entry ( struct cpufreq_frequency_table * powernow_table ,
unsigned int entry )
2009-02-04 22:37:50 +03:00
{
2009-09-16 19:09:32 +04:00
powernow_table [ entry ] . frequency = CPUFREQ_ENTRY_INVALID ;
2009-02-04 22:37:50 +03:00
}
2005-04-17 02:20:36 +04:00
static void print_basics ( struct powernow_k8_data * data )
{
int j ;
for ( j = 0 ; j < data - > numps ; j + + ) {
2009-02-04 22:37:50 +03:00
if ( data - > powernow_table [ j ] . frequency ! =
CPUFREQ_ENTRY_INVALID ) {
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_INFO PFX
" %d : pstate %d (%d MHz) \n " , j ,
2007-11-21 06:38:27 +03:00
data - > powernow_table [ j ] . index ,
2007-07-13 09:34:10 +04:00
data - > powernow_table [ j ] . frequency / 1000 ) ;
2006-06-05 03:37:58 +04:00
} else {
2009-02-04 22:37:50 +03:00
printk ( KERN_INFO PFX
" %d : fid 0x%x (%d MHz), vid 0x%x \n " ,
2007-07-13 09:34:10 +04:00
j ,
data - > powernow_table [ j ] . index & 0xff ,
data - > powernow_table [ j ] . frequency / 1000 ,
data - > powernow_table [ j ] . index > > 8 ) ;
2006-06-05 03:37:58 +04:00
}
}
2005-04-17 02:20:36 +04:00
}
if ( data - > batps )
2009-02-04 22:37:50 +03:00
printk ( KERN_INFO PFX " Only %d pstates on battery \n " ,
data - > batps ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-22 15:48:33 +04:00
static u32 freq_from_fid_did ( u32 fid , u32 did )
{
u32 mhz = 0 ;
if ( boot_cpu_data . x86 = = 0x10 )
mhz = ( 100 * ( fid + 0x10 ) ) > > did ;
else if ( boot_cpu_data . x86 = = 0x11 )
mhz = ( 100 * ( fid + 8 ) ) > > did ;
else
BUG ( ) ;
return mhz * 1000 ;
}
2009-02-04 22:37:50 +03:00
static int fill_powernow_table ( struct powernow_k8_data * data ,
struct pst_s * pst , u8 maxvid )
2005-04-17 02:20:36 +04:00
{
struct cpufreq_frequency_table * powernow_table ;
unsigned int j ;
2009-02-04 22:37:50 +03:00
if ( data - > batps ) {
/* use ACPI support to get full speed on mains power */
printk ( KERN_WARNING PFX
" Only %d pstates usable (use ACPI driver for full "
" range \n " , data - > batps ) ;
2005-04-17 02:20:36 +04:00
data - > numps = data - > batps ;
}
2009-02-04 22:37:50 +03:00
for ( j = 1 ; j < data - > numps ; j + + ) {
2005-04-17 02:20:36 +04:00
if ( pst [ j - 1 ] . fid > = pst [ j ] . fid ) {
printk ( KERN_ERR PFX " PST out of sequence \n " ) ;
return - EINVAL ;
}
}
if ( data - > numps < 2 ) {
printk ( KERN_ERR PFX " no p states to transition \n " ) ;
return - ENODEV ;
}
if ( check_pst_table ( data , pst , maxvid ) )
return - EINVAL ;
powernow_table = kmalloc ( ( sizeof ( struct cpufreq_frequency_table )
* ( data - > numps + 1 ) ) , GFP_KERNEL ) ;
if ( ! powernow_table ) {
printk ( KERN_ERR PFX " powernow_table memory alloc failure \n " ) ;
return - ENOMEM ;
}
for ( j = 0 ; j < data - > numps ; j + + ) {
2009-02-04 22:37:50 +03:00
int freq ;
2005-04-17 02:20:36 +04:00
powernow_table [ j ] . index = pst [ j ] . fid ; /* lower 8 bits */
powernow_table [ j ] . index | = ( pst [ j ] . vid < < 8 ) ; /* upper 8 bits */
2009-02-04 22:37:50 +03:00
freq = find_khz_freq_from_fid ( pst [ j ] . fid ) ;
powernow_table [ j ] . frequency = freq ;
2005-04-17 02:20:36 +04:00
}
powernow_table [ data - > numps ] . frequency = CPUFREQ_TABLE_END ;
powernow_table [ data - > numps ] . index = 0 ;
if ( query_current_values_with_pending_wait ( data ) ) {
kfree ( powernow_table ) ;
return - EIO ;
}
dprintk ( " cfid 0x%x, cvid 0x%x \n " , data - > currfid , data - > currvid ) ;
data - > powernow_table = powernow_table ;
2009-03-13 07:19:50 +03:00
if ( cpumask_first ( cpu_core_mask ( data - > cpu ) ) = = data - > cpu )
2007-04-30 23:15:05 +04:00
print_basics ( data ) ;
2005-04-17 02:20:36 +04:00
for ( j = 0 ; j < data - > numps ; j + + )
2009-02-04 22:37:50 +03:00
if ( ( pst [ j ] . fid = = data - > currfid ) & &
( pst [ j ] . vid = = data - > currvid ) )
2005-04-17 02:20:36 +04:00
return 0 ;
dprintk ( " currfid/vid do not match PST, ignoring \n " ) ;
return 0 ;
}
/* Find and validate the PSB/PST table in BIOS. */
static int find_psb_table ( struct powernow_k8_data * data )
{
struct psb_s * psb ;
unsigned int i ;
u32 mvs ;
u8 maxvid ;
u32 cpst = 0 ;
u32 thiscpuid ;
for ( i = 0xc0000 ; i < 0xffff0 ; i + = 0x10 ) {
/* Scan BIOS looking for the signature. */
/* It can not be at ffff0 - it is too big. */
psb = phys_to_virt ( i ) ;
if ( memcmp ( psb , PSB_ID_STRING , PSB_ID_STRING_LEN ) ! = 0 )
continue ;
dprintk ( " found PSB header at 0x%p \n " , psb ) ;
dprintk ( " table vers: 0x%x \n " , psb - > tableversion ) ;
if ( psb - > tableversion ! = PSB_VERSION_1_4 ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " PSB table is not v1.4 \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
dprintk ( " flags: 0x%x \n " , psb - > flags1 ) ;
if ( psb - > flags1 ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " unknown flags \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
data - > vstable = psb - > vstable ;
2009-02-04 22:37:50 +03:00
dprintk ( " voltage stabilization time: %d(*20us) \n " ,
data - > vstable ) ;
2005-04-17 02:20:36 +04:00
dprintk ( " flags2: 0x%x \n " , psb - > flags2 ) ;
data - > rvo = psb - > flags2 & 3 ;
data - > irt = ( ( psb - > flags2 ) > > 2 ) & 3 ;
mvs = ( ( psb - > flags2 ) > > 4 ) & 3 ;
data - > vidmvs = 1 < < mvs ;
data - > batps = ( ( psb - > flags2 ) > > 6 ) & 3 ;
dprintk ( " ramp voltage offset: %d \n " , data - > rvo ) ;
dprintk ( " isochronous relief time: %d \n " , data - > irt ) ;
dprintk ( " maximum voltage step: %d - 0x%x \n " , mvs , data - > vidmvs ) ;
dprintk ( " numpst: 0x%x \n " , psb - > num_tables ) ;
cpst = psb - > num_tables ;
2009-02-04 22:37:50 +03:00
if ( ( psb - > cpuid = = 0x00000fc0 ) | |
( psb - > cpuid = = 0x00000fe0 ) ) {
2005-04-17 02:20:36 +04:00
thiscpuid = cpuid_eax ( CPUID_PROCESSOR_SIGNATURE ) ;
2009-02-04 22:37:50 +03:00
if ( ( thiscpuid = = 0x00000fc0 ) | |
( thiscpuid = = 0x00000fe0 ) )
2005-04-17 02:20:36 +04:00
cpst = 1 ;
}
if ( cpst ! = 1 ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " numpst must be 1 \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
data - > plllock = psb - > plllocktime ;
dprintk ( " plllocktime: 0x%x (units 1us) \n " , psb - > plllocktime ) ;
dprintk ( " maxfid: 0x%x \n " , psb - > maxfid ) ;
dprintk ( " maxvid: 0x%x \n " , psb - > maxvid ) ;
maxvid = psb - > maxvid ;
data - > numps = psb - > numps ;
dprintk ( " numpstates: 0x%x \n " , data - > numps ) ;
2009-02-04 22:37:50 +03:00
return fill_powernow_table ( data ,
( struct pst_s * ) ( psb + 1 ) , maxvid ) ;
2005-04-17 02:20:36 +04:00
}
/*
* If you see this message , complain to BIOS manufacturer . If
* he tells you " we do not support Linux " or some similar
* nonsense , remember that Windows 2000 uses the same legacy
* mechanism that the old Linux PSB driver uses . Tell them it
* is broken with Windows 2000.
*
* The reference to the AMD documentation is chapter 9 in the
* BIOS and Kernel Developer ' s Guide , which is available on
* www . amd . com
*/
2009-02-04 13:56:11 +03:00
printk ( KERN_ERR FW_BUG PFX " No PSB or ACPI _PSS objects \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2009-02-04 22:37:50 +03:00
static void powernow_k8_acpi_pst_values ( struct powernow_k8_data * data ,
unsigned int index )
2005-04-17 02:20:36 +04:00
{
2009-02-04 22:37:50 +03:00
acpi_integer control ;
2008-08-20 00:34:59 +04:00
if ( ! data - > acpi_data . state_count | | ( cpu_family = = CPU_HW_PSTATE ) )
2005-04-17 02:20:36 +04:00
return ;
2009-04-23 22:45:04 +04:00
control = data - > acpi_data . states [ index ] . control ;
data - > irt = ( control > > IRT_SHIFT ) & IRT_MASK ;
data - > rvo = ( control > > RVO_SHIFT ) & RVO_MASK ;
data - > exttype = ( control > > EXT_TYPE_SHIFT ) & EXT_TYPE_MASK ;
data - > plllock = ( control > > PLL_L_SHIFT ) & PLL_L_MASK ;
data - > vidmvs = 1 < < ( ( control > > MVS_SHIFT ) & MVS_MASK ) ;
data - > vstable = ( control > > VST_SHIFT ) & VST_MASK ;
}
2005-04-17 02:20:36 +04:00
static int powernow_k8_cpu_init_acpi ( struct powernow_k8_data * data )
{
struct cpufreq_frequency_table * powernow_table ;
2009-01-01 05:08:47 +03:00
int ret_val = - ENODEV ;
2009-06-05 20:37:07 +04:00
acpi_integer control , status ;
2005-04-17 02:20:36 +04:00
2008-08-20 00:34:59 +04:00
if ( acpi_processor_register_performance ( & data - > acpi_data , data - > cpu ) ) {
2005-06-01 06:03:46 +04:00
dprintk ( " register performance failed: bad ACPI data \n " ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
/* verify the data contained in the ACPI structures */
2008-08-20 00:34:59 +04:00
if ( data - > acpi_data . state_count < = 1 ) {
2005-04-17 02:20:36 +04:00
dprintk ( " No ACPI P-States \n " ) ;
goto err_out ;
}
2009-06-05 20:37:07 +04:00
control = data - > acpi_data . control_register . space_id ;
status = data - > acpi_data . status_register . space_id ;
if ( ( control ! = ACPI_ADR_SPACE_FIXED_HARDWARE ) | |
( status ! = ACPI_ADR_SPACE_FIXED_HARDWARE ) ) {
2005-04-17 02:20:36 +04:00
dprintk ( " Invalid control/status registers (%x - %x) \n " ,
2009-06-05 20:37:07 +04:00
control , status ) ;
2005-04-17 02:20:36 +04:00
goto err_out ;
}
/* fill in data->powernow_table */
powernow_table = kmalloc ( ( sizeof ( struct cpufreq_frequency_table )
2008-08-20 00:34:59 +04:00
* ( data - > acpi_data . state_count + 1 ) ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! powernow_table ) {
dprintk ( " powernow_table memory alloc failure \n " ) ;
goto err_out ;
}
2009-08-22 04:15:28 +04:00
/* fill in data */
data - > numps = data - > acpi_data . state_count ;
powernow_k8_acpi_pst_values ( data , 0 ) ;
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2006-06-05 03:37:58 +04:00
ret_val = fill_powernow_table_pstate ( data , powernow_table ) ;
else
ret_val = fill_powernow_table_fidvid ( data , powernow_table ) ;
if ( ret_val )
goto err_out_mem ;
2009-02-04 22:37:50 +03:00
powernow_table [ data - > acpi_data . state_count ] . frequency =
CPUFREQ_TABLE_END ;
2008-08-20 00:34:59 +04:00
powernow_table [ data - > acpi_data . state_count ] . index = 0 ;
2006-06-05 03:37:58 +04:00
data - > powernow_table = powernow_table ;
2009-03-13 07:19:50 +03:00
if ( cpumask_first ( cpu_core_mask ( data - > cpu ) ) = = data - > cpu )
2007-04-30 23:15:05 +04:00
print_basics ( data ) ;
2006-06-05 03:37:58 +04:00
/* notify BIOS that we exist */
acpi_processor_notify_smm ( THIS_MODULE ) ;
2009-06-07 01:51:36 +04:00
if ( ! zalloc_cpumask_var ( & data - > acpi_data . shared_cpu_map , GFP_KERNEL ) ) {
2009-01-01 05:08:47 +03:00
printk ( KERN_ERR PFX
" unable to alloc powernow_k8_data cpumask \n " ) ;
ret_val = - ENOMEM ;
goto err_out_mem ;
}
2006-06-05 03:37:58 +04:00
return 0 ;
err_out_mem :
kfree ( powernow_table ) ;
err_out :
2008-08-20 00:34:59 +04:00
acpi_processor_unregister_performance ( & data - > acpi_data , data - > cpu ) ;
2006-06-05 03:37:58 +04:00
2009-02-04 22:37:50 +03:00
/* data->acpi_data.state_count informs us at ->exit()
* whether ACPI was used */
2008-08-20 00:34:59 +04:00
data - > acpi_data . state_count = 0 ;
2006-06-05 03:37:58 +04:00
2009-01-01 05:08:47 +03:00
return ret_val ;
2006-06-05 03:37:58 +04:00
}
2009-02-04 22:37:50 +03:00
static int fill_powernow_table_pstate ( struct powernow_k8_data * data ,
struct cpufreq_frequency_table * powernow_table )
2006-06-05 03:37:58 +04:00
{
int i ;
2007-10-18 01:52:08 +04:00
u32 hi = 0 , lo = 0 ;
rdmsr ( MSR_PSTATE_CUR_LIMIT , hi , lo ) ;
data - > max_hw_pstate = ( hi & HW_PSTATE_MAX_MASK ) > > HW_PSTATE_MAX_SHIFT ;
2006-06-05 03:37:58 +04:00
2008-08-20 00:34:59 +04:00
for ( i = 0 ; i < data - > acpi_data . state_count ; i + + ) {
2006-06-05 03:37:58 +04:00
u32 index ;
2008-08-20 00:34:59 +04:00
index = data - > acpi_data . states [ i ] . control & HW_PSTATE_MASK ;
2007-10-18 01:52:08 +04:00
if ( index > data - > max_hw_pstate ) {
2009-02-04 22:37:50 +03:00
printk ( KERN_ERR PFX " invalid pstate %d - "
" bad value %d. \n " , i , index ) ;
printk ( KERN_ERR PFX " Please report to BIOS "
" manufacturer \n " ) ;
2009-09-16 19:09:32 +04:00
invalidate_entry ( powernow_table , i ) ;
2007-10-18 01:52:08 +04:00
continue ;
2006-06-05 03:37:58 +04:00
}
rdmsr ( MSR_PSTATE_DEF_BASE + index , lo , hi ) ;
if ( ! ( hi & HW_PSTATE_VALID_MASK ) ) {
dprintk ( " invalid pstate %d, ignoring \n " , index ) ;
2009-09-16 19:09:32 +04:00
invalidate_entry ( powernow_table , i ) ;
2006-06-05 03:37:58 +04:00
continue ;
}
2007-10-18 01:52:08 +04:00
powernow_table [ i ] . index = index ;
2006-06-05 03:37:58 +04:00
2009-04-22 15:48:33 +04:00
/* Frequency may be rounded for these */
if ( boot_cpu_data . x86 = = 0x10 | | boot_cpu_data . x86 = = 0x11 ) {
powernow_table [ i ] . frequency =
freq_from_fid_did ( lo & 0x3f , ( lo > > 6 ) & 7 ) ;
} else
powernow_table [ i ] . frequency =
data - > acpi_data . states [ i ] . core_frequency * 1000 ;
2006-06-05 03:37:58 +04:00
}
return 0 ;
}
2009-02-04 22:37:50 +03:00
static int fill_powernow_table_fidvid ( struct powernow_k8_data * data ,
struct cpufreq_frequency_table * powernow_table )
2006-06-05 03:37:58 +04:00
{
int i ;
2009-02-04 22:37:50 +03:00
2008-08-20 00:34:59 +04:00
for ( i = 0 ; i < data - > acpi_data . state_count ; i + + ) {
2005-07-29 23:55:40 +04:00
u32 fid ;
u32 vid ;
2009-02-04 22:37:50 +03:00
u32 freq , index ;
acpi_integer status , control ;
2005-07-29 23:55:40 +04:00
if ( data - > exttype ) {
2009-02-04 22:37:50 +03:00
status = data - > acpi_data . states [ i ] . status ;
fid = status & EXT_FID_MASK ;
vid = ( status > > VID_SHIFT ) & EXT_VID_MASK ;
2005-07-28 20:40:04 +04:00
} else {
2009-02-04 22:37:50 +03:00
control = data - > acpi_data . states [ i ] . control ;
fid = control & FID_MASK ;
vid = ( control > > VID_SHIFT ) & VID_MASK ;
2005-07-28 20:40:04 +04:00
}
2005-04-17 02:20:36 +04:00
dprintk ( " %d : fid 0x%x, vid 0x%x \n " , i , fid , vid ) ;
2009-02-04 22:37:50 +03:00
index = fid | ( vid < < 8 ) ;
powernow_table [ i ] . index = index ;
freq = find_khz_freq_from_fid ( fid ) ;
powernow_table [ i ] . frequency = freq ;
2005-04-17 02:20:36 +04:00
/* verify frequency is OK */
2009-02-04 22:37:50 +03:00
if ( ( freq > ( MAX_FREQ * 1000 ) ) | | ( freq < ( MIN_FREQ * 1000 ) ) ) {
dprintk ( " invalid freq %u kHz, ignoring \n " , freq ) ;
2009-09-16 19:09:32 +04:00
invalidate_entry ( powernow_table , i ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2009-02-04 22:37:50 +03:00
/* verify voltage is OK -
* BIOSs are using " off " to indicate invalid */
2005-07-28 20:40:04 +04:00
if ( vid = = VID_OFF ) {
2005-04-17 02:20:36 +04:00
dprintk ( " invalid vid %u, ignoring \n " , vid ) ;
2009-09-16 19:09:32 +04:00
invalidate_entry ( powernow_table , i ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2009-02-04 22:37:50 +03:00
if ( freq ! = ( data - > acpi_data . states [ i ] . core_frequency * 1000 ) ) {
printk ( KERN_INFO PFX " invalid freq entries "
" %u kHz vs. %u kHz \n " , freq ,
( unsigned int )
( data - > acpi_data . states [ i ] . core_frequency
* 1000 ) ) ;
2009-09-16 19:09:32 +04:00
invalidate_entry ( powernow_table , i ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
}
return 0 ;
}
static void powernow_k8_cpu_exit_acpi ( struct powernow_k8_data * data )
{
2008-08-20 00:34:59 +04:00
if ( data - > acpi_data . state_count )
2009-02-04 22:37:50 +03:00
acpi_processor_unregister_performance ( & data - > acpi_data ,
data - > cpu ) ;
2009-01-01 05:08:47 +03:00
free_cpumask_var ( data - > acpi_data . shared_cpu_map ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-03 19:46:43 +03:00
static int get_transition_latency ( struct powernow_k8_data * data )
{
int max_latency = 0 ;
int i ;
for ( i = 0 ; i < data - > acpi_data . state_count ; i + + ) {
int cur_latency = data - > acpi_data . states [ i ] . transition_latency
+ data - > acpi_data . states [ i ] . bus_master_latency ;
if ( cur_latency > max_latency )
max_latency = cur_latency ;
}
2009-04-22 15:48:30 +04:00
if ( max_latency = = 0 ) {
/*
* Fam 11 h always returns 0 as transition latency .
* This is intended and means " very fast " . While cpufreq core
* and governors currently can handle that gracefully , better
* set it to 1 to avoid problems in the future .
* For all others it ' s a BIOS bug .
*/
2009-10-06 19:36:53 +04:00
if ( boot_cpu_data . x86 ! = 0x11 )
2009-04-22 15:48:30 +04:00
printk ( KERN_ERR FW_WARN PFX " Invalid zero transition "
" latency \n " ) ;
max_latency = 1 ;
}
2009-02-03 19:46:43 +03:00
/* value in usecs, needs to be in nanoseconds */
return 1000 * max_latency ;
}
2005-04-17 02:20:36 +04:00
/* Take a frequency, and issue the fid/vid transition command */
2009-02-04 22:37:50 +03:00
static int transition_frequency_fidvid ( struct powernow_k8_data * data ,
unsigned int index )
2005-04-17 02:20:36 +04:00
{
2006-06-05 03:37:58 +04:00
u32 fid = 0 ;
u32 vid = 0 ;
2005-06-01 06:03:46 +04:00
int res , i ;
2005-04-17 02:20:36 +04:00
struct cpufreq_freqs freqs ;
dprintk ( " cpu %d transition to index %u \n " , smp_processor_id ( ) , index ) ;
2006-06-05 03:37:58 +04:00
/* fid/vid correctness check for k8 */
2005-04-17 02:20:36 +04:00
/* fid are the lower 8 bits of the index we stored into
2006-06-05 03:37:58 +04:00
* the cpufreq frequency table in find_psb_table , vid
* are the upper 8 bits .
2005-04-17 02:20:36 +04:00
*/
fid = data - > powernow_table [ index ] . index & 0xFF ;
vid = ( data - > powernow_table [ index ] . index & 0xFF00 ) > > 8 ;
dprintk ( " table matched fid 0x%x, giving vid 0x%x \n " , fid , vid ) ;
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( ( data - > currvid = = vid ) & & ( data - > currfid = = fid ) ) {
dprintk ( " target matches current values (fid 0x%x, vid 0x%x) \n " ,
fid , vid ) ;
return 0 ;
}
dprintk ( " cpu %d, changing to fid 0x%x, vid 0x%x \n " ,
smp_processor_id ( ) , fid , vid ) ;
freqs . old = find_khz_freq_from_fid ( data - > currfid ) ;
freqs . new = find_khz_freq_from_fid ( fid ) ;
2006-06-05 03:37:58 +04:00
2009-06-12 15:28:37 +04:00
for_each_cpu ( i , data - > available_cores ) {
2005-06-01 06:03:46 +04:00
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
}
2005-04-17 02:20:36 +04:00
res = transition_fid_vid ( data , fid , vid ) ;
freqs . new = find_khz_freq_from_fid ( data - > currfid ) ;
2006-06-05 03:37:58 +04:00
2009-06-12 15:28:37 +04:00
for_each_cpu ( i , data - > available_cores ) {
2006-06-05 03:37:58 +04:00
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
}
return res ;
}
/* Take a frequency, and issue the hardware pstate transition command */
2009-02-04 22:37:50 +03:00
static int transition_frequency_pstate ( struct powernow_k8_data * data ,
unsigned int index )
2006-06-05 03:37:58 +04:00
{
u32 pstate = 0 ;
int res , i ;
struct cpufreq_freqs freqs ;
dprintk ( " cpu %d transition to index %u \n " , smp_processor_id ( ) , index ) ;
2007-10-18 01:52:08 +04:00
/* get MSR index for hardware pstate transition */
2006-06-05 03:37:58 +04:00
pstate = index & HW_PSTATE_MASK ;
2007-10-18 01:52:08 +04:00
if ( pstate > data - > max_hw_pstate )
2006-06-05 03:37:58 +04:00
return 0 ;
2009-02-04 22:37:50 +03:00
freqs . old = find_khz_freq_from_pstate ( data - > powernow_table ,
data - > currpstate ) ;
2007-10-18 01:52:08 +04:00
freqs . new = find_khz_freq_from_pstate ( data - > powernow_table , pstate ) ;
2006-06-05 03:37:58 +04:00
2009-06-12 15:28:37 +04:00
for_each_cpu ( i , data - > available_cores ) {
2006-06-05 03:37:58 +04:00
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
}
res = transition_pstate ( data , pstate ) ;
2007-10-18 01:52:08 +04:00
freqs . new = find_khz_freq_from_pstate ( data - > powernow_table , pstate ) ;
2006-06-05 03:37:58 +04:00
2009-06-12 15:28:37 +04:00
for_each_cpu ( i , data - > available_cores ) {
2005-06-01 06:03:46 +04:00
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
2006-05-31 01:25:14 +04:00
}
2005-04-17 02:20:36 +04:00
return res ;
}
/* Driver entry point to switch to the target frequency */
2009-02-04 22:37:50 +03:00
static int powernowk8_target ( struct cpufreq_policy * pol ,
unsigned targfreq , unsigned relation )
2005-04-17 02:20:36 +04:00
{
2009-11-03 07:27:56 +03:00
cpumask_var_t oldmask ;
2008-01-30 15:33:11 +03:00
struct powernow_k8_data * data = per_cpu ( powernow_data , pol - > cpu ) ;
2006-04-19 02:07:28 +04:00
u32 checkfid ;
u32 checkvid ;
2005-04-17 02:20:36 +04:00
unsigned int newstate ;
int ret = - EIO ;
2006-04-07 21:49:51 +04:00
if ( ! data )
return - EINVAL ;
2006-04-19 02:07:28 +04:00
checkfid = data - > currfid ;
checkvid = data - > currvid ;
2009-11-03 07:27:56 +03:00
/* only run on specific CPU from here on. */
/* This is poor form: use a workqueue or smp_call_function_single */
if ( ! alloc_cpumask_var ( & oldmask , GFP_KERNEL ) )
return - ENOMEM ;
cpumask_copy ( oldmask , tsk_cpumask ( current ) ) ;
set_cpus_allowed_ptr ( current , cpumask_of ( pol - > cpu ) ) ;
2005-04-17 02:20:36 +04:00
if ( smp_processor_id ( ) ! = pol - > cpu ) {
2005-11-21 18:23:08 +03:00
printk ( KERN_ERR PFX " limiting to cpu %u failed \n " , pol - > cpu ) ;
2005-04-17 02:20:36 +04:00
goto err_out ;
}
if ( pending_bit_stuck ( ) ) {
printk ( KERN_ERR PFX " failing targ, change pending bit set \n " ) ;
goto err_out ;
}
dprintk ( " targ: cpu %d, %d kHz, min %d, max %d, relation %d \n " ,
pol - > cpu , targfreq , pol - > min , pol - > max , relation ) ;
2006-05-31 01:43:54 +04:00
if ( query_current_values_with_pending_wait ( data ) )
2005-04-17 02:20:36 +04:00
goto err_out ;
2007-10-18 01:52:08 +04:00
if ( cpu_family ! = CPU_HW_PSTATE ) {
2006-06-05 03:37:58 +04:00
dprintk ( " targ: curr fid 0x%x, vid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
data - > currfid , data - > currvid ) ;
2009-02-04 22:37:50 +03:00
if ( ( checkvid ! = data - > currvid ) | |
( checkfid ! = data - > currfid ) ) {
2006-06-05 03:37:58 +04:00
printk ( KERN_INFO PFX
2009-02-04 22:37:50 +03:00
" error - out of sync, fix 0x%x 0x%x, "
" vid 0x%x 0x%x \n " ,
checkfid , data - > currfid ,
checkvid , data - > currvid ) ;
2006-06-05 03:37:58 +04:00
}
2005-04-17 02:20:36 +04:00
}
2009-02-04 22:37:50 +03:00
if ( cpufreq_frequency_table_target ( pol , data - > powernow_table ,
targfreq , relation , & newstate ) )
2005-04-17 02:20:36 +04:00
goto err_out ;
2006-03-26 13:37:14 +04:00
mutex_lock ( & fidvid_mutex ) ;
2005-06-01 06:03:46 +04:00
2005-04-17 02:20:36 +04:00
powernow_k8_acpi_pst_values ( data , newstate ) ;
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2006-06-05 03:37:58 +04:00
ret = transition_frequency_pstate ( data , newstate ) ;
else
ret = transition_frequency_fidvid ( data , newstate ) ;
if ( ret ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR PFX " transition frequency failed \n " ) ;
ret = 1 ;
2006-03-26 13:37:14 +04:00
mutex_unlock ( & fidvid_mutex ) ;
2005-04-17 02:20:36 +04:00
goto err_out ;
}
2006-03-26 13:37:14 +04:00
mutex_unlock ( & fidvid_mutex ) ;
2005-06-01 06:03:46 +04:00
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2009-02-04 22:37:50 +03:00
pol - > cur = find_khz_freq_from_pstate ( data - > powernow_table ,
newstate ) ;
2006-06-05 03:37:58 +04:00
else
pol - > cur = find_khz_freq_from_fid ( data - > currfid ) ;
2005-04-17 02:20:36 +04:00
ret = 0 ;
err_out :
2009-11-03 07:27:56 +03:00
set_cpus_allowed_ptr ( current , oldmask ) ;
free_cpumask_var ( oldmask ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
/* Driver entry point to verify the policy and range of frequencies */
static int powernowk8_verify ( struct cpufreq_policy * pol )
{
2008-01-30 15:33:11 +03:00
struct powernow_k8_data * data = per_cpu ( powernow_data , pol - > cpu ) ;
2005-04-17 02:20:36 +04:00
2006-04-07 21:49:51 +04:00
if ( ! data )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
return cpufreq_frequency_table_verify ( pol , data - > powernow_table ) ;
}
2009-06-12 15:25:37 +04:00
struct init_on_cpu {
struct powernow_k8_data * data ;
int rc ;
} ;
static void __cpuinit powernowk8_cpu_init_on_cpu ( void * _init_on_cpu )
{
struct init_on_cpu * init_on_cpu = _init_on_cpu ;
if ( pending_bit_stuck ( ) ) {
printk ( KERN_ERR PFX " failing init, change pending bit set \n " ) ;
init_on_cpu - > rc = - ENODEV ;
return ;
}
if ( query_current_values_with_pending_wait ( init_on_cpu - > data ) ) {
init_on_cpu - > rc = - ENODEV ;
return ;
}
if ( cpu_family = = CPU_OPTERON )
fidvid_msr_init ( ) ;
init_on_cpu - > rc = 0 ;
}
2005-04-17 02:20:36 +04:00
/* per CPU init entry point to the driver */
2006-01-16 03:56:36 +03:00
static int __cpuinit powernowk8_cpu_init ( struct cpufreq_policy * pol )
2005-04-17 02:20:36 +04:00
{
2009-06-10 23:41:37 +04:00
static const char ACPI_PSS_BIOS_BUG_MSG [ ] =
KERN_ERR FW_BUG PFX " No compatible ACPI _PSS objects found. \n "
2009-07-07 00:05:40 +04:00
FW_BUG PFX " Try again with latest BIOS. \n " ;
2005-04-17 02:20:36 +04:00
struct powernow_k8_data * data ;
2009-06-12 15:25:37 +04:00
struct init_on_cpu init_on_cpu ;
2006-04-07 21:49:48 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2005-11-21 18:23:08 +03:00
if ( ! cpu_online ( pol - > cpu ) )
return - ENODEV ;
2009-06-12 15:25:37 +04:00
smp_call_function_single ( pol - > cpu , check_supported_cpu , & rc , 1 ) ;
if ( rc )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-10-21 02:16:15 +04:00
data = kzalloc ( sizeof ( struct powernow_k8_data ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
printk ( KERN_ERR PFX " unable to alloc powernow_k8_data " ) ;
return - ENOMEM ;
}
data - > cpu = pol - > cpu ;
[CPUFREQ] powernow-k8: ignore out-of-range PstateStatus value
A workaround for AMD CPU family 11h erratum 311 might cause that the
P-state Status Register shows a "current P-state" which is larger than
the "current P-state limit" in P-state Current Limit Register. For the
wrong P-state value there is no ACPI _PSS object defined and
powernow-k8/cpufreq can't determine the proper CPU frequency for that
state.
As a consequence this can cause a panic during boot (potentially with
all recent kernel versions -- at least I have reproduced it with
various 2.6.27 kernels and with the current .28 series), as an
example:
powernow-k8: Found 1 AMD Turion(tm)X2 Ultra DualCore Mobile ZM-82 processors (2 \
)
powernow-k8: 0 : pstate 0 (2200 MHz)
powernow-k8: 1 : pstate 1 (1100 MHz)
powernow-k8: 2 : pstate 2 (600 MHz)
BUG: unable to handle kernel paging request at ffff88086e7528b8
IP: [<ffffffff80486361>] cpufreq_stats_update+0x4a/0x5f
PGD 202063 PUD 0
Oops: 0002 [#1] SMP
last sysfs file:
CPU 1
Modules linked in:
Pid: 1, comm: swapper Not tainted 2.6.28-rc3-dirty #16
RIP: 0010:[<ffffffff80486361>] [<ffffffff80486361>] cpufreq_stats_update+0x4a/0\
f
Synaptics claims to have extended capabilities, but I'm not able to read them.<6\
6
RAX: 0000000000000000 RBX: 0000000000000001 RCX: ffff88006e7528c0
RDX: 00000000ffffffff RSI: ffff88006e54af00 RDI: ffffffff808f056c
RBP: 00000000fffee697 R08: 0000000000000003 R09: ffff88006e73f080
R10: 0000000000000001 R11: 00000000002191c0 R12: ffff88006fb83c10
R13: 00000000ffffffff R14: 0000000000000001 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff88006fb50740(0000) knlGS:0000000000000000
Unable to initialize Synaptics hardware.
CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: ffff88086e7528b8 CR3: 0000000000201000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process swapper (pid: 1, threadinfo ffff88006fb82000, task ffff88006fb816d0)
Stack:
ffff88006e74da50 0000000000000000 ffff88006e54af00 ffffffff804863c7
ffff88006e74da50 0000000000000000 00000000ffffffff 0000000000000000
ffff88006fb83c10 ffffffff8024b46c ffffffff808f0560 ffff88006fb83c10
Call Trace:
[<ffffffff804863c7>] ? cpufreq_stat_notifier_trans+0x51/0x83
[<ffffffff8024b46c>] ? notifier_call_chain+0x29/0x4c
[<ffffffff8024b561>] ? __srcu_notifier_call_chain+0x46/0x61
[<ffffffff8048496d>] ? cpufreq_notify_transition+0x93/0xa9
[<ffffffff8021ab8d>] ? powernowk8_target+0x1e8/0x5f3
[<ffffffff80486687>] ? cpufreq_governor_performance+0x1b/0x20
[<ffffffff80484886>] ? __cpufreq_governor+0x71/0xa8
[<ffffffff80484b21>] ? __cpufreq_set_policy+0x101/0x13e
[<ffffffff80485bcd>] ? cpufreq_add_dev+0x3f0/0x4cd
[<ffffffff8048577a>] ? handle_update+0x0/0x8
[<ffffffff803c2062>] ? sysdev_driver_register+0xb6/0x10d
[<ffffffff8056592c>] ? powernowk8_init+0x0/0x7e
[<ffffffff8048604c>] ? cpufreq_register_driver+0x8f/0x140
[<ffffffff80209056>] ? _stext+0x56/0x14f
[<ffffffff802c2234>] ? proc_register+0x122/0x17d
[<ffffffff802c23a0>] ? create_proc_entry+0x73/0x8a
[<ffffffff8025c259>] ? register_irq_proc+0x92/0xaa
[<ffffffff8025c2c8>] ? init_irq_proc+0x57/0x69
[<ffffffff807fc85f>] ? kernel_init+0x116/0x169
[<ffffffff8020cc79>] ? child_rip+0xa/0x11
[<ffffffff807fc749>] ? kernel_init+0x0/0x169
[<ffffffff8020cc6f>] ? child_rip+0x0/0x11
Code: 05 c5 83 36 00 48 c7 c2 48 5d 86 80 48 8b 04 d8 48 8b 40 08 48 8b 34 02 48\
RIP [<ffffffff80486361>] cpufreq_stats_update+0x4a/0x5f
RSP <ffff88006fb83b20>
CR2: ffff88086e7528b8
---[ end trace 0678bac75e67a2f7 ]---
Kernel panic - not syncing: Attempted to kill init!
In short, aftereffect of the wrong P-state is that
cpufreq_stats_update() uses "-1" as index for some array in
cpufreq_stats_update (unsigned int cpu)
{
...
if (stat->time_in_state)
stat->time_in_state[stat->last_index] =
cputime64_add(stat->time_in_state[stat->last_index],
cputime_sub(cur_time, stat->last_time));
...
}
Fortunately, the wrong P-state value is returned only if the core is
in P-state 0. This fix solves the problem by detecting the
out-of-range P-state, ignoring it, and using "0" instead.
Cc: Mark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
Signed-off-by: Dave Jones <davej@redhat.com>
2008-11-21 16:49:25 +03:00
data - > currpstate = HW_PSTATE_INVALID ;
2005-04-17 02:20:36 +04:00
2009-02-17 02:31:58 +03:00
if ( powernow_k8_cpu_init_acpi ( data ) ) {
2005-04-17 02:20:36 +04:00
/*
* Use the PSB BIOS structure . This is only availabe on
* an UP version , and is deprecated by AMD .
*/
2006-06-21 09:32:56 +04:00
if ( num_online_cpus ( ) ! = 1 ) {
2009-04-22 15:48:32 +04:00
printk_once ( ACPI_PSS_BIOS_BUG_MSG ) ;
2009-02-18 22:11:00 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
}
if ( pol - > cpu ! = 0 ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " No ACPI _PSS objects for "
" CPU other than CPU0. Complain to your BIOS "
" vendor. \n " ) ;
2009-02-18 22:11:00 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
}
rc = find_psb_table ( data ) ;
2009-02-18 22:11:00 +03:00
if ( rc )
goto err_out ;
2009-02-03 19:46:43 +03:00
/* Take a crude guess here.
* That guess was in microseconds , so multiply with 1000 */
pol - > cpuinfo . transition_latency = (
( ( data - > rvo + 8 ) * data - > vstable * VST_UNITS_20US ) +
( ( 1 < < data - > irt ) * 30 ) ) * 1000 ;
} else /* ACPI _PSS objects available */
pol - > cpuinfo . transition_latency = get_transition_latency ( data ) ;
2005-04-17 02:20:36 +04:00
/* only run on specific CPU from here on */
2009-06-12 15:25:37 +04:00
init_on_cpu . data = data ;
smp_call_function_single ( data - > cpu , powernowk8_cpu_init_on_cpu ,
& init_on_cpu , 1 ) ;
rc = init_on_cpu . rc ;
if ( rc ! = 0 )
goto err_out_exit_acpi ;
2005-04-17 02:20:36 +04:00
2008-08-20 00:34:59 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2009-01-04 16:18:06 +03:00
cpumask_copy ( pol - > cpus , cpumask_of ( pol - > cpu ) ) ;
2008-08-20 00:34:59 +04:00
else
2009-03-13 07:19:50 +03:00
cpumask_copy ( pol - > cpus , cpu_core_mask ( pol - > cpu ) ) ;
2009-01-04 16:18:06 +03:00
data - > available_cores = pol - > cpus ;
2005-04-17 02:20:36 +04:00
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2009-02-04 22:37:50 +03:00
pol - > cur = find_khz_freq_from_pstate ( data - > powernow_table ,
data - > currpstate ) ;
2006-06-05 03:37:58 +04:00
else
pol - > cur = find_khz_freq_from_fid ( data - > currfid ) ;
2005-04-17 02:20:36 +04:00
dprintk ( " policy current frequency %d kHz \n " , pol - > cur ) ;
/* min/max the cpu is capable of */
if ( cpufreq_frequency_table_cpuinfo ( pol , data - > powernow_table ) ) {
2008-09-01 16:27:03 +04:00
printk ( KERN_ERR FW_BUG PFX " invalid powernow_table \n " ) ;
2005-04-17 02:20:36 +04:00
powernow_k8_cpu_exit_acpi ( data ) ;
kfree ( data - > powernow_table ) ;
kfree ( data ) ;
return - EINVAL ;
}
cpufreq_frequency_table_get_attr ( data - > powernow_table , pol - > cpu ) ;
2006-06-08 19:30:17 +04:00
if ( cpu_family = = CPU_HW_PSTATE )
2009-02-04 22:37:50 +03:00
dprintk ( " cpu_init done, current pstate 0x%x \n " ,
data - > currpstate ) ;
2006-06-05 03:37:58 +04:00
else
dprintk ( " cpu_init done, current fid 0x%x, vid 0x%x \n " ,
data - > currfid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:33:11 +03:00
per_cpu ( powernow_data , pol - > cpu ) = data ;
2005-04-17 02:20:36 +04:00
return 0 ;
2009-06-12 15:25:37 +04:00
err_out_exit_acpi :
2005-04-17 02:20:36 +04:00
powernow_k8_cpu_exit_acpi ( data ) ;
2009-02-18 22:11:00 +03:00
err_out :
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
return - ENODEV ;
}
2009-02-04 22:37:50 +03:00
static int __devexit powernowk8_cpu_exit ( struct cpufreq_policy * pol )
2005-04-17 02:20:36 +04:00
{
2008-01-30 15:33:11 +03:00
struct powernow_k8_data * data = per_cpu ( powernow_data , pol - > cpu ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - EINVAL ;
powernow_k8_cpu_exit_acpi ( data ) ;
cpufreq_frequency_table_put_attr ( pol - > cpu ) ;
kfree ( data - > powernow_table ) ;
kfree ( data ) ;
return 0 ;
}
2009-06-12 15:25:37 +04:00
static void query_values_on_cpu ( void * _err )
{
int * err = _err ;
struct powernow_k8_data * data = __get_cpu_var ( powernow_data ) ;
* err = query_current_values_with_pending_wait ( data ) ;
}
2009-02-04 22:37:50 +03:00
static unsigned int powernowk8_get ( unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
2009-06-11 19:26:54 +04:00
struct powernow_k8_data * data = per_cpu ( powernow_data , cpu ) ;
2005-04-17 02:20:36 +04:00
unsigned int khz = 0 ;
2009-06-12 15:25:37 +04:00
int err ;
2006-03-27 19:57:20 +04:00
if ( ! data )
return - EINVAL ;
2009-06-12 15:25:37 +04:00
smp_call_function_single ( cpu , query_values_on_cpu , & err , true ) ;
if ( err )
2005-04-17 02:20:36 +04:00
goto out ;
2007-01-30 18:53:54 +03:00
if ( cpu_family = = CPU_HW_PSTATE )
2008-04-05 05:11:05 +04:00
khz = find_khz_freq_from_pstate ( data - > powernow_table ,
data - > currpstate ) ;
2007-01-30 18:53:54 +03:00
else
khz = find_khz_freq_from_fid ( data - > currfid ) ;
2005-04-17 02:20:36 +04:00
2005-09-23 22:10:42 +04:00
out :
2005-04-17 02:20:36 +04:00
return khz ;
}
2009-02-04 22:37:50 +03:00
static struct freq_attr * powernow_k8_attr [ ] = {
2005-04-17 02:20:36 +04:00
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
2007-02-27 01:55:48 +03:00
static struct cpufreq_driver cpufreq_amd64_driver = {
2005-04-17 02:20:36 +04:00
. verify = powernowk8_verify ,
. target = powernowk8_target ,
. init = powernowk8_cpu_init ,
. exit = __devexit_p ( powernowk8_cpu_exit ) ,
. get = powernowk8_get ,
. name = " powernow-k8 " ,
. owner = THIS_MODULE ,
. attr = powernow_k8_attr ,
} ;
/* driver entry point for init */
2006-01-16 03:56:36 +03:00
static int __cpuinit powernowk8_init ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int i , supported_cpus = 0 ;
2006-03-24 14:15:07 +03:00
for_each_online_cpu ( i ) {
2009-06-12 15:25:37 +04:00
int rc ;
smp_call_function_single ( i , check_supported_cpu , & rc , 1 ) ;
if ( rc = = 0 )
2005-04-17 02:20:36 +04:00
supported_cpus + + ;
}
if ( supported_cpus = = num_online_cpus ( ) ) {
2006-06-05 03:37:58 +04:00
printk ( KERN_INFO PFX " Found %d %s "
2007-05-18 21:22:28 +04:00
" processors (%d cpu cores) ( " VERSION " ) \n " ,
2007-08-23 05:44:20 +04:00
num_online_nodes ( ) ,
2007-05-18 21:22:28 +04:00
boot_cpu_data . x86_model_id , supported_cpus ) ;
2005-04-17 02:20:36 +04:00
return cpufreq_register_driver ( & cpufreq_amd64_driver ) ;
}
return - ENODEV ;
}
/* driver entry point for term */
static void __exit powernowk8_exit ( void )
{
dprintk ( " exit \n " ) ;
cpufreq_unregister_driver ( & cpufreq_amd64_driver ) ;
}
2009-02-04 22:37:50 +03:00
MODULE_AUTHOR ( " Paul Devriendt <paul.devriendt@amd.com> and "
" Mark Langsdorf <mark.langsdorf@amd.com> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " AMD Athlon 64 and Opteron processor frequency driver. " ) ;
MODULE_LICENSE ( " GPL " ) ;
late_initcall ( powernowk8_init ) ;
module_exit ( powernowk8_exit ) ;