2005-04-17 02:20:36 +04:00
/*
2012-01-06 18:59:33 +04:00
* ( c ) 2003 - 2012 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
*
2012-01-06 18:59:33 +04:00
* Maintainer :
2012-10-31 03:30:38 +04:00
* Andreas Herrmann < herrmann . der . user @ googlemail . 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 >
2010-07-18 16:27:13 +04:00
* ( C ) 2004 Pavel Machek < pavel @ ucw . cz >
2005-04-17 02:20:36 +04:00
* 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
*
2012-01-06 18:59:33 +04:00
* Processor information obtained from Chapter 9 ( Power and Thermal
* Management ) of the " BIOS and Kernel Developer's Guide (BKDG) for
* the AMD Athlon 64 and AMD Opteron Processors " and section " 2. x
* Power Management " in BKDGs for newer AMD CPU families.
*
* Tables for specific CPUs can be inferred from AMD ' s processor
* power and thermal data sheets , ( e . g . 30417. pdf , 30430. pdf , 43375. pdf )
2005-04-17 02:20:36 +04:00
*/
2014-05-11 21:26:16 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
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>
2012-01-26 03:09:12 +04:00
# include <asm/cpu_device_id.h>
2005-04-17 02:20:36 +04:00
# 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>
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
2010-03-18 20:41:46 +03:00
static struct cpufreq_driver cpufreq_amd64_driver ;
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 ) ;
}
/* 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 ;
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 ;
2005-07-28 20:45:10 +04:00
do {
2005-10-22 01:21:03 +04:00
if ( i + + > 10000 ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " 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 ;
2011-03-27 17:04:46 +04:00
pr_debug ( " cpu%d, init lo 0x%x, hi 0x%x \n " , smp_processor_id ( ) , lo , hi ) ;
2005-04-17 02:20:36 +04:00
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 ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " internal error - overflow on fid write \n " ) ;
2005-04-17 02:20:36 +04:00
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
2011-03-27 17:04:46 +04:00
pr_debug ( " writing fid 0x%x, lo 0x%x, hi 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " fid trans failed, fid 0x%x, curr 0x%x \n " , fid ,
2009-02-04 22:37:50 +03:00
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 ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " internal error - overflow on vid write \n " ) ;
2005-04-17 02:20:36 +04:00
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
2011-03-27 17:04:46 +04:00
pr_debug ( " writing vid 0x%x, lo 0x%x, hi 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " fid changed on vid trans, old 0x%x new 0x%x \n " ,
savefid , data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
if ( vid ! = data - > currvid ) {
2014-05-11 21:26:16 +04:00
pr_err ( " vid trans failed, vid 0x%x, curr 0x%x \n " ,
2009-02-04 22:37:50 +03:00
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 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 ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " 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 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " transitioned (cpu%d): new fid 0x%x, vid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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
2014-05-11 21:26:16 +04:00
pr_debug ( " 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 ) ;
2011-03-27 17:04:46 +04:00
pr_debug ( " ph1 maxvid=0x%x \n " , maxvid ) ;
2005-06-01 06:03:46 +04:00
if ( reqvid < maxvid ) /* lower numbers are higher voltages */
reqvid = maxvid ;
2005-04-17 02:20:36 +04:00
while ( data - > currvid > reqvid ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " ph1: curr 0x%x, req vid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 {
2011-03-27 17:04:46 +04:00
pr_debug ( " ph1: changing vid for rvo, req 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " ph1 err, currfid changed 0x%x \n " , data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " ph1 complete, currfid 0x%x, currvid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " ph2 null fid transition 0x%x \n " , data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2014-05-11 21:26:16 +04:00
pr_debug ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " ph2: vid changed, save 0x%x, curr 0x%x \n " ,
2005-04-17 02:20:36 +04:00
savevid , data - > currvid ) ;
return 1 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " ph2 complete, currfid 0x%x, currvid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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 ;
2011-03-27 17:04:46 +04:00
pr_debug ( " ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
smp_processor_id ( ) ,
data - > currfid , data - > currvid ) ;
if ( reqvid ! = data - > currvid ) {
if ( write_new_vid ( data , reqvid ) )
return 1 ;
if ( savefid ! = data - > currfid ) {
2014-05-11 21:26:16 +04:00
pr_err ( " ph3: bad fid change, save 0x%x, curr 0x%x \n " ,
savefid , data - > currfid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
if ( data - > currvid ! = reqvid ) {
2014-05-11 21:26:16 +04:00
pr_err ( " ph3: failed vid transition \n , req 0x%x, curr 0x%x " ,
reqvid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
}
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( savereqvid ! = data - > currvid ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " ph3 failed, currvid 0x%x \n " , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
if ( savefid ! = data - > currfid ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " ph3 failed, currfid changed 0x%x \n " ,
2005-04-17 02:20:36 +04:00
data - > currfid ) ;
return 1 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " ph3 complete, currfid 0x%x, currvid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
data - > currfid , data - > currvid ) ;
return 0 ;
}
2012-01-26 03:09:12 +04:00
static const struct x86_cpu_id powernow_k8_ids [ ] = {
/* IO based frequency switching */
{ X86_VENDOR_AMD , 0xf } ,
{ }
} ;
MODULE_DEVICE_TABLE ( x86cpu , powernow_k8_ids ) ;
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
eax = cpuid_eax ( CPUID_PROCESSOR_SIGNATURE ) ;
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 ) ) {
2014-05-11 21:26:16 +04:00
pr_info ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_info ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_info ( " Power state transitions not supported \n " ) ;
2009-06-12 15:25:37 +04:00
return ;
2006-06-05 03:37:58 +04:00
}
2012-09-04 12:28:09 +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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 */
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 */
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 */
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " lastfid invalid \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( lastfid > LO_FID_TABLE_TOP )
2014-05-11 21:26:16 +04:00
pr_info ( FW_BUG " 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 ) {
2014-05-11 21:26:16 +04:00
pr_info ( " fid 0x%x (%d MHz), vid 0x%x \n " ,
data - > powernow_table [ j ] . driver_data & 0xff ,
data - > powernow_table [ j ] . frequency / 1000 ,
data - > powernow_table [ j ] . driver_data > > 8 ) ;
2006-06-05 03:37:58 +04:00
}
2005-04-17 02:20:36 +04:00
}
if ( data - > batps )
2014-05-11 21:26:16 +04:00
pr_info ( " Only %d pstates on battery \n " , data - > batps ) ;
2005-04-17 02:20:36 +04:00
}
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 */
2014-05-11 21:26:16 +04:00
pr_warn ( " 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " PST out of sequence \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
}
if ( data - > numps < 2 ) {
2014-05-11 21:26:16 +04:00
pr_err ( " no p states to transition \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
if ( check_pst_table ( data , pst , maxvid ) )
return - EINVAL ;
2014-03-28 17:41:46 +04:00
powernow_table = kzalloc ( ( sizeof ( * powernow_table )
2005-04-17 02:20:36 +04:00
* ( data - > numps + 1 ) ) , GFP_KERNEL ) ;
if ( ! powernow_table ) {
2014-05-11 21:26:16 +04:00
pr_err ( " powernow_table memory alloc failure \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
for ( j = 0 ; j < data - > numps ; j + + ) {
2009-02-04 22:37:50 +03:00
int freq ;
2013-03-30 14:55:15 +04:00
powernow_table [ j ] . driver_data = pst [ j ] . fid ; /* lower 8 bits */
powernow_table [ j ] . driver_data | = ( 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 ;
2013-03-30 14:55:15 +04:00
powernow_table [ data - > numps ] . driver_data = 0 ;
2005-04-17 02:20:36 +04:00
if ( query_current_values_with_pending_wait ( data ) ) {
kfree ( powernow_table ) ;
return - EIO ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " cfid 0x%x, cvid 0x%x \n " , data - > currfid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
data - > powernow_table = powernow_table ;
2015-05-26 16:11:31 +03:00
if ( cpumask_first ( topology_core_cpumask ( 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 ;
2011-03-27 17:04:46 +04:00
pr_debug ( " currfid/vid do not match PST, ignoring \n " ) ;
2005-04-17 02:20:36 +04:00
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 ;
2011-03-27 17:04:46 +04:00
pr_debug ( " found PSB header at 0x%p \n " , psb ) ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " table vers: 0x%x \n " , psb - > tableversion ) ;
2005-04-17 02:20:36 +04:00
if ( psb - > tableversion ! = PSB_VERSION_1_4 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " PSB table is not v1.4 \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 - > flags1 ) ;
2005-04-17 02:20:36 +04:00
if ( psb - > flags1 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " unknown flags \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
data - > vstable = psb - > vstable ;
2011-03-27 17:04:46 +04:00
pr_debug ( " voltage stabilization time: %d(*20us) \n " ,
2009-02-04 22:37:50 +03:00
data - > vstable ) ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " flags2: 0x%x \n " , psb - > flags2 ) ;
2005-04-17 02:20:36 +04:00
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 ;
2011-03-27 17:04:46 +04:00
pr_debug ( " ramp voltage offset: %d \n " , data - > rvo ) ;
pr_debug ( " isochronous relief time: %d \n " , data - > irt ) ;
pr_debug ( " maximum voltage step: %d - 0x%x \n " , mvs , data - > vidmvs ) ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " numpst: 0x%x \n " , psb - > num_tables ) ;
2005-04-17 02:20:36 +04:00
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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " numpst must be 1 \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
data - > plllock = psb - > plllocktime ;
2011-03-27 17:04:46 +04:00
pr_debug ( " plllocktime: 0x%x (units 1us) \n " , psb - > plllocktime ) ;
pr_debug ( " maxfid: 0x%x \n " , psb - > maxfid ) ;
pr_debug ( " maxvid: 0x%x \n " , psb - > maxvid ) ;
2005-04-17 02:20:36 +04:00
maxvid = psb - > maxvid ;
data - > numps = psb - > numps ;
2011-03-27 17:04:46 +04:00
pr_debug ( " 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
*/
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " No PSB or ACPI _PSS objects \n " ) ;
pr_err ( " Make sure that your BIOS is up to date and Cool'N'Quiet support is enabled in BIOS setup \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
{
2010-01-28 05:53:19 +03:00
u64 control ;
2009-02-04 22:37:50 +03:00
2012-09-04 12:28:09 +04:00
if ( ! data - > acpi_data . state_count )
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 ;
2010-01-28 05:53:19 +03:00
u64 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 ) ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " 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 ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " No ACPI P-States \n " ) ;
2005-04-17 02:20:36 +04:00
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 ) ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " Invalid control/status registers (%llx - %llx) \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 */
2014-03-28 17:41:46 +04:00
powernow_table = kzalloc ( ( sizeof ( * powernow_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 ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " powernow_table memory alloc failure \n " ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2012-09-04 12:28:09 +04:00
ret_val = fill_powernow_table_fidvid ( data , powernow_table ) ;
2006-06-05 03:37:58 +04:00
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 ;
2006-06-05 03:37:58 +04:00
data - > powernow_table = powernow_table ;
2015-05-26 16:11:31 +03:00
if ( cpumask_first ( topology_core_cpumask ( 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 ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " unable to alloc powernow_k8_data cpumask \n " ) ;
2009-01-01 05:08:47 +03:00
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 :
2015-07-22 23:11:16 +03:00
acpi_processor_unregister_performance ( 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_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 ;
2010-01-28 05:53:19 +03:00
u64 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
2011-03-27 17:04:46 +04:00
pr_debug ( " %d : fid 0x%x, vid 0x%x \n " , i , fid , vid ) ;
2005-04-17 02:20:36 +04:00
2009-02-04 22:37:50 +03:00
index = fid | ( vid < < 8 ) ;
2013-03-30 14:55:15 +04:00
powernow_table [ i ] . driver_data = index ;
2009-02-04 22:37:50 +03:00
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 ) ) ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " 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 ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " 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 ) ) {
2014-05-11 21:26:16 +04:00
pr_info ( " invalid freq entries %u kHz vs. %u kHz \n " ,
freq , ( unsigned int )
2009-02-04 22:37:50 +03:00
( 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 )
2015-07-22 23:11:16 +03:00
acpi_processor_unregister_performance ( 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 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_WARN " Invalid zero transition latency \n " ) ;
2009-04-22 15:48:30 +04:00
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
{
2013-03-24 10:26:43 +04:00
struct cpufreq_policy * policy ;
2006-06-05 03:37:58 +04:00
u32 fid = 0 ;
u32 vid = 0 ;
2013-03-24 10:26:43 +04:00
int res ;
2005-04-17 02:20:36 +04:00
struct cpufreq_freqs freqs ;
2011-03-27 17:04:46 +04:00
pr_debug ( " cpu %d transition to index %u \n " , smp_processor_id ( ) , index ) ;
2005-04-17 02:20:36 +04:00
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
*/
2013-03-30 14:55:15 +04:00
fid = data - > powernow_table [ index ] . driver_data & 0xFF ;
vid = ( data - > powernow_table [ index ] . driver_data & 0xFF00 ) > > 8 ;
2005-04-17 02:20:36 +04:00
2011-03-27 17:04:46 +04:00
pr_debug ( " table matched fid 0x%x, giving vid 0x%x \n " , fid , vid ) ;
2005-04-17 02:20:36 +04:00
if ( query_current_values_with_pending_wait ( data ) )
return 1 ;
if ( ( data - > currvid = = vid ) & & ( data - > currfid = = fid ) ) {
2011-03-27 17:04:46 +04:00
pr_debug ( " target matches current values (fid 0x%x, vid 0x%x) \n " ,
2005-04-17 02:20:36 +04:00
fid , vid ) ;
return 0 ;
}
2011-03-27 17:04:46 +04:00
pr_debug ( " cpu %d, changing to fid 0x%x, vid 0x%x \n " ,
2005-04-17 02:20:36 +04:00
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
2013-03-24 10:26:43 +04:00
policy = cpufreq_cpu_get ( smp_processor_id ( ) ) ;
cpufreq_cpu_put ( policy ) ;
2014-03-24 12:05:45 +04:00
cpufreq_freq_transition_begin ( policy , & freqs ) ;
2005-04-17 02:20:36 +04:00
res = transition_fid_vid ( data , fid , vid ) ;
2014-03-24 12:05:45 +04:00
cpufreq_freq_transition_end ( policy , & freqs , res ) ;
2006-06-05 03:37:58 +04:00
return res ;
}
2012-09-19 01:24:59 +04:00
struct powernowk8_target_arg {
struct cpufreq_policy * pol ;
2013-10-25 18:15:48 +04:00
unsigned newstate ;
2012-09-19 01:24:59 +04:00
} ;
static long powernowk8_target_fn ( void * arg )
2005-04-17 02:20:36 +04:00
{
2012-09-19 01:24:59 +04:00
struct powernowk8_target_arg * pta = arg ;
struct cpufreq_policy * pol = pta - > pol ;
2013-10-25 18:15:48 +04:00
unsigned newstate = pta - > newstate ;
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 ;
2012-09-19 01:24:59 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
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 ;
2005-04-17 02:20:36 +04:00
if ( pending_bit_stuck ( ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " failing targ, change pending bit set \n " ) ;
2012-09-19 01:24:59 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2013-10-25 18:15:48 +04:00
pr_debug ( " targ: cpu %d, %d kHz, min %d, max %d \n " ,
pol - > cpu , data - > powernow_table [ newstate ] . frequency , pol - > min ,
pol - > max ) ;
2005-04-17 02:20:36 +04:00
2006-05-31 01:43:54 +04:00
if ( query_current_values_with_pending_wait ( data ) )
2012-09-19 01:24:59 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
2012-09-04 12:28:09 +04:00
pr_debug ( " targ: curr fid 0x%x, vid 0x%x \n " ,
2014-05-11 21:26:16 +04:00
data - > currfid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
2012-09-04 12:28:09 +04:00
if ( ( checkvid ! = data - > currvid ) | |
( checkfid ! = data - > currfid ) ) {
2014-05-11 21:26:16 +04:00
pr_info ( " error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x \n " ,
2012-09-04 12:28:09 +04:00
checkfid , data - > currfid ,
checkvid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
}
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 ) ;
2012-09-04 12:28:09 +04:00
ret = transition_frequency_fidvid ( data , newstate ) ;
2006-06-05 03:37:58 +04:00
if ( ret ) {
2014-05-11 21:26:16 +04:00
pr_err ( " transition frequency failed \n " ) ;
2006-03-26 13:37:14 +04:00
mutex_unlock ( & fidvid_mutex ) ;
2012-09-19 01:24:59 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2006-03-26 13:37:14 +04:00
mutex_unlock ( & fidvid_mutex ) ;
2005-06-01 06:03:46 +04:00
2012-09-04 12:28:09 +04:00
pol - > cur = find_khz_freq_from_fid ( data - > currfid ) ;
2005-04-17 02:20:36 +04:00
2012-09-19 01:24:59 +04:00
return 0 ;
}
/* Driver entry point to switch to the target frequency */
2013-10-25 18:15:48 +04:00
static int powernowk8_target ( struct cpufreq_policy * pol , unsigned index )
2012-09-19 01:24:59 +04:00
{
2013-10-25 18:15:48 +04:00
struct powernowk8_target_arg pta = { . pol = pol , . newstate = index } ;
2012-09-19 01:24:59 +04:00
2012-10-23 02:55:10 +04:00
return work_on_cpu ( pol - > cpu , powernowk8_target_fn , & pta ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-12 15:25:37 +04:00
struct init_on_cpu {
struct powernow_k8_data * data ;
int rc ;
} ;
2013-06-19 21:54:04 +04:00
static void powernowk8_cpu_init_on_cpu ( void * _init_on_cpu )
2009-06-12 15:25:37 +04:00
{
struct init_on_cpu * init_on_cpu = _init_on_cpu ;
if ( pending_bit_stuck ( ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( " failing init, change pending bit set \n " ) ;
2009-06-12 15:25:37 +04:00
init_on_cpu - > rc = - ENODEV ;
return ;
}
if ( query_current_values_with_pending_wait ( init_on_cpu - > data ) ) {
init_on_cpu - > rc = - ENODEV ;
return ;
}
2012-09-04 12:28:09 +04:00
fidvid_msr_init ( ) ;
2009-06-12 15:25:37 +04:00
init_on_cpu - > rc = 0 ;
}
2014-05-11 21:26:16 +04:00
# define MISSING_PSS_MSG \
FW_BUG " No compatible ACPI _PSS objects found. \n " \
FW_BUG " First, make sure Cool'N'Quiet is enabled in the BIOS. \n " \
FW_BUG " If that doesn't help, try upgrading your BIOS. \n "
2012-09-05 02:50:26 +04:00
2005-04-17 02:20:36 +04:00
/* per CPU init entry point to the driver */
2013-06-19 21:54:04 +04:00
static int powernowk8_cpu_init ( struct cpufreq_policy * pol )
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 ;
2014-02-17 14:48:21 +04:00
int rc , cpu ;
2005-04-17 02:20:36 +04:00
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 ;
2013-08-06 21:23:06 +04:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
2014-05-11 21:26:16 +04:00
pr_err ( " unable to alloc powernow_k8_data " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
data - > cpu = pol - > cpu ;
2009-02-17 02:31:58 +03:00
if ( powernow_k8_cpu_init_acpi ( data ) ) {
2005-04-17 02:20:36 +04:00
/*
2011-03-17 22:24:16 +03:00
* Use the PSB BIOS structure . This is only available on
2005-04-17 02:20:36 +04:00
* an UP version , and is deprecated by AMD .
*/
2006-06-21 09:32:56 +04:00
if ( num_online_cpus ( ) ! = 1 ) {
2014-05-11 21:26:16 +04:00
pr_err_once ( MISSING_PSS_MSG ) ;
2009-02-18 22:11:00 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
}
if ( pol - > cpu ! = 0 ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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
2015-05-26 16:11:31 +03:00
cpumask_copy ( pol - > cpus , topology_core_cpumask ( pol - > cpu ) ) ;
2009-01-04 16:18:06 +03:00
data - > available_cores = pol - > cpus ;
2005-04-17 02:20:36 +04:00
/* min/max the cpu is capable of */
2013-09-16 17:26:27 +04:00
if ( cpufreq_table_validate_and_show ( pol , data - > powernow_table ) ) {
2014-05-11 21:26:16 +04:00
pr_err ( FW_BUG " 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 ;
}
2012-09-04 12:28:09 +04:00
pr_debug ( " cpu_init done, current fid 0x%x, vid 0x%x \n " ,
2014-05-11 21:26:16 +04:00
data - > currfid , data - > currvid ) ;
2005-04-17 02:20:36 +04:00
2014-02-17 14:48:21 +04:00
/* Point all the CPUs in this policy to the same data */
for_each_cpu ( cpu , pol - > cpus )
per_cpu ( powernow_data , 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 ;
}
2012-11-21 04:18:49 +04:00
static int 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 ) ;
2014-02-17 14:48:21 +04:00
int cpu ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - EINVAL ;
powernow_k8_cpu_exit_acpi ( data ) ;
kfree ( data - > powernow_table ) ;
kfree ( data ) ;
2014-02-17 14:48:21 +04:00
for_each_cpu ( cpu , pol - > cpus )
per_cpu ( powernow_data , cpu ) = NULL ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-06-12 15:25:37 +04:00
static void query_values_on_cpu ( void * _err )
{
int * err = _err ;
2010-12-18 18:28:55 +03:00
struct powernow_k8_data * data = __this_cpu_read ( powernow_data ) ;
2009-06-12 15:25:37 +04:00
* 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 )
2009-12-14 13:44:15 +03:00
return 0 ;
2006-03-27 19:57:20 +04:00
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 ;
2012-09-04 12:28:09 +04:00
khz = find_khz_freq_from_fid ( data - > currfid ) ;
2007-01-30 18:53:54 +03:00
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 ;
}
2007-02-27 01:55:48 +03:00
static struct cpufreq_driver cpufreq_amd64_driver = {
2013-10-29 17:26:06 +04:00
. flags = CPUFREQ_ASYNC_NOTIFICATION ,
2013-10-03 18:58:17 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = powernowk8_target ,
2009-11-19 14:31:01 +03:00
. bios_limit = acpi_processor_get_bios_limit ,
. init = powernowk8_cpu_init ,
2012-11-21 04:18:30 +04:00
. exit = powernowk8_cpu_exit ,
2009-11-19 14:31:01 +03:00
. get = powernowk8_get ,
. name = " powernow-k8 " ,
2013-10-03 18:58:17 +04:00
. attr = cpufreq_generic_attr ,
2005-04-17 02:20:36 +04:00
} ;
2013-01-20 14:24:29 +04:00
static void __request_acpi_cpufreq ( void )
{
const char * cur_drv , * drv = " acpi-cpufreq " ;
cur_drv = cpufreq_get_current_driver ( ) ;
if ( ! cur_drv )
goto request ;
if ( strncmp ( cur_drv , drv , min_t ( size_t , strlen ( cur_drv ) , strlen ( drv ) ) ) )
2014-05-11 21:26:16 +04:00
pr_warn ( " WTF driver: %s \n " , cur_drv ) ;
2013-01-20 14:24:29 +04:00
return ;
request :
2014-05-11 21:26:16 +04:00
pr_warn ( " This CPU is not supported anymore, using acpi-cpufreq instead. \n " ) ;
2013-01-20 14:24:29 +04:00
request_module ( drv ) ;
}
2005-04-17 02:20:36 +04:00
/* driver entry point for init */
2013-06-19 21:54:04 +04:00
static int powernowk8_init ( void )
2005-04-17 02:20:36 +04:00
{
2012-09-04 12:28:09 +04:00
unsigned int i , supported_cpus = 0 ;
2013-01-20 14:24:30 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
2012-09-04 12:28:09 +04:00
if ( static_cpu_has ( X86_FEATURE_HW_PSTATE ) ) {
2013-01-20 14:24:29 +04:00
__request_acpi_cpufreq ( ) ;
2012-01-26 03:09:12 +04:00
return - ENODEV ;
2012-09-04 12:28:09 +04:00
}
2012-01-26 03:09:12 +04:00
if ( ! x86_match_cpu ( powernow_k8_ids ) )
return - ENODEV ;
2013-01-20 14:24:30 +04:00
get_online_cpus ( ) ;
2006-03-24 14:15:07 +03:00
for_each_online_cpu ( i ) {
2013-01-20 14:24:30 +04:00
smp_call_function_single ( i , check_supported_cpu , & ret , 1 ) ;
if ( ! ret )
2005-04-17 02:20:36 +04:00
supported_cpus + + ;
}
2013-01-20 14:24:30 +04:00
if ( supported_cpus ! = num_online_cpus ( ) ) {
put_online_cpus ( ) ;
2010-03-31 23:56:42 +04:00
return - ENODEV ;
2013-01-20 14:24:30 +04:00
}
put_online_cpus ( ) ;
2010-03-31 23:56:42 +04:00
2013-01-20 14:24:30 +04:00
ret = cpufreq_register_driver ( & cpufreq_amd64_driver ) ;
if ( ret )
return ret ;
2010-03-31 23:56:42 +04:00
2014-05-11 21:26:16 +04:00
pr_info ( " Found %d %s (%d cpu cores) ( " VERSION " ) \n " ,
2013-01-20 14:24:30 +04:00
num_online_nodes ( ) , boot_cpu_data . x86_model_id , supported_cpus ) ;
2010-03-31 23:56:42 +04:00
2013-01-20 14:24:30 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/* driver entry point for term */
static void __exit powernowk8_exit ( void )
{
2011-03-27 17:04:46 +04:00
pr_debug ( " exit \n " ) ;
2005-04-17 02:20:36 +04:00
cpufreq_unregister_driver ( & cpufreq_amd64_driver ) ;
}
2014-05-11 21:26:16 +04:00
MODULE_AUTHOR ( " Paul Devriendt <paul.devriendt@amd.com> " ) ;
MODULE_AUTHOR ( " 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 ) ;