2005-04-17 02:20:36 +04:00
/*
* acpi_power . c - ACPI Bus Power Management ( $ Revision : 39 $ )
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* 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 .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
/*
* ACPI power - managed devices may be controlled in two ways :
* 1. via " Device Specific (D-State) Control "
* 2. via " Power Resource Control " .
* This module is used to manage devices relying on Power Resource Control .
*
* An ACPI " power resource object " describes a software controllable power
* plane , clock plane , or other resource used by a power managed device .
* A device may rely on multiple power resources , and a power resource
* may be shared by multiple devices .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
2008-11-08 02:57:45 +03:00
# define _COMPONENT ACPI_POWER_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " power " ) ;
2005-04-17 02:20:36 +04:00
# define ACPI_POWER_CLASS "power_resource"
# define ACPI_POWER_DEVICE_NAME "Power Resource"
# define ACPI_POWER_FILE_INFO "info"
# define ACPI_POWER_FILE_STATUS "state"
# define ACPI_POWER_RESOURCE_STATE_OFF 0x00
# define ACPI_POWER_RESOURCE_STATE_ON 0x01
# define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
2008-08-11 10:57:50 +04:00
int acpi_power_nocheck ;
module_param_named ( power_nocheck , acpi_power_nocheck , bool , 000 ) ;
2005-08-05 08:44:28 +04:00
static int acpi_power_add ( struct acpi_device * device ) ;
static int acpi_power_remove ( struct acpi_device * device , int type ) ;
2007-02-16 10:05:39 +03:00
static int acpi_power_resume ( struct acpi_device * device ) ;
2005-04-17 02:20:36 +04:00
static int acpi_power_open_fs ( struct inode * inode , struct file * file ) ;
2007-07-23 16:44:41 +04:00
static struct acpi_device_id power_device_ids [ ] = {
{ ACPI_POWER_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , power_device_ids ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_power_driver = {
2007-02-13 07:33:40 +03:00
. name = " power " ,
2005-08-05 08:44:28 +04:00
. class = ACPI_POWER_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = power_device_ids ,
2005-08-05 08:44:28 +04:00
. ops = {
. add = acpi_power_add ,
. remove = acpi_power_remove ,
2007-02-16 09:47:06 +03:00
. resume = acpi_power_resume ,
2005-08-05 08:44:28 +04:00
} ,
2005-04-17 02:20:36 +04:00
} ;
2007-02-16 09:47:06 +03:00
struct acpi_power_reference {
struct list_head node ;
struct acpi_device * device ;
} ;
2005-08-05 08:44:28 +04:00
struct acpi_power_resource {
2006-05-20 00:54:40 +04:00
struct acpi_device * device ;
2005-08-05 08:44:28 +04:00
acpi_bus_id name ;
u32 system_level ;
u32 order ;
2007-02-16 09:47:06 +03:00
struct mutex resource_lock ;
struct list_head reference ;
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static struct list_head acpi_power_resource_list ;
2005-04-17 02:20:36 +04:00
2006-07-04 21:06:00 +04:00
static const struct file_operations acpi_power_fops = {
2008-04-29 12:02:27 +04:00
. owner = THIS_MODULE ,
2005-08-05 08:44:28 +04:00
. open = acpi_power_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
} ;
/* --------------------------------------------------------------------------
Power Resource Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
2005-08-05 08:44:28 +04:00
acpi_power_get_context ( acpi_handle handle ,
struct acpi_power_resource * * resource )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_device * device = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! resource )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_get_device ( handle , & device ) ;
if ( result ) {
2006-06-27 07:04:31 +04:00
printk ( KERN_WARNING PREFIX " Getting context [%p] \n " , handle ) ;
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2006-10-01 02:28:50 +04:00
* resource = acpi_driver_data ( device ) ;
2008-04-19 00:27:29 +04:00
if ( ! * resource )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
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
}
2008-08-11 10:55:05 +04:00
static int acpi_power_get_state ( acpi_handle handle , int * state )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2008-10-10 10:22:59 +04:00
unsigned long long sta = 0 ;
2008-12-16 12:02:22 +03:00
char node_name [ 5 ] ;
struct acpi_buffer buffer = { sizeof ( node_name ) , node_name } ;
2005-04-17 02:20:36 +04:00
2008-08-11 10:55:05 +04:00
if ( ! handle | | ! state )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-08-11 10:55:05 +04:00
status = acpi_evaluate_integer ( handle , " _STA " , NULL , & sta ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-10-22 14:19:09 +04:00
* state = ( sta & 0x01 ) ? ACPI_POWER_RESOURCE_STATE_ON :
ACPI_POWER_RESOURCE_STATE_OFF ;
2005-04-17 02:20:36 +04:00
2008-12-16 12:02:22 +03:00
acpi_get_name ( handle , ACPI_SINGLE_NAME , & buffer ) ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] is %s \n " ,
2008-12-16 12:02:22 +03:00
node_name ,
2008-10-27 11:04:53 +03:00
* state ? " on " : " off " ) ) ;
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
static int acpi_power_get_list_state ( struct acpi_handle_list * list , int * state )
2005-04-17 02:20:36 +04:00
{
2007-10-22 14:19:09 +04:00
int result = 0 , state1 ;
2005-08-05 08:44:28 +04:00
u32 i = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! list | | ! state )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
/* The state of the list is 'on' IFF all resources are 'on'. */
2008-08-11 10:55:05 +04:00
/* */
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < list - > count ; i + + ) {
2008-08-11 10:55:05 +04:00
/*
* The state of the power resource can be obtained by
* using the ACPI handle . In such case it is unnecessary to
* get the Power resource first and then get its state again .
*/
result = acpi_power_get_state ( list - > handles [ i ] , & state1 ) ;
2005-04-17 02:20:36 +04:00
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2007-10-22 14:19:09 +04:00
* state = state1 ;
2005-04-17 02:20:36 +04:00
if ( * state ! = ACPI_POWER_RESOURCE_STATE_ON )
break ;
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource list is %s \n " ,
2005-08-05 08:44:28 +04:00
* state ? " on " : " off " ) ) ;
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
}
2007-02-16 09:47:06 +03:00
static int acpi_power_on ( acpi_handle handle , struct acpi_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-10-22 14:19:09 +04:00
int result = 0 , state ;
2007-02-16 09:47:06 +03:00
int found = 0 ;
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2005-04-17 02:20:36 +04:00
struct acpi_power_resource * resource = NULL ;
2007-02-16 09:47:06 +03:00
struct list_head * node , * next ;
struct acpi_power_reference * ref ;
2005-04-17 02:20:36 +04:00
result = acpi_power_get_context ( handle , & resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2007-02-16 09:47:06 +03:00
mutex_lock ( & resource - > resource_lock ) ;
list_for_each_safe ( node , next , & resource - > reference ) {
ref = container_of ( node , struct acpi_power_reference , node ) ;
if ( dev - > handle = = ref - > device - > handle ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] already referenced by resource [%s] \n " ,
dev - > pnp . bus_id , resource - > name ) ) ;
found = 1 ;
break ;
}
}
if ( ! found ) {
ref = kmalloc ( sizeof ( struct acpi_power_reference ) ,
irqs_disabled ( ) ? GFP_ATOMIC : GFP_KERNEL ) ;
if ( ! ref ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " kmalloc() failed \n " ) ) ;
mutex_unlock ( & resource - > resource_lock ) ;
return - ENOMEM ;
}
list_add_tail ( & ref - > node , & resource - > reference ) ;
ref - > device = dev ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] added to resource [%s] references \n " ,
dev - > pnp . bus_id , resource - > name ) ) ;
}
mutex_unlock ( & resource - > resource_lock ) ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:43 +04:00
status = acpi_evaluate_object ( resource - > device - > handle , " _ON " , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-08-11 10:57:50 +04:00
if ( ! acpi_power_nocheck ) {
/*
* If acpi_power_nocheck is set , it is unnecessary to check
* the power state after power transition .
*/
result = acpi_power_get_state ( resource - > device - > handle ,
& state ) ;
if ( result )
return result ;
if ( state ! = ACPI_POWER_RESOURCE_STATE_ON )
return - ENOEXEC ;
}
2005-04-17 02:20:36 +04:00
/* Update the power resource's _device_ power state */
2006-05-20 00:54:40 +04:00
resource - > device - > power . state = ACPI_STATE_D0 ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] turned on \n " ,
2005-08-05 08:44:28 +04:00
resource - > name ) ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-16 09:47:06 +03:00
static int acpi_power_off_device ( acpi_handle handle , struct acpi_device * dev )
2005-04-17 02:20:36 +04:00
{
2007-10-22 14:19:09 +04:00
int result = 0 , state ;
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2005-04-17 02:20:36 +04:00
struct acpi_power_resource * resource = NULL ;
2007-02-16 09:47:06 +03:00
struct list_head * node , * next ;
struct acpi_power_reference * ref ;
2005-04-17 02:20:36 +04:00
result = acpi_power_get_context ( handle , & resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
2007-02-16 09:47:06 +03:00
mutex_lock ( & resource - > resource_lock ) ;
list_for_each_safe ( node , next , & resource - > reference ) {
ref = container_of ( node , struct acpi_power_reference , node ) ;
if ( dev - > handle = = ref - > device - > handle ) {
list_del ( & ref - > node ) ;
kfree ( ref ) ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] removed from resource [%s] references \n " ,
dev - > pnp . bus_id , resource - > name ) ) ;
break ;
}
}
2005-04-17 02:20:36 +04:00
2007-02-16 09:47:06 +03:00
if ( ! list_empty ( & resource - > reference ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Cannot turn resource [%s] off - resource is in use \n " ,
resource - > name ) ) ;
mutex_unlock ( & resource - > resource_lock ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-16 09:47:06 +03:00
mutex_unlock ( & resource - > resource_lock ) ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:43 +04:00
status = acpi_evaluate_object ( resource - > device - > handle , " _OFF " , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-08-11 10:57:50 +04:00
if ( ! acpi_power_nocheck ) {
/*
* If acpi_power_nocheck is set , it is unnecessary to check
* the power state after power transition .
*/
result = acpi_power_get_state ( handle , & state ) ;
if ( result )
return result ;
if ( state ! = ACPI_POWER_RESOURCE_STATE_OFF )
return - ENOEXEC ;
}
2005-04-17 02:20:36 +04:00
/* Update the power resource's _device_ power state */
2006-08-24 07:18:06 +04:00
resource - > device - > power . state = ACPI_STATE_D3 ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] turned off \n " ,
2005-08-05 08:44:28 +04:00
resource - > name ) ) ;
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
}
2008-07-07 05:33:34 +04:00
/**
* acpi_device_sleep_wake - execute _DSW ( Device Sleep Wake ) or ( deprecated in
* ACPI 3.0 ) _PSW ( Power State Wake )
* @ dev : Device to handle .
* @ enable : 0 - disable , 1 - enable the wake capabilities of the device .
* @ sleep_state : Target sleep state of the system .
* @ dev_state : Target power state of the device .
*
* Execute _DSW ( Device Sleep Wake ) or ( deprecated in ACPI 3.0 ) _PSW ( Power
* State Wake ) for the device , if present . On failure reset the device ' s
* wakeup . flags . valid flag .
*
* RETURN VALUE :
* 0 if either _DSW or _PSW has been successfully executed
* 0 if neither _DSW nor _PSW has been found
* - ENODEV if the execution of either _DSW or _PSW has failed
*/
int acpi_device_sleep_wake ( struct acpi_device * dev ,
int enable , int sleep_state , int dev_state )
{
union acpi_object in_arg [ 3 ] ;
struct acpi_object_list arg_list = { 3 , in_arg } ;
acpi_status status = AE_OK ;
/*
* Try to execute _DSW first .
*
* Three agruments are needed for the _DSW object :
* Argument 0 : enable / disable the wake capabilities
* Argument 1 : target system state
* Argument 2 : target device state
* When _DSW object is called to disable the wake capabilities , maybe
* the first argument is filled . The values of the other two agruments
* are meaningless .
*/
in_arg [ 0 ] . type = ACPI_TYPE_INTEGER ;
in_arg [ 0 ] . integer . value = enable ;
in_arg [ 1 ] . type = ACPI_TYPE_INTEGER ;
in_arg [ 1 ] . integer . value = sleep_state ;
in_arg [ 2 ] . type = ACPI_TYPE_INTEGER ;
in_arg [ 2 ] . integer . value = dev_state ;
status = acpi_evaluate_object ( dev - > handle , " _DSW " , & arg_list , NULL ) ;
if ( ACPI_SUCCESS ( status ) ) {
return 0 ;
} else if ( status ! = AE_NOT_FOUND ) {
printk ( KERN_ERR PREFIX " _DSW execution failed \n " ) ;
dev - > wakeup . flags . valid = 0 ;
return - ENODEV ;
}
/* Execute _PSW */
arg_list . count = 1 ;
in_arg [ 0 ] . integer . value = enable ;
status = acpi_evaluate_object ( dev - > handle , " _PSW " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
printk ( KERN_ERR PREFIX " _PSW execution failed \n " ) ;
dev - > wakeup . flags . valid = 0 ;
return - ENODEV ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Prepare a wakeup device , two steps ( Ref ACPI 2.0 : P229 ) :
* 1. Power on the power resources required for the wakeup device
2008-07-07 05:33:34 +04:00
* 2. Execute _DSW ( Device Sleep Wake ) or ( deprecated in ACPI 3.0 ) _PSW ( Power
* State Wake ) for the device , if present
2005-04-17 02:20:36 +04:00
*/
2008-07-07 05:33:34 +04:00
int acpi_enable_wakeup_device_power ( struct acpi_device * dev , int sleep_state )
2005-04-17 02:20:36 +04:00
{
2008-07-07 05:34:11 +04:00
int i , err ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2008-07-07 05:33:34 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-07-07 05:34:11 +04:00
/*
* Do not execute the code below twice in a row without calling
* acpi_disable_wakeup_device_power ( ) in between for the same device
*/
if ( dev - > wakeup . flags . prepared )
return 0 ;
2005-04-17 02:20:36 +04:00
/* Open power resource */
for ( i = 0 ; i < dev - > wakeup . resources . count ; i + + ) {
2008-07-07 05:33:34 +04:00
int ret = acpi_power_on ( dev - > wakeup . resources . handles [ i ] , dev ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Transition power state \n " ) ;
2005-04-17 02:20:36 +04:00
dev - > wakeup . flags . valid = 0 ;
2008-07-07 05:33:34 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
}
2008-07-07 05:33:34 +04:00
/*
* Passing 3 as the third argument below means the device may be placed
* in arbitrary power state afterwards .
*/
2008-07-07 05:34:11 +04:00
err = acpi_device_sleep_wake ( dev , 1 , sleep_state , 3 ) ;
if ( ! err )
dev - > wakeup . flags . prepared = 1 ;
return err ;
2005-04-17 02:20:36 +04:00
}
/*
* Shutdown a wakeup device , counterpart of above method
2008-07-07 05:33:34 +04:00
* 1. Execute _DSW ( Device Sleep Wake ) or ( deprecated in ACPI 3.0 ) _PSW ( Power
* State Wake ) for the device , if present
2005-04-17 02:20:36 +04:00
* 2. Shutdown down the power resources
*/
2005-08-05 08:44:28 +04:00
int acpi_disable_wakeup_device_power ( struct acpi_device * dev )
2005-04-17 02:20:36 +04:00
{
2008-07-07 05:33:34 +04:00
int i , ret ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2008-07-07 05:33:34 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-07-07 05:34:11 +04:00
/*
* Do not execute the code below twice in a row without calling
* acpi_enable_wakeup_device_power ( ) in between for the same device
*/
if ( ! dev - > wakeup . flags . prepared )
return 0 ;
dev - > wakeup . flags . prepared = 0 ;
2008-07-07 05:33:34 +04:00
ret = acpi_device_sleep_wake ( dev , 0 , 0 , 0 ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
/* Close power resource */
for ( i = 0 ; i < dev - > wakeup . resources . count ; i + + ) {
2007-02-16 09:47:06 +03:00
ret = acpi_power_off_device ( dev - > wakeup . resources . handles [ i ] , dev ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Transition power state \n " ) ;
2005-04-17 02:20:36 +04:00
dev - > wakeup . flags . valid = 0 ;
2008-07-07 05:33:34 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-27 08:41:40 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Device Power Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
int acpi_power_get_inferred_state ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_handle_list * list = NULL ;
int list_state = 0 ;
int i = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
device - > power . state = ACPI_STATE_UNKNOWN ;
/*
* We know a device ' s inferred power state when all the resources
* required for a given D - state are ' on ' .
*/
2005-08-05 08:44:28 +04:00
for ( i = ACPI_STATE_D0 ; i < ACPI_STATE_D3 ; i + + ) {
2005-04-17 02:20:36 +04:00
list = & device - > power . states [ i ] . resources ;
if ( list - > count < 1 )
continue ;
result = acpi_power_get_list_state ( list , & list_state ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
if ( list_state = = ACPI_POWER_RESOURCE_STATE_ON ) {
device - > power . state = i ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
}
device - > power . state = ACPI_STATE_D3 ;
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_power_transition ( struct acpi_device * device , int state )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
struct acpi_handle_list * cl = NULL ; /* Current Resources */
struct acpi_handle_list * tl = NULL ; /* Target Resources */
int i = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ( state < ACPI_STATE_D0 ) | | ( state > ACPI_STATE_D3 ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
if ( ( device - > power . state < ACPI_STATE_D0 )
| | ( device - > power . state > ACPI_STATE_D3 ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
cl = & device - > power . states [ device - > power . state ] . resources ;
tl = & device - > power . states [ state ] . resources ;
/* TBD: Resources must be ordered. */
/*
* First we reference all power resources required in the target list
* ( e . g . so the device doesn ' t lose power while transitioning ) .
*/
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < tl - > count ; i + + ) {
2007-02-16 09:47:06 +03:00
result = acpi_power_on ( tl - > handles [ i ] , device ) ;
2005-04-17 02:20:36 +04:00
if ( result )
goto end ;
}
2007-02-16 10:23:07 +03:00
if ( device - > power . state = = state ) {
goto end ;
}
2005-04-17 02:20:36 +04:00
/*
* Then we dereference all power resources used in the current list .
*/
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < cl - > count ; i + + ) {
2007-02-16 09:47:06 +03:00
result = acpi_power_off_device ( cl - > handles [ i ] , device ) ;
2005-04-17 02:20:36 +04:00
if ( result )
goto end ;
}
2007-02-21 10:05:58 +03:00
end :
2008-02-05 10:31:19 +03:00
if ( result )
2007-02-21 10:05:58 +03:00
device - > power . state = ACPI_STATE_UNKNOWN ;
2008-02-05 10:31:19 +03:00
else {
2007-02-21 10:05:58 +03:00
/* We shouldn't change the state till all above operations succeed */
device - > power . state = state ;
}
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
}
/* --------------------------------------------------------------------------
FS Interface ( / proc )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static struct proc_dir_entry * acpi_power_dir ;
2005-04-17 02:20:36 +04:00
static int acpi_power_seq_show ( struct seq_file * seq , void * offset )
{
2007-02-16 09:47:06 +03:00
int count = 0 ;
2007-10-22 14:19:09 +04:00
int result = 0 , state ;
2005-04-17 02:20:36 +04:00
struct acpi_power_resource * resource = NULL ;
2007-02-16 09:47:06 +03:00
struct list_head * node , * next ;
struct acpi_power_reference * ref ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
resource = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! resource )
goto end ;
2008-08-11 10:55:05 +04:00
result = acpi_power_get_state ( resource - > device - > handle , & state ) ;
2007-02-16 09:47:06 +03:00
if ( result )
goto end ;
2005-04-17 02:20:36 +04:00
seq_puts ( seq , " state: " ) ;
2007-10-22 14:19:09 +04:00
switch ( state ) {
2005-04-17 02:20:36 +04:00
case ACPI_POWER_RESOURCE_STATE_ON :
seq_puts ( seq , " on \n " ) ;
break ;
case ACPI_POWER_RESOURCE_STATE_OFF :
seq_puts ( seq , " off \n " ) ;
break ;
default :
seq_puts ( seq , " unknown \n " ) ;
break ;
}
2007-02-16 09:47:06 +03:00
mutex_lock ( & resource - > resource_lock ) ;
list_for_each_safe ( node , next , & resource - > reference ) {
ref = container_of ( node , struct acpi_power_reference , node ) ;
count + + ;
}
mutex_unlock ( & resource - > resource_lock ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " system level: S%d \n "
2005-08-05 08:44:28 +04:00
" order: %d \n "
" reference count: %d \n " ,
resource - > system_level ,
2007-02-16 09:47:06 +03:00
resource - > order , count ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int acpi_power_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_power_seq_show , PDE ( inode ) - > data ) ;
}
2005-08-05 08:44:28 +04:00
static int acpi_power_add_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * entry = NULL ;
2005-04-17 02:20:36 +04:00
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_device_dir ( device ) ) {
acpi_device_dir ( device ) = proc_mkdir ( acpi_device_bid ( device ) ,
2005-08-05 08:44:28 +04:00
acpi_power_dir ) ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_device_dir ( device ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
/* 'status' [R] */
2008-04-29 12:02:27 +04:00
entry = proc_create_data ( ACPI_POWER_FILE_STATUS ,
S_IRUGO , acpi_device_dir ( device ) ,
& acpi_power_fops , acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-06-27 08:41:40 +04:00
return - EIO ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_power_remove_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
if ( acpi_device_dir ( device ) ) {
remove_proc_entry ( ACPI_POWER_FILE_STATUS ,
acpi_device_dir ( device ) ) ;
remove_proc_entry ( acpi_device_bid ( device ) , acpi_power_dir ) ;
acpi_device_dir ( device ) = NULL ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static int acpi_power_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2007-10-22 14:19:09 +04:00
int result = 0 , state ;
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2005-04-17 02:20:36 +04:00
struct acpi_power_resource * resource = NULL ;
2005-08-05 08:44:28 +04:00
union acpi_object acpi_object ;
struct acpi_buffer buffer = { sizeof ( acpi_object ) , & acpi_object } ;
2005-04-17 02:20:36 +04:00
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-12-19 23:56:11 +03:00
resource = kzalloc ( sizeof ( struct acpi_power_resource ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! resource )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:40 +04:00
resource - > device = device ;
2007-02-16 09:47:06 +03:00
mutex_init ( & resource - > resource_lock ) ;
INIT_LIST_HEAD ( & resource - > reference ) ;
2005-04-17 02:20:36 +04:00
strcpy ( resource - > name , device - > pnp . bus_id ) ;
strcpy ( acpi_device_name ( device ) , ACPI_POWER_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_POWER_CLASS ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = resource ;
2005-04-17 02:20:36 +04:00
/* Evalute the object to get the system level and resource order. */
2006-05-20 00:54:43 +04:00
status = acpi_evaluate_object ( device - > handle , NULL , NULL , & buffer ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
result = - ENODEV ;
goto end ;
}
resource - > system_level = acpi_object . power_resource . system_level ;
resource - > order = acpi_object . power_resource . resource_order ;
2008-08-11 10:55:05 +04:00
result = acpi_power_get_state ( device - > handle , & state ) ;
2005-04-17 02:20:36 +04:00
if ( result )
goto end ;
2007-10-22 14:19:09 +04:00
switch ( state ) {
2005-04-17 02:20:36 +04:00
case ACPI_POWER_RESOURCE_STATE_ON :
device - > power . state = ACPI_STATE_D0 ;
break ;
case ACPI_POWER_RESOURCE_STATE_OFF :
device - > power . state = ACPI_STATE_D3 ;
break ;
default :
device - > power . state = ACPI_STATE_UNKNOWN ;
break ;
}
result = acpi_power_add_fs ( device ) ;
if ( result )
goto end ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " %s [%s] (%s) \n " , acpi_device_name ( device ) ,
2007-10-22 14:19:09 +04:00
acpi_device_bid ( device ) , state ? " on " : " off " ) ;
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
if ( result )
kfree ( resource ) ;
2005-08-05 08:44:28 +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_power_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
struct acpi_power_resource * resource = NULL ;
2007-02-16 09:47:06 +03:00
struct list_head * node , * next ;
2005-04-17 02:20:36 +04:00
if ( ! device | | ! acpi_driver_data ( device ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
resource = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
acpi_power_remove_fs ( device ) ;
2007-02-16 09:47:06 +03:00
mutex_lock ( & resource - > resource_lock ) ;
list_for_each_safe ( node , next , & resource - > reference ) {
struct acpi_power_reference * ref = container_of ( node , struct acpi_power_reference , node ) ;
list_del ( & ref - > node ) ;
kfree ( ref ) ;
}
mutex_unlock ( & resource - > resource_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( resource ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-16 10:05:39 +03:00
static int acpi_power_resume ( struct acpi_device * device )
2007-02-16 09:47:06 +03:00
{
2007-10-22 14:19:09 +04:00
int result = 0 , state ;
2007-02-16 09:47:06 +03:00
struct acpi_power_resource * resource = NULL ;
struct acpi_power_reference * ref ;
if ( ! device | | ! acpi_driver_data ( device ) )
return - EINVAL ;
2008-09-23 01:37:34 +04:00
resource = acpi_driver_data ( device ) ;
2007-02-16 09:47:06 +03:00
2008-08-11 10:55:05 +04:00
result = acpi_power_get_state ( device - > handle , & state ) ;
2007-02-16 09:47:06 +03:00
if ( result )
return result ;
mutex_lock ( & resource - > resource_lock ) ;
2007-10-22 14:19:09 +04:00
if ( state = = ACPI_POWER_RESOURCE_STATE_OFF & &
2007-02-16 09:47:06 +03:00
! list_empty ( & resource - > reference ) ) {
ref = container_of ( resource - > reference . next , struct acpi_power_reference , node ) ;
mutex_unlock ( & resource - > resource_lock ) ;
result = acpi_power_on ( device - > handle , ref - > device ) ;
return result ;
}
mutex_unlock ( & resource - > resource_lock ) ;
return 0 ;
}
2009-03-25 01:49:53 +03:00
int __init acpi_power_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & acpi_power_resource_list ) ;
acpi_power_dir = proc_mkdir ( ACPI_POWER_CLASS , acpi_root_dir ) ;
if ( ! acpi_power_dir )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_register_driver ( & acpi_power_driver ) ;
if ( result < 0 ) {
remove_proc_entry ( ACPI_POWER_CLASS , acpi_root_dir ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
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
}