2005-04-16 15:20:36 -07: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 00:44:28 -04:00
ACPI_MODULE_NAME ( " acpi_power " )
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
static int acpi_power_add ( struct acpi_device * device ) ;
static int acpi_power_remove ( struct acpi_device * device , int type ) ;
2005-04-16 15:20:36 -07:00
static int acpi_power_open_fs ( struct inode * inode , struct file * file ) ;
static struct acpi_driver acpi_power_driver = {
2005-08-05 00: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 ,
} ,
2005-04-16 15:20:36 -07:00
} ;
2005-08-05 00:44:28 -04:00
struct acpi_power_resource {
2006-05-19 16:54:40 -04:00
struct acpi_device * device ;
2005-08-05 00:44:28 -04:00
acpi_bus_id name ;
u32 system_level ;
u32 order ;
int state ;
int references ;
2005-04-16 15:20:36 -07:00
} ;
2005-08-05 00:44:28 -04:00
static struct list_head acpi_power_resource_list ;
2005-04-16 15:20:36 -07:00
2006-07-04 13:06:00 -04:00
static const struct file_operations acpi_power_fops = {
2005-08-05 00:44:28 -04:00
. open = acpi_power_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-16 15:20:36 -07:00
} ;
/* --------------------------------------------------------------------------
Power Resource Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
2005-08-05 00:44:28 -04:00
acpi_power_get_context ( acpi_handle handle ,
struct acpi_power_resource * * resource )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
struct acpi_device * device = NULL ;
2005-04-16 15:20:36 -07:00
if ( ! resource )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
result = acpi_bus_get_device ( handle , & device ) ;
if ( result ) {
2006-06-26 23:04:31 -04:00
printk ( KERN_WARNING PREFIX " Getting context [%p] \n " , handle ) ;
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
* resource = ( struct acpi_power_resource * ) acpi_driver_data ( device ) ;
2005-04-16 15:20:36 -07:00
if ( ! resource )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_get_state ( struct acpi_power_resource * resource )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
acpi_status status = AE_OK ;
unsigned long sta = 0 ;
2005-04-16 15:20:36 -07:00
if ( ! resource )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2006-05-19 16:54:43 -04:00
status = acpi_evaluate_integer ( resource - > device - > handle , " _STA " , NULL , & sta ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
resource - > name , resource - > state ? " on " : " off " ) ) ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_get_list_state ( struct acpi_handle_list * list , int * state )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
2005-04-16 15:20:36 -07:00
struct acpi_power_resource * resource = NULL ;
2005-08-05 00:44:28 -04:00
u32 i = 0 ;
2005-04-16 15:20:36 -07:00
if ( ! list | | ! state )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
/* The state of the list is 'on' IFF all resources are 'on'. */
2005-08-05 00:44:28 -04:00
for ( i = 0 ; i < list - > count ; i + + ) {
2005-04-16 15:20:36 -07:00
result = acpi_power_get_context ( list - > handles [ i ] , & resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
* state ? " on " : " off " ) ) ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_on ( acpi_handle handle )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
acpi_status status = AE_OK ;
struct acpi_device * device = NULL ;
2005-04-16 15:20:36 -07:00
struct acpi_power_resource * resource = NULL ;
result = acpi_power_get_context ( handle , & resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
resource - > references + + ;
2005-08-05 00:44:28 -04:00
if ( ( resource - > references > 1 )
| | ( resource - > state = = ACPI_POWER_RESOURCE_STATE_ON ) ) {
2005-04-16 15:20:36 -07:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] already on \n " ,
2005-08-05 00:44:28 -04:00
resource - > name ) ) ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2006-05-19 16:54:43 -04:00
status = acpi_evaluate_object ( resource - > device - > handle , " _ON " , NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
if ( resource - > state ! = ACPI_POWER_RESOURCE_STATE_ON )
2006-06-27 00:41:40 -04:00
return - ENOEXEC ;
2005-04-16 15:20:36 -07:00
/* Update the power resource's _device_ power state */
2006-05-19 16:54:40 -04:00
device = resource - > device ;
resource - > device - > power . state = ACPI_STATE_D0 ;
2005-04-16 15:20:36 -07:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] turned on \n " ,
2005-08-05 00:44:28 -04:00
resource - > name ) ) ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_off_device ( acpi_handle handle )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
acpi_status status = AE_OK ;
struct acpi_device * device = NULL ;
2005-04-16 15:20:36 -07:00
struct acpi_power_resource * resource = NULL ;
result = acpi_power_get_context ( handle , & resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
if ( resource - > references )
resource - > references - - ;
if ( resource - > references ) {
2005-08-05 00:44:28 -04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Resource [%s] is still in use, dereferencing \n " ,
device - > pnp . bus_id ) ) ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
if ( resource - > state = = ACPI_POWER_RESOURCE_STATE_OFF ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] already off \n " ,
2005-08-05 00:44:28 -04:00
device - > pnp . bus_id ) ) ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2006-05-19 16:54:43 -04:00
status = acpi_evaluate_object ( resource - > device - > handle , " _OFF " , NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
result = acpi_power_get_state ( resource ) ;
if ( result )
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
if ( resource - > state ! = ACPI_POWER_RESOURCE_STATE_OFF )
2006-06-27 00:41:40 -04:00
return - ENOEXEC ;
2005-04-16 15:20:36 -07:00
/* Update the power resource's _device_ power state */
2006-05-19 16:54:40 -04:00
device = resource - > device ;
2005-04-16 15:20:36 -07:00
device - > power . state = ACPI_STATE_D3 ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Resource [%s] turned off \n " ,
2005-08-05 00:44:28 -04:00
resource - > name ) ) ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
int acpi_enable_wakeup_device_power ( struct acpi_device * dev )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00: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-16 15:20:36 -07:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2006-06-27 00:41:40 -04:00
return - 1 ;
2005-04-16 15:20:36 -07:00
arg . integer . value = 1 ;
/* Open power resource */
for ( i = 0 ; i < dev - > wakeup . resources . count ; i + + ) {
ret = acpi_power_on ( dev - > wakeup . resources . handles [ i ] ) ;
if ( ret ) {
2006-06-26 23:41:38 -04:00
printk ( KERN_ERR PREFIX " Transition power state \n " ) ;
2005-04-16 15:20:36 -07:00
dev - > wakeup . flags . valid = 0 ;
2006-06-27 00:41:40 -04:00
return - 1 ;
2005-04-16 15:20:36 -07:00
}
}
/* Execute PSW */
status = acpi_evaluate_object ( dev - > handle , " _PSW " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
2006-06-26 23:41:38 -04:00
printk ( KERN_ERR PREFIX " Evaluate _PSW \n " ) ;
2005-04-16 15:20:36 -07:00
dev - > wakeup . flags . valid = 0 ;
ret = - 1 ;
}
2006-06-27 00:41:40 -04:00
return ret ;
2005-04-16 15:20:36 -07:00
}
/*
* Shutdown a wakeup device , counterpart of above method
* 1. Disable _PSW ( power state wake )
* 2. Shutdown down the power resources
*/
2005-08-05 00:44:28 -04:00
int acpi_disable_wakeup_device_power ( struct acpi_device * dev )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00: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-16 15:20:36 -07:00
if ( ! dev | | ! dev - > wakeup . flags . valid )
2006-06-27 00:41:40 -04:00
return - 1 ;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
arg . integer . value = 0 ;
2005-04-16 15:20:36 -07:00
/* Execute PSW */
status = acpi_evaluate_object ( dev - > handle , " _PSW " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
2006-06-26 23:41:38 -04:00
printk ( KERN_ERR PREFIX " Evaluate _PSW \n " ) ;
2005-04-16 15:20:36 -07:00
dev - > wakeup . flags . valid = 0 ;
2006-06-27 00:41:40 -04:00
return - 1 ;
2005-04-16 15:20:36 -07:00
}
/* Close power resource */
for ( i = 0 ; i < dev - > wakeup . resources . count ; i + + ) {
ret = acpi_power_off_device ( dev - > wakeup . resources . handles [ i ] ) ;
if ( ret ) {
2006-06-26 23:41:38 -04:00
printk ( KERN_ERR PREFIX " Transition power state \n " ) ;
2005-04-16 15:20:36 -07:00
dev - > wakeup . flags . valid = 0 ;
2006-06-27 00:41:40 -04:00
return - 1 ;
2005-04-16 15:20:36 -07:00
}
}
2006-06-27 00:41:40 -04:00
return ret ;
2005-04-16 15:20:36 -07:00
}
/* --------------------------------------------------------------------------
Device Power Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 00:44:28 -04:00
int acpi_power_get_inferred_state ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
struct acpi_handle_list * list = NULL ;
int list_state = 0 ;
int i = 0 ;
2005-04-16 15:20:36 -07:00
if ( ! device )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
for ( i = ACPI_STATE_D0 ; i < ACPI_STATE_D3 ; i + + ) {
2005-04-16 15:20:36 -07: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 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
if ( list_state = = ACPI_POWER_RESOURCE_STATE_ON ) {
device - > power . state = i ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
}
device - > power . state = ACPI_STATE_D3 ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
int acpi_power_transition ( struct acpi_device * device , int state )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00: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-16 15:20:36 -07:00
if ( ! device | | ( state < ACPI_STATE_D0 ) | | ( state > ACPI_STATE_D3 ) )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
if ( ( device - > power . state < ACPI_STATE_D0 )
| | ( device - > power . state > ACPI_STATE_D3 ) )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
for ( i = 0 ; i < tl - > count ; i + + ) {
2005-04-16 15:20:36 -07:00
result = acpi_power_on ( tl - > handles [ i ] ) ;
if ( result )
goto end ;
}
/*
* Then we dereference all power resources used in the current list .
*/
2005-08-05 00:44:28 -04:00
for ( i = 0 ; i < cl - > count ; i + + ) {
2005-04-16 15:20:36 -07:00
result = acpi_power_off_device ( cl - > handles [ i ] ) ;
if ( result )
goto end ;
}
/* We shouldn't change the state till all above operations succeed */
device - > power . state = state ;
2005-08-05 00:44:28 -04:00
end :
2005-04-16 15:20:36 -07:00
if ( result )
2006-06-26 23:04:31 -04:00
printk ( KERN_WARNING PREFIX " Transitioning device [%s] to D%d \n " ,
device - > pnp . bus_id , state ) ;
2005-04-16 15:20:36 -07:00
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
}
/* --------------------------------------------------------------------------
FS Interface ( / proc )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 00:44:28 -04:00
static struct proc_dir_entry * acpi_power_dir ;
2005-04-16 15:20:36 -07:00
static int acpi_power_seq_show ( struct seq_file * seq , void * offset )
{
struct acpi_power_resource * resource = NULL ;
resource = ( struct acpi_power_resource * ) seq - > private ;
if ( ! resource )
goto end ;
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 ;
}
seq_printf ( seq , " system level: S%d \n "
2005-08-05 00:44:28 -04:00
" order: %d \n "
" reference count: %d \n " ,
resource - > system_level ,
resource - > order , resource - > references ) ;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
end :
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
static int acpi_power_add_fs ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
struct proc_dir_entry * entry = NULL ;
2005-04-16 15:20:36 -07:00
if ( ! device )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( ! acpi_device_dir ( device ) ) {
acpi_device_dir ( device ) = proc_mkdir ( acpi_device_bid ( device ) ,
2005-08-05 00:44:28 -04:00
acpi_power_dir ) ;
2005-04-16 15:20:36 -07:00
if ( ! acpi_device_dir ( device ) )
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
/* 'status' [R] */
entry = create_proc_entry ( ACPI_POWER_FILE_STATUS ,
2005-08-05 00:44:28 -04:00
S_IRUGO , acpi_device_dir ( device ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! entry )
2006-06-27 00:41:40 -04:00
return - EIO ;
2005-04-16 15:20:36 -07:00
else {
entry - > proc_fops = & acpi_power_fops ;
entry - > data = acpi_driver_data ( device ) ;
}
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_remove_fs ( struct acpi_device * device )
2005-04-16 15:20:36 -07: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 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 00:44:28 -04:00
static int acpi_power_add ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
acpi_status status = AE_OK ;
2005-04-16 15:20:36 -07:00
struct acpi_power_resource * resource = NULL ;
2005-08-05 00:44:28 -04:00
union acpi_object acpi_object ;
struct acpi_buffer buffer = { sizeof ( acpi_object ) , & acpi_object } ;
2005-04-16 15:20:36 -07:00
if ( ! device )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
resource = kmalloc ( sizeof ( struct acpi_power_resource ) , GFP_KERNEL ) ;
if ( ! resource )
2006-06-27 00:41:40 -04:00
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
memset ( resource , 0 , sizeof ( struct acpi_power_resource ) ) ;
2006-05-19 16:54:40 -04:00
resource - > device = device ;
2005-04-16 15:20:36 -07: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-19 16:54:43 -04:00
status = acpi_evaluate_object ( device - > handle , NULL , NULL , & buffer ) ;
2005-04-16 15:20:36 -07: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 00:44:28 -04:00
2005-04-16 15:20:36 -07:00
printk ( KERN_INFO PREFIX " %s [%s] (%s) \n " , acpi_device_name ( device ) ,
2005-08-05 00:44:28 -04:00
acpi_device_bid ( device ) , resource - > state ? " on " : " off " ) ;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
end :
2005-04-16 15:20:36 -07:00
if ( result )
kfree ( resource ) ;
2005-08-05 00:44:28 -04:00
2006-06-27 00:41:40 -04:00
return result ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int acpi_power_remove ( struct acpi_device * device , int type )
2005-04-16 15:20:36 -07:00
{
struct acpi_power_resource * resource = NULL ;
if ( ! device | | ! acpi_driver_data ( device ) )
2006-06-27 00:41:40 -04:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2005-08-05 00:44:28 -04:00
resource = ( struct acpi_power_resource * ) acpi_driver_data ( device ) ;
2005-04-16 15:20:36 -07:00
acpi_power_remove_fs ( device ) ;
kfree ( resource ) ;
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
static int __init acpi_power_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-05 00:44:28 -04:00
int result = 0 ;
2005-04-16 15:20:36 -07:00
if ( acpi_disabled )
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07: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 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
result = acpi_bus_register_driver ( & acpi_power_driver ) ;
if ( result < 0 ) {
remove_proc_entry ( ACPI_POWER_CLASS , acpi_root_dir ) ;
2006-06-27 00:41:40 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
2006-06-27 00:41:40 -04:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
subsys_initcall ( acpi_power_init ) ;