2005-04-17 02:20:36 +04:00
/*
* acpi_ec . c - ACPI Embedded Controller Driver ( $ Revision : 38 $ )
*
* Copyright ( C ) 2004 Luming Yu < luming . yu @ intel . com >
* 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 .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/delay.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
2005-03-19 09:10:05 +03:00
# include <linux/interrupt.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
# include <acpi/actypes.h>
# define _COMPONENT ACPI_EC_COMPONENT
2005-08-12 01:32:05 +04:00
ACPI_MODULE_NAME ( " acpi_ec " )
2005-04-17 02:20:36 +04:00
# define ACPI_EC_COMPONENT 0x00100000
# define ACPI_EC_CLASS "embedded_controller"
# define ACPI_EC_HID "PNP0C09"
# define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver"
# define ACPI_EC_DEVICE_NAME "Embedded Controller"
# define ACPI_EC_FILE_INFO "info"
# define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
# define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
2005-03-19 09:10:05 +03:00
# define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
2005-04-17 02:20:36 +04:00
# define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
# define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */
# define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */
2005-03-19 09:10:05 +03:00
# define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */
2005-04-17 02:20:36 +04:00
# define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
2005-08-12 01:32:05 +04:00
# define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */
# define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */
2005-04-17 02:20:36 +04:00
# define ACPI_EC_COMMAND_READ 0x80
# define ACPI_EC_COMMAND_WRITE 0x81
2005-03-19 09:10:05 +03:00
# define ACPI_EC_BURST_ENABLE 0x82
# define ACPI_EC_BURST_DISABLE 0x83
2005-04-17 02:20:36 +04:00
# define ACPI_EC_COMMAND_QUERY 0x84
2005-07-23 12:08:00 +04:00
# define EC_POLLING 0xFF
# define EC_BURST 0x00
2005-08-12 01:32:05 +04:00
static int acpi_ec_remove ( struct acpi_device * device , int type ) ;
static int acpi_ec_start ( struct acpi_device * device ) ;
static int acpi_ec_stop ( struct acpi_device * device , int type ) ;
static int acpi_ec_burst_add ( struct acpi_device * device ) ;
static int acpi_ec_polling_add ( struct acpi_device * device ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_ec_driver = {
2005-08-12 01:32:05 +04:00
. name = ACPI_EC_DRIVER_NAME ,
. class = ACPI_EC_CLASS ,
. ids = ACPI_EC_HID ,
. ops = {
. add = acpi_ec_polling_add ,
. remove = acpi_ec_remove ,
. start = acpi_ec_start ,
. stop = acpi_ec_stop ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2005-07-23 12:08:00 +04:00
union acpi_ec {
struct {
2005-08-12 01:32:05 +04:00
u32 mode ;
acpi_handle handle ;
unsigned long uid ;
unsigned long gpe_bit ;
struct acpi_generic_address status_addr ;
struct acpi_generic_address command_addr ;
struct acpi_generic_address data_addr ;
unsigned long global_lock ;
2005-07-23 12:08:00 +04:00
} common ;
struct {
2005-08-12 01:32:05 +04:00
u32 mode ;
acpi_handle handle ;
unsigned long uid ;
unsigned long gpe_bit ;
struct acpi_generic_address status_addr ;
struct acpi_generic_address command_addr ;
struct acpi_generic_address data_addr ;
unsigned long global_lock ;
unsigned int expect_event ;
atomic_t leaving_burst ; /* 0 : No, 1 : Yes, 2: abort */
atomic_t pending_gpe ;
struct semaphore sem ;
wait_queue_head_t wait ;
} burst ;
2005-07-23 12:08:00 +04:00
struct {
2005-08-12 01:32:05 +04:00
u32 mode ;
acpi_handle handle ;
unsigned long uid ;
unsigned long gpe_bit ;
struct acpi_generic_address status_addr ;
struct acpi_generic_address command_addr ;
struct acpi_generic_address data_addr ;
unsigned long global_lock ;
spinlock_t lock ;
} polling ;
2005-04-17 02:20:36 +04:00
} ;
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_wait ( union acpi_ec * ec , u8 event ) ;
2005-07-23 12:08:00 +04:00
static int acpi_ec_burst_wait ( union acpi_ec * ec , unsigned int event ) ;
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_read ( union acpi_ec * ec , u8 address , u32 * data ) ;
static int acpi_ec_burst_read ( union acpi_ec * ec , u8 address , u32 * data ) ;
static int acpi_ec_polling_write ( union acpi_ec * ec , u8 address , u8 data ) ;
static int acpi_ec_burst_write ( union acpi_ec * ec , u8 address , u8 data ) ;
static int acpi_ec_polling_query ( union acpi_ec * ec , u32 * data ) ;
static int acpi_ec_burst_query ( union acpi_ec * ec , u32 * data ) ;
static void acpi_ec_gpe_polling_query ( void * ec_cxt ) ;
static void acpi_ec_gpe_burst_query ( void * ec_cxt ) ;
static u32 acpi_ec_gpe_polling_handler ( void * data ) ;
static u32 acpi_ec_gpe_burst_handler ( void * data ) ;
2005-07-23 12:08:00 +04:00
static acpi_status __init
2005-08-12 01:32:05 +04:00
acpi_fake_ecdt_polling_callback ( acpi_handle handle ,
u32 Level , void * context , void * * retval ) ;
2005-07-23 12:08:00 +04:00
static acpi_status __init
2005-08-12 01:32:05 +04:00
acpi_fake_ecdt_burst_callback ( acpi_handle handle ,
u32 Level , void * context , void * * retval ) ;
static int __init acpi_ec_polling_get_real_ecdt ( void ) ;
static int __init acpi_ec_burst_get_real_ecdt ( void ) ;
2005-04-17 02:20:36 +04:00
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
2005-08-12 01:32:05 +04:00
static union acpi_ec * ec_ecdt ;
2005-04-17 02:20:36 +04:00
/* External interfaces use first EC only, so remember */
static struct acpi_device * first_ec ;
2005-08-04 01:38:04 +04:00
static int acpi_ec_polling_mode = EC_POLLING ;
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Transaction Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-07-23 12:08:00 +04:00
static inline u32 acpi_ec_read_status ( union acpi_ec * ec )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
u32 status = 0 ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_read ( 8 , & status , & ec - > common . status_addr ) ;
2005-03-19 09:10:05 +03:00
return status ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_wait ( union acpi_ec * ec , u8 event )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
return acpi_ec_polling_wait ( ec , event ) ;
2005-07-23 12:08:00 +04:00
else
2005-08-12 01:32:05 +04:00
return acpi_ec_burst_wait ( ec , event ) ;
2005-07-23 12:08:00 +04:00
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_wait ( union acpi_ec * ec , u8 event )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
u32 acpi_ec_status = 0 ;
u32 i = ACPI_EC_UDELAY_COUNT ;
2005-07-23 12:08:00 +04:00
if ( ! ec )
return - EINVAL ;
/* Poll the EC status register waiting for the event to occur. */
switch ( event ) {
case ACPI_EC_EVENT_OBF :
do {
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_read ( 8 , & acpi_ec_status ,
& ec - > common . status_addr ) ;
2005-07-23 12:08:00 +04:00
if ( acpi_ec_status & ACPI_EC_FLAG_OBF )
return 0 ;
udelay ( ACPI_EC_UDELAY ) ;
2005-08-12 01:32:05 +04:00
} while ( - - i > 0 ) ;
2005-07-23 12:08:00 +04:00
break ;
case ACPI_EC_EVENT_IBE :
do {
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_read ( 8 , & acpi_ec_status ,
& ec - > common . status_addr ) ;
2005-07-23 12:08:00 +04:00
if ( ! ( acpi_ec_status & ACPI_EC_FLAG_IBF ) )
return 0 ;
udelay ( ACPI_EC_UDELAY ) ;
2005-08-12 01:32:05 +04:00
} while ( - - i > 0 ) ;
2005-07-23 12:08:00 +04:00
break ;
default :
return - EINVAL ;
}
return - ETIME ;
}
static int acpi_ec_burst_wait ( union acpi_ec * ec , unsigned int event )
2005-03-19 09:10:05 +03:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
2005-03-19 09:10:05 +03:00
ACPI_FUNCTION_TRACE ( " acpi_ec_wait " ) ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
ec - > burst . expect_event = event ;
2005-03-19 09:10:05 +03:00
smp_mb ( ) ;
2005-08-10 09:40:00 +04:00
switch ( event ) {
case ACPI_EC_EVENT_OBF :
if ( acpi_ec_read_status ( ec ) & event ) {
ec - > burst . expect_event = 0 ;
return_VALUE ( 0 ) ;
}
break ;
case ACPI_EC_EVENT_IBE :
if ( ~ acpi_ec_read_status ( ec ) & event ) {
ec - > burst . expect_event = 0 ;
return_VALUE ( 0 ) ;
}
break ;
}
result = wait_event_timeout ( ec - > burst . wait ,
2005-08-12 01:32:05 +04:00
! ec - > burst . expect_event ,
msecs_to_jiffies ( ACPI_EC_DELAY ) ) ;
2005-07-23 12:08:00 +04:00
ec - > burst . expect_event = 0 ;
2005-03-19 09:10:05 +03:00
smp_mb ( ) ;
/*
* Verify that the event in question has actually happened by
* querying EC status . Do the check even if operation timed - out
* to make sure that we did not miss interrupt .
*/
2005-04-17 02:20:36 +04:00
switch ( event ) {
case ACPI_EC_EVENT_OBF :
2005-03-19 09:10:05 +03:00
if ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_OBF )
return_VALUE ( 0 ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-03-19 09:10:05 +03:00
2005-04-17 02:20:36 +04:00
case ACPI_EC_EVENT_IBE :
2005-03-19 09:10:05 +03:00
if ( ~ acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_IBF )
return_VALUE ( 0 ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2005-03-19 09:10:05 +03:00
return_VALUE ( - ETIME ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_enter_burst_mode ( union acpi_ec * ec )
2005-03-19 09:10:05 +03:00
{
2005-08-12 01:32:05 +04:00
u32 tmp = 0 ;
int status = 0 ;
2005-03-19 09:10:05 +03:00
ACPI_FUNCTION_TRACE ( " acpi_ec_enter_burst_mode " ) ;
status = acpi_ec_read_status ( ec ) ;
2005-08-12 01:32:05 +04:00
if ( status ! = - EINVAL & & ! ( status & ACPI_EC_FLAG_BURST ) ) {
2005-08-10 09:40:00 +04:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
2005-08-12 01:32:05 +04:00
if ( status )
2005-08-10 09:40:00 +04:00
goto end ;
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_BURST_ENABLE ,
& ec - > common . command_addr ) ;
2005-03-19 09:10:05 +03:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF ) ;
2005-08-10 09:40:00 +04:00
if ( status )
2005-03-19 09:10:05 +03:00
return_VALUE ( - EINVAL ) ;
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_read ( 8 , & tmp , & ec - > common . data_addr ) ;
2005-08-12 01:32:05 +04:00
if ( tmp ! = 0x90 ) { /* Burst ACK byte */
2005-03-19 09:10:05 +03:00
return_VALUE ( - EINVAL ) ;
}
2005-07-23 08:26:33 +04:00
}
2005-08-12 01:32:05 +04:00
atomic_set ( & ec - > burst . leaving_burst , 0 ) ;
2005-03-19 09:10:05 +03:00
return_VALUE ( 0 ) ;
2005-08-12 01:32:05 +04:00
end :
2005-08-10 09:40:00 +04:00
printk ( " Error in acpi_ec_wait \n " ) ;
return_VALUE ( - 1 ) ;
2005-03-19 09:10:05 +03:00
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_leave_burst_mode ( union acpi_ec * ec )
2005-03-19 09:10:05 +03:00
{
ACPI_FUNCTION_TRACE ( " acpi_ec_leave_burst_mode " ) ;
2005-08-10 09:40:00 +04:00
atomic_set ( & ec - > burst . leaving_burst , 1 ) ;
2005-03-19 09:10:05 +03:00
return_VALUE ( 0 ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_read ( union acpi_ec * ec , u8 address , u32 * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
2005-07-23 12:08:00 +04:00
return acpi_ec_polling_read ( ec , address , data ) ;
else
return acpi_ec_burst_read ( ec , address , data ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_write ( union acpi_ec * ec , u8 address , u8 data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
2005-07-23 12:08:00 +04:00
return acpi_ec_polling_write ( ec , address , data ) ;
else
return acpi_ec_burst_write ( ec , address , data ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_read ( union acpi_ec * ec , u8 address , u32 * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
int result = 0 ;
unsigned long flags = 0 ;
u32 glk = 0 ;
2005-07-23 12:08:00 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_read " ) ;
if ( ! ec | | ! data )
return_VALUE ( - EINVAL ) ;
* data = 0 ;
if ( ec - > common . global_lock ) {
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
spin_lock_irqsave ( & ec - > polling . lock , flags ) ;
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_READ ,
& ec - > common . command_addr ) ;
2005-07-23 12:08:00 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( result )
goto end ;
acpi_hw_low_level_write ( 8 , address , & ec - > common . data_addr ) ;
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF ) ;
if ( result )
goto end ;
acpi_hw_low_level_read ( 8 , data , & ec - > common . data_addr ) ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Read [%02x] from address [%02x] \n " ,
2005-08-12 01:32:05 +04:00
* data , address ) ) ;
end :
2005-07-23 12:08:00 +04:00
spin_unlock_irqrestore ( & ec - > polling . lock , flags ) ;
if ( ec - > common . global_lock )
acpi_release_global_lock ( glk ) ;
return_VALUE ( result ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_write ( union acpi_ec * ec , u8 address , u8 data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
unsigned long flags = 0 ;
u32 glk = 0 ;
2005-07-23 12:08:00 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_write " ) ;
if ( ! ec )
return_VALUE ( - EINVAL ) ;
if ( ec - > common . global_lock ) {
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
spin_lock_irqsave ( & ec - > polling . lock , flags ) ;
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_WRITE ,
& ec - > common . command_addr ) ;
2005-07-23 12:08:00 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( result )
goto end ;
acpi_hw_low_level_write ( 8 , address , & ec - > common . data_addr ) ;
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( result )
goto end ;
acpi_hw_low_level_write ( 8 , data , & ec - > common . data_addr ) ;
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( result )
goto end ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Wrote [%02x] to address [%02x] \n " ,
2005-08-12 01:32:05 +04:00
data , address ) ) ;
2005-07-23 12:08:00 +04:00
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
spin_unlock_irqrestore ( & ec - > polling . lock , flags ) ;
if ( ec - > common . global_lock )
acpi_release_global_lock ( glk ) ;
return_VALUE ( result ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_burst_read ( union acpi_ec * ec , u8 address , u32 * data )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int status = 0 ;
u32 glk ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_read " ) ;
if ( ! ec | | ! data )
return_VALUE ( - EINVAL ) ;
* data = 0 ;
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock ) {
2005-04-17 02:20:36 +04:00
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
2005-03-19 09:10:05 +03:00
WARN_ON ( in_interrupt ( ) ) ;
2005-07-23 12:08:00 +04:00
down ( & ec - > burst . sem ) ;
2005-03-19 09:10:05 +03:00
2005-08-10 09:40:00 +04:00
acpi_ec_enter_burst_mode ( ec ) ;
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( status ) {
printk ( " read EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
goto end ;
2005-08-10 09:40:00 +04:00
}
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_READ ,
& ec - > common . command_addr ) ;
2005-03-19 09:10:05 +03:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( status ) {
2005-08-10 09:40:00 +04:00
printk ( " read EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
}
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_write ( 8 , address , & ec - > common . data_addr ) ;
2005-08-12 01:32:05 +04:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF ) ;
if ( status ) {
2005-08-10 09:40:00 +04:00
printk ( " read EC, OB not full \n " ) ;
2005-04-17 02:20:36 +04:00
goto end ;
2005-03-19 09:10:05 +03:00
}
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_read ( 8 , data , & ec - > common . data_addr ) ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Read [%02x] from address [%02x] \n " ,
2005-08-12 01:32:05 +04:00
* data , address ) ) ;
end :
2005-03-19 09:10:05 +03:00
acpi_ec_leave_burst_mode ( ec ) ;
2005-07-23 12:08:00 +04:00
up ( & ec - > burst . sem ) ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock )
2005-04-17 02:20:36 +04:00
acpi_release_global_lock ( glk ) ;
2005-03-19 09:10:05 +03:00
return_VALUE ( status ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_burst_write ( union acpi_ec * ec , u8 address , u8 data )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int status = 0 ;
u32 glk ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_write " ) ;
if ( ! ec )
return_VALUE ( - EINVAL ) ;
2005-08-10 09:40:00 +04:00
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock ) {
2005-04-17 02:20:36 +04:00
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
2005-03-19 09:10:05 +03:00
WARN_ON ( in_interrupt ( ) ) ;
2005-07-23 12:08:00 +04:00
down ( & ec - > burst . sem ) ;
2005-03-19 09:10:05 +03:00
2005-08-10 09:40:00 +04:00
acpi_ec_enter_burst_mode ( ec ) ;
2005-03-19 09:10:05 +03:00
2005-08-10 09:40:00 +04:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
2005-08-12 01:32:05 +04:00
if ( status ) {
2005-08-10 09:40:00 +04:00
printk ( " write EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
}
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_WRITE ,
& ec - > common . command_addr ) ;
2005-03-19 09:10:05 +03:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
2005-08-10 09:40:00 +04:00
if ( status ) {
2005-08-12 01:32:05 +04:00
printk ( " write EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
}
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_write ( 8 , address , & ec - > common . data_addr ) ;
2005-03-19 09:10:05 +03:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
2005-08-12 01:32:05 +04:00
if ( status ) {
2005-08-10 09:40:00 +04:00
printk ( " write EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
}
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_write ( 8 , data , & ec - > common . data_addr ) ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Wrote [%02x] to address [%02x] \n " ,
2005-08-12 01:32:05 +04:00
data , address ) ) ;
2005-04-17 02:20:36 +04:00
2005-03-19 09:10:05 +03:00
acpi_ec_leave_burst_mode ( ec ) ;
2005-07-23 12:08:00 +04:00
up ( & ec - > burst . sem ) ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock )
2005-04-17 02:20:36 +04:00
acpi_release_global_lock ( glk ) ;
2005-03-19 09:10:05 +03:00
return_VALUE ( status ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Externally callable EC access functions . For now , assume 1 EC only
*/
2005-08-12 01:32:05 +04:00
int ec_read ( u8 addr , u8 * val )
2005-04-17 02:20:36 +04:00
{
2005-07-23 12:08:00 +04:00
union acpi_ec * ec ;
2005-04-17 02:20:36 +04:00
int err ;
u32 temp_data ;
if ( ! first_ec )
return - ENODEV ;
ec = acpi_driver_data ( first_ec ) ;
err = acpi_ec_read ( ec , addr , & temp_data ) ;
if ( ! err ) {
* val = temp_data ;
return 0 ;
2005-08-12 01:32:05 +04:00
} else
2005-04-17 02:20:36 +04:00
return err ;
}
2005-08-12 01:32:05 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( ec_read ) ;
2005-08-12 01:32:05 +04:00
int ec_write ( u8 addr , u8 val )
2005-04-17 02:20:36 +04:00
{
2005-07-23 12:08:00 +04:00
union acpi_ec * ec ;
2005-04-17 02:20:36 +04:00
int err ;
if ( ! first_ec )
return - ENODEV ;
ec = acpi_driver_data ( first_ec ) ;
err = acpi_ec_write ( ec , addr , val ) ;
return err ;
}
2005-08-12 01:32:05 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( ec_write ) ;
2005-08-12 01:32:05 +04:00
static int acpi_ec_query ( union acpi_ec * ec , u32 * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
2005-07-23 12:08:00 +04:00
return acpi_ec_polling_query ( ec , data ) ;
else
return acpi_ec_burst_query ( ec , data ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_query ( union acpi_ec * ec , u32 * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
unsigned long flags = 0 ;
u32 glk = 0 ;
2005-07-23 12:08:00 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_query " ) ;
if ( ! ec | | ! data )
return_VALUE ( - EINVAL ) ;
* data = 0 ;
if ( ec - > common . global_lock ) {
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
/*
* Query the EC to find out which _Qxx method we need to evaluate .
* Note that successful completion of the query causes the ACPI_EC_SCI
* bit to be cleared ( and thus clearing the interrupt source ) .
*/
spin_lock_irqsave ( & ec - > polling . lock , flags ) ;
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_QUERY ,
& ec - > common . command_addr ) ;
2005-07-23 12:08:00 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF ) ;
if ( result )
goto end ;
acpi_hw_low_level_read ( 8 , data , & ec - > common . data_addr ) ;
if ( ! * data )
result = - ENODATA ;
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
spin_unlock_irqrestore ( & ec - > polling . lock , flags ) ;
if ( ec - > common . global_lock )
acpi_release_global_lock ( glk ) ;
return_VALUE ( result ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_burst_query ( union acpi_ec * ec , u32 * data )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int status = 0 ;
u32 glk ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_query " ) ;
if ( ! ec | | ! data )
return_VALUE ( - EINVAL ) ;
* data = 0 ;
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock ) {
2005-04-17 02:20:36 +04:00
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
}
2005-07-23 12:08:00 +04:00
down ( & ec - > burst . sem ) ;
2005-08-10 09:40:00 +04:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBE ) ;
if ( status ) {
printk ( " query EC, IB not empty \n " ) ;
2005-03-19 09:10:05 +03:00
goto end ;
2005-08-10 09:40:00 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Query the EC to find out which _Qxx method we need to evaluate .
* Note that successful completion of the query causes the ACPI_EC_SCI
* bit to be cleared ( and thus clearing the interrupt source ) .
*/
2005-08-12 01:32:05 +04:00
acpi_hw_low_level_write ( 8 , ACPI_EC_COMMAND_QUERY ,
& ec - > common . command_addr ) ;
2005-03-19 09:10:05 +03:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF ) ;
2005-08-12 01:32:05 +04:00
if ( status ) {
2005-08-10 09:40:00 +04:00
printk ( " query EC, OB not full \n " ) ;
2005-04-17 02:20:36 +04:00
goto end ;
2005-03-19 09:10:05 +03:00
}
2005-07-23 12:08:00 +04:00
acpi_hw_low_level_read ( 8 , data , & ec - > common . data_addr ) ;
2005-04-17 02:20:36 +04:00
if ( ! * data )
2005-03-19 09:10:05 +03:00
status = - ENODATA ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
up ( & ec - > burst . sem ) ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
if ( ec - > common . global_lock )
2005-04-17 02:20:36 +04:00
acpi_release_global_lock ( glk ) ;
2005-03-19 09:10:05 +03:00
return_VALUE ( status ) ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Event Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-07-23 12:08:00 +04:00
union acpi_ec_query_data {
2005-08-12 01:32:05 +04:00
acpi_handle handle ;
u8 data ;
2005-04-17 02:20:36 +04:00
} ;
2005-08-12 01:32:05 +04:00
static void acpi_ec_gpe_query ( void * ec_cxt )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
2005-07-23 12:08:00 +04:00
acpi_ec_gpe_polling_query ( ec_cxt ) ;
else
acpi_ec_gpe_burst_query ( ec_cxt ) ;
}
2005-08-12 01:32:05 +04:00
static void acpi_ec_gpe_polling_query ( void * ec_cxt )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
union acpi_ec * ec = ( union acpi_ec * ) ec_cxt ;
u32 value = 0 ;
unsigned long flags = 0 ;
static char object_name [ 5 ] = { ' _ ' , ' Q ' , ' 0 ' , ' 0 ' , ' \0 ' } ;
const char hex [ ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' ,
' 8 ' , ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F '
} ;
2005-07-23 12:08:00 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_gpe_query " ) ;
if ( ! ec_cxt )
goto end ;
spin_lock_irqsave ( & ec - > polling . lock , flags ) ;
acpi_hw_low_level_read ( 8 , & value , & ec - > common . command_addr ) ;
spin_unlock_irqrestore ( & ec - > polling . lock , flags ) ;
/* TBD: Implement asynch events!
* NOTE : All we care about are EC - SCI ' s . Other EC events are
* handled via polling ( yuck ! ) . This is because some systems
* treat EC - SCIs as level ( versus EDGE ! ) triggered , preventing
* a purely interrupt - driven approach ( grumble , grumble ) .
*/
if ( ! ( value & ACPI_EC_FLAG_SCI ) )
goto end ;
if ( acpi_ec_query ( ec , & value ) )
goto end ;
object_name [ 2 ] = hex [ ( ( value > > 4 ) & 0x0F ) ] ;
object_name [ 3 ] = hex [ ( value & 0x0F ) ] ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Evaluating %s \n " , object_name ) ) ;
acpi_evaluate_object ( ec - > common . handle , object_name , NULL , NULL ) ;
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
acpi_enable_gpe ( NULL , ec - > common . gpe_bit , ACPI_NOT_ISR ) ;
}
2005-08-12 01:32:05 +04:00
static void acpi_ec_gpe_burst_query ( void * ec_cxt )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
union acpi_ec * ec = ( union acpi_ec * ) ec_cxt ;
u32 value ;
int result = - ENODATA ;
static char object_name [ 5 ] = { ' _ ' , ' Q ' , ' 0 ' , ' 0 ' , ' \0 ' } ;
const char hex [ ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' ,
' 8 ' , ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F '
} ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_gpe_query " ) ;
2005-03-19 09:10:05 +03:00
if ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_SCI )
result = acpi_ec_query ( ec , & value ) ;
2005-04-17 02:20:36 +04:00
2005-03-19 09:10:05 +03:00
if ( result )
2005-04-17 02:20:36 +04:00
goto end ;
object_name [ 2 ] = hex [ ( ( value > > 4 ) & 0x0F ) ] ;
object_name [ 3 ] = hex [ ( value & 0x0F ) ] ;
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " Evaluating %s \n " , object_name ) ) ;
2005-07-23 12:08:00 +04:00
acpi_evaluate_object ( ec - > common . handle , object_name , NULL , NULL ) ;
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
atomic_dec ( & ec - > burst . pending_gpe ) ;
2005-03-19 09:10:05 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 01:32:05 +04:00
static u32 acpi_ec_gpe_handler ( void * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
if ( acpi_ec_polling_mode )
2005-07-23 12:08:00 +04:00
return acpi_ec_gpe_polling_handler ( data ) ;
else
2005-08-12 01:32:05 +04:00
return acpi_ec_gpe_burst_handler ( data ) ;
2005-07-23 12:08:00 +04:00
}
2005-08-12 01:32:05 +04:00
static u32 acpi_ec_gpe_polling_handler ( void * data )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
union acpi_ec * ec = ( union acpi_ec * ) data ;
2005-07-23 12:08:00 +04:00
if ( ! ec )
return ACPI_INTERRUPT_NOT_HANDLED ;
acpi_disable_gpe ( NULL , ec - > common . gpe_bit , ACPI_ISR ) ;
status = acpi_os_queue_for_execution ( OSD_PRIORITY_GPE ,
2005-08-12 01:32:05 +04:00
acpi_ec_gpe_query , ec ) ;
2005-07-23 12:08:00 +04:00
if ( status = = AE_OK )
return ACPI_INTERRUPT_HANDLED ;
else
return ACPI_INTERRUPT_NOT_HANDLED ;
}
2005-08-12 01:32:05 +04:00
static u32 acpi_ec_gpe_burst_handler ( void * data )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
u32 value ;
union acpi_ec * ec = ( union acpi_ec * ) data ;
2005-04-17 02:20:36 +04:00
if ( ! ec )
return ACPI_INTERRUPT_NOT_HANDLED ;
2005-08-10 09:40:00 +04:00
acpi_clear_gpe ( NULL , ec - > common . gpe_bit , ACPI_ISR ) ;
2005-03-19 09:10:05 +03:00
value = acpi_ec_read_status ( ec ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
switch ( ec - > burst . expect_event ) {
2005-08-10 09:40:00 +04:00
case ACPI_EC_EVENT_OBF :
if ( ! ( value & ACPI_EC_FLAG_OBF ) )
break ;
case ACPI_EC_EVENT_IBE :
if ( ( value & ACPI_EC_FLAG_IBF ) )
break ;
ec - > burst . expect_event = 0 ;
2005-07-23 12:08:00 +04:00
wake_up ( & ec - > burst . wait ) ;
2005-08-10 09:40:00 +04:00
return ACPI_INTERRUPT_HANDLED ;
default :
break ;
2005-03-19 09:10:05 +03:00
}
2005-08-12 01:32:05 +04:00
if ( value & ACPI_EC_FLAG_SCI ) {
atomic_add ( 1 , & ec - > burst . pending_gpe ) ;
2005-03-19 09:10:05 +03:00
status = acpi_os_queue_for_execution ( OSD_PRIORITY_GPE ,
2005-08-12 01:32:05 +04:00
acpi_ec_gpe_query , ec ) ;
2005-04-23 07:07:10 +04:00
return status = = AE_OK ?
2005-08-12 01:32:05 +04:00
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED ;
}
2005-07-23 12:08:00 +04:00
acpi_enable_gpe ( NULL , ec - > common . gpe_bit , ACPI_ISR ) ;
2005-03-19 09:10:05 +03:00
return status = = AE_OK ?
2005-08-12 01:32:05 +04:00
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Address Space Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static acpi_status
2005-08-12 01:32:05 +04:00
acpi_ec_space_setup ( acpi_handle region_handle ,
u32 function , void * handler_context , void * * return_context )
2005-04-17 02:20:36 +04:00
{
/*
* The EC object is in the handler context and is needed
* when calling the acpi_ec_space_handler .
*/
2005-08-12 01:32:05 +04:00
* return_context = ( function ! = ACPI_REGION_DEACTIVATE ) ?
handler_context : NULL ;
2005-04-17 02:20:36 +04:00
return AE_OK ;
}
static acpi_status
2005-08-12 01:32:05 +04:00
acpi_ec_space_handler ( u32 function ,
acpi_physical_address address ,
u32 bit_width ,
acpi_integer * value ,
void * handler_context , void * region_context )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
union acpi_ec * ec = NULL ;
u64 temp = * value ;
acpi_integer f_v = 0 ;
int i = 0 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_space_handler " ) ;
if ( ( address > 0xFF ) | | ! value | | ! handler_context )
return_VALUE ( AE_BAD_PARAMETER ) ;
2005-03-19 09:54:47 +03:00
if ( bit_width ! = 8 & & acpi_strict ) {
2005-08-12 01:32:05 +04:00
printk ( KERN_WARNING PREFIX
" acpi_ec_space_handler: bit_width should be 8 \n " ) ;
2005-03-19 09:54:47 +03:00
return_VALUE ( AE_BAD_PARAMETER ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 01:32:05 +04:00
ec = ( union acpi_ec * ) handler_context ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
next_byte :
2005-04-17 02:20:36 +04:00
switch ( function ) {
case ACPI_READ :
2005-03-19 09:54:47 +03:00
temp = 0 ;
2005-08-12 01:32:05 +04:00
result = acpi_ec_read ( ec , ( u8 ) address , ( u32 * ) & temp ) ;
2005-04-17 02:20:36 +04:00
break ;
case ACPI_WRITE :
2005-03-19 09:54:47 +03:00
result = acpi_ec_write ( ec , ( u8 ) address , ( u8 ) temp ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
result = - EINVAL ;
goto out ;
break ;
}
bit_width - = 8 ;
2005-03-19 09:54:47 +03:00
if ( bit_width ) {
if ( function = = ACPI_READ )
f_v | = temp < < 8 * i ;
if ( function = = ACPI_WRITE )
temp > > = 8 ;
2005-04-17 02:20:36 +04:00
i + + ;
2005-03-31 07:12:13 +04:00
address + + ;
2005-04-17 02:20:36 +04:00
goto next_byte ;
}
2005-03-19 09:54:47 +03:00
if ( function = = ACPI_READ ) {
f_v | = temp < < 8 * i ;
2005-04-17 02:20:36 +04:00
* value = f_v ;
}
2005-08-12 01:32:05 +04:00
out :
2005-04-17 02:20:36 +04:00
switch ( result ) {
case - EINVAL :
return_VALUE ( AE_BAD_PARAMETER ) ;
break ;
case - ENODEV :
return_VALUE ( AE_NOT_FOUND ) ;
break ;
case - ETIME :
return_VALUE ( AE_TIME ) ;
break ;
default :
return_VALUE ( AE_OK ) ;
}
}
/* --------------------------------------------------------------------------
FS Interface ( / proc )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-12 01:32:05 +04:00
static struct proc_dir_entry * acpi_ec_dir ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
static int acpi_ec_read_info ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
union acpi_ec * ec = ( union acpi_ec * ) seq - > private ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_read_info " ) ;
if ( ! ec )
goto end ;
seq_printf ( seq , " gpe bit: 0x%02x \n " ,
2005-08-12 01:32:05 +04:00
( u32 ) ec - > common . gpe_bit ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " ports: 0x%02x, 0x%02x \n " ,
2005-08-12 01:32:05 +04:00
( u32 ) ec - > common . status_addr . address ,
( u32 ) ec - > common . data_addr . address ) ;
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " use global lock: %s \n " ,
2005-08-12 01:32:05 +04:00
ec - > common . global_lock ? " yes " : " no " ) ;
2005-07-23 12:08:00 +04:00
acpi_enable_gpe ( NULL , ec - > common . gpe_bit , ACPI_NOT_ISR ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
end :
2005-04-17 02:20:36 +04:00
return_VALUE ( 0 ) ;
}
static int acpi_ec_info_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_ec_read_info , PDE ( inode ) - > data ) ;
}
static struct file_operations acpi_ec_info_ops = {
2005-08-12 01:32:05 +04:00
. open = acpi_ec_info_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
} ;
2005-08-12 01:32:05 +04:00
static int acpi_ec_add_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
struct proc_dir_entry * entry = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_add_fs " ) ;
if ( ! acpi_device_dir ( device ) ) {
acpi_device_dir ( device ) = proc_mkdir ( acpi_device_bid ( device ) ,
2005-08-12 01:32:05 +04:00
acpi_ec_dir ) ;
2005-04-17 02:20:36 +04:00
if ( ! acpi_device_dir ( device ) )
return_VALUE ( - ENODEV ) ;
}
entry = create_proc_entry ( ACPI_EC_FILE_INFO , S_IRUGO ,
2005-08-12 01:32:05 +04:00
acpi_device_dir ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
ACPI_DEBUG_PRINT ( ( ACPI_DB_WARN ,
2005-08-12 01:32:05 +04:00
" Unable to create '%s' fs entry \n " ,
ACPI_EC_FILE_INFO ) ) ;
2005-04-17 02:20:36 +04:00
else {
entry - > proc_fops = & acpi_ec_info_ops ;
entry - > data = acpi_driver_data ( device ) ;
entry - > owner = THIS_MODULE ;
}
return_VALUE ( 0 ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_remove_fs ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
ACPI_FUNCTION_TRACE ( " acpi_ec_remove_fs " ) ;
if ( acpi_device_dir ( device ) ) {
remove_proc_entry ( ACPI_EC_FILE_INFO , acpi_device_dir ( device ) ) ;
remove_proc_entry ( acpi_device_bid ( device ) , acpi_ec_dir ) ;
acpi_device_dir ( device ) = NULL ;
}
return_VALUE ( 0 ) ;
}
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2005-08-12 01:32:05 +04:00
static int acpi_ec_polling_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
union acpi_ec * ec = NULL ;
unsigned long uid ;
2005-07-23 12:08:00 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_add " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
ec = kmalloc ( sizeof ( union acpi_ec ) , GFP_KERNEL ) ;
if ( ! ec )
return_VALUE ( - ENOMEM ) ;
memset ( ec , 0 , sizeof ( union acpi_ec ) ) ;
ec - > common . handle = device - > handle ;
ec - > common . uid = - 1 ;
spin_lock_init ( & ec - > polling . lock ) ;
strcpy ( acpi_device_name ( device ) , ACPI_EC_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_EC_CLASS ) ;
acpi_driver_data ( device ) = ec ;
/* Use the global lock for all EC transactions? */
2005-08-12 01:32:05 +04:00
acpi_evaluate_integer ( ec - > common . handle , " _GLK " , NULL ,
& ec - > common . global_lock ) ;
2005-07-23 12:08:00 +04:00
/* If our UID matches the UID for the ECDT-enumerated EC,
2005-08-12 01:32:05 +04:00
we now have the * real * EC info , so kill the makeshift one . */
2005-07-23 12:08:00 +04:00
acpi_evaluate_integer ( ec - > common . handle , " _UID " , NULL , & uid ) ;
if ( ec_ecdt & & ec_ecdt - > common . uid = = uid ) {
acpi_remove_address_space_handler ( ACPI_ROOT_OBJECT ,
2005-08-12 01:32:05 +04:00
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ) ;
acpi_remove_gpe_handler ( NULL , ec_ecdt - > common . gpe_bit ,
& acpi_ec_gpe_handler ) ;
2005-07-23 12:08:00 +04:00
kfree ( ec_ecdt ) ;
}
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
2005-08-12 01:32:05 +04:00
status =
acpi_evaluate_integer ( ec - > common . handle , " _GPE " , NULL ,
& ec - > common . gpe_bit ) ;
2005-07-23 12:08:00 +04:00
if ( ACPI_FAILURE ( status ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
2005-08-12 01:32:05 +04:00
" Error obtaining GPE bit assignment \n " ) ) ;
2005-07-23 12:08:00 +04:00
result = - ENODEV ;
goto end ;
}
result = acpi_ec_add_fs ( device ) ;
if ( result )
goto end ;
printk ( KERN_INFO PREFIX " %s [%s] (gpe %d) \n " ,
2005-08-12 01:32:05 +04:00
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
( u32 ) ec - > common . gpe_bit ) ;
2005-07-23 12:08:00 +04:00
if ( ! first_ec )
first_ec = device ;
2005-08-12 01:32:05 +04:00
end :
2005-07-23 12:08:00 +04:00
if ( result )
kfree ( ec ) ;
return_VALUE ( result ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_burst_add ( struct acpi_device * device )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
acpi_status status = AE_OK ;
union acpi_ec * ec = NULL ;
unsigned long uid ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_add " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
2005-07-23 12:08:00 +04:00
ec = kmalloc ( sizeof ( union acpi_ec ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ec )
return_VALUE ( - ENOMEM ) ;
2005-07-23 12:08:00 +04:00
memset ( ec , 0 , sizeof ( union acpi_ec ) ) ;
ec - > common . handle = device - > handle ;
ec - > common . uid = - 1 ;
2005-08-12 01:32:05 +04:00
atomic_set ( & ec - > burst . pending_gpe , 0 ) ;
atomic_set ( & ec - > burst . leaving_burst , 1 ) ;
init_MUTEX ( & ec - > burst . sem ) ;
init_waitqueue_head ( & ec - > burst . wait ) ;
2005-04-17 02:20:36 +04:00
strcpy ( acpi_device_name ( device ) , ACPI_EC_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_EC_CLASS ) ;
acpi_driver_data ( device ) = ec ;
/* Use the global lock for all EC transactions? */
2005-08-12 01:32:05 +04:00
acpi_evaluate_integer ( ec - > common . handle , " _GLK " , NULL ,
& ec - > common . global_lock ) ;
2005-04-17 02:20:36 +04:00
/* If our UID matches the UID for the ECDT-enumerated EC,
2005-08-12 01:32:05 +04:00
we now have the * real * EC info , so kill the makeshift one . */
2005-07-23 12:08:00 +04:00
acpi_evaluate_integer ( ec - > common . handle , " _UID " , NULL , & uid ) ;
if ( ec_ecdt & & ec_ecdt - > common . uid = = uid ) {
2005-04-17 02:20:36 +04:00
acpi_remove_address_space_handler ( ACPI_ROOT_OBJECT ,
2005-08-12 01:32:05 +04:00
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ) ;
2005-03-19 09:10:05 +03:00
2005-08-12 01:32:05 +04:00
acpi_remove_gpe_handler ( NULL , ec_ecdt - > common . gpe_bit ,
& acpi_ec_gpe_handler ) ;
2005-04-17 02:20:36 +04:00
kfree ( ec_ecdt ) ;
}
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
2005-08-12 01:32:05 +04:00
status =
acpi_evaluate_integer ( ec - > common . handle , " _GPE " , NULL ,
& ec - > common . gpe_bit ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
2005-08-12 01:32:05 +04:00
" Error obtaining GPE bit assignment \n " ) ) ;
2005-04-17 02:20:36 +04:00
result = - ENODEV ;
goto end ;
}
result = acpi_ec_add_fs ( device ) ;
if ( result )
goto end ;
2005-08-10 09:40:00 +04:00
printk ( " burst-mode-ec-10-Aug \n " ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " %s [%s] (gpe %d) \n " ,
2005-08-12 01:32:05 +04:00
acpi_device_name ( device ) , acpi_device_bid ( device ) ,
( u32 ) ec - > common . gpe_bit ) ;
2005-04-17 02:20:36 +04:00
if ( ! first_ec )
first_ec = device ;
2005-08-12 01:32:05 +04:00
end :
2005-04-17 02:20:36 +04:00
if ( result )
kfree ( ec ) ;
return_VALUE ( result ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
union acpi_ec * ec = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_remove " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
ec = acpi_driver_data ( device ) ;
acpi_ec_remove_fs ( device ) ;
kfree ( ec ) ;
return_VALUE ( 0 ) ;
}
static acpi_status
2005-08-12 01:32:05 +04:00
acpi_ec_io_ports ( struct acpi_resource * resource , void * context )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
union acpi_ec * ec = ( union acpi_ec * ) context ;
2005-04-17 02:20:36 +04:00
struct acpi_generic_address * addr ;
if ( resource - > id ! = ACPI_RSTYPE_IO ) {
return AE_OK ;
}
/*
* The first address region returned is the data port , and
* the second address region returned is the status / command
* port .
*/
2005-07-23 12:08:00 +04:00
if ( ec - > common . data_addr . register_bit_width = = 0 ) {
addr = & ec - > common . data_addr ;
} else if ( ec - > common . command_addr . register_bit_width = = 0 ) {
addr = & ec - > common . command_addr ;
2005-04-17 02:20:36 +04:00
} else {
return AE_CTRL_TERMINATE ;
}
addr - > address_space_id = ACPI_ADR_SPACE_SYSTEM_IO ;
addr - > register_bit_width = 8 ;
addr - > register_bit_offset = 0 ;
addr - > address = resource - > data . io . min_base_address ;
return AE_OK ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_start ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
union acpi_ec * ec = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_start " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
ec = acpi_driver_data ( device ) ;
if ( ! ec )
return_VALUE ( - EINVAL ) ;
/*
* Get I / O port addresses . Convert to GAS format .
*/
2005-07-23 12:08:00 +04:00
status = acpi_walk_resources ( ec - > common . handle , METHOD_NAME__CRS ,
2005-08-12 01:32:05 +04:00
acpi_ec_io_ports , ec ) ;
if ( ACPI_FAILURE ( status )
| | ec - > common . command_addr . register_bit_width = = 0 ) {
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
" Error getting I/O port addresses " ) ) ;
2005-04-17 02:20:36 +04:00
return_VALUE ( - ENODEV ) ;
}
2005-07-23 12:08:00 +04:00
ec - > common . status_addr = ec - > common . command_addr ;
2005-04-17 02:20:36 +04:00
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO , " gpe=0x%02x, ports=0x%2x,0x%2x \n " ,
2005-08-12 01:32:05 +04:00
( u32 ) ec - > common . gpe_bit ,
( u32 ) ec - > common . command_addr . address ,
( u32 ) ec - > common . data_addr . address ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Install GPE handler
*/
2005-07-23 12:08:00 +04:00
status = acpi_install_gpe_handler ( NULL , ec - > common . gpe_bit ,
2005-08-12 01:32:05 +04:00
ACPI_GPE_EDGE_TRIGGERED ,
& acpi_ec_gpe_handler , ec ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
return_VALUE ( - ENODEV ) ;
}
2005-08-12 01:32:05 +04:00
acpi_set_gpe_type ( NULL , ec - > common . gpe_bit , ACPI_GPE_TYPE_RUNTIME ) ;
acpi_enable_gpe ( NULL , ec - > common . gpe_bit , ACPI_NOT_ISR ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
status = acpi_install_address_space_handler ( ec - > common . handle ,
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ,
& acpi_ec_space_setup , ec ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-08-12 01:32:05 +04:00
acpi_remove_gpe_handler ( NULL , ec - > common . gpe_bit ,
& acpi_ec_gpe_handler ) ;
2005-04-17 02:20:36 +04:00
return_VALUE ( - ENODEV ) ;
}
return_VALUE ( AE_OK ) ;
}
2005-08-12 01:32:05 +04:00
static int acpi_ec_stop ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
union acpi_ec * ec = NULL ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_stop " ) ;
if ( ! device )
return_VALUE ( - EINVAL ) ;
ec = acpi_driver_data ( device ) ;
2005-07-23 12:08:00 +04:00
status = acpi_remove_address_space_handler ( ec - > common . handle ,
2005-08-12 01:32:05 +04:00
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
2005-08-12 01:32:05 +04:00
status =
acpi_remove_gpe_handler ( NULL , ec - > common . gpe_bit ,
& acpi_ec_gpe_handler ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return_VALUE ( - ENODEV ) ;
return_VALUE ( 0 ) ;
}
static acpi_status __init
2005-08-12 01:32:05 +04:00
acpi_fake_ecdt_callback ( acpi_handle handle ,
u32 Level , void * context , void * * retval )
2005-04-17 02:20:36 +04:00
{
2005-07-23 12:08:00 +04:00
if ( acpi_ec_polling_mode )
return acpi_fake_ecdt_polling_callback ( handle ,
2005-08-12 01:32:05 +04:00
Level , context , retval ) ;
2005-07-23 12:08:00 +04:00
else
return acpi_fake_ecdt_burst_callback ( handle ,
2005-08-12 01:32:05 +04:00
Level , context , retval ) ;
2005-07-23 12:08:00 +04:00
}
static acpi_status __init
2005-08-12 01:32:05 +04:00
acpi_fake_ecdt_polling_callback ( acpi_handle handle ,
u32 Level , void * context , void * * retval )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
2005-07-23 12:08:00 +04:00
status = acpi_walk_resources ( handle , METHOD_NAME__CRS ,
2005-08-12 01:32:05 +04:00
acpi_ec_io_ports , ec_ecdt ) ;
2005-07-23 12:08:00 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
ec_ecdt - > common . status_addr = ec_ecdt - > common . command_addr ;
ec_ecdt - > common . uid = - 1 ;
acpi_evaluate_integer ( handle , " _UID " , NULL , & ec_ecdt - > common . uid ) ;
2005-08-12 01:32:05 +04:00
status =
acpi_evaluate_integer ( handle , " _GPE " , NULL ,
& ec_ecdt - > common . gpe_bit ) ;
2005-07-23 12:08:00 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
spin_lock_init ( & ec_ecdt - > polling . lock ) ;
ec_ecdt - > common . global_lock = TRUE ;
ec_ecdt - > common . handle = handle ;
2005-08-12 01:32:05 +04:00
printk ( KERN_INFO PREFIX " GPE=0x%02x, ports=0x%2x, 0x%2x \n " ,
( u32 ) ec_ecdt - > common . gpe_bit ,
( u32 ) ec_ecdt - > common . command_addr . address ,
( u32 ) ec_ecdt - > common . data_addr . address ) ;
2005-07-23 12:08:00 +04:00
return AE_CTRL_TERMINATE ;
}
static acpi_status __init
2005-08-12 01:32:05 +04:00
acpi_fake_ecdt_burst_callback ( acpi_handle handle ,
u32 Level , void * context , void * * retval )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
init_MUTEX ( & ec_ecdt - > burst . sem ) ;
init_waitqueue_head ( & ec_ecdt - > burst . wait ) ;
2005-04-17 02:20:36 +04:00
status = acpi_walk_resources ( handle , METHOD_NAME__CRS ,
2005-08-12 01:32:05 +04:00
acpi_ec_io_ports , ec_ecdt ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
2005-07-23 12:08:00 +04:00
ec_ecdt - > common . status_addr = ec_ecdt - > common . command_addr ;
2005-04-17 02:20:36 +04:00
2005-07-23 12:08:00 +04:00
ec_ecdt - > common . uid = - 1 ;
acpi_evaluate_integer ( handle , " _UID " , NULL , & ec_ecdt - > common . uid ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
status =
acpi_evaluate_integer ( handle , " _GPE " , NULL ,
& ec_ecdt - > common . gpe_bit ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
2005-07-23 12:08:00 +04:00
ec_ecdt - > common . global_lock = TRUE ;
ec_ecdt - > common . handle = handle ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
printk ( KERN_INFO PREFIX " GPE=0x%02x, ports=0x%2x, 0x%2x \n " ,
( u32 ) ec_ecdt - > common . gpe_bit ,
( u32 ) ec_ecdt - > common . command_addr . address ,
( u32 ) ec_ecdt - > common . data_addr . address ) ;
2005-04-17 02:20:36 +04:00
return AE_CTRL_TERMINATE ;
}
/*
* Some BIOS ( such as some from Gateway laptops ) access EC region very early
* such as in BAT0 . _INI or EC . _INI before an EC device is found and
* do not provide an ECDT . According to ACPI spec , ECDT isn ' t mandatorily
* required , but if EC regison is accessed early , it is required .
* The routine tries to workaround the BIOS bug by pre - scan EC device
* It assumes that _CRS , _HID , _GPE , _UID methods of EC don ' t touch any
* op region ( since _REG isn ' t invoked yet ) . The assumption is true for
* all systems found .
*/
2005-08-12 01:32:05 +04:00
static int __init acpi_ec_fake_ecdt ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " Try to make an fake ECDT \n " ) ;
2005-07-23 12:08:00 +04:00
ec_ecdt = kmalloc ( sizeof ( union acpi_ec ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ec_ecdt ) {
ret = - ENOMEM ;
goto error ;
}
2005-07-23 12:08:00 +04:00
memset ( ec_ecdt , 0 , sizeof ( union acpi_ec ) ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
status = acpi_get_devices ( ACPI_EC_HID ,
acpi_fake_ecdt_callback , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
kfree ( ec_ecdt ) ;
ec_ecdt = NULL ;
ret = - ENODEV ;
goto error ;
}
return 0 ;
2005-08-12 01:32:05 +04:00
error :
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR PREFIX " Can't make an fake ECDT \n " ) ;
return ret ;
}
2005-08-12 01:32:05 +04:00
static int __init acpi_ec_get_real_ecdt ( void )
2005-07-23 12:08:00 +04:00
{
if ( acpi_ec_polling_mode )
return acpi_ec_polling_get_real_ecdt ( ) ;
else
return acpi_ec_burst_get_real_ecdt ( ) ;
}
2005-08-12 01:32:05 +04:00
static int __init acpi_ec_polling_get_real_ecdt ( void )
2005-07-23 12:08:00 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
struct acpi_table_ecdt * ecdt_ptr ;
2005-07-23 12:08:00 +04:00
2005-08-12 01:32:05 +04:00
status = acpi_get_firmware_table ( " ECDT " , 1 , ACPI_LOGICAL_ADDRESSING ,
( struct acpi_table_header * * )
& ecdt_ptr ) ;
2005-07-23 12:08:00 +04:00
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
printk ( KERN_INFO PREFIX " Found ECDT \n " ) ;
/*
* Generate a temporary ec context to use until the namespace is scanned
*/
ec_ecdt = kmalloc ( sizeof ( union acpi_ec ) , GFP_KERNEL ) ;
if ( ! ec_ecdt )
return - ENOMEM ;
memset ( ec_ecdt , 0 , sizeof ( union acpi_ec ) ) ;
ec_ecdt - > common . command_addr = ecdt_ptr - > ec_control ;
ec_ecdt - > common . status_addr = ecdt_ptr - > ec_control ;
ec_ecdt - > common . data_addr = ecdt_ptr - > ec_data ;
ec_ecdt - > common . gpe_bit = ecdt_ptr - > gpe_bit ;
spin_lock_init ( & ec_ecdt - > polling . lock ) ;
/* use the GL just to be safe */
ec_ecdt - > common . global_lock = TRUE ;
ec_ecdt - > common . uid = ecdt_ptr - > uid ;
2005-08-12 01:32:05 +04:00
status =
acpi_get_handle ( NULL , ecdt_ptr - > ec_id , & ec_ecdt - > common . handle ) ;
2005-07-23 12:08:00 +04:00
if ( ACPI_FAILURE ( status ) ) {
goto error ;
}
return 0 ;
2005-08-12 01:32:05 +04:00
error :
2005-07-23 12:08:00 +04:00
printk ( KERN_ERR PREFIX " Could not use ECDT \n " ) ;
kfree ( ec_ecdt ) ;
ec_ecdt = NULL ;
return - ENODEV ;
}
2005-08-12 01:32:05 +04:00
static int __init acpi_ec_burst_get_real_ecdt ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
struct acpi_table_ecdt * ecdt_ptr ;
2005-04-17 02:20:36 +04:00
2005-03-19 09:10:05 +03:00
status = acpi_get_firmware_table ( " ECDT " , 1 , ACPI_LOGICAL_ADDRESSING ,
2005-08-12 01:32:05 +04:00
( struct acpi_table_header * * )
& ecdt_ptr ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
printk ( KERN_INFO PREFIX " Found ECDT \n " ) ;
/*
* Generate a temporary ec context to use until the namespace is scanned
*/
2005-07-23 12:08:00 +04:00
ec_ecdt = kmalloc ( sizeof ( union acpi_ec ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ec_ecdt )
return - ENOMEM ;
2005-07-23 12:08:00 +04:00
memset ( ec_ecdt , 0 , sizeof ( union acpi_ec ) ) ;
2005-08-12 01:32:05 +04:00
init_MUTEX ( & ec_ecdt - > burst . sem ) ;
init_waitqueue_head ( & ec_ecdt - > burst . wait ) ;
2005-07-23 12:08:00 +04:00
ec_ecdt - > common . command_addr = ecdt_ptr - > ec_control ;
ec_ecdt - > common . status_addr = ecdt_ptr - > ec_control ;
ec_ecdt - > common . data_addr = ecdt_ptr - > ec_data ;
ec_ecdt - > common . gpe_bit = ecdt_ptr - > gpe_bit ;
2005-04-17 02:20:36 +04:00
/* use the GL just to be safe */
2005-07-23 12:08:00 +04:00
ec_ecdt - > common . global_lock = TRUE ;
ec_ecdt - > common . uid = ecdt_ptr - > uid ;
2005-04-17 02:20:36 +04:00
2005-08-12 01:32:05 +04:00
status =
acpi_get_handle ( NULL , ecdt_ptr - > ec_id , & ec_ecdt - > common . handle ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
goto error ;
}
return 0 ;
2005-08-12 01:32:05 +04:00
error :
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR PREFIX " Could not use ECDT \n " ) ;
kfree ( ec_ecdt ) ;
ec_ecdt = NULL ;
return - ENODEV ;
}
static int __initdata acpi_fake_ecdt_enabled ;
2005-08-12 01:32:05 +04:00
int __init acpi_ec_ecdt_probe ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
acpi_status status ;
int ret ;
2005-04-17 02:20:36 +04:00
ret = acpi_ec_get_real_ecdt ( ) ;
/* Try to make a fake ECDT */
if ( ret & & acpi_fake_ecdt_enabled ) {
ret = acpi_ec_fake_ecdt ( ) ;
}
if ( ret )
return 0 ;
/*
* Install GPE handler
*/
2005-07-23 12:08:00 +04:00
status = acpi_install_gpe_handler ( NULL , ec_ecdt - > common . gpe_bit ,
2005-08-12 01:32:05 +04:00
ACPI_GPE_EDGE_TRIGGERED ,
& acpi_ec_gpe_handler , ec_ecdt ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
goto error ;
}
2005-08-12 01:32:05 +04:00
acpi_set_gpe_type ( NULL , ec_ecdt - > common . gpe_bit , ACPI_GPE_TYPE_RUNTIME ) ;
acpi_enable_gpe ( NULL , ec_ecdt - > common . gpe_bit , ACPI_NOT_ISR ) ;
status = acpi_install_address_space_handler ( ACPI_ROOT_OBJECT ,
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ,
& acpi_ec_space_setup ,
ec_ecdt ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) ) {
2005-07-23 12:08:00 +04:00
acpi_remove_gpe_handler ( NULL , ec_ecdt - > common . gpe_bit ,
2005-08-12 01:32:05 +04:00
& acpi_ec_gpe_handler ) ;
2005-04-17 02:20:36 +04:00
goto error ;
}
return 0 ;
2005-08-12 01:32:05 +04:00
error :
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR PREFIX " Could not use ECDT \n " ) ;
kfree ( ec_ecdt ) ;
ec_ecdt = NULL ;
return - ENODEV ;
}
2005-08-12 01:32:05 +04:00
static int __init acpi_ec_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 01:32:05 +04:00
int result = 0 ;
2005-04-17 02:20:36 +04:00
ACPI_FUNCTION_TRACE ( " acpi_ec_init " ) ;
if ( acpi_disabled )
return_VALUE ( 0 ) ;
acpi_ec_dir = proc_mkdir ( ACPI_EC_CLASS , acpi_root_dir ) ;
if ( ! acpi_ec_dir )
return_VALUE ( - ENODEV ) ;
/* Now register the driver for the EC */
result = acpi_bus_register_driver ( & acpi_ec_driver ) ;
if ( result < 0 ) {
remove_proc_entry ( ACPI_EC_CLASS , acpi_root_dir ) ;
return_VALUE ( - ENODEV ) ;
}
return_VALUE ( result ) ;
}
subsys_initcall ( acpi_ec_init ) ;
/* EC driver currently not unloadable */
#if 0
2005-08-12 01:32:05 +04:00
static void __exit acpi_ec_exit ( void )
2005-04-17 02:20:36 +04:00
{
ACPI_FUNCTION_TRACE ( " acpi_ec_exit " ) ;
acpi_bus_unregister_driver ( & acpi_ec_driver ) ;
remove_proc_entry ( ACPI_EC_CLASS , acpi_root_dir ) ;
return_VOID ;
}
2005-08-12 01:32:05 +04:00
# endif /* 0 */
2005-04-17 02:20:36 +04:00
static int __init acpi_fake_ecdt_setup ( char * str )
{
acpi_fake_ecdt_enabled = 1 ;
return 0 ;
}
2005-08-04 01:38:04 +04:00
2005-04-17 02:20:36 +04:00
__setup ( " acpi_fake_ecdt " , acpi_fake_ecdt_setup ) ;
2005-07-23 12:08:00 +04:00
static int __init acpi_ec_set_polling_mode ( char * str )
{
2005-08-04 01:38:04 +04:00
int burst ;
if ( ! get_option ( & str , & burst ) )
return 0 ;
if ( burst ) {
acpi_ec_polling_mode = EC_BURST ;
acpi_ec_driver . ops . add = acpi_ec_burst_add ;
} else {
acpi_ec_polling_mode = EC_POLLING ;
acpi_ec_driver . ops . add = acpi_ec_polling_add ;
}
2005-08-12 01:32:05 +04:00
printk ( KERN_INFO PREFIX " EC %s mode. \n " , burst ? " burst " : " polling " ) ;
2005-07-23 12:08:00 +04:00
return 0 ;
}
2005-08-12 01:32:05 +04:00
2005-08-04 01:38:04 +04:00
__setup ( " ec_burst= " , acpi_ec_set_polling_mode ) ;