2005-04-16 15:20:36 -07:00
/*
* pnpacpi - - PnP ACPI driver
*
* Copyright ( c ) 2004 Matthieu Castet < castet . matthieu @ free . fr >
* Copyright ( c ) 2004 Li Shaohua < shaohua . li @ intel . com >
2007-05-08 00:28:35 -07:00
*
2005-04-16 15:20:36 -07:00
* 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 , 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
*/
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
# include <linux/acpi.h>
# include <linux/pnp.h>
2007-07-23 14:43:51 +02:00
# include <linux/mod_devicetable.h>
2005-04-16 15:20:36 -07:00
# include <acpi/acpi_bus.h>
2007-07-23 14:43:51 +02:00
# include <acpi/actypes.h>
2008-04-28 16:33:51 -06:00
# include "../base.h"
2005-04-16 15:20:36 -07:00
# include "pnpacpi.h"
static int num = 0 ;
2006-01-06 01:31:00 -05:00
/* We need only to blacklist devices that have already an acpi driver that
* can ' t use pnp layer . We don ' t need to blacklist device that are directly
* used by the kernel ( PCI root , . . . ) , as it is harmless and there were
* already present in pnpbios . But there is an exception for devices that
* have irqs ( PIC , Timer ) because we call acpi_register_gsi .
2007-07-26 10:41:21 -07:00
* Finally , only devices that have a CRS method need to be in this list .
2006-01-06 01:31:00 -05:00
*/
2008-02-11 16:05:35 +01:00
static struct acpi_device_id excluded_id_list [ ] __initdata = {
2007-07-26 10:41:20 -07:00
{ " PNP0C09 " , 0 } , /* EC */
{ " PNP0C0F " , 0 } , /* Link device */
{ " PNP0000 " , 0 } , /* PIC */
{ " PNP0100 " , 0 } , /* Timer */
2007-07-23 14:43:51 +02:00
{ " " , 0 } ,
} ;
2008-04-29 22:52:01 +02:00
static inline int __init is_exclusive_device ( struct acpi_device * dev )
2005-04-16 15:20:36 -07:00
{
2007-07-23 14:43:51 +02:00
return ( ! acpi_match_device_ids ( dev , excluded_id_list ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Compatible Device IDs
*/
# define TEST_HEX(c) \
if ( ! ( ( ' 0 ' < = ( c ) & & ( c ) < = ' 9 ' ) | | ( ' A ' < = ( c ) & & ( c ) < = ' F ' ) ) ) \
return 0
# define TEST_ALPHA(c) \
if ( ! ( ' @ ' < = ( c ) | | ( c ) < = ' Z ' ) ) \
return 0
static int __init ispnpidacpi ( char * id )
{
TEST_ALPHA ( id [ 0 ] ) ;
TEST_ALPHA ( id [ 1 ] ) ;
TEST_ALPHA ( id [ 2 ] ) ;
TEST_HEX ( id [ 3 ] ) ;
TEST_HEX ( id [ 4 ] ) ;
TEST_HEX ( id [ 5 ] ) ;
TEST_HEX ( id [ 6 ] ) ;
if ( id [ 7 ] ! = ' \0 ' )
return 0 ;
return 1 ;
}
2008-04-28 16:34:05 -06:00
static int pnpacpi_get_resources ( struct pnp_dev * dev )
2005-04-16 15:20:36 -07:00
{
2008-04-28 16:34:07 -06:00
dev_dbg ( & dev - > dev , " get resources \n " ) ;
2008-04-28 16:34:39 -06:00
return pnpacpi_parse_allocated_resource ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2008-04-28 16:34:05 -06:00
static int pnpacpi_set_resources ( struct pnp_dev * dev )
2005-04-16 15:20:36 -07:00
{
acpi_handle handle = dev - > data ;
struct acpi_buffer buffer ;
2008-04-28 16:34:03 -06:00
int ret ;
2005-04-16 15:20:36 -07:00
acpi_status status ;
2008-04-28 16:34:07 -06:00
dev_dbg ( & dev - > dev , " set resources \n " ) ;
2008-04-28 16:34:03 -06:00
ret = pnpacpi_build_resource_template ( dev , & buffer ) ;
2005-04-16 15:20:36 -07:00
if ( ret )
return ret ;
2008-04-28 16:34:06 -06:00
ret = pnpacpi_encode_resources ( dev , & buffer ) ;
2005-04-16 15:20:36 -07:00
if ( ret ) {
kfree ( buffer . pointer ) ;
return ret ;
}
status = acpi_set_current_resources ( handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
ret = - EINVAL ;
kfree ( buffer . pointer ) ;
return ret ;
}
static int pnpacpi_disable_resources ( struct pnp_dev * dev )
{
acpi_status status ;
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
2007-07-26 10:41:20 -07:00
status = acpi_evaluate_object ( ( acpi_handle ) dev - > data ,
" _DIS " , NULL , NULL ) ;
2005-04-16 15:20:36 -07:00
return ACPI_FAILURE ( status ) ? - ENODEV : 0 ;
}
2007-07-28 03:33:16 -04:00
# ifdef CONFIG_ACPI_SLEEP
2007-07-20 10:03:20 +08:00
static int pnpacpi_suspend ( struct pnp_dev * dev , pm_message_t state )
{
2007-10-16 23:31:06 -07:00
int power_state ;
2008-06-05 01:15:40 +02:00
power_state = acpi_pm_device_sleep_state ( & dev - > dev , NULL ) ;
2007-10-16 23:31:06 -07:00
if ( power_state < 0 )
power_state = ( state . event = = PM_EVENT_ON ) ?
ACPI_STATE_D0 : ACPI_STATE_D3 ;
return acpi_bus_set_power ( ( acpi_handle ) dev - > data , power_state ) ;
2007-07-20 10:03:20 +08:00
}
static int pnpacpi_resume ( struct pnp_dev * dev )
{
2007-07-26 10:41:20 -07:00
return acpi_bus_set_power ( ( acpi_handle ) dev - > data , ACPI_STATE_D0 ) ;
2007-07-20 10:03:20 +08:00
}
2007-07-28 03:33:16 -04:00
# endif
2007-07-20 10:03:20 +08:00
2005-11-07 01:01:48 -08:00
static struct pnp_protocol pnpacpi_protocol = {
2007-07-26 10:41:21 -07:00
. name = " Plug and Play ACPI " ,
. get = pnpacpi_get_resources ,
. set = pnpacpi_set_resources ,
2005-04-16 15:20:36 -07:00
. disable = pnpacpi_disable_resources ,
2007-07-28 03:33:16 -04:00
# ifdef CONFIG_ACPI_SLEEP
2007-07-20 10:03:20 +08:00
. suspend = pnpacpi_suspend ,
. resume = pnpacpi_resume ,
2007-07-28 03:33:16 -04:00
# endif
2005-04-16 15:20:36 -07:00
} ;
static int __init pnpacpi_add_device ( struct acpi_device * device )
{
acpi_handle temp = NULL ;
acpi_status status ;
struct pnp_dev * dev ;
2006-01-06 01:31:00 -05:00
status = acpi_get_handle ( device - > handle , " _CRS " , & temp ) ;
if ( ACPI_FAILURE ( status ) | | ! ispnpidacpi ( acpi_device_hid ( device ) ) | |
2007-07-26 10:41:20 -07:00
is_exclusive_device ( device ) )
2005-04-16 15:20:36 -07:00
return 0 ;
2008-04-28 16:33:54 -06:00
dev = pnp_alloc_dev ( & pnpacpi_protocol , num , acpi_device_hid ( device ) ) ;
if ( ! dev )
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
2008-04-28 16:33:54 -06:00
2005-04-16 15:20:36 -07:00
dev - > data = device - > handle ;
2007-07-26 10:41:21 -07:00
/* .enabled means the device can decode the resources */
2005-04-16 15:20:36 -07:00
dev - > active = device - > status . enabled ;
status = acpi_get_handle ( device - > handle , " _SRS " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
dev - > capabilities | = PNP_CONFIGURABLE ;
dev - > capabilities | = PNP_READ ;
2008-01-12 19:37:49 -05:00
if ( device - > flags . dynamic_status & & ( dev - > capabilities & PNP_CONFIGURABLE ) )
2005-04-16 15:20:36 -07:00
dev - > capabilities | = PNP_WRITE ;
if ( device - > flags . removable )
dev - > capabilities | = PNP_REMOVABLE ;
status = acpi_get_handle ( device - > handle , " _DIS " , & temp ) ;
if ( ACPI_SUCCESS ( status ) )
dev - > capabilities | = PNP_DISABLE ;
if ( strlen ( acpi_device_name ( device ) ) )
strncpy ( dev - > name , acpi_device_name ( device ) , sizeof ( dev - > name ) ) ;
else
strncpy ( dev - > name , acpi_device_bid ( device ) , sizeof ( dev - > name ) ) ;
2008-04-28 16:34:39 -06:00
if ( dev - > active )
pnpacpi_parse_allocated_resource ( dev ) ;
2005-04-16 15:20:36 -07:00
2008-04-28 16:34:39 -06:00
if ( dev - > capabilities & PNP_CONFIGURABLE )
pnpacpi_parse_resource_option_data ( dev ) ;
2007-05-08 00:28:35 -07:00
2005-04-16 15:20:36 -07:00
if ( device - > flags . compatible_ids ) {
struct acpi_compatible_id_list * cid_list = device - > pnp . cid_list ;
int i ;
for ( i = 0 ; i < cid_list - > count ; i + + ) {
if ( ! ispnpidacpi ( cid_list - > id [ i ] . value ) )
continue ;
2008-04-28 16:33:52 -06:00
pnp_add_id ( dev , cid_list - > id [ i ] . value ) ;
2005-04-16 15:20:36 -07:00
}
}
/* clear out the damaged flags */
if ( ! dev - > active )
2008-04-28 16:34:09 -06:00
pnp_init_resources ( dev ) ;
2005-04-16 15:20:36 -07:00
pnp_add_device ( dev ) ;
2007-07-26 10:41:20 -07:00
num + + ;
2005-04-16 15:20:36 -07:00
return AE_OK ;
}
static acpi_status __init pnpacpi_add_device_handler ( acpi_handle handle ,
2007-07-26 10:41:20 -07:00
u32 lvl , void * context ,
void * * rv )
2005-04-16 15:20:36 -07:00
{
struct acpi_device * device ;
if ( ! acpi_bus_get_device ( handle , & device ) )
pnpacpi_add_device ( device ) ;
else
return AE_CTRL_DEPTH ;
return AE_OK ;
}
2007-05-08 00:28:35 -07:00
static int __init acpi_pnp_match ( struct device * dev , void * _pnp )
{
2007-07-26 10:41:20 -07:00
struct acpi_device * acpi = to_acpi_device ( dev ) ;
struct pnp_dev * pnp = _pnp ;
2007-05-08 00:28:35 -07:00
/* true means it matched */
return acpi - > flags . hardware_id
2007-07-26 10:41:20 -07:00
& & ! acpi_get_physical_device ( acpi - > handle )
& & compare_pnp_id ( pnp - > id , acpi - > pnp . hardware_id ) ;
2007-05-08 00:28:35 -07:00
}
2007-07-26 10:41:20 -07:00
static int __init acpi_pnp_find_device ( struct device * dev , acpi_handle * handle )
2007-05-08 00:28:35 -07:00
{
2007-07-26 10:41:20 -07:00
struct device * adev ;
struct acpi_device * acpi ;
2007-05-08 00:28:35 -07:00
adev = bus_find_device ( & acpi_bus_type , NULL ,
2007-07-26 10:41:20 -07:00
to_pnp_dev ( dev ) , acpi_pnp_match ) ;
2007-05-08 00:28:35 -07:00
if ( ! adev )
return - ENODEV ;
acpi = to_acpi_device ( adev ) ;
* handle = acpi - > handle ;
put_device ( adev ) ;
return 0 ;
}
/* complete initialization of a PNPACPI device includes having
* pnpdev - > dev . archdata . acpi_handle point to its ACPI sibling .
*/
static struct acpi_bus_type __initdata acpi_pnp_bus = {
2007-07-26 10:41:21 -07:00
. bus = & pnp_bus_type ,
2007-05-08 00:28:35 -07:00
. find_device = acpi_pnp_find_device ,
} ;
2005-04-16 15:20:36 -07:00
int pnpacpi_disabled __initdata ;
2005-11-07 01:01:48 -08:00
static int __init pnpacpi_init ( void )
2005-04-16 15:20:36 -07:00
{
if ( acpi_disabled | | pnpacpi_disabled ) {
pnp_info ( " PnP ACPI: disabled " ) ;
return 0 ;
}
pnp_info ( " PnP ACPI init " ) ;
pnp_register_protocol ( & pnpacpi_protocol ) ;
2007-05-08 00:28:35 -07:00
register_acpi_bus_type ( & acpi_pnp_bus ) ;
2005-04-16 15:20:36 -07:00
acpi_get_devices ( NULL , pnpacpi_add_device_handler , NULL , NULL ) ;
pnp_info ( " PnP ACPI: found %d devices " , num ) ;
2007-05-08 00:28:35 -07:00
unregister_acpi_bus_type ( & acpi_pnp_bus ) ;
2007-05-08 00:35:54 -07:00
pnp_platform_devices = 1 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-07-26 10:41:20 -07:00
2005-04-16 15:20:36 -07:00
subsys_initcall ( pnpacpi_init ) ;
static int __init pnpacpi_setup ( char * str )
{
if ( str = = NULL )
return 1 ;
if ( ! strncmp ( str , " off " , 3 ) )
pnpacpi_disabled = 1 ;
return 1 ;
}
2007-07-26 10:41:20 -07:00
2005-04-16 15:20:36 -07:00
__setup ( " pnpacpi= " , pnpacpi_setup ) ;