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>
# define _COMPONENT ACPI_POWER_COMPONENT
2005-08-05 08:44:28 +04:00
ACPI_MODULE_NAME ( " acpi_power " )
2005-04-17 02:20:36 +04:00
# define ACPI_POWER_COMPONENT 0x00800000
# define ACPI_POWER_CLASS "power_resource"
# define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver"
# 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
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 09:47:06 +03:00
static int acpi_power_resume ( struct acpi_device * device , int state ) ;
2005-04-17 02:20:36 +04:00
static int acpi_power_open_fs ( struct inode * inode , struct file * file ) ;
static struct acpi_driver acpi_power_driver = {
2005-08-05 08:44:28 +04:00
. name = ACPI_POWER_DRIVER_NAME ,
. class = ACPI_POWER_CLASS ,
. ids = ACPI_POWER_HID ,
. 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 ;
int state ;
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 = {
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 ) ;
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
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_state ( struct acpi_power_resource * resource )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
unsigned long sta = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! resource )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-05-20 00:54:43 +04:00
status = acpi_evaluate_integer ( resource - > device - > 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
if ( sta & 0x01 )
resource - > state = ACPI_POWER_RESOURCE_STATE_ON ;
else
resource - > state = ACPI_POWER_RESOURCE_STATE_OFF ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] is %s \n " ,
2005-08-05 08:44:28 +04:00
resource - > name , resource - > 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
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
2005-04-17 02:20:36 +04:00
struct acpi_power_resource * resource = NULL ;
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'. */
2005-08-05 08:44:28 +04:00
for ( i = 0 ; i < list - > count ; i + + ) {
2005-04-17 02:20:36 +04:00
result = acpi_power_get_context ( list - > handles [ i ] , & resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
* state = resource - > state ;
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
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
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
2007-02-16 09:47:06 +03:00
if ( resource - > state = = ACPI_POWER_RESOURCE_STATE_ON ) {
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] already 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
}
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
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
if ( resource - > state ! = ACPI_POWER_RESOURCE_STATE_ON )
2006-06-27 08:41:40 +04:00
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
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
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
if ( resource - > state = = ACPI_POWER_RESOURCE_STATE_OFF ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] already off \n " ,
2007-02-16 09:47:06 +03:00
resource - > name ) ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
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
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
if ( resource - > state ! = ACPI_POWER_RESOURCE_STATE_OFF )
2006-06-27 08:41:40 +04:00
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
}
/*
* Prepare a wakeup device , two steps ( Ref ACPI 2.0 : P229 ) :
* 1. Power on the power resources required for the wakeup device
* 2. Enable _PSW ( power state wake ) for the device if present
*/
2005-08-05 08:44:28 +04:00
int acpi_enable_wakeup_device_power ( struct acpi_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
union acpi_object arg = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list arg_list = { 1 , & arg } ;
acpi_status status = AE_OK ;
int i ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2006-06-27 08:41:40 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
arg . integer . value = 1 ;
/* Open power resource */
for ( i = 0 ; i < dev - > wakeup . resources . count ; i + + ) {
2007-02-16 09:47:06 +03:00
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 ;
2006-06-27 08:41:40 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
}
/* Execute PSW */
status = acpi_evaluate_object ( dev - > handle , " _PSW " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Evaluate _PSW \n " ) ;
2005-04-17 02:20:36 +04:00
dev - > wakeup . flags . valid = 0 ;
ret = - 1 ;
}
2006-06-27 08:41:40 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Shutdown a wakeup device , counterpart of above method
* 1. Disable _PSW ( power state wake )
* 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
{
2005-08-05 08:44:28 +04:00
union acpi_object arg = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list arg_list = { 1 , & arg } ;
acpi_status status = AE_OK ;
int i ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2006-06-27 08:41:40 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
arg . integer . value = 0 ;
2005-04-17 02:20:36 +04:00
/* Execute PSW */
status = acpi_evaluate_object ( dev - > handle , " _PSW " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Evaluate _PSW \n " ) ;
2005-04-17 02:20:36 +04:00
dev - > wakeup . flags . valid = 0 ;
2006-06-27 08:41:40 +04:00
return - 1 ;
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 ;
2006-06-27 08:41:40 +04:00
return - 1 ;
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 ;
device - > power . state = ACPI_STATE_UNKNOWN ;
if ( ! cl - > count & & ! tl - > count ) {
result = - ENODEV ;
goto end ;
}
/* 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 ;
}
/*
* 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 ;
}
/* We shouldn't change the state till all above operations succeed */
device - > power . state = state ;
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
if ( result )
2006-06-27 07:04:31 +04:00
printk ( KERN_WARNING PREFIX " Transitioning device [%s] to D%d \n " ,
device - > pnp . bus_id , 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 ;
int result = 0 ;
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 ;
2007-02-16 09:47:06 +03:00
result = acpi_power_get_state ( resource ) ;
if ( result )
goto end ;
2005-04-17 02:20:36 +04:00
seq_puts ( seq , " state: " ) ;
switch ( resource - > state ) {
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] */
entry = create_proc_entry ( ACPI_POWER_FILE_STATUS ,
2005-08-05 08:44:28 +04:00
S_IRUGO , acpi_device_dir ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-06-27 08:41:40 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
else {
entry - > proc_fops = & acpi_power_fops ;
entry - > data = acpi_driver_data ( device ) ;
}
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_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
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
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 ) ;
acpi_driver_data ( device ) = resource ;
/* 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 ;
result = acpi_power_get_state ( resource ) ;
if ( result )
goto end ;
switch ( resource - > state ) {
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 ) ,
2005-08-05 08:44:28 +04:00
acpi_device_bid ( device ) , resource - > 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 09:47:06 +03:00
static int acpi_power_resume ( struct acpi_device * device , int state )
{
int result = 0 ;
struct acpi_power_resource * resource = NULL ;
struct acpi_power_reference * ref ;
if ( ! device | | ! acpi_driver_data ( device ) )
return - EINVAL ;
resource = ( struct acpi_power_resource * ) acpi_driver_data ( device ) ;
result = acpi_power_get_state ( resource ) ;
if ( result )
return result ;
mutex_lock ( & resource - > resource_lock ) ;
if ( ( resource - > state = = ACPI_POWER_RESOURCE_STATE_ON ) & &
list_empty ( & resource - > reference ) ) {
mutex_unlock ( & resource - > resource_lock ) ;
result = acpi_power_off_device ( device - > handle , NULL ) ;
return result ;
}
if ( ( resource - > state = = ACPI_POWER_RESOURCE_STATE_OFF ) & &
! 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 ;
}
2005-08-05 08:44:28 +04:00
static 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
if ( acpi_disabled )
2006-06-27 08:41:40 +04:00
return 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
}
subsys_initcall ( acpi_power_init ) ;