2005-04-17 02:20:36 +04:00
/*
* acpi_bus . c - ACPI Bus Driver ( $ Revision : 80 $ )
*
* 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 .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/ioport.h>
2006-07-12 09:47:00 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# include <linux/list.h>
# include <linux/sched.h>
# include <linux/pm.h>
# include <linux/device.h>
# include <linux/proc_fs.h>
2008-02-10 01:24:08 +03:00
# include <linux/acpi.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_X86
# include <asm/mpspec.h>
# endif
2008-02-15 12:27:20 +03:00
# include <linux/pci.h>
2005-04-17 02:20:36 +04:00
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
2009-03-13 21:08:26 +03:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
# define _COMPONENT ACPI_BUS_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " bus " ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
struct acpi_device * acpi_root ;
struct proc_dir_entry * acpi_root_dir ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_root_dir ) ;
# define STRUCT_TO_INT(s) (*((int*)&s))
2008-08-11 10:59:59 +04:00
static int set_power_nocheck ( const struct dmi_system_id * id )
{
printk ( KERN_NOTICE PREFIX " %s detected - "
" disable power check in power transistion \n " , id - > ident ) ;
acpi_power_nocheck = 1 ;
return 0 ;
}
static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table [ ] = {
{
set_power_nocheck , " HP Pavilion 05 " , {
DMI_MATCH ( DMI_BIOS_VENDOR , " Phoenix Technologies LTD " ) ,
DMI_MATCH ( DMI_SYS_VENDOR , " HP Pavilion 05 " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " 2001211RE101GLEND " ) } , NULL } ,
{ } ,
} ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Device Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
int acpi_bus_get_device ( acpi_handle handle , struct acpi_device * * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
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
/* TBD: Support fixed-feature devices */
2005-08-05 08:44:28 +04:00
status = acpi_get_data ( handle , acpi_bus_data_handler , ( void * * ) device ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) | | ! * device ) {
2006-07-25 21:30:57 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " No context for object [%p] \n " ,
handle ) ) ;
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
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( acpi_bus_get_device ) ;
2005-08-05 08:44:28 +04:00
int acpi_bus_get_status ( struct acpi_device * device )
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 ;
2005-08-05 08:44:28 +04:00
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
/*
* Evaluate _STA if present .
*/
if ( device - > flags . dynamic_status ) {
2005-08-05 08:44:28 +04:00
status =
acpi_evaluate_integer ( 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-08-05 08:44:28 +04:00
STRUCT_TO_INT ( device - > status ) = ( int ) sta ;
2005-04-17 02:20:36 +04:00
}
/*
2008-08-11 09:40:22 +04:00
* According to ACPI spec some device can be present and functional
* even if the parent is not present but functional .
* In such conditions the child device should not inherit the status
* from the parent .
2005-04-17 02:20:36 +04:00
*/
else
2007-04-25 22:20:58 +04:00
STRUCT_TO_INT ( device - > status ) =
ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING ;
2005-04-17 02:20:36 +04:00
if ( device - > status . functional & & ! device - > status . present ) {
2008-08-11 09:40:22 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] status [%08x]: "
" functional but not present; \n " ,
device - > pnp . bus_id ,
( u32 ) STRUCT_TO_INT ( device - > status ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] status [%08x] \n " ,
device - > pnp . bus_id ,
( u32 ) STRUCT_TO_INT ( device - > status ) ) ) ;
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
EXPORT_SYMBOL ( acpi_bus_get_status ) ;
2005-04-17 02:20:36 +04:00
2008-01-17 10:51:21 +03:00
void acpi_bus_private_data_handler ( acpi_handle handle ,
u32 function , void * context )
{
return ;
}
EXPORT_SYMBOL ( acpi_bus_private_data_handler ) ;
int acpi_bus_get_private_data ( acpi_handle handle , void * * data )
{
acpi_status status = AE_OK ;
if ( ! * data )
return - EINVAL ;
status = acpi_get_data ( handle , acpi_bus_private_data_handler , data ) ;
if ( ACPI_FAILURE ( status ) | | ! * data ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " No context for object [%p] \n " ,
handle ) ) ;
return - ENODEV ;
}
return 0 ;
}
EXPORT_SYMBOL ( acpi_bus_get_private_data ) ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Power Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
int acpi_bus_get_power ( acpi_handle handle , int * state )
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_device * device = NULL ;
2008-10-10 10:22:59 +04:00
unsigned long long psc = 0 ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_get_device ( handle , & device ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
* state = ACPI_STATE_UNKNOWN ;
if ( ! device - > flags . power_manageable ) {
/* TBD: Non-recursive algorithm for walking up hierarchy */
if ( device - > parent )
* state = device - > parent - > power . state ;
else
* state = ACPI_STATE_D0 ;
2005-08-05 08:44:28 +04:00
} else {
2005-04-17 02:20:36 +04:00
/*
2007-02-10 09:32:16 +03:00
* Get the device ' s power state either directly ( via _PSC ) or
2005-04-17 02:20:36 +04:00
* indirectly ( via power resources ) .
*/
if ( device - > power . flags . explicit_get ) {
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_integer ( device - > handle , " _PSC " ,
NULL , & psc ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-08-05 08:44:28 +04:00
device - > power . state = ( int ) psc ;
} else if ( device - > power . flags . power_resources ) {
2005-04-17 02:20:36 +04:00
result = acpi_power_get_inferred_state ( device ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
* state = device - > power . state ;
}
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device [%s] power state is D%d \n " ,
2005-08-05 08:44:28 +04:00
device - > pnp . bus_id , device - > power . state ) ) ;
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
EXPORT_SYMBOL ( acpi_bus_get_power ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
int acpi_bus_set_power ( acpi_handle handle , int state )
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_device * device = NULL ;
char object_name [ 5 ] = { ' _ ' , ' P ' , ' S ' , ' 0 ' + state , ' \0 ' } ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_get_device ( handle , & device ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
if ( ( 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
/* Make sure this is a valid target state */
if ( ! device - > flags . power_manageable ) {
2006-07-25 21:30:57 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device `[%s]' is not power manageable \n " ,
2007-09-13 02:06:57 +04:00
kobject_name ( & device - > dev . kobj ) ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-03-19 08:16:18 +03:00
/*
2007-10-22 14:19:09 +04:00
* Get device ' s current power state
2005-03-19 08:16:18 +03:00
*/
2008-08-11 10:57:50 +04:00
if ( ! acpi_power_nocheck ) {
/*
* Maybe the incorrect power state is returned on the bogus
* bios , which is different with the real power state .
* For example : the bios returns D0 state and the real power
* state is D3 . OS expects to set the device to D0 state . In
* such case if OS uses the power state returned by the BIOS ,
* the device can ' t be transisted to the correct power state .
* So if the acpi_power_nocheck is set , it is unnecessary to
* get the power state by calling acpi_bus_get_power .
*/
acpi_bus_get_power ( device - > handle , & device - > power . state ) ;
}
2008-01-24 06:41:20 +03:00
if ( ( state = = device - > power . state ) & & ! device - > flags . force_power_state ) {
2007-02-16 10:23:07 +03:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device is already at D%d \n " ,
state ) ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-16 10:23:07 +03:00
2005-04-17 02:20:36 +04:00
if ( ! device - > power . states [ state ] . flags . valid ) {
2006-06-27 07:04:31 +04:00
printk ( KERN_WARNING PREFIX " Device does not support D%d \n " , state ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
if ( device - > parent & & ( state < device - > parent - > power . state ) ) {
2006-06-27 07:04:31 +04:00
printk ( KERN_WARNING PREFIX
2006-06-27 07:58:43 +04:00
" Cannot set device to a higher-powered "
2006-06-27 07:04:31 +04:00
" state than parent \n " ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
/*
* Transition Power
* - - - - - - - - - - - - - - - -
* On transitions to a high - powered state we first apply power ( via
* power resources ) then evalute _PSx . Conversly for transitions to
* a lower - powered state .
2005-03-19 08:16:18 +03:00
*/
2005-04-17 02:20:36 +04:00
if ( state < device - > power . state ) {
if ( device - > power . flags . power_resources ) {
result = acpi_power_transition ( device , state ) ;
if ( result )
goto end ;
}
if ( device - > power . states [ state ] . flags . explicit_set ) {
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_object ( device - > handle ,
object_name , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
result = - ENODEV ;
goto end ;
}
}
2005-08-05 08:44:28 +04:00
} else {
2005-04-17 02:20:36 +04:00
if ( device - > power . states [ state ] . flags . explicit_set ) {
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_object ( device - > handle ,
object_name , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
result = - ENODEV ;
goto end ;
}
}
if ( device - > power . flags . power_resources ) {
result = acpi_power_transition ( device , state ) ;
if ( result )
goto end ;
}
}
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
2009-05-08 08:07:30 +04:00
" Device [%s] failed to transition to D%d \n " ,
2006-06-27 07:04:31 +04:00
device - > pnp . bus_id , state ) ;
2007-10-12 01:53:58 +04:00
else {
device - > power . state = state ;
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Device [%s] transitioned to D%d \n " ,
device - > pnp . bus_id , state ) ) ;
2007-10-12 01:53:58 +04:00
}
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
EXPORT_SYMBOL ( acpi_bus_set_power ) ;
2005-04-17 02:20:36 +04:00
2008-07-07 05:30:55 +04:00
bool acpi_bus_power_manageable ( acpi_handle handle )
{
struct acpi_device * device ;
int result ;
result = acpi_bus_get_device ( handle , & device ) ;
return result ? false : device - > flags . power_manageable ;
}
EXPORT_SYMBOL ( acpi_bus_power_manageable ) ;
2008-07-07 05:34:48 +04:00
bool acpi_bus_can_wakeup ( acpi_handle handle )
{
struct acpi_device * device ;
int result ;
result = acpi_bus_get_device ( handle , & device ) ;
return result ? false : device - > wakeup . flags . valid ;
}
EXPORT_SYMBOL ( acpi_bus_can_wakeup ) ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Event Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2007-08-23 23:20:26 +04:00
# ifdef CONFIG_ACPI_PROC_EVENT
2005-04-17 02:20:36 +04:00
static DEFINE_SPINLOCK ( acpi_bus_event_lock ) ;
LIST_HEAD ( acpi_bus_event_list ) ;
DECLARE_WAIT_QUEUE_HEAD ( acpi_bus_event_queue ) ;
2005-08-05 08:44:28 +04:00
extern int event_is_open ;
2005-04-17 02:20:36 +04:00
2007-09-26 19:43:16 +04:00
int acpi_bus_generate_proc_event4 ( const char * device_class , const char * bus_id , u8 type , int data )
2005-04-17 02:20:36 +04:00
{
2007-09-26 19:43:16 +04:00
struct acpi_bus_event * event ;
2005-08-05 08:44:28 +04:00
unsigned long flags = 0 ;
2005-04-17 02:20:36 +04:00
/* drop event on the floor if no one's listening */
if ( ! event_is_open )
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
event = kmalloc ( sizeof ( struct acpi_bus_event ) , GFP_ATOMIC ) ;
if ( ! event )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-09-26 19:43:16 +04:00
strcpy ( event - > device_class , device_class ) ;
strcpy ( event - > bus_id , bus_id ) ;
2005-04-17 02:20:36 +04:00
event - > type = type ;
event - > data = data ;
spin_lock_irqsave ( & acpi_bus_event_lock , flags ) ;
list_add_tail ( & event - > node , & acpi_bus_event_list ) ;
spin_unlock_irqrestore ( & acpi_bus_event_lock , flags ) ;
wake_up_interruptible ( & acpi_bus_event_queue ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2007-09-26 19:43:16 +04:00
}
EXPORT_SYMBOL_GPL ( acpi_bus_generate_proc_event4 ) ;
int acpi_bus_generate_proc_event ( struct acpi_device * device , u8 type , int data )
{
if ( ! device )
return - EINVAL ;
return acpi_bus_generate_proc_event4 ( device - > pnp . device_class ,
device - > pnp . bus_id , type , data ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
2007-08-23 23:20:26 +04:00
EXPORT_SYMBOL ( acpi_bus_generate_proc_event ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
int acpi_bus_receive_event ( struct acpi_bus_event * event )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
unsigned long flags = 0 ;
struct acpi_bus_event * entry = NULL ;
2005-04-17 02:20:36 +04:00
DECLARE_WAITQUEUE ( wait , current ) ;
if ( ! event )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( list_empty ( & acpi_bus_event_list ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
add_wait_queue ( & acpi_bus_event_queue , & wait ) ;
if ( list_empty ( & acpi_bus_event_list ) )
schedule ( ) ;
remove_wait_queue ( & acpi_bus_event_queue , & wait ) ;
set_current_state ( TASK_RUNNING ) ;
if ( signal_pending ( current ) )
2006-06-27 08:41:40 +04:00
return - ERESTARTSYS ;
2005-04-17 02:20:36 +04:00
}
spin_lock_irqsave ( & acpi_bus_event_lock , flags ) ;
2008-04-16 01:34:47 +04:00
if ( ! list_empty ( & acpi_bus_event_list ) ) {
entry = list_entry ( acpi_bus_event_list . next ,
struct acpi_bus_event , node ) ;
2005-04-17 02:20:36 +04:00
list_del ( & entry - > node ) ;
2008-04-16 01:34:47 +04:00
}
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & acpi_bus_event_lock , flags ) ;
if ( ! entry )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
memcpy ( event , entry , sizeof ( struct acpi_bus_event ) ) ;
kfree ( entry ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-08-23 23:20:26 +04:00
# endif /* CONFIG_ACPI_PROC_EVENT */
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Notification Handling
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
2005-08-05 08:44:28 +04:00
acpi_bus_check_device ( struct acpi_device * device , int * status_changed )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = 0 ;
2005-04-17 02:20:36 +04:00
struct acpi_device_status old_status ;
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( status_changed )
* status_changed = 0 ;
old_status = device - > status ;
/*
* Make sure this device ' s parent is present before we go about
* messing with the device .
*/
if ( device - > parent & & ! device - > parent - > status . present ) {
device - > status = device - > parent - > status ;
if ( STRUCT_TO_INT ( old_status ) ! = STRUCT_TO_INT ( device - > status ) ) {
if ( status_changed )
* status_changed = 1 ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
status = acpi_bus_get_status ( device ) ;
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( STRUCT_TO_INT ( old_status ) = = STRUCT_TO_INT ( device - > status ) )
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
if ( status_changed )
* status_changed = 1 ;
2005-08-05 08:44:28 +04:00
2005-04-17 02:20:36 +04:00
/*
* Device Insertion / Removal
*/
if ( ( device - > status . present ) & & ! ( old_status . present ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device insertion detected \n " ) ) ;
/* TBD: Handle device insertion */
2005-08-05 08:44:28 +04:00
} else if ( ! ( device - > status . present ) & & ( old_status . present ) ) {
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Device removal detected \n " ) ) ;
/* TBD: Handle device removal */
}
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_bus_check_scope ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
int status_changed = 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
/* Status Change? */
result = acpi_bus_check_device ( device , & status_changed ) ;
if ( result )
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
if ( ! status_changed )
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
/*
* TBD : Enumerate child devices within this device ' s scope and
* run acpi_bus_check_device ( ) ' s on them .
*/
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-08-28 06:04:29 +04:00
static BLOCKING_NOTIFIER_HEAD ( acpi_bus_notify_list ) ;
int register_acpi_bus_notifier ( struct notifier_block * nb )
{
return blocking_notifier_chain_register ( & acpi_bus_notify_list , nb ) ;
}
EXPORT_SYMBOL_GPL ( register_acpi_bus_notifier ) ;
void unregister_acpi_bus_notifier ( struct notifier_block * nb )
{
blocking_notifier_chain_unregister ( & acpi_bus_notify_list , nb ) ;
}
EXPORT_SYMBOL_GPL ( unregister_acpi_bus_notifier ) ;
2005-04-17 02:20:36 +04:00
/**
* acpi_bus_notify
* - - - - - - - - - - - - - - -
* Callback for all ' system - level ' device notifications ( values 0x00 - 0x7F ) .
*/
2005-08-05 08:44:28 +04:00
static void acpi_bus_notify ( acpi_handle handle , u32 type , void * data )
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
2008-08-28 06:04:29 +04:00
blocking_notifier_call_chain ( & acpi_bus_notify_list ,
type , ( void * ) handle ) ;
2005-04-17 02:20:36 +04:00
if ( acpi_bus_get_device ( handle , & device ) )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
switch ( type ) {
case ACPI_NOTIFY_BUS_CHECK :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received BUS CHECK notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_check_scope ( device ) ;
2007-02-10 09:32:16 +03:00
/*
2005-04-17 02:20:36 +04:00
* TBD : We ' ll need to outsource certain events to non - ACPI
2005-08-05 08:44:28 +04:00
* drivers via the device manager ( device . c ) .
2005-04-17 02:20:36 +04:00
*/
break ;
case ACPI_NOTIFY_DEVICE_CHECK :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received DEVICE CHECK notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_check_device ( device , NULL ) ;
2007-02-10 09:32:16 +03:00
/*
2005-04-17 02:20:36 +04:00
* TBD : We ' ll need to outsource certain events to non - ACPI
2005-08-05 08:44:28 +04:00
* drivers via the device manager ( device . c ) .
2005-04-17 02:20:36 +04:00
*/
break ;
case ACPI_NOTIFY_DEVICE_WAKE :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received DEVICE WAKE notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD */
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received EJECT REQUEST notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD */
break ;
case ACPI_NOTIFY_DEVICE_CHECK_LIGHT :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received DEVICE CHECK LIGHT notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD: Exactly what does 'light' mean? */
break ;
case ACPI_NOTIFY_FREQUENCY_MISMATCH :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received FREQUENCY MISMATCH notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD */
break ;
case ACPI_NOTIFY_BUS_MODE_MISMATCH :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received BUS MODE MISMATCH notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD */
break ;
case ACPI_NOTIFY_POWER_FAULT :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received POWER FAULT notification for device [%s] \n " ,
device - > pnp . bus_id ) ) ;
2005-04-17 02:20:36 +04:00
/* TBD */
break ;
default :
2005-08-05 08:44:28 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
" Received unknown/unsupported notification [%08x] \n " ,
type ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Initialization / Cleanup
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-05 08:44:28 +04:00
static int __init acpi_bus_init_irq ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
union acpi_object arg = { ACPI_TYPE_INTEGER } ;
struct acpi_object_list arg_list = { 1 , & arg } ;
char * message = NULL ;
2005-04-17 02:20:36 +04:00
2007-02-10 09:32:16 +03:00
/*
2005-04-17 02:20:36 +04:00
* Let the system know what interrupt model we are using by
* evaluating the \ _PIC object , if exists .
*/
switch ( acpi_irq_model ) {
case ACPI_IRQ_MODEL_PIC :
message = " PIC " ;
break ;
case ACPI_IRQ_MODEL_IOAPIC :
message = " IOAPIC " ;
break ;
case ACPI_IRQ_MODEL_IOSAPIC :
message = " IOSAPIC " ;
break ;
2006-12-22 20:50:04 +03:00
case ACPI_IRQ_MODEL_PLATFORM :
message = " platform specific model " ;
break ;
2005-04-17 02:20:36 +04:00
default :
printk ( KERN_WARNING PREFIX " Unknown interrupt routing model \n " ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
printk ( KERN_INFO PREFIX " Using %s for interrupt routing \n " , message ) ;
arg . integer . value = acpi_irq_model ;
status = acpi_evaluate_object ( NULL , " \\ _PIC " , & arg_list , NULL ) ;
if ( ACPI_FAILURE ( status ) & & ( status ! = AE_NOT_FOUND ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Evaluating _PIC " ) ) ;
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-06-10 09:42:13 +04:00
u8 acpi_gbl_permanent_mmap ;
2007-02-02 19:48:19 +03:00
2005-08-05 08:44:28 +04:00
void __init acpi_early_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
acpi_status status = AE_OK ;
2005-04-17 02:20:36 +04:00
if ( acpi_disabled )
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
2006-03-18 00:44:00 +03:00
printk ( KERN_INFO PREFIX " Core revision %08x \n " , ACPI_CA_VERSION ) ;
2005-04-17 02:20:36 +04:00
/* enable workarounds, unless strict ACPI spec. compliance */
if ( ! acpi_strict )
acpi_gbl_enable_interpreter_slack = TRUE ;
2007-02-02 19:48:19 +03:00
acpi_gbl_permanent_mmap = 1 ;
status = acpi_reallocate_root_table ( ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_ERR PREFIX
" Unable to reallocate ACPI tables \n " ) ;
goto error0 ;
}
2005-04-17 02:20:36 +04:00
status = acpi_initialize_subsystem ( ) ;
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_ERR PREFIX
" Unable to initialize the ACPI Interpreter \n " ) ;
2005-04-17 02:20:36 +04:00
goto error0 ;
}
status = acpi_load_tables ( ) ;
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_ERR PREFIX
" Unable to load the System Description Tables \n " ) ;
2005-04-17 02:20:36 +04:00
goto error0 ;
}
# ifdef CONFIG_X86
if ( ! acpi_ioapic ) {
/* compatible (0) means level (3) */
2007-02-02 19:48:22 +03:00
if ( ! ( acpi_sci_flags & ACPI_MADT_TRIGGER_MASK ) ) {
acpi_sci_flags & = ~ ACPI_MADT_TRIGGER_MASK ;
acpi_sci_flags | = ACPI_MADT_TRIGGER_LEVEL ;
}
2005-04-17 02:20:36 +04:00
/* Set PIC-mode SCI trigger type */
2007-02-02 19:48:22 +03:00
acpi_pic_sci_set_trigger ( acpi_gbl_FADT . sci_interrupt ,
2007-02-02 19:48:22 +03:00
( acpi_sci_flags & ACPI_MADT_TRIGGER_MASK ) > > 2 ) ;
2005-04-17 02:20:36 +04:00
} else {
/*
2007-02-02 19:48:22 +03:00
* now that acpi_gbl_FADT is initialized ,
2005-04-17 02:20:36 +04:00
* update it with result from INT_SRC_OVR parsing
*/
2007-02-02 19:48:22 +03:00
acpi_gbl_FADT . sci_interrupt = acpi_sci_override_gsi ;
2005-04-17 02:20:36 +04:00
}
# endif
2005-08-05 08:44:28 +04:00
status =
acpi_enable_subsystem ( ~
( ACPI_NO_HARDWARE_INIT |
ACPI_NO_ACPI_ENABLE ) ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_ERR PREFIX " Unable to enable ACPI \n " ) ;
goto error0 ;
}
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
error0 :
2005-04-17 02:20:36 +04:00
disable_acpi ( ) ;
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 int __init acpi_bus_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
extern acpi_status acpi_os_initialize1 ( void ) ;
2005-04-17 02:20:36 +04:00
2009-03-04 22:55:27 +03:00
acpi_os_initialize1 ( ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
status =
acpi_enable_subsystem ( ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_ERR PREFIX
" Unable to start the ACPI Interpreter \n " ) ;
2005-04-17 02:20:36 +04:00
goto error1 ;
}
/*
* ACPI 2.0 requires the EC driver to be loaded and work before
* the EC device is found in the namespace ( i . e . before acpi_initialize_objects ( )
* is called ) .
*
2007-02-10 09:32:16 +03:00
* This is accomplished by looking for the ECDT table , and getting
2005-04-17 02:20:36 +04:00
* the EC parameters out of that .
*/
status = acpi_ec_ecdt_probe ( ) ;
/* Ignore result. Not having an ECDT is not fatal. */
status = acpi_initialize_objects ( ACPI_FULL_INITIALIZATION ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( KERN_ERR PREFIX " Unable to initialize ACPI objects \n " ) ;
goto error1 ;
}
2008-10-06 06:31:36 +04:00
/*
* Maybe EC region is required at bus_scan / acpi_get_devices . So it
* is necessary to enable it as early as possible .
*/
acpi_boot_ec_enable ( ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " Interpreter enabled \n " ) ;
2007-02-10 09:32:16 +03:00
/* Initialize sleep structures */
acpi_sleep_init ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Get the system interrupt model and evaluate \ _PIC .
*/
result = acpi_bus_init_irq ( ) ;
if ( result )
goto error1 ;
/*
* Register the for all standard device notifications .
*/
2005-08-05 08:44:28 +04:00
status =
acpi_install_notify_handler ( ACPI_ROOT_OBJECT , ACPI_SYSTEM_NOTIFY ,
& acpi_bus_notify , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_ERR PREFIX
" Unable to register for device notifications \n " ) ;
2005-04-17 02:20:36 +04:00
goto error1 ;
}
/*
* Create the top ACPI proc directory
*/
acpi_root_dir = proc_mkdir ( ACPI_BUS_FILE_ROOT , NULL ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
/* Mimic structured exception handling */
2005-08-05 08:44:28 +04:00
error1 :
2005-04-17 02:20:36 +04:00
acpi_terminate ( ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2007-11-03 02:19:59 +03:00
struct kobject * acpi_kobj ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
static int __init acpi_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 ) {
printk ( KERN_INFO PREFIX " Interpreter disabled. \n " ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2007-11-06 00:16:15 +03:00
acpi_kobj = kobject_create_and_add ( " acpi " , firmware_kobj ) ;
2007-11-03 02:19:59 +03:00
if ( ! acpi_kobj ) {
2008-03-06 05:24:51 +03:00
printk ( KERN_WARNING " %s: kset create error \n " , __func__ ) ;
2007-11-03 02:19:59 +03:00
acpi_kobj = NULL ;
}
2005-04-17 02:20:36 +04:00
2009-03-25 01:50:09 +03:00
init_acpi_device_notify ( ) ;
2005-04-17 02:20:36 +04:00
result = acpi_bus_init ( ) ;
if ( ! result ) {
2008-02-15 12:27:20 +03:00
pci_mmcfg_late_init ( ) ;
2007-12-14 01:38:03 +03:00
if ( ! ( pm_flags & PM_APM ) )
pm_flags | = PM_ACPI ;
2005-04-17 02:20:36 +04:00
else {
2005-08-05 08:44:28 +04:00
printk ( KERN_INFO PREFIX
" APM is already active, exiting \n " ) ;
2005-04-17 02:20:36 +04:00
disable_acpi ( ) ;
result = - ENODEV ;
}
} else
disable_acpi ( ) ;
2009-03-25 01:49:38 +03:00
if ( acpi_disabled )
return result ;
2008-08-11 10:59:59 +04:00
/*
* If the laptop falls into the DMI check table , the power state check
* will be disabled in the course of device power transistion .
*/
dmi_check_system ( power_nocheck_dmi_table ) ;
2009-03-25 01:49:43 +03:00
acpi_scan_init ( ) ;
2009-03-25 01:49:48 +03:00
acpi_ec_init ( ) ;
2009-03-25 01:49:53 +03:00
acpi_power_init ( ) ;
2009-03-25 01:49:58 +03:00
acpi_system_init ( ) ;
2009-03-25 01:50:03 +03:00
acpi_debug_init ( ) ;
2009-03-25 01:50:14 +03:00
acpi_sleep_proc_init ( ) ;
2009-03-25 01:50:19 +03:00
acpi_wakeup_device_init ( ) ;
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
subsys_initcall ( acpi_init ) ;