2005-04-17 02:20:36 +04:00
/*
2009-01-18 06:36:14 +03:00
* acpi - cpufreq . c - ACPI Processor P - States Driver
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>
2009-09-17 18:11:28 +04:00
# include <trace/events/power.h>
2005-04-17 02:20:36 +04:00
# include <linux/acpi.h>
2009-01-18 06:36:14 +03:00
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <acpi/processor.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>
2009-01-18 06:36:14 +03:00
# define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
" acpi-cpufreq " , msg )
2005-04-17 02:20:36 +04:00
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: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 ;
unsigned int resume ;
unsigned int cpu_feature ;
2005-04-17 02:20:36 +04:00
} ;
2009-10-29 16:34:13 +03:00
static DEFINE_PER_CPU ( struct acpi_cpufreq_data * , acfreq_data ) ;
2008-01-30 15:33:12 +03:00
2009-10-29 16:34:13 +03:00
static DEFINE_PER_CPU ( struct aperfmperf , acfreq_old_perf ) ;
x86, acpi_cpufreq: Fix the NULL pointer dereference in get_measured_perf
Fix for a regression that was introduced by earlier commit
18b2646fe3babeb40b34a0c1751e0bf5adfdc64c on Mon Apr 6 11:26:08 2009
Regression resulted in the below error happened on systems with
software coordination where per_cpu acpi data will not be initiated for
secondary CPUs in a P-state domain.
On Tue, 2009-04-14 at 23:01 -0700, Zhang, Yanmin wrote:
My machine hanged with kernel 2.6.30-rc2 when script read
> /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor.
>
> opps happens in get_measured_perf:
>
> cur.aperf.whole = readin.aperf.whole -
> per_cpu(drv_data, cpu)->saved_aperf;
>
> Because per_cpu(drv_data, cpu)=NULL.
>
> So function get_measured_perf should check if (per_cpu(drv_data,
> cpu)==NULL)
> and return 0 if it's NULL.
--------------sys log------------------
BUG: unable to handle kernel NULL pointer dereference at
0000000000000020
IP: [<ffffffff8021af75>] get_measured_perf+0x4a/0xf9
PGD a7dd88067 PUD a7ccf5067 PMD 0
Oops: 0000 [#1] SMP
last sysfs file: /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
CPU 0
Modules linked in: video output
Pid: 2091, comm: kondemand/0 Not tainted 2.6.30-rc2 #1 MP Server
RIP: 0010:[<ffffffff8021af75>] [<ffffffff8021af75>]
get_measured_perf+0x4a/0xf9
RSP: 0018:ffff880a7d56de20 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 00000046241a42b6 RCX: ffff88004d219000
RDX: 000000000000b660 RSI: 0000000000000020 RDI: 0000000000000001
RBP: ffff880a7f052000 R08: 00000046241a42b6 R09: ffffffff807639f0
R10: 00000000ffffffea R11: ffffffff802207f4 R12: ffff880a7f052000
R13: ffff88004d20e460 R14: 0000000000ddd5a6 R15: 0000000000000001
FS: 0000000000000000(0000) GS:ffff88004d200000(0000)
knlGS:0000000000000000
CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 0000000000000020 CR3: 0000000a7f1bf000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process kondemand/0 (pid: 2091, threadinfo ffff880a7d56c000, task
ffff880a7d4d18c0)
Stack:
ffff880a7f052078 ffffffff803efd54 00000046241a42b6 000000462ffa9e95
0000000000000001 0000000000000001 00000000ffffffea ffffffff8064f41a
0000000000000012 0000000000000012 ffff880a7f052000 ffffffff80650547
Call Trace:
[<ffffffff803efd54>] ? kobject_get+0x12/0x17
[<ffffffff8064f41a>] ? __cpufreq_driver_getavg+0x42/0x57
[<ffffffff80650547>] ? do_dbs_timer+0x147/0x272
[<ffffffff80650400>] ? do_dbs_timer+0x0/0x272
[<ffffffff802474ca>] ? worker_thread+0x15b/0x1f5
[<ffffffff8024a02c>] ? autoremove_wake_function+0x0/0x2e
[<ffffffff8024736f>] ? worker_thread+0x0/0x1f5
[<ffffffff80249f0d>] ? kthread+0x54/0x83
[<ffffffff8020c87a>] ? child_rip+0xa/0x20
[<ffffffff80249eb9>] ? kthread+0x0/0x83
[<ffffffff8020c870>] ? child_rip+0x0/0x20
Code: 99 a6 03 00 31 c9 85 c0 0f 85 c3 00 00 00 89 df 4c 8b 44 24 10 48
c7 c2 60 b6 00 00 48 8b 0c fd e0 30 a5 80 4c 89 c3 48 8b 04 0a <48> 2b
58 20 48 8b 44 24 18 48 89 1c 24 48 8b 34 0a 48 2b 46 28
RIP [<ffffffff8021af75>] get_measured_perf+0x4a/0xf9
RSP <ffff880a7d56de20>
CR2: 0000000000000020
---[ end trace 2b8fac9a49e19ad4 ]---
Tested-by: "Zhang, Yanmin" <yanmin_zhang@linux.intel.com>
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
2009-04-15 21:37:33 +04:00
2007-08-08 02:40:30 +04:00
/* acpi_perf_data is a pointer to percpu data. */
static struct acpi_processor_performance * acpi_perf_data ;
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 )
{
2007-10-19 22:35:04 +04:00
struct cpuinfo_x86 * cpu = & cpu_data ( cpuid ) ;
2006-10-03 23:33:14 +04:00
2009-06-08 14:27:54 +04:00
return cpu_has ( cpu , X86_FEATURE_EST ) ;
2006-10-03 23:33:14 +04:00
}
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 ;
2009-01-18 06:36:14 +03: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 ;
2009-01-18 06:36:14 +03: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 ;
} ;
struct drv_cmd {
2006-10-03 23:33:14 +04:00
unsigned int type ;
2009-01-15 17:46:08 +03:00
const struct cpumask * mask ;
2009-01-18 06:36:14 +03:00
union {
struct msr_addr msr ;
struct io_addr io ;
} addr ;
2006-10-03 23:29:15 +04:00
u32 val ;
} ;
2009-04-13 21:27:49 +04:00
/* Called via smp_call_function_single(), on the target CPU */
static void do_drv_read ( void * _cmd )
2005-04-17 02:20:36 +04:00
{
2009-01-17 02:31:15 +03:00
struct drv_cmd * cmd = _cmd ;
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
2009-04-13 21:27:49 +04:00
/* Called via smp_call_function_many(), on the target CPUs */
static void do_drv_write ( void * _cmd )
2006-10-03 23:29:15 +04:00
{
2009-01-17 02:31:15 +03:00
struct drv_cmd * cmd = _cmd ;
2007-05-24 02:42:13 +04:00
u32 lo , hi ;
2006-10-03 23:33:14 +04:00
switch ( cmd - > type ) {
2006-10-03 23:35:23 +04:00
case SYSTEM_INTEL_MSR_CAPABLE :
2007-05-24 02:42:13 +04:00
rdmsr ( cmd - > addr . msr . reg , lo , hi ) ;
lo = ( lo & ~ INTEL_MSR_RANGE ) | ( cmd - > val & INTEL_MSR_RANGE ) ;
wrmsr ( cmd - > addr . msr . reg , lo , hi ) ;
2006-10-03 23:33:14 +04:00
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
{
cmd - > val = 0 ;
2009-04-13 21:27:49 +04:00
smp_call_function_single ( cpumask_any ( cmd - > mask ) , do_drv_read , cmd , 1 ) ;
2006-10-03 23:29:15 +04:00
}
static void drv_write ( struct drv_cmd * cmd )
{
2009-04-15 19:05:13 +04:00
int this_cpu ;
this_cpu = get_cpu ( ) ;
if ( cpumask_test_cpu ( this_cpu , cmd - > mask ) )
do_drv_write ( cmd ) ;
2009-04-13 21:27:49 +04:00
smp_call_function_many ( cmd - > mask , do_drv_write , cmd , 1 ) ;
2009-04-15 19:05:13 +04:00
put_cpu ( ) ;
2006-10-03 23:29:15 +04:00
}
2005-04-17 02:20:36 +04:00
2009-01-04 16:18:08 +03:00
static u32 get_cur_val ( const struct cpumask * mask )
2006-10-03 23:29:15 +04:00
{
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
2009-01-04 16:18:08 +03:00
if ( unlikely ( cpumask_empty ( mask ) ) )
2006-10-03 23:29:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2009-10-29 16:34:13 +03:00
switch ( per_cpu ( acfreq_data , cpumask_first ( mask ) ) - > cpu_feature ) {
2006-10-03 23:33:14 +04:00
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 ;
2009-10-29 16:34:13 +03:00
perf = per_cpu ( acfreq_data , cpumask_first ( mask ) ) - > acpi_data ;
2006-10-03 23:33:14 +04:00
cmd . addr . io . port = perf - > control_register . address ;
cmd . addr . io . bit_width = perf - > control_register . bit_width ;
break ;
default :
return 0 ;
}
2009-01-15 17:46:08 +03:00
cmd . mask = mask ;
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
2009-04-13 21:27:49 +04:00
/* Called via smp_call_function_single(), on the target CPU */
static void read_measured_perf_ctrs ( void * _cur )
2009-01-04 16:18:10 +03:00
{
2009-09-02 13:49:52 +04:00
struct aperfmperf * am = _cur ;
2009-01-04 16:18:10 +03:00
2009-09-02 13:49:52 +04:00
get_aperfmperf ( am ) ;
2009-01-04 16:18:10 +03: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 .
*/
2008-08-04 22:59:07 +04:00
static unsigned int get_measured_perf ( struct cpufreq_policy * policy ,
unsigned int cpu )
2006-10-03 23:38:45 +04:00
{
2009-09-02 13:49:52 +04:00
struct aperfmperf perf ;
unsigned long ratio ;
2006-10-03 23:38:45 +04:00
unsigned int retval ;
2009-09-02 13:49:52 +04:00
if ( smp_call_function_single ( cpu , read_measured_perf_ctrs , & perf , 1 ) )
2006-10-03 23:38:45 +04:00
return 0 ;
2009-10-29 16:34:13 +03:00
ratio = calc_aperfmperf_ratio ( & per_cpu ( acfreq_old_perf , cpu ) , & perf ) ;
per_cpu ( acfreq_old_perf , cpu ) = perf ;
2006-10-03 23:38:45 +04:00
2009-09-02 13:49:52 +04:00
retval = ( policy - > cpuinfo . max_freq * ratio ) > > APERFMPERF_SHIFT ;
2006-10-03 23:38:45 +04:00
return retval ;
}
2006-10-03 23:29:15 +04:00
static unsigned int get_cur_freq_on_cpu ( unsigned int cpu )
{
2009-10-29 16:34:13 +03:00
struct acpi_cpufreq_data * data = per_cpu ( acfreq_data , cpu ) ;
2006-10-03 23:35:23 +04:00
unsigned int freq ;
2008-04-28 23:13:43 +04:00
unsigned int cached_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
}
2008-04-28 23:13:43 +04:00
cached_freq = data - > freq_table [ data - > acpi_data - > state ] . frequency ;
2009-01-04 16:18:10 +03:00
freq = extract_freq ( get_cur_val ( cpumask_of ( cpu ) ) , data ) ;
2008-04-28 23:13:43 +04:00
if ( freq ! = cached_freq ) {
/*
* The dreaded BIOS frequency change behind our back .
* Force set the frequency on next target call .
*/
data - > resume = 1 ;
}
2006-10-03 23:29:15 +04:00
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
}
2009-01-17 02:31:15 +03:00
static unsigned int check_freqs ( const struct cpumask * 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
2009-01-18 06:36:14 +03: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
{
2009-10-29 16:34:13 +03:00
struct acpi_cpufreq_data * data = per_cpu ( acfreq_data , policy - > cpu ) ;
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * perf ;
struct cpufreq_freqs freqs ;
struct drv_cmd cmd ;
2006-12-19 23:58:55 +03:00
unsigned int next_state = 0 ; /* Index into freq_table */
unsigned int next_perf_state = 0 ; /* Index into perf table */
2006-10-03 23:35:23 +04:00
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 ) ;
2009-01-04 16:18:08 +03:00
if ( unlikely ( result ) ) {
result = - ENODEV ;
goto out ;
}
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 ) ;
2009-01-04 16:18:08 +03:00
goto out ;
2006-10-03 23:29:15 +04:00
}
2005-12-14 23:05:00 +03:00
}
2009-09-17 18:11:28 +04:00
trace_power_frequency ( POWER_PSTATE , data - > freq_table [ next_state ] . frequency ) ;
2008-11-24 03:49:58 +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 ;
2007-05-24 02:42:13 +04:00
cmd . val = ( u32 ) perf - > states [ next_perf_state ] . control ;
2006-10-03 23:35:23 +04:00
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 :
2009-01-04 16:18:08 +03:00
result = - ENODEV ;
goto out ;
2006-10-03 23:35:23 +04:00
}
2005-12-14 23:05:00 +03:00
2009-01-04 16:18:08 +03:00
/* cpufreq holds the hotplug lock, so we are safe from here on */
2006-10-03 23:29:15 +04:00
if ( policy - > shared_type ! = CPUFREQ_SHARED_TYPE_ANY )
2009-01-15 17:46:08 +03:00
cmd . mask = policy - > cpus ;
2006-10-03 23:29:15 +04:00
else
2009-01-15 17:46:08 +03:00
cmd . mask = cpumask_of ( policy - > cpu ) ;
2005-12-14 23:05:00 +03:00
2006-12-19 23:58:55 +03:00
freqs . old = perf - > states [ perf - > state ] . core_frequency * 1000 ;
freqs . new = data - > freq_table [ next_state ] . frequency ;
2009-01-04 16:18:08 +03:00
for_each_cpu ( i , cmd . mask ) {
2006-10-03 23:29:15 +04:00
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 ) {
2009-01-04 16:18:08 +03:00
if ( ! check_freqs ( cmd . mask , freqs . new , data ) ) {
2006-10-03 23:29:15 +04:00
dprintk ( " acpi_cpufreq_target failed (%d) \n " ,
2006-10-03 23:35:23 +04:00
policy - > cpu ) ;
2009-01-04 16:18:08 +03:00
result = - EAGAIN ;
goto out ;
2005-12-14 23:05:00 +03:00
}
}
2009-01-04 16:18:08 +03:00
for_each_cpu ( i , cmd . mask ) {
2006-10-03 23:29:15 +04:00
freqs . cpu = i ;
cpufreq_notify_transition ( & freqs , CPUFREQ_POSTCHANGE ) ;
}
perf - > state = next_perf_state ;
2009-01-04 16:18:08 +03:00
out :
2006-10-03 23:29:15 +04:00
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
{
2009-10-29 16:34:13 +03:00
struct acpi_cpufreq_data * data = per_cpu ( acfreq_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
2009-01-18 06:36:14 +03: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
}
2009-01-01 05:08:47 +03:00
static void free_acpi_perf_data ( void )
{
unsigned int i ;
/* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
for_each_possible_cpu ( i )
free_cpumask_var ( per_cpu_ptr ( acpi_perf_data , i )
- > shared_cpu_map ) ;
free_percpu ( acpi_perf_data ) ;
}
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 . . .
*/
2007-08-08 02:40:30 +04:00
static int __init acpi_cpufreq_early_init ( void )
2005-12-14 23:05:00 +03:00
{
2009-01-01 05:08:47 +03:00
unsigned int i ;
2005-12-14 23:05:00 +03:00
dprintk ( " acpi_cpufreq_early_init \n " ) ;
2007-08-08 02:40:30 +04:00
acpi_perf_data = alloc_percpu ( struct acpi_processor_performance ) ;
if ( ! acpi_perf_data ) {
dprintk ( " Memory allocation error for acpi_perf_data. \n " ) ;
return - ENOMEM ;
2005-12-14 23:05:00 +03:00
}
2009-01-01 05:08:47 +03:00
for_each_possible_cpu ( i ) {
2009-06-07 01:51:36 +04:00
if ( ! zalloc_cpumask_var_node (
2009-01-01 05:08:47 +03:00
& per_cpu_ptr ( acpi_perf_data , i ) - > shared_cpu_map ,
GFP_KERNEL , cpu_to_node ( i ) ) ) {
2009-01-01 05:08:47 +03:00
/* Freeing a NULL pointer is OK: alloc_percpu zeroes. */
free_acpi_perf_data ( ) ;
return - ENOMEM ;
}
}
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 ;
2007-10-03 23:15:40 +04:00
static int sw_any_bug_found ( const struct dmi_system_id * d )
2006-09-02 01:02:24 +04:00
{
bios_with_sw_any_bug = 1 ;
return 0 ;
}
2007-10-03 23:15:40 +04:00
static const 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 " ) ,
} ,
} ,
{ }
} ;
2009-08-26 21:19:37 +04:00
static int acpi_cpufreq_blacklist ( struct cpuinfo_x86 * c )
{
2009-09-25 21:30:08 +04:00
/* Intel Xeon Processor 7100 Series Specification Update
* http : //www.intel.com/Assets/PDF/specupdate/314554.pdf
2009-08-26 21:19:37 +04:00
* AL30 : A Machine Check Exception ( MCE ) Occurring during an
* Enhanced Intel SpeedStep Technology Ratio Change May Cause
2009-09-25 21:30:08 +04:00
* Both Processor Cores to Lock Up . */
2009-08-26 21:19:37 +04:00
if ( c - > x86_vendor = = X86_VENDOR_INTEL ) {
if ( ( c - > x86 = = 15 ) & &
( c - > x86_model = = 6 ) & &
2009-09-25 21:30:08 +04:00
( c - > x86_mask = = 8 ) ) {
printk ( KERN_INFO " acpi-cpufreq: Intel(R) "
" Xeon(R) 7100 Errata AL30, processors may "
" lock up on frequency changes: disabling "
" acpi-cpufreq. \n " ) ;
2009-08-26 21:19:37 +04:00
return - ENODEV ;
2009-09-25 21:30:08 +04:00
}
2009-08-26 21:19:37 +04:00
}
return 0 ;
}
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 ;
2007-10-19 22:35:04 +04:00
struct cpuinfo_x86 * c = & cpu_data ( policy - > cpu ) ;
2006-10-03 23:35:23 +04:00
struct acpi_processor_performance * perf ;
2009-09-25 21:30:08 +04:00
# ifdef CONFIG_SMP
static int blacklisted ;
# endif
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_cpu_init \n " ) ;
2009-08-26 21:19:37 +04:00
# ifdef CONFIG_SMP
2009-09-25 21:30:08 +04:00
if ( blacklisted )
return blacklisted ;
blacklisted = acpi_cpufreq_blacklist ( c ) ;
if ( blacklisted )
return blacklisted ;
2009-08-26 21:19:37 +04:00
# endif
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
2009-02-20 10:29:08 +03:00
data - > acpi_data = per_cpu_ptr ( acpi_perf_data , cpu ) ;
2009-10-29 16:34:13 +03:00
per_cpu ( acfreq_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 ) {
2009-01-04 16:18:06 +03:00
cpumask_copy ( policy - > cpus , perf - > shared_cpu_map ) ;
2006-09-02 01:02:24 +04:00
}
2009-01-04 16:18:06 +03:00
cpumask_copy ( policy - > related_cpus , perf - > shared_cpu_map ) ;
2006-09-02 01:02:24 +04:00
# ifdef CONFIG_SMP
dmi_check_system ( sw_any_bug_dmi_table ) ;
2009-01-04 16:18:06 +03:00
if ( bios_with_sw_any_bug & & cpumask_weight ( policy - > cpus ) = = 1 ) {
2006-09-02 01:02:24 +04:00
policy - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
2009-01-04 16:18:06 +03:00
cpumask_copy ( policy - > cpus , cpu_core_mask ( cpu ) ) ;
2006-09-02 01:02:24 +04:00
}
# 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 ;
2009-01-18 06:36:14 +03: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
}
2009-03-20 00:41:40 +03:00
/* Check for high latency (>20uS) from buggy BIOSes, like on T42 */
if ( perf - > control_register . space_id = = ACPI_ADR_SPACE_FIXED_HARDWARE & &
policy - > cpuinfo . transition_latency > 20 * 1000 ) {
policy - > cpuinfo . transition_latency = 20 * 1000 ;
2009-05-27 01:58:39 +04:00
printk_once ( KERN_INFO
" P-state transition latency capped at 20 uS \n " ) ;
2009-03-20 00:41:40 +03:00
}
2005-04-17 02:20:36 +04:00
/* table init */
2009-01-18 06:36:14 +03:00
for ( i = 0 ; i < perf - > state_count ; i + + ) {
if ( i > 0 & & perf - > states [ i ] . core_frequency > =
2007-06-14 05:24:02 +04:00
data - > freq_table [ valid_states - 1 ] . frequency / 1000 )
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 ;
2006-12-19 23:58:55 +03:00
perf - > state = 0 ;
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 ;
2009-04-17 18:22:08 +04:00
if ( perf - > states [ 0 ] . core_frequency * 1000 ! = policy - > cpuinfo . max_freq )
printk ( KERN_WARNING FW_WARN " P-state 0 is not max freq \n " ) ;
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 */
2009-09-02 12:56:56 +04:00
if ( cpu_has ( c , X86_FEATURE_APERFMPERF ) )
acpi_cpufreq_driver . getavg = get_measured_perf ;
2006-10-03 23:38:45 +04:00
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 ) ;
2009-10-29 16:34:13 +03:00
per_cpu ( acfreq_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
{
2009-10-29 16:34:13 +03:00
struct acpi_cpufreq_data * data = per_cpu ( acfreq_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 ) ;
2009-10-29 16:34:13 +03:00
per_cpu ( acfreq_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
{
2009-10-29 16:34:13 +03:00
struct acpi_cpufreq_data * data = per_cpu ( acfreq_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 = {
2009-11-19 14:31:01 +03:00
. verify = acpi_cpufreq_verify ,
. target = acpi_cpufreq_target ,
. bios_limit = acpi_processor_get_bios_limit ,
. 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
{
2007-08-08 02:40:30 +04:00
int ret ;
2008-09-25 06:04:31 +04:00
if ( acpi_disabled )
return 0 ;
2005-04-17 02:20:36 +04:00
dprintk ( " acpi_cpufreq_init \n " ) ;
2007-08-08 02:40:30 +04:00
ret = acpi_cpufreq_early_init ( ) ;
if ( ret )
return ret ;
2005-12-14 23:05:00 +03:00
2008-07-14 06:59:44 +04:00
ret = cpufreq_register_driver ( & acpi_cpufreq_driver ) ;
if ( ret )
2009-01-01 05:08:47 +03:00
free_acpi_perf_data ( ) ;
2008-07-14 06:59:44 +04:00
return ret ;
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
{
dprintk ( " acpi_cpufreq_exit \n " ) ;
cpufreq_unregister_driver ( & acpi_cpufreq_driver ) ;
2007-08-08 02:40:30 +04:00
free_percpu ( acpi_perf_data ) ;
2005-04-17 02:20:36 +04:00
}
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 " ) ;