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
2008-10-24 13:22:04 +04:00
# ifdef CONFIG_X86
2008-09-01 16:27:04 +04:00
# include <asm/cpufeature.h>
2008-10-24 13:22:04 +04:00
# endif
2005-04-17 02:20:36 +04:00
# include <acpi/acpi_bus.h>
2008-11-08 02:57:45 +03:00
# include <acpi/acpi_drivers.h>
2005-04-17 02:20:36 +04:00
# include <acpi/processor.h>
# define ACPI_PROCESSOR_CLASS "processor"
# define ACPI_PROCESSOR_FILE_PERFORMANCE "performance"
# define _COMPONENT ACPI_PROCESSOR_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " processor_perflib " ) ;
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
2007-10-31 17:41:42 +03:00
/* Use cpufreq debug layer for _PPC changes. */
# define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
" cpufreq-core " , msg )
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 .
*/
2008-07-30 09:32:58 +04:00
/* ignore_ppc:
* - 1 - > cpufreq low level drivers not initialized - > _PSS , etc . not called yet
* ignore _PPC
* 0 - > cpufreq low level drivers initialized - > consider _PPC values
* 1 - > ignore _PPC totally - > forced by user through boot param
*/
2008-08-12 19:48:27 +04:00
static int ignore_ppc = - 1 ;
2008-08-16 04:11:28 +04:00
module_param ( ignore_ppc , int , 0644 ) ;
2007-05-19 06:59:28 +04:00
MODULE_PARM_DESC ( ignore_ppc , " If the frequency of your machine gets wrongly " \
" limited by BIOS, this should help " ) ;
2005-04-17 02:20:36 +04:00
# define PPC_REGISTERED 1
# define PPC_IN_USE 2
2008-07-30 09:32:58 +04:00
static int acpi_processor_ppc_status ;
2005-04-17 02:20:36 +04:00
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 ;
2008-07-30 09:32:58 +04:00
if ( event = = CPUFREQ_START & & ignore_ppc < = 0 ) {
ignore_ppc = 0 ;
return 0 ;
}
2007-05-19 06:59:28 +04:00
if ( ignore_ppc )
return 0 ;
2005-04-17 02:20:36 +04:00
if ( event ! = CPUFREQ_INCOMPATIBLE )
2008-07-30 09:32:59 +04:00
return 0 ;
mutex_lock ( & performance_mutex ) ;
2005-04-17 02:20:36 +04:00
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
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
2006-11-23 04:42:01 +03:00
if ( ppc > = pr - > performance - > state_count )
2005-04-17 02:20:36 +04:00
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 ;
2008-10-10 10:22:59 +04:00
unsigned long 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
}
2007-10-31 17:41:42 +03:00
cpufreq_printk ( " CPU %d: _PPC is %d - frequency %s limited \n " , pr - > id ,
( int ) ppc , ppc ? " " : " not " ) ;
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
{
2007-05-19 06:59:28 +04:00
int ret ;
if ( ignore_ppc )
return 0 ;
ret = acpi_processor_get_platform_limit ( pr ) ;
2005-04-17 02:20:36 +04:00
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 :
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
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
}
2006-10-01 02:28:50 +04:00
pss = 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 :
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2005-04-17 02:20:36 +04:00
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 )
2008-09-01 16:27:04 +04:00
goto update_bios ;
2005-04-17 02:20:36 +04:00
result = acpi_processor_get_performance_states ( pr ) ;
if ( result )
2008-09-01 16:27:04 +04:00
goto update_bios ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2008-09-01 16:27:04 +04:00
/*
* Having _PPC but missing frequencies ( _PSS , _PCT ) is a very good hint that
* the BIOS is older than the CPU and does not know its frequencies
*/
update_bios :
2008-10-24 13:22:04 +04:00
# ifdef CONFIG_X86
2008-09-01 16:27:04 +04:00
if ( ACPI_SUCCESS ( acpi_get_handle ( pr - > handle , " _PPC " , & handle ) ) ) {
if ( boot_cpu_has ( X86_FEATURE_EST ) )
printk ( KERN_WARNING FW_BUG " BIOS needs update for CPU "
" frequency support \n " ) ;
}
2008-10-24 13:22:04 +04:00
# endif
2008-09-01 16:27:04 +04:00
return result ;
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 ;
2007-02-02 19:48:19 +03:00
/* Can't write pstate_control to smi_command if either value is zero */
2007-02-02 19:48:22 +03:00
if ( ( ! acpi_gbl_FADT . smi_command ) | | ( ! acpi_gbl_FADT . pstate_control ) ) {
2007-02-02 19:48:19 +03:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " No SMI port or pstate_control \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 ,
2007-02-02 19:48:19 +03:00
" Writing pstate_control [0x%x] to smi_command [0x%x] \n " ,
2007-02-02 19:48:22 +03:00
acpi_gbl_FADT . pstate_control , acpi_gbl_FADT . smi_command ) ) ;
2005-04-17 02:20:36 +04:00
2007-02-02 19:48:22 +03:00
status = acpi_os_write_port ( acpi_gbl_FADT . smi_command ,
( u32 ) acpi_gbl_FADT . pstate_control , 8 ) ;
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 ,
2007-02-02 19:48:19 +03:00
" Failed to write pstate_control [0x%x] to "
2007-02-02 19:48:22 +03:00
" smi_command [0x%x] " , acpi_gbl_FADT . pstate_control ,
acpi_gbl_FADT . smi_command ) ) ;
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 = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
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 )
{
2006-10-01 02:28:50 +04:00
struct acpi_processor * pr = seq - > private ;
2005-08-05 08:44:28 +04:00
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
}
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 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] */
2008-04-29 12:02:27 +04:00
proc_create_data ( ACPI_PROCESSOR_FILE_PERFORMANCE , S_IFREG | S_IRUGO ,
acpi_device_dir ( device ) ,
& acpi_processor_perf_fops , acpi_driver_data ( device ) ) ;
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
}
2006-10-01 02:28:50 +04:00
psd = buffer . pointer ;
2005-12-14 23:05:00 +03:00
if ( ! psd | | ( psd - > type ! = ACPI_TYPE_PACKAGE ) ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX " Invalid _PSD data \n " ) ;
2005-12-14 23:05:00 +03:00
result = - EFAULT ;
goto end ;
}
if ( psd - > package . count ! = 1 ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX " Invalid _PSD data \n " ) ;
2005-12-14 23:05:00 +03:00
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 ) ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX " Invalid _PSD data \n " ) ;
2005-12-14 23:05:00 +03:00
result = - EFAULT ;
goto end ;
}
if ( pdomain - > num_entries ! = ACPI_PSD_REV0_ENTRIES ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX " Unknown _PSD:num_entries \n " ) ;
2005-12-14 23:05:00 +03:00
result = - EFAULT ;
goto end ;
}
if ( pdomain - > revision ! = ACPI_PSD_REV0_REVISION ) {
2008-09-28 10:51:56 +04:00
printk ( KERN_ERR PREFIX " Unknown _PSD:revision \n " ) ;
2005-12-14 23:05:00 +03:00
result = - EFAULT ;
goto end ;
}
end :
2006-06-30 11:19:10 +04:00
kfree ( 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 (
2007-08-08 02:40:30 +04:00
struct acpi_processor_performance * performance )
2005-12-14 23:05:00 +03:00
{
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 ) {
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , i ) ;
2005-12-14 23:05:00 +03:00
if ( ! pr ) {
/* Look only at processors in ACPI namespace */
continue ;
}
if ( pr - > performance ) {
retval = - EBUSY ;
continue ;
}
2007-08-08 02:40:30 +04:00
if ( ! performance | | ! percpu_ptr ( performance , i ) ) {
2005-12-14 23:05:00 +03:00
retval = - EINVAL ;
continue ;
}
2007-08-08 02:40:30 +04:00
pr - > performance = percpu_ptr ( performance , i ) ;
2005-12-14 23:05:00 +03:00
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 ) {
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , i ) ;
2005-12-14 23:05:00 +03:00
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 ) {
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , i ) ;
2005-12-14 23:05:00 +03:00
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 ;
2008-06-10 03:22:23 +04:00
match_pr = per_cpu ( processors , j ) ;
2005-12-14 23:05:00 +03:00
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 ;
2008-06-10 03:22:23 +04:00
match_pr = per_cpu ( processors , j ) ;
2005-12-14 23:05:00 +03:00
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 :
2006-04-27 13:25:00 +04:00
for_each_possible_cpu ( i ) {
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , i ) ;
2005-12-14 23:05:00 +03:00
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
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , cpu ) ;
2005-04-17 02:20:36 +04:00
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
2008-06-10 03:22:23 +04:00
pr = per_cpu ( processors , cpu ) ;
2005-04-17 02:20:36 +04:00
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 ) ;