2005-04-17 02:20:36 +04:00
/*
* processor_perflib . c - ACPI Processor P - States Library ( $ Revision : 71 $ )
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
* Copyright ( C ) 2004 Dominik Brodowski < linux @ brodo . de >
* Copyright ( C ) 2004 Anil S Keshavamurthy < anil . s . keshavamurthy @ intel . com >
* - Added processor hotplug support
*
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* 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>
# include <linux/cpufreq.h>
# ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2006-04-27 13:25:00 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# endif
# include <acpi/acpi_bus.h>
# include <acpi/processor.h>
# define ACPI_PROCESSOR_COMPONENT 0x01000000
# define ACPI_PROCESSOR_CLASS "processor"
# define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver"
# define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
# define _COMPONENT ACPI_PROCESSOR_COMPONENT
2005-08-05 08:44:28 +04:00
ACPI_MODULE_NAME ( " acpi_processor " )
2005-04-17 02:20:36 +04:00
2006-04-27 13:25:00 +04:00
static DEFINE_MUTEX ( performance_mutex ) ;
2005-04-17 02:20:36 +04:00
/*
* _PPC support is implemented as a CPUfreq policy notifier :
* This means each time a CPUfreq driver registered also with
* the ACPI core is asked to change the speed policy , the maximum
* value is adjusted so that it is within the platform limit .
*
* Also , when a new platform limit value is detected , the CPUfreq
* policy is adjusted accordingly .
*/
# define PPC_REGISTERED 1
# define PPC_IN_USE 2
static int acpi_processor_ppc_status = 0 ;
static int acpi_processor_ppc_notifier ( struct notifier_block * nb ,
2005-08-05 08:44:28 +04:00
unsigned long event , void * data )
2005-04-17 02:20:36 +04:00
{
struct cpufreq_policy * policy = data ;
struct acpi_processor * pr ;
unsigned int ppc = 0 ;
2006-04-27 13:25:00 +04:00
mutex_lock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( event ! = CPUFREQ_INCOMPATIBLE )
goto out ;
pr = processors [ policy - > cpu ] ;
if ( ! pr | | ! pr - > performance )
goto out ;
2005-08-05 08:44:28 +04:00
ppc = ( unsigned int ) pr - > performance_platform_limit ;
2005-04-17 02:20:36 +04:00
if ( ! ppc )
goto out ;
if ( ppc > pr - > performance - > state_count )
goto out ;
cpufreq_verify_within_limits ( policy , 0 ,
2005-08-05 08:44:28 +04:00
pr - > performance - > states [ ppc ] .
core_frequency * 1000 ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
out :
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static struct notifier_block acpi_ppc_notifier_block = {
. notifier_call = acpi_processor_ppc_notifier ,
} ;
2005-08-05 08:44:28 +04:00
static int acpi_processor_get_platform_limit ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = 0 ;
unsigned long ppc = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! pr )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/*
* _PPC indicates the maximum state currently supported by the platform
* ( e . g . 0 = states 0. . n ; 1 = states 1. . n ; etc .
*/
status = acpi_evaluate_integer ( pr - > handle , " _PPC " , NULL , & ppc ) ;
if ( status ! = AE_NOT_FOUND )
acpi_processor_ppc_status | = PPC_IN_USE ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) & & status ! = AE_NOT_FOUND ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Evaluating _PPC " ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
pr - > performance_platform_limit = ( int ) ppc ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
int acpi_processor_ppc_has_changed ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
int ret = acpi_processor_get_platform_limit ( pr ) ;
if ( ret < 0 )
return ( ret ) ;
else
return cpufreq_update_policy ( pr - > id ) ;
}
2005-08-05 08:44:28 +04:00
void acpi_processor_ppc_init ( void )
{
if ( ! cpufreq_register_notifier
( & acpi_ppc_notifier_block , CPUFREQ_POLICY_NOTIFIER ) )
2005-04-17 02:20:36 +04:00
acpi_processor_ppc_status | = PPC_REGISTERED ;
else
2005-08-05 08:44:28 +04:00
printk ( KERN_DEBUG
" Warning: Processor Platform Limit not supported. \n " ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
void acpi_processor_ppc_exit ( void )
{
2005-04-17 02:20:36 +04:00
if ( acpi_processor_ppc_status & PPC_REGISTERED )
2005-08-05 08:44:28 +04:00
cpufreq_unregister_notifier ( & acpi_ppc_notifier_block ,
CPUFREQ_POLICY_NOTIFIER ) ;
2005-04-17 02:20:36 +04:00
acpi_processor_ppc_status & = ~ PPC_REGISTERED ;
}
2005-08-05 08:44:28 +04:00
static int acpi_processor_get_performance_control ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
acpi_status status = 0 ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
union acpi_object * pct = NULL ;
union acpi_object obj = { 0 } ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( pr - > handle , " _PCT " , NULL , & buffer ) ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Evaluating _PCT " ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
pct = ( union acpi_object * ) buffer . pointer ;
2005-04-17 02:20:36 +04:00
if ( ! pct | | ( pct - > type ! = ACPI_TYPE_PACKAGE )
2005-08-05 08:44:28 +04:00
| | ( pct - > package . count ! = 2 ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _PCT data \n " ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
goto end ;
}
/*
* control_register
*/
obj = pct - > package . elements [ 0 ] ;
if ( ( obj . type ! = ACPI_TYPE_BUFFER )
2005-08-05 08:44:28 +04:00
| | ( obj . buffer . length < sizeof ( struct acpi_pct_register ) )
| | ( obj . buffer . pointer = = NULL ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _PCT data (control_register) \n " ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
goto end ;
}
2005-08-05 08:44:28 +04:00
memcpy ( & pr - > performance - > control_register , obj . buffer . pointer ,
sizeof ( struct acpi_pct_register ) ) ;
2005-04-17 02:20:36 +04:00
/*
* status_register
*/
obj = pct - > package . elements [ 1 ] ;
if ( ( obj . type ! = ACPI_TYPE_BUFFER )
2005-08-05 08:44:28 +04:00
| | ( obj . buffer . length < sizeof ( struct acpi_pct_register ) )
| | ( obj . buffer . pointer = = NULL ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _PCT data (status_register) \n " ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
goto end ;
}
2005-08-05 08:44:28 +04:00
memcpy ( & pr - > performance - > status_register , obj . buffer . pointer ,
sizeof ( struct acpi_pct_register ) ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
acpi_os_free ( buffer . pointer ) ;
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_processor_get_performance_states ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_buffer format = { sizeof ( " NNNNNN " ) , " NNNNNN " } ;
struct acpi_buffer state = { 0 , NULL } ;
union acpi_object * pss = NULL ;
int i ;
2005-04-17 02:20:36 +04:00
status = acpi_evaluate_object ( pr - > handle , " _PSS " , NULL , & buffer ) ;
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Evaluating _PSS " ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
pss = ( union acpi_object * ) buffer . pointer ;
2005-04-17 02:20:36 +04:00
if ( ! pss | | ( pss - > type ! = ACPI_TYPE_PACKAGE ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Invalid _PSS data \n " ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
goto end ;
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Found %d performance states \n " ,
2005-08-05 08:44:28 +04:00
pss - > package . count ) ) ;
2005-04-17 02:20:36 +04:00
pr - > performance - > state_count = pss - > package . count ;
2005-08-05 08:44:28 +04:00
pr - > performance - > states =
kmalloc ( sizeof ( struct acpi_processor_px ) * pss - > package . count ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! pr - > performance - > states ) {
result = - ENOMEM ;
goto end ;
}
for ( i = 0 ; i < pr - > performance - > state_count ; i + + ) {
struct acpi_processor_px * px = & ( pr - > performance - > states [ i ] ) ;
state . length = sizeof ( struct acpi_processor_px ) ;
state . pointer = px ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Extracting state %d \n " , i ) ) ;
status = acpi_extract_package ( & ( pss - > package . elements [ i ] ) ,
2005-08-05 08:44:28 +04:00
& format , & state ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Invalid _PSS data " ) ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
kfree ( pr - > performance - > states ) ;
goto end ;
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x] \n " ,
i ,
( u32 ) px - > core_frequency ,
( u32 ) px - > power ,
( u32 ) px - > transition_latency ,
( u32 ) px - > bus_master_latency ,
( u32 ) px - > control , ( u32 ) px - > status ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! px - > core_frequency ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX
" Invalid _PSS data: freq is zero \n " ) ;
2005-04-17 02:20:36 +04:00
result = - EFAULT ;
kfree ( pr - > performance - > states ) ;
goto end ;
}
}
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
acpi_os_free ( buffer . pointer ) ;
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_processor_get_performance_info ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
acpi_handle handle = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! pr | | ! pr - > performance | | ! pr - > handle )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
status = acpi_get_handle ( pr - > handle , " _PCT " , & handle ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" ACPI-based processor performance control unavailable \n " ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
result = acpi_processor_get_performance_control ( pr ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
result = acpi_processor_get_performance_states ( pr ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
result = acpi_processor_get_platform_limit ( pr ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
int acpi_processor_notify_smm ( struct module * calling_module )
{
acpi_status status ;
static int is_done = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! ( acpi_processor_ppc_status & PPC_REGISTERED ) )
2006-06-27 08:41:40 +04:00
return - EBUSY ;
2005-04-17 02:20:36 +04:00
if ( ! try_module_get ( calling_module ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/* is_done is set to negative if an error occured,
* and to postitive if _no_ error occured , but SMM
* was already notified . This avoids double notification
* which might lead to unexpected results . . .
*/
if ( is_done > 0 ) {
module_put ( calling_module ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-08-05 08:44:28 +04:00
} else if ( is_done < 0 ) {
2005-04-17 02:20:36 +04:00
module_put ( calling_module ) ;
2006-06-27 08:41:40 +04:00
return is_done ;
2005-04-17 02:20:36 +04:00
}
is_done = - EIO ;
/* Can't write pstate_cnt to smi_cmd if either value is zero */
2005-08-05 08:44:28 +04:00
if ( ( ! acpi_fadt . smi_cmd ) | | ( ! acpi_fadt . pstate_cnt ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " No SMI port or pstate_cnt \n " ) ) ;
2005-04-17 02:20:36 +04:00
module_put ( calling_module ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Writing pstate_cnt [0x%x] to smi_cmd [0x%x] \n " ,
acpi_fadt . pstate_cnt , acpi_fadt . smi_cmd ) ) ;
2005-04-17 02:20:36 +04:00
/* FADT v1 doesn't support pstate_cnt, many BIOS vendors use
* it anyway , so we need to support it . . . */
if ( acpi_fadt_is_v1 ) {
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Using v1.0 FADT reserved value for pstate_cnt \n " ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
status = acpi_os_write_port ( acpi_fadt . smi_cmd ,
( u32 ) acpi_fadt . pstate_cnt , 8 ) ;
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status ,
" Failed to write pstate_cnt [0x%x] to "
" smi_cmd [0x%x] " , acpi_fadt . pstate_cnt ,
acpi_fadt . smi_cmd ) ) ;
2005-04-17 02:20:36 +04:00
module_put ( calling_module ) ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
/* Success. If there's no _PPC, we need to fear nothing, so
* we can allow the cpufreq driver to be rmmod ' ed . */
is_done = 1 ;
if ( ! ( acpi_processor_ppc_status & PPC_IN_USE ) )
module_put ( calling_module ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
EXPORT_SYMBOL ( acpi_processor_notify_smm ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
/* /proc/acpi/processor/../performance interface (DEPRECATED) */
static int acpi_processor_perf_open_fs ( struct inode * inode , struct file * file ) ;
static struct file_operations acpi_processor_perf_fops = {
2005-08-05 08:44:28 +04:00
. open = acpi_processor_perf_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
static int acpi_processor_perf_seq_show ( struct seq_file * seq , void * offset )
{
2005-08-05 08:44:28 +04:00
struct acpi_processor * pr = ( struct acpi_processor * ) seq - > private ;
int i ;
2005-04-17 02:20:36 +04:00
if ( ! pr )
goto end ;
if ( ! pr - > performance ) {
seq_puts ( seq , " <not supported> \n " ) ;
goto end ;
}
seq_printf ( seq , " state count: %d \n "
2005-08-05 08:44:28 +04:00
" active state: P%d \n " ,
pr - > performance - > state_count , pr - > performance - > state ) ;
2005-04-17 02:20:36 +04:00
seq_puts ( seq , " states: \n " ) ;
for ( i = 0 ; i < pr - > performance - > state_count ; i + + )
2005-08-05 08:44:28 +04:00
seq_printf ( seq ,
" %cP%d: %d MHz, %d mW, %d uS \n " ,
( i = = pr - > performance - > state ? ' * ' : ' ' ) , i ,
( u32 ) pr - > performance - > states [ i ] . core_frequency ,
( u32 ) pr - > performance - > states [ i ] . power ,
( u32 ) pr - > performance - > states [ i ] . transition_latency ) ;
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int acpi_processor_perf_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_processor_perf_seq_show ,
2005-08-05 08:44:28 +04:00
PDE ( inode ) - > data ) ;
2005-04-17 02:20:36 +04:00
}
static ssize_t
2005-08-05 08:44:28 +04:00
acpi_processor_write_performance ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * data )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct seq_file * m = ( struct seq_file * ) file - > private_data ;
struct acpi_processor * pr = ( struct acpi_processor * ) m - > private ;
2005-04-17 02:20:36 +04:00
struct acpi_processor_performance * perf ;
2005-08-05 08:44:28 +04:00
char state_string [ 12 ] = { ' \0 ' } ;
unsigned int new_state = 0 ;
struct cpufreq_policy policy ;
2005-04-17 02:20:36 +04:00
if ( ! pr | | ( count > sizeof ( state_string ) - 1 ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
perf = pr - > performance ;
if ( ! perf )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( state_string , buffer , count ) )
2006-06-27 08:41:40 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
state_string [ count ] = ' \0 ' ;
new_state = simple_strtoul ( state_string , NULL , 0 ) ;
if ( new_state > = perf - > state_count )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
cpufreq_get_policy ( & policy , pr - > id ) ;
policy . cpu = pr - > id ;
policy . min = perf - > states [ new_state ] . core_frequency * 1000 ;
policy . max = perf - > states [ new_state ] . core_frequency * 1000 ;
result = cpufreq_set_policy ( & policy ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static void acpi_cpufreq_add_file ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * entry = NULL ;
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
if ( acpi_bus_get_device ( pr - > handle , & device ) )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
/* add file 'performance' [R/W] */
entry = create_proc_entry ( ACPI_PROCESSOR_FILE_PERFORMANCE ,
2005-08-05 08:44:28 +04:00
S_IFREG | S_IRUGO | S_IWUSR ,
acpi_device_dir ( device ) ) ;
2006-06-27 07:58:43 +04:00
if ( entry ) {
2006-01-07 00:47:00 +03:00
acpi_processor_perf_fops . write = acpi_processor_write_performance ;
2005-04-17 02:20:36 +04:00
entry - > proc_fops = & acpi_processor_perf_fops ;
entry - > data = acpi_driver_data ( device ) ;
entry - > owner = THIS_MODULE ;
}
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static void acpi_cpufreq_remove_file ( struct acpi_processor * pr )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
if ( acpi_bus_get_device ( pr - > handle , & device ) )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
/* remove file 'performance' */
remove_proc_entry ( ACPI_PROCESSOR_FILE_PERFORMANCE ,
2005-08-05 08:44:28 +04:00
acpi_device_dir ( device ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
# else
2005-08-05 08:44:28 +04:00
static void acpi_cpufreq_add_file ( struct acpi_processor * pr )
{
return ;
}
static void acpi_cpufreq_remove_file ( struct acpi_processor * pr )
{
return ;
}
# endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
2005-04-17 02:20:36 +04:00
2005-12-14 23:05:00 +03:00
static int acpi_processor_get_psd ( struct acpi_processor * pr )
{
int result = 0 ;
acpi_status status = AE_OK ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
struct acpi_buffer format = { sizeof ( " NNNNN " ) , " NNNNN " } ;
struct acpi_buffer state = { 0 , NULL } ;
union acpi_object * psd = NULL ;
struct acpi_psd_package * pdomain ;
status = acpi_evaluate_object ( pr - > handle , " _PSD " , NULL , & buffer ) ;
if ( ACPI_FAILURE ( status ) ) {
2006-05-11 08:28:12 +04:00
return - ENODEV ;
2005-12-14 23:05:00 +03:00
}
psd = ( union acpi_object * ) buffer . pointer ;
if ( ! psd | | ( psd - > type ! = ACPI_TYPE_PACKAGE ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Invalid _PSD data \n " ) ) ;
result = - EFAULT ;
goto end ;
}
if ( psd - > package . count ! = 1 ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Invalid _PSD data \n " ) ) ;
result = - EFAULT ;
goto end ;
}
pdomain = & ( pr - > performance - > domain_info ) ;
state . length = sizeof ( struct acpi_psd_package ) ;
state . pointer = pdomain ;
status = acpi_extract_package ( & ( psd - > package . elements [ 0 ] ) ,
& format , & state ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Invalid _PSD data \n " ) ) ;
result = - EFAULT ;
goto end ;
}
if ( pdomain - > num_entries ! = ACPI_PSD_REV0_ENTRIES ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Unknown _PSD:num_entries \n " ) ) ;
result = - EFAULT ;
goto end ;
}
if ( pdomain - > revision ! = ACPI_PSD_REV0_REVISION ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Unknown _PSD:revision \n " ) ) ;
result = - EFAULT ;
goto end ;
}
end :
acpi_os_free ( buffer . pointer ) ;
2006-05-11 08:28:12 +04:00
return result ;
2005-12-14 23:05:00 +03:00
}
int acpi_processor_preregister_performance (
struct acpi_processor_performance * * performance )
{
int count , count_target ;
int retval = 0 ;
unsigned int i , j ;
cpumask_t covered_cpus ;
struct acpi_processor * pr ;
struct acpi_psd_package * pdomain ;
struct acpi_processor * match_pr ;
struct acpi_psd_package * match_pdomain ;
2006-06-16 06:19:31 +04:00
mutex_lock ( & performance_mutex ) ;
2005-12-14 23:05:00 +03:00
retval = 0 ;
/* Call _PSD for all CPUs */
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( i ) {
2005-12-14 23:05:00 +03:00
pr = processors [ i ] ;
if ( ! pr ) {
/* Look only at processors in ACPI namespace */
continue ;
}
if ( pr - > performance ) {
retval = - EBUSY ;
continue ;
}
if ( ! performance | | ! performance [ i ] ) {
retval = - EINVAL ;
continue ;
}
pr - > performance = performance [ i ] ;
cpu_set ( i , pr - > performance - > shared_cpu_map ) ;
if ( acpi_processor_get_psd ( pr ) ) {
retval = - EINVAL ;
continue ;
}
}
if ( retval )
goto err_ret ;
/*
* Now that we have _PSD data from all CPUs , lets setup P - state
* domain info .
*/
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( i ) {
2005-12-14 23:05:00 +03:00
pr = processors [ i ] ;
if ( ! pr )
continue ;
/* Basic validity check for domain info */
pdomain = & ( pr - > performance - > domain_info ) ;
if ( ( pdomain - > revision ! = ACPI_PSD_REV0_REVISION ) | |
( pdomain - > num_entries ! = ACPI_PSD_REV0_ENTRIES ) ) {
retval = - EINVAL ;
goto err_ret ;
}
if ( pdomain - > coord_type ! = DOMAIN_COORD_TYPE_SW_ALL & &
pdomain - > coord_type ! = DOMAIN_COORD_TYPE_SW_ANY & &
pdomain - > coord_type ! = DOMAIN_COORD_TYPE_HW_ALL ) {
retval = - EINVAL ;
goto err_ret ;
}
}
cpus_clear ( covered_cpus ) ;
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( i ) {
2005-12-14 23:05:00 +03:00
pr = processors [ i ] ;
if ( ! pr )
continue ;
if ( cpu_isset ( i , covered_cpus ) )
continue ;
pdomain = & ( pr - > performance - > domain_info ) ;
cpu_set ( i , pr - > performance - > shared_cpu_map ) ;
cpu_set ( i , covered_cpus ) ;
if ( pdomain - > num_processors < = 1 )
continue ;
/* Validate the Domain info */
count_target = pdomain - > num_processors ;
count = 1 ;
2006-06-26 08:34:43 +04:00
if ( pdomain - > coord_type = = DOMAIN_COORD_TYPE_SW_ALL )
2005-12-14 23:05:00 +03:00
pr - > performance - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
2006-06-26 08:34:43 +04:00
else if ( pdomain - > coord_type = = DOMAIN_COORD_TYPE_HW_ALL )
pr - > performance - > shared_type = CPUFREQ_SHARED_TYPE_HW ;
else if ( pdomain - > coord_type = = DOMAIN_COORD_TYPE_SW_ANY )
2005-12-14 23:05:00 +03:00
pr - > performance - > shared_type = CPUFREQ_SHARED_TYPE_ANY ;
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( j ) {
2005-12-14 23:05:00 +03:00
if ( i = = j )
continue ;
match_pr = processors [ j ] ;
if ( ! match_pr )
continue ;
match_pdomain = & ( match_pr - > performance - > domain_info ) ;
if ( match_pdomain - > domain ! = pdomain - > domain )
continue ;
/* Here i and j are in the same domain */
if ( match_pdomain - > num_processors ! = count_target ) {
retval = - EINVAL ;
goto err_ret ;
}
if ( pdomain - > coord_type ! = match_pdomain - > coord_type ) {
retval = - EINVAL ;
goto err_ret ;
}
cpu_set ( j , covered_cpus ) ;
cpu_set ( j , pr - > performance - > shared_cpu_map ) ;
count + + ;
}
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( j ) {
2005-12-14 23:05:00 +03:00
if ( i = = j )
continue ;
match_pr = processors [ j ] ;
if ( ! match_pr )
continue ;
match_pdomain = & ( match_pr - > performance - > domain_info ) ;
if ( match_pdomain - > domain ! = pdomain - > domain )
continue ;
match_pr - > performance - > shared_type =
pr - > performance - > shared_type ;
match_pr - > performance - > shared_cpu_map =
pr - > performance - > shared_cpu_map ;
}
}
err_ret :
if ( retval ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR , " Error while parsing _PSD domain information. Assuming no coordination \n " ) ) ;
}
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( i ) {
2005-12-14 23:05:00 +03:00
pr = processors [ i ] ;
if ( ! pr | | ! pr - > performance )
continue ;
/* Assume no coordination on any error parsing domain info */
if ( retval ) {
cpus_clear ( pr - > performance - > shared_cpu_map ) ;
cpu_set ( i , pr - > performance - > shared_cpu_map ) ;
pr - > performance - > shared_type = CPUFREQ_SHARED_TYPE_ALL ;
}
pr - > performance = NULL ; /* Will be set for real in register */
}
2006-06-16 06:19:31 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-05-11 08:28:12 +04:00
return retval ;
2005-12-14 23:05:00 +03:00
}
EXPORT_SYMBOL ( acpi_processor_preregister_performance ) ;
2005-04-17 02:20:36 +04:00
int
2005-08-05 08:44:28 +04:00
acpi_processor_register_performance ( struct acpi_processor_performance
* performance , unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
struct acpi_processor * pr ;
if ( ! ( acpi_processor_ppc_status & PPC_REGISTERED ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-04-27 13:25:00 +04:00
mutex_lock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
pr = processors [ cpu ] ;
if ( ! pr ) {
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
if ( pr - > performance ) {
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-06-27 08:41:40 +04:00
return - EBUSY ;
2005-04-17 02:20:36 +04:00
}
2006-06-10 20:54:13 +04:00
WARN_ON ( ! performance ) ;
2005-04-17 02:20:36 +04:00
pr - > performance = performance ;
if ( acpi_processor_get_performance_info ( pr ) ) {
pr - > performance = NULL ;
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-06-27 08:41:40 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
acpi_cpufreq_add_file ( pr ) ;
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
EXPORT_SYMBOL ( acpi_processor_register_performance ) ;
2005-04-17 02:20:36 +04:00
void
2005-08-05 08:44:28 +04:00
acpi_processor_unregister_performance ( struct acpi_processor_performance
* performance , unsigned int cpu )
2005-04-17 02:20:36 +04:00
{
struct acpi_processor * pr ;
2006-04-27 13:25:00 +04:00
mutex_lock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
pr = processors [ cpu ] ;
if ( ! pr ) {
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2006-06-10 20:54:13 +04:00
if ( pr - > performance )
kfree ( pr - > performance - > states ) ;
2005-04-17 02:20:36 +04:00
pr - > performance = NULL ;
acpi_cpufreq_remove_file ( pr ) ;
2006-04-27 13:25:00 +04:00
mutex_unlock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_processor_unregister_performance ) ;