2005-04-17 02:20:36 +04:00
/*
2006-10-03 23:29:15 +04:00
* acpi - cpufreq . c - ACPI Processor P - States Driver ( $ Revision : 1.4 $ )
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
* Copyright ( C ) 2002 - 2004 Dominik Brodowski < linux @ brodo . de >
2006-10-03 23:29:15 +04:00
* Copyright ( C ) 2006 Denis Sadykov < denis . m . sadykov @ intel . com >
2005-04-17 02:20:36 +04:00
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
2006-10-03 23:29:15 +04:00
# include <linux/smp.h>
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/cpufreq.h>
2005-08-25 23:59:00 +04:00
# include <linux/compiler.h>
2006-09-02 01:02:24 +04:00
# include <linux/dmi.h>
2005-04-17 02:20:36 +04:00
# include <linux/acpi.h>
# include <acpi/processor.h>
2006-10-03 23:29:15 +04:00
# include <asm/io.h>
2006-10-03 23:33:14 +04:00
# include <asm/msr.h>
2006-10-03 23:29:15 +04:00
# include <asm/processor.h>
# include <asm/cpufeature.h>
# include <asm/delay.h>
# include <asm/uaccess.h>
2005-04-17 02:20:36 +04:00
# define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
MODULE_AUTHOR ( " Paul Diefenbaugh, Dominik Brodowski " ) ;
MODULE_DESCRIPTION ( " ACPI Processor P-States Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2006-10-03 23:33:14 +04:00
enum {
UNDEFINED_CAPABLE = 0 ,
SYSTEM_INTEL_MSR_CAPABLE ,
SYSTEM_IO_CAPABLE ,
} ;
# define INTEL_MSR_RANGE (0xffff)
2006-10-03 23:38:45 +04:00
# define CPUID_6_ECX_APERFMPERF_CAPABILITY (0x1)
2006-10-03 23:33:14 +04:00
2006-10-03 23:29:15 +04:00
struct acpi_cpufreq_data {
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * acpi_data ;
struct cpufreq_frequency_table * freq_table ;
2006-10-03 23:38:45 +04:00
unsigned int max_freq ;
2006-10-03 23:35:23 +04:00
unsigned int resume ;
unsigned int cpu_feature ;
2005-04-17 02:20:36 +04:00
} ;
2006-10-03 23:35:23 +04:00
static struct acpi_cpufreq_data * drv_data [ NR_CPUS ] ;
static struct acpi_processor_performance * acpi_perf_data [ NR_CPUS ] ;
2005-04-17 02:20:36 +04:00
static struct cpufreq_driver acpi_cpufreq_driver ;
2005-08-25 23:59:00 +04:00
static unsigned int acpi_pstate_strict ;
2006-10-03 23:33:14 +04:00
static int check_est_cpu ( unsigned int cpuid )
{
struct cpuinfo_x86 * cpu = & cpu_data [ cpuid ] ;
if ( cpu - > x86_vendor ! = X86_VENDOR_INTEL | |
2006-10-03 23:35:23 +04:00
! cpu_has ( cpu , X86_FEATURE_EST ) )
2006-10-03 23:33:14 +04:00
return 0 ;
return 1 ;
}
static unsigned extract_io ( u32 value , struct acpi_cpufreq_data * data )
2006-10-03 23:29:15 +04:00
{
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * perf ;
int i ;
2006-10-03 23:29:15 +04:00
perf = data - > acpi_data ;
2006-10-18 08:41:48 +04:00
for ( i = 0 ; i < perf - > state_count ; i + + ) {
2006-10-03 23:29:15 +04:00
if ( value = = perf - > states [ i ] . status )
return data - > freq_table [ i ] . frequency ;
}
return 0 ;
}
2006-10-03 23:33:14 +04:00
static unsigned extract_msr ( u32 msr , struct acpi_cpufreq_data * data )
{
int i ;
2006-10-03 23:37:42 +04:00
struct acpi_processor_performance * perf ;
2006-10-03 23:33:14 +04:00
msr & = INTEL_MSR_RANGE ;
2006-10-03 23:37:42 +04:00
perf = data - > acpi_data ;
2006-10-18 08:41:48 +04:00
for ( i = 0 ; data - > freq_table [ i ] . frequency ! = CPUFREQ_TABLE_END ; i + + ) {
2006-10-03 23:37:42 +04:00
if ( msr = = perf - > states [ data - > freq_table [ i ] . index ] . status )
2006-10-03 23:33:14 +04:00
return data - > freq_table [ i ] . frequency ;
}
return data - > freq_table [ 0 ] . frequency ;
}
static unsigned extract_freq ( u32 val , struct acpi_cpufreq_data * data )
{
switch ( data - > cpu_feature ) {
2006-10-03 23:35:23 +04:00
case SYSTEM_INTEL_MSR_CAPABLE :
2006-10-03 23:33:14 +04:00
return extract_msr ( val , data ) ;
2006-10-03 23:35:23 +04:00
case SYSTEM_IO_CAPABLE :
2006-10-03 23:33:14 +04:00
return extract_io ( val , data ) ;
2006-10-03 23:35:23 +04:00
default :
2006-10-03 23:33:14 +04:00
return 0 ;
}
}
struct msr_addr {
u32 reg ;
} ;
2006-10-03 23:29:15 +04:00
struct io_addr {
u16 port ;
u8 bit_width ;
} ;
2006-10-03 23:33:14 +04:00
typedef union {
struct msr_addr msr ;
struct io_addr io ;
} drv_addr_union ;
2006-10-03 23:29:15 +04:00
struct drv_cmd {
2006-10-03 23:33:14 +04:00
unsigned int type ;
2006-10-03 23:29:15 +04:00
cpumask_t mask ;
2006-10-03 23:33:14 +04:00
drv_addr_union addr ;
2006-10-03 23:29:15 +04:00
u32 val ;
} ;
static void do_drv_read ( struct drv_cmd * cmd )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:33:14 +04:00
u32 h ;
switch ( cmd - > type ) {
2006-10-03 23:35:23 +04:00
case SYSTEM_INTEL_MSR_CAPABLE :
2006-10-03 23:33:14 +04:00
rdmsr ( cmd - > addr . msr . reg , cmd - > val , h ) ;
break ;
2006-10-03 23:35:23 +04:00
case SYSTEM_IO_CAPABLE :
2006-12-13 21:41:16 +03:00
acpi_os_read_port ( ( acpi_io_address ) cmd - > addr . io . port ,
& cmd - > val ,
( u32 ) cmd - > addr . io . bit_width ) ;
2006-10-03 23:33:14 +04:00
break ;
2006-10-03 23:35:23 +04:00
default :
2006-10-03 23:33:14 +04:00
break ;
}
2006-10-03 23:29:15 +04:00
}
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
static void do_drv_write ( struct drv_cmd * cmd )
{
2006-10-03 23:33:14 +04:00
u32 h = 0 ;
switch ( cmd - > type ) {
2006-10-03 23:35:23 +04:00
case SYSTEM_INTEL_MSR_CAPABLE :
2006-10-03 23:33:14 +04:00
wrmsr ( cmd - > addr . msr . reg , cmd - > val , h ) ;
break ;
2006-10-03 23:35:23 +04:00
case SYSTEM_IO_CAPABLE :
2006-12-13 21:41:16 +03:00
acpi_os_write_port ( ( acpi_io_address ) cmd - > addr . io . port ,
cmd - > val ,
( u32 ) cmd - > addr . io . bit_width ) ;
2006-10-03 23:33:14 +04:00
break ;
2006-10-03 23:35:23 +04:00
default :
2006-10-03 23:33:14 +04:00
break ;
}
2006-10-03 23:29:15 +04:00
}
2005-04-17 02:20:36 +04:00
2006-10-18 08:41:48 +04:00
static void drv_read ( struct drv_cmd * cmd )
2006-10-03 23:29:15 +04:00
{
2006-10-03 23:35:23 +04:00
cpumask_t saved_mask = current - > cpus_allowed ;
2006-10-03 23:29:15 +04:00
cmd - > val = 0 ;
set_cpus_allowed ( current , cmd - > mask ) ;
do_drv_read ( cmd ) ;
set_cpus_allowed ( current , saved_mask ) ;
}
static void drv_write ( struct drv_cmd * cmd )
{
2006-10-03 23:35:23 +04:00
cpumask_t saved_mask = current - > cpus_allowed ;
unsigned int i ;
2006-10-03 23:29:15 +04:00
for_each_cpu_mask ( i , cmd - > mask ) {
set_cpus_allowed ( current , cpumask_of_cpu ( i ) ) ;
do_drv_write ( cmd ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:29:15 +04:00
set_cpus_allowed ( current , saved_mask ) ;
return ;
}
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
static u32 get_cur_val ( cpumask_t mask )
{
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * perf ;
struct drv_cmd cmd ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
if ( unlikely ( cpus_empty ( mask ) ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:33:14 +04:00
switch ( drv_data [ first_cpu ( mask ) ] - > cpu_feature ) {
case SYSTEM_INTEL_MSR_CAPABLE :
cmd . type = SYSTEM_INTEL_MSR_CAPABLE ;
cmd . addr . msr . reg = MSR_IA32_PERF_STATUS ;
break ;
case SYSTEM_IO_CAPABLE :
cmd . type = SYSTEM_IO_CAPABLE ;
perf = drv_data [ first_cpu ( mask ) ] - > acpi_data ;
cmd . addr . io . port = perf - > control_register . address ;
cmd . addr . io . bit_width = perf - > control_register . bit_width ;
break ;
default :
return 0 ;
}
2006-10-03 23:29:15 +04:00
cmd . mask = mask ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
drv_read ( & cmd ) ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
dprintk ( " get_cur_val = %u \n " , cmd . val ) ;
return cmd . val ;
}
2005-04-17 02:20:36 +04:00
2006-10-03 23:38:45 +04:00
/*
* Return the measured active ( C0 ) frequency on this CPU since last call
* to this function .
* Input : cpu number
* Return : Average CPU frequency in terms of max frequency ( zero on error )
*
* We use IA32_MPERF and IA32_APERF MSRs to get the measured performance
* over a period of time , while CPU is in C0 state .
* IA32_MPERF counts at the rate of max advertised frequency
* IA32_APERF counts at the rate of actual CPU frequency
* Only IA32_APERF / IA32_MPERF ratio is architecturally defined and
* no meaning should be associated with absolute values of these MSRs .
*/
static unsigned int get_measured_perf ( unsigned int cpu )
{
union {
struct {
u32 lo ;
u32 hi ;
} split ;
u64 whole ;
} aperf_cur , mperf_cur ;
cpumask_t saved_mask ;
unsigned int perf_percent ;
unsigned int retval ;
saved_mask = current - > cpus_allowed ;
set_cpus_allowed ( current , cpumask_of_cpu ( cpu ) ) ;
if ( get_cpu ( ) ! = cpu ) {
/* We were not able to run on requested processor */
put_cpu ( ) ;
return 0 ;
}
rdmsr ( MSR_IA32_APERF , aperf_cur . split . lo , aperf_cur . split . hi ) ;
rdmsr ( MSR_IA32_MPERF , mperf_cur . split . lo , mperf_cur . split . hi ) ;
wrmsr ( MSR_IA32_APERF , 0 , 0 ) ;
wrmsr ( MSR_IA32_MPERF , 0 , 0 ) ;
# ifdef __i386__
/*
* We dont want to do 64 bit divide with 32 bit kernel
* Get an approximate value . Return failure in case we cannot get
* an approximate value .
*/
if ( unlikely ( aperf_cur . split . hi | | mperf_cur . split . hi ) ) {
int shift_count ;
u32 h ;
h = max_t ( u32 , aperf_cur . split . hi , mperf_cur . split . hi ) ;
shift_count = fls ( h ) ;
aperf_cur . whole > > = shift_count ;
mperf_cur . whole > > = shift_count ;
}
if ( ( ( unsigned long ) ( - 1 ) / 100 ) < aperf_cur . split . lo ) {
int shift_count = 7 ;
aperf_cur . split . lo > > = shift_count ;
mperf_cur . split . lo > > = shift_count ;
}
2006-10-18 08:41:48 +04:00
if ( aperf_cur . split . lo & & mperf_cur . split . lo )
2006-10-03 23:38:45 +04:00
perf_percent = ( aperf_cur . split . lo * 100 ) / mperf_cur . split . lo ;
2006-10-18 08:41:48 +04:00
else
2006-10-03 23:38:45 +04:00
perf_percent = 0 ;
# else
if ( unlikely ( ( ( unsigned long ) ( - 1 ) / 100 ) < aperf_cur . whole ) ) {
int shift_count = 7 ;
aperf_cur . whole > > = shift_count ;
mperf_cur . whole > > = shift_count ;
}
2006-10-18 08:41:48 +04:00
if ( aperf_cur . whole & & mperf_cur . whole )
2006-10-03 23:38:45 +04:00
perf_percent = ( aperf_cur . whole * 100 ) / mperf_cur . whole ;
2006-10-18 08:41:48 +04:00
else
2006-10-03 23:38:45 +04:00
perf_percent = 0 ;
# endif
retval = drv_data [ cpu ] - > max_freq * perf_percent / 100 ;
put_cpu ( ) ;
set_cpus_allowed ( current , saved_mask ) ;
dprintk ( " cpu %d: performance percent %d \n " , cpu , perf_percent ) ;
return retval ;
}
2006-10-03 23:29:15 +04:00
static unsigned int get_cur_freq_on_cpu ( unsigned int cpu )
{
2006-10-03 23:35:23 +04:00
struct acpi_cpufreq_data * data = drv_data [ cpu ] ;
unsigned int freq ;
2006-10-03 23:29:15 +04:00
dprintk ( " get_cur_freq_on_cpu (%d) \n " , cpu ) ;
if ( unlikely ( data = = NULL | |
2006-10-03 23:35:23 +04:00
data - > acpi_data = = NULL | | data - > freq_table = = NULL ) ) {
2006-10-03 23:29:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:29:15 +04:00
freq = extract_freq ( get_cur_val ( cpumask_of_cpu ( cpu ) ) , data ) ;
dprintk ( " cur freq = %u \n " , freq ) ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
return freq ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:29:15 +04:00
static unsigned int check_freqs ( cpumask_t mask , unsigned int freq ,
2006-10-03 23:35:23 +04:00
struct acpi_cpufreq_data * data )
2006-10-03 23:29:15 +04:00
{
2006-10-03 23:35:23 +04:00
unsigned int cur_freq ;
unsigned int i ;
2005-04-17 02:20:36 +04:00
2006-10-18 08:41:48 +04:00
for ( i = 0 ; i < 100 ; i + + ) {
2006-10-03 23:29:15 +04:00
cur_freq = extract_freq ( get_cur_val ( mask ) , data ) ;
if ( cur_freq = = freq )
return 1 ;
udelay ( 10 ) ;
}
return 0 ;
}
static int acpi_cpufreq_target ( struct cpufreq_policy * policy ,
2006-10-03 23:35:23 +04:00
unsigned int target_freq , unsigned int relation )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:35:23 +04:00
struct acpi_cpufreq_data * data = drv_data [ policy - > cpu ] ;
struct acpi_processor_performance * perf ;
struct cpufreq_freqs freqs ;
cpumask_t online_policy_cpus ;
struct drv_cmd cmd ;
unsigned int msr ;
unsigned int next_state = 0 ;
unsigned int next_perf_state = 0 ;
unsigned int i ;
int result = 0 ;
2006-10-03 23:29:15 +04:00
dprintk ( " acpi_cpufreq_target %d (%d) \n " , target_freq , policy - > cpu ) ;
if ( unlikely ( data = = NULL | |
2006-10-18 08:41:48 +04:00
data - > acpi_data = = NULL | | data - > freq_table = = NULL ) ) {
2006-10-03 23:29:15 +04:00
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
perf = data - > acpi_data ;
2005-04-17 02:20:36 +04:00
result = cpufreq_frequency_table_target ( policy ,
2006-10-03 23:35:23 +04:00
data - > freq_table ,
target_freq ,
relation , & next_state ) ;
2005-12-14 23:05:00 +03:00
if ( unlikely ( result ) )
2006-10-03 23:29:15 +04:00
return - ENODEV ;
2005-12-14 23:05:00 +03:00
2006-03-29 02:03:00 +04:00
# ifdef CONFIG_HOTPLUG_CPU
2005-12-14 23:05:00 +03:00
/* cpufreq holds the hotplug lock, so we are safe from here on */
cpus_and ( online_policy_cpus , cpu_online_map , policy - > cpus ) ;
2006-03-29 02:03:00 +04:00
# else
online_policy_cpus = policy - > cpus ;
# endif
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
next_perf_state = data - > freq_table [ next_state ] . index ;
2006-10-03 23:36:30 +04:00
if ( perf - > state = = next_perf_state ) {
2006-10-03 23:29:15 +04:00
if ( unlikely ( data - > resume ) ) {
2006-10-03 23:35:23 +04:00
dprintk ( " Called after resume, resetting to P%d \n " ,
next_perf_state ) ;
2006-10-03 23:29:15 +04:00
data - > resume = 0 ;
} else {
2006-10-03 23:35:23 +04:00
dprintk ( " Already at target state (P%d) \n " ,
next_perf_state ) ;
2006-10-03 23:29:15 +04:00
return 0 ;
}
2005-12-14 23:05:00 +03:00
}
2006-10-03 23:35:23 +04:00
switch ( data - > cpu_feature ) {
case SYSTEM_INTEL_MSR_CAPABLE :
cmd . type = SYSTEM_INTEL_MSR_CAPABLE ;
cmd . addr . msr . reg = MSR_IA32_PERF_CTL ;
msr =
( u32 ) perf - > states [ next_perf_state ] .
control & INTEL_MSR_RANGE ;
cmd . val = ( cmd . val & ~ INTEL_MSR_RANGE ) | msr ;
break ;
case SYSTEM_IO_CAPABLE :
cmd . type = SYSTEM_IO_CAPABLE ;
cmd . addr . io . port = perf - > control_register . address ;
cmd . addr . io . bit_width = perf - > control_register . bit_width ;
cmd . val = ( u32 ) perf - > states [ next_perf_state ] . control ;
break ;
default :
return - ENODEV ;
}
2005-12-14 23:05:00 +03:00
2006-10-03 23:29:15 +04:00
cpus_clear ( cmd . mask ) ;
2005-12-14 23:05:00 +03:00
2006-10-03 23:29:15 +04:00
if ( policy - > shared_type ! = CPUFREQ_SHARED_TYPE_ANY )
cmd . mask = online_policy_cpus ;
else
cpu_set ( policy - > cpu , cmd . mask ) ;
2005-12-14 23:05:00 +03:00
2006-10-03 23:36:30 +04:00
freqs . old = data - > freq_table [ perf - > state ] . frequency ;
freqs . new = data - > freq_table [ next_perf_state ] . frequency ;
2006-10-03 23:29:15 +04:00
for_each_cpu_mask ( i , cmd . mask ) {
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_PRECHANGE ) ;
2005-12-14 23:05:00 +03:00
}
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
drv_write ( & cmd ) ;
2005-12-14 23:05:00 +03:00
2006-10-03 23:29:15 +04:00
if ( acpi_pstate_strict ) {
if ( ! check_freqs ( cmd . mask , freqs . new , data ) ) {
dprintk ( " acpi_cpufreq_target failed (%d) \n " ,
2006-10-03 23:35:23 +04:00
policy - > cpu ) ;
2006-10-03 23:29:15 +04:00
return - EAGAIN ;
2005-12-14 23:05:00 +03:00
}
}
2006-10-03 23:29:15 +04:00
for_each_cpu_mask ( i , cmd . mask ) {
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
}
perf - > state = next_perf_state ;
return result ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:35:23 +04:00
static int acpi_cpufreq_verify ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:29:15 +04:00
struct acpi_cpufreq_data * data = drv_data [ policy - > cpu ] ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_verify \n " ) ;
2006-10-03 23:29:15 +04:00
return cpufreq_frequency_table_verify ( policy , data - > freq_table ) ;
2005-04-17 02:20:36 +04:00
}
static unsigned long
2006-10-03 23:35:23 +04:00
acpi_cpufreq_guess_freq ( struct acpi_cpufreq_data * data , unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * perf = data - > acpi_data ;
2005-12-14 23:05:00 +03:00
2005-04-17 02:20:36 +04:00
if ( cpu_khz ) {
/* search the closest match to cpu_khz */
unsigned int i ;
unsigned long freq ;
2005-12-14 23:05:00 +03:00
unsigned long freqn = perf - > states [ 0 ] . core_frequency * 1000 ;
2005-04-17 02:20:36 +04:00
2006-10-18 08:41:48 +04:00
for ( i = 0 ; i < ( perf - > state_count - 1 ) ; i + + ) {
2005-04-17 02:20:36 +04:00
freq = freqn ;
2006-10-18 08:41:48 +04:00
freqn = perf - > states [ i + 1 ] . core_frequency * 1000 ;
2005-04-17 02:20:36 +04:00
if ( ( 2 * cpu_khz ) > ( freqn + freq ) ) {
2005-12-14 23:05:00 +03:00
perf - > state = i ;
2006-10-03 23:35:23 +04:00
return freq ;
2005-04-17 02:20:36 +04:00
}
}
2006-10-18 08:41:48 +04:00
perf - > state = perf - > state_count - 1 ;
2006-10-03 23:35:23 +04:00
return freqn ;
2005-12-14 23:05:00 +03:00
} else {
2005-04-17 02:20:36 +04:00
/* assume CPU is at P0... */
2005-12-14 23:05:00 +03:00
perf - > state = 0 ;
return perf - > states [ 0 ] . core_frequency * 1000 ;
}
2005-04-17 02:20:36 +04:00
}
2005-12-14 23:05:00 +03:00
/*
* acpi_cpufreq_early_init - initialize ACPI P - States library
*
* Initialize the ACPI P - States library ( drivers / acpi / processor_perflib . c )
* in order to determine correct frequency and voltage pairings . We can
* do _PDC and _PSD and find out the processor dependency for the
* actual init that will happen later . . .
*/
2006-10-03 23:29:15 +04:00
static int acpi_cpufreq_early_init ( void )
2005-12-14 23:05:00 +03:00
{
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * data ;
cpumask_t covered ;
unsigned int i , j ;
2005-12-14 23:05:00 +03:00
dprintk ( " acpi_cpufreq_early_init \n " ) ;
2006-06-25 16:46:43 +04:00
for_each_possible_cpu ( i ) {
2006-10-03 23:35:23 +04:00
data = kzalloc ( sizeof ( struct acpi_processor_performance ) ,
GFP_KERNEL ) ;
2005-12-14 23:05:00 +03:00
if ( ! data ) {
2006-10-03 23:29:15 +04:00
for_each_cpu_mask ( j , covered ) {
2005-12-14 23:05:00 +03:00
kfree ( acpi_perf_data [ j ] ) ;
acpi_perf_data [ j ] = NULL ;
}
2006-10-03 23:35:23 +04:00
return - ENOMEM ;
2005-12-14 23:05:00 +03:00
}
acpi_perf_data [ i ] = data ;
2006-10-03 23:29:15 +04:00
cpu_set ( i , covered ) ;
2005-12-14 23:05:00 +03:00
}
/* Do initialization in ACPI core */
2006-10-03 23:29:15 +04:00
acpi_processor_preregister_performance ( acpi_perf_data ) ;
return 0 ;
2005-12-14 23:05:00 +03:00
}
2006-10-21 09:37:39 +04:00
# ifdef CONFIG_SMP
2006-09-02 01:02:24 +04:00
/*
* Some BIOSes do SW_ANY coordination internally , either set it up in hw
* or do it in BIOS firmware and won ' t inform about it to OS . If not
* detected , this has a side effect of making CPU run at a different speed
* than OS intended it to run at . Detect it and handle it cleanly .
*/
static int bios_with_sw_any_bug ;
2006-09-25 22:23:32 +04:00
static int sw_any_bug_found ( struct dmi_system_id * d )
2006-09-02 01:02:24 +04:00
{
bios_with_sw_any_bug = 1 ;
return 0 ;
}
2006-09-25 22:23:32 +04:00
static struct dmi_system_id sw_any_bug_dmi_table [ ] = {
2006-09-02 01:02:24 +04:00
{
. callback = sw_any_bug_found ,
. ident = " Supermicro Server X6DLP " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Supermicro " ) ,
DMI_MATCH ( DMI_BIOS_VERSION , " 080010 " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " X6DLP " ) ,
} ,
} ,
{ }
} ;
2006-10-21 09:37:39 +04:00
# endif
2006-09-02 01:02:24 +04:00
2006-10-03 23:35:23 +04:00
static int acpi_cpufreq_cpu_init ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:35:23 +04:00
unsigned int i ;
unsigned int valid_states = 0 ;
unsigned int cpu = policy - > cpu ;
struct acpi_cpufreq_data * data ;
unsigned int result = 0 ;
struct cpuinfo_x86 * c = & cpu_data [ policy - > cpu ] ;
struct acpi_processor_performance * perf ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_cpu_init \n " ) ;
2005-12-14 23:05:00 +03:00
if ( ! acpi_perf_data [ cpu ] )
2006-10-03 23:35:23 +04:00
return - ENODEV ;
2005-12-14 23:05:00 +03:00
2006-10-03 23:29:15 +04:00
data = kzalloc ( sizeof ( struct acpi_cpufreq_data ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
2006-10-03 23:35:23 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2005-12-14 23:05:00 +03:00
data - > acpi_data = acpi_perf_data [ cpu ] ;
2006-10-03 23:29:15 +04:00
drv_data [ cpu ] = data ;
2005-04-17 02:20:36 +04:00
2006-10-18 08:41:48 +04:00
if ( cpu_has ( c , X86_FEATURE_CONSTANT_TSC ) )
2006-10-03 23:29:15 +04:00
acpi_cpufreq_driver . flags | = CPUFREQ_CONST_LOOPS ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:29:15 +04:00
result = acpi_processor_register_performance ( data - > acpi_data , cpu ) ;
2005-04-17 02:20:36 +04:00
if ( result )
goto err_free ;
2005-12-14 23:05:00 +03:00
perf = data - > acpi_data ;
policy - > shared_type = perf - > shared_type ;
2006-10-18 08:41:48 +04:00
2006-06-26 08:34:43 +04:00
/*
2006-10-18 08:41:48 +04:00
* Will let policy - > cpus know about dependency only when software
2006-06-26 08:34:43 +04:00
* coordination is required .
*/
if ( policy - > shared_type = = CPUFREQ_SHARED_TYPE_ALL | |
2006-09-02 01:02:24 +04:00
policy - > shared_type = = CPUFREQ_SHARED_TYPE_ANY ) {
2006-06-26 08:34:43 +04:00
policy - > cpus = perf - > shared_cpu_map ;
2006-09-02 01:02:24 +04:00
}
# ifdef CONFIG_SMP
dmi_check_system ( sw_any_bug_dmi_table ) ;
if ( bios_with_sw_any_bug & & cpus_weight ( policy - > cpus ) = = 1 ) {
policy - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
policy - > cpus = cpu_core_map [ cpu ] ;
}
# endif
2005-12-14 23:05:00 +03:00
2005-04-17 02:20:36 +04:00
/* capability check */
2005-12-14 23:05:00 +03:00
if ( perf - > state_count < = 1 ) {
2005-04-17 02:20:36 +04:00
dprintk ( " No P-States \n " ) ;
result = - ENODEV ;
goto err_unreg ;
}
2005-12-14 23:05:00 +03:00
2006-10-03 23:29:15 +04:00
if ( perf - > control_register . space_id ! = perf - > status_register . space_id ) {
result = - ENODEV ;
goto err_unreg ;
}
switch ( perf - > control_register . space_id ) {
2006-10-03 23:35:23 +04:00
case ACPI_ADR_SPACE_SYSTEM_IO :
2006-10-03 23:29:15 +04:00
dprintk ( " SYSTEM IO addr space \n " ) ;
2006-10-03 23:33:14 +04:00
data - > cpu_feature = SYSTEM_IO_CAPABLE ;
break ;
2006-10-03 23:35:23 +04:00
case ACPI_ADR_SPACE_FIXED_HARDWARE :
2006-10-03 23:33:14 +04:00
dprintk ( " HARDWARE addr space \n " ) ;
if ( ! check_est_cpu ( cpu ) ) {
result = - ENODEV ;
goto err_unreg ;
}
data - > cpu_feature = SYSTEM_INTEL_MSR_CAPABLE ;
2006-10-03 23:29:15 +04:00
break ;
2006-10-03 23:35:23 +04:00
default :
2006-10-03 23:29:15 +04:00
dprintk ( " Unknown addr space %d \n " ,
2006-10-03 23:35:23 +04:00
( u32 ) ( perf - > control_register . space_id ) ) ;
2005-04-17 02:20:36 +04:00
result = - ENODEV ;
goto err_unreg ;
}
2006-10-18 08:41:48 +04:00
data - > freq_table = kmalloc ( sizeof ( struct cpufreq_frequency_table ) *
( perf - > state_count + 1 ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! data - > freq_table ) {
result = - ENOMEM ;
goto err_unreg ;
}
/* detect transition latency */
policy - > cpuinfo . transition_latency = 0 ;
2006-10-18 08:41:48 +04:00
for ( i = 0 ; i < perf - > state_count ; i + + ) {
2006-10-03 23:35:23 +04:00
if ( ( perf - > states [ i ] . transition_latency * 1000 ) >
policy - > cpuinfo . transition_latency )
policy - > cpuinfo . transition_latency =
perf - > states [ i ] . transition_latency * 1000 ;
2005-04-17 02:20:36 +04:00
}
policy - > governor = CPUFREQ_DEFAULT_GOVERNOR ;
2006-10-03 23:38:45 +04:00
data - > max_freq = perf - > states [ 0 ] . core_frequency * 1000 ;
2005-04-17 02:20:36 +04:00
/* table init */
2006-10-18 08:41:48 +04:00
for ( i = 0 ; i < perf - > state_count ; i + + ) {
if ( i > 0 & & perf - > states [ i ] . core_frequency = =
perf - > states [ i - 1 ] . core_frequency )
2006-10-03 23:29:15 +04:00
continue ;
data - > freq_table [ valid_states ] . index = i ;
data - > freq_table [ valid_states ] . frequency =
2006-10-03 23:35:23 +04:00
perf - > states [ i ] . core_frequency * 1000 ;
2006-10-03 23:29:15 +04:00
valid_states + + ;
2005-04-17 02:20:36 +04:00
}
2006-11-14 04:47:44 +03:00
data - > freq_table [ valid_states ] . frequency = CPUFREQ_TABLE_END ;
2005-04-17 02:20:36 +04:00
result = cpufreq_frequency_table_cpuinfo ( policy , data - > freq_table ) ;
2006-10-18 08:41:48 +04:00
if ( result )
2005-04-17 02:20:36 +04:00
goto err_freqfree ;
2006-12-15 21:52:45 +03:00
switch ( perf - > control_register . space_id ) {
2006-10-03 23:35:23 +04:00
case ACPI_ADR_SPACE_SYSTEM_IO :
2006-10-03 23:33:14 +04:00
/* Current speed is unknown and not detectable by IO port */
policy - > cur = acpi_cpufreq_guess_freq ( data , policy - > cpu ) ;
break ;
2006-10-03 23:35:23 +04:00
case ACPI_ADR_SPACE_FIXED_HARDWARE :
2006-10-03 23:36:30 +04:00
acpi_cpufreq_driver . get = get_cur_freq_on_cpu ;
2006-12-15 21:52:45 +03:00
policy - > cur = get_cur_freq_on_cpu ( cpu ) ;
2006-10-03 23:33:14 +04:00
break ;
2006-10-03 23:35:23 +04:00
default :
2006-10-03 23:33:14 +04:00
break ;
}
2005-04-17 02:20:36 +04:00
/* notify BIOS that we exist */
acpi_processor_notify_smm ( THIS_MODULE ) ;
2006-10-03 23:38:45 +04:00
/* Check for APERF/MPERF support in hardware */
if ( c - > x86_vendor = = X86_VENDOR_INTEL & & c - > cpuid_level > = 6 ) {
unsigned int ecx ;
ecx = cpuid_ecx ( 6 ) ;
2006-10-18 08:41:48 +04:00
if ( ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY )
2006-10-03 23:38:45 +04:00
acpi_cpufreq_driver . getavg = get_measured_perf ;
}
2006-10-03 23:29:15 +04:00
dprintk ( " CPU%u - ACPI performance management activated. \n " , cpu ) ;
2005-12-14 23:05:00 +03:00
for ( i = 0 ; i < perf - > state_count ; i + + )
2005-04-17 02:20:36 +04:00
dprintk ( " %cP%d: %d MHz, %d mW, %d uS \n " ,
2006-10-03 23:35:23 +04:00
( i = = perf - > state ? ' * ' : ' ' ) , i ,
2005-12-14 23:05:00 +03:00
( u32 ) perf - > states [ i ] . core_frequency ,
( u32 ) perf - > states [ i ] . power ,
( u32 ) perf - > states [ i ] . transition_latency ) ;
2005-04-17 02:20:36 +04:00
cpufreq_frequency_table_get_attr ( data - > freq_table , policy - > cpu ) ;
2006-10-03 23:35:23 +04:00
2005-05-18 21:49:00 +04:00
/*
* the first call to - > target ( ) should result in us actually
* writing something to the appropriate registers .
*/
data - > resume = 1 ;
2006-10-03 23:35:23 +04:00
2006-10-03 23:29:15 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2006-10-18 08:41:48 +04:00
err_freqfree :
2005-04-17 02:20:36 +04:00
kfree ( data - > freq_table ) ;
2006-10-18 08:41:48 +04:00
err_unreg :
2005-12-14 23:05:00 +03:00
acpi_processor_unregister_performance ( perf , cpu ) ;
2006-10-18 08:41:48 +04:00
err_free :
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
2006-10-03 23:29:15 +04:00
drv_data [ cpu ] = NULL ;
2005-04-17 02:20:36 +04:00
2006-10-03 23:35:23 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:35:23 +04:00
static int acpi_cpufreq_cpu_exit ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:29:15 +04:00
struct acpi_cpufreq_data * data = drv_data [ policy - > cpu ] ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_cpu_exit \n " ) ;
if ( data ) {
cpufreq_frequency_table_put_attr ( policy - > cpu ) ;
2006-10-03 23:29:15 +04:00
drv_data [ policy - > cpu ] = NULL ;
2006-10-03 23:35:23 +04:00
acpi_processor_unregister_performance ( data - > acpi_data ,
policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
}
2006-10-03 23:35:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:35:23 +04:00
static int acpi_cpufreq_resume ( struct cpufreq_policy * policy )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:29:15 +04:00
struct acpi_cpufreq_data * data = drv_data [ policy - > cpu ] ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_resume \n " ) ;
data - > resume = 1 ;
2006-10-03 23:35:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:35:23 +04:00
static struct freq_attr * acpi_cpufreq_attr [ ] = {
2005-04-17 02:20:36 +04:00
& cpufreq_freq_attr_scaling_available_freqs ,
NULL ,
} ;
static struct cpufreq_driver acpi_cpufreq_driver = {
2006-10-03 23:35:23 +04:00
. verify = acpi_cpufreq_verify ,
. target = acpi_cpufreq_target ,
. init = acpi_cpufreq_cpu_init ,
. exit = acpi_cpufreq_cpu_exit ,
. resume = acpi_cpufreq_resume ,
. name = " acpi-cpufreq " ,
. owner = THIS_MODULE ,
. attr = acpi_cpufreq_attr ,
2005-04-17 02:20:36 +04:00
} ;
2006-10-03 23:35:23 +04:00
static int __init acpi_cpufreq_init ( void )
2005-04-17 02:20:36 +04:00
{
dprintk ( " acpi_cpufreq_init \n " ) ;
2006-10-03 23:29:15 +04:00
acpi_cpufreq_early_init ( ) ;
2005-12-14 23:05:00 +03:00
2006-10-03 23:35:23 +04:00
return cpufreq_register_driver ( & acpi_cpufreq_driver ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 23:35:23 +04:00
static void __exit acpi_cpufreq_exit ( void )
2005-04-17 02:20:36 +04:00
{
2006-10-03 23:35:23 +04:00
unsigned int i ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_exit \n " ) ;
cpufreq_unregister_driver ( & acpi_cpufreq_driver ) ;
2006-06-25 16:46:43 +04:00
for_each_possible_cpu ( i ) {
2005-12-14 23:05:00 +03:00
kfree ( acpi_perf_data [ i ] ) ;
acpi_perf_data [ i ] = NULL ;
}
2005-04-17 02:20:36 +04:00
return ;
}
2005-08-25 23:59:00 +04:00
module_param ( acpi_pstate_strict , uint , 0644 ) ;
2006-10-03 23:35:23 +04:00
MODULE_PARM_DESC ( acpi_pstate_strict ,
2006-10-18 08:41:48 +04:00
" value 0 or non-zero. non-zero -> strict ACPI checks are "
" performed during frequency changes. " ) ;
2005-04-17 02:20:36 +04:00
late_initcall ( acpi_cpufreq_init ) ;
module_exit ( acpi_cpufreq_exit ) ;
MODULE_ALIAS ( " acpi " ) ;