2005-04-17 02:20:36 +04:00
/*
2008-09-25 21:00:31 +04:00
* ec . c - ACPI Embedded Controller Driver ( v2 .1 )
2005-04-17 02:20:36 +04:00
*
2008-09-25 21:00:31 +04:00
* Copyright ( C ) 2006 - 2008 Alexey Starikovskiy < astarikovskiy @ suse . de >
2007-03-07 22:28:00 +03:00
* Copyright ( C ) 2006 Denis Sadykov < denis . m . sadykov @ intel . com >
2005-04-17 02:20:36 +04:00
* 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 .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
2008-09-25 21:00:31 +04:00
/* Uncomment next line to get verbose printout */
2008-01-24 06:34:09 +03:00
/* #define DEBUG */
2005-04-17 02:20:36 +04:00
# 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>
2007-05-29 16:43:02 +04:00
# include <linux/list.h>
2008-09-25 21:00:31 +04:00
# include <linux/spinlock.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <acpi/acpi_bus.h>
# include <acpi/acpi_drivers.h>
2009-07-07 07:40:19 +04:00
# include <linux/dmi.h>
2005-04-17 02:20:36 +04:00
# define ACPI_EC_CLASS "embedded_controller"
# define ACPI_EC_DEVICE_NAME "Embedded Controller"
# define ACPI_EC_FILE_INFO "info"
2007-05-29 16:43:02 +04:00
2006-12-07 18:42:16 +03:00
# define PREFIX "ACPI: EC: "
2007-05-29 16:42:57 +04:00
2006-09-26 19:50:33 +04:00
/* EC status register */
2005-04-17 02:20:36 +04:00
# 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 */
2007-05-29 16:42:57 +04:00
2006-09-26 19:50:33 +04:00
/* EC commands */
2006-12-07 18:42:17 +03:00
enum ec_command {
2006-12-07 18:42:17 +03:00
ACPI_EC_COMMAND_READ = 0x80 ,
ACPI_EC_COMMAND_WRITE = 0x81 ,
ACPI_EC_BURST_ENABLE = 0x82 ,
ACPI_EC_BURST_DISABLE = 0x83 ,
ACPI_EC_COMMAND_QUERY = 0x84 ,
2006-12-07 18:42:17 +03:00
} ;
2007-05-29 16:43:02 +04:00
2006-12-07 18:42:16 +03:00
# define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */
2006-09-26 19:50:33 +04:00
# define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
2009-04-01 08:25:10 +04:00
# define ACPI_EC_CDELAY 10 /* Wait 10us before polling EC */
2009-08-30 03:06:14 +04:00
# define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */
2006-09-26 19:50:33 +04:00
2008-11-09 19:01:06 +03:00
# define ACPI_EC_STORM_THRESHOLD 8 / * number of false interrupts
2008-09-25 21:00:31 +04:00
per one transaction */
2007-10-22 14:18:30 +04:00
enum {
EC_FLAGS_QUERY_PENDING , /* Query is pending */
2008-09-25 21:00:31 +04:00
EC_FLAGS_GPE_STORM , /* GPE storm detected */
EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and
* OpReg are installed */
2005-04-17 02:20:36 +04:00
} ;
2006-09-26 19:50:33 +04:00
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
2007-03-07 22:28:00 +03:00
/* External interfaces use first EC only, so remember */
2007-05-29 16:43:02 +04:00
typedef int ( * acpi_ec_query_func ) ( void * data ) ;
struct acpi_ec_query_handler {
struct list_head node ;
acpi_ec_query_func func ;
acpi_handle handle ;
void * data ;
u8 query_bit ;
} ;
2008-09-26 00:54:28 +04:00
struct transaction {
2008-09-25 21:00:31 +04:00
const u8 * wdata ;
u8 * rdata ;
unsigned short irq_count ;
2008-09-26 00:54:28 +04:00
u8 command ;
2008-11-12 01:40:19 +03:00
u8 wi ;
u8 ri ;
2008-09-25 21:00:31 +04:00
u8 wlen ;
u8 rlen ;
2008-11-08 21:42:30 +03:00
bool done ;
2008-09-25 21:00:31 +04:00
} ;
2006-12-19 23:56:12 +03:00
static struct acpi_ec {
2006-09-26 19:50:33 +04:00
acpi_handle handle ;
2006-12-07 18:42:16 +03:00
unsigned long gpe ;
2006-09-26 19:50:33 +04:00
unsigned long command_addr ;
unsigned long data_addr ;
2006-09-26 19:50:33 +04:00
unsigned long global_lock ;
2007-10-22 14:18:30 +04:00
unsigned long flags ;
2006-12-07 18:42:16 +03:00
struct mutex lock ;
2006-09-26 19:50:33 +04:00
wait_queue_head_t wait ;
2007-05-29 16:43:02 +04:00
struct list_head list ;
2008-09-26 00:54:28 +04:00
struct transaction * curr ;
spinlock_t curr_lock ;
2007-03-07 22:28:00 +03:00
} * boot_ec , * first_ec ;
2006-09-26 19:50:33 +04:00
2009-02-21 20:18:13 +03:00
static int EC_FLAGS_MSI ; /* Out-of-spec MSI controller */
2009-10-02 20:21:33 +04:00
static int EC_FLAGS_VALIDATE_ECDT ; /* ASUStec ECDTs need to be validated */
2009-10-02 20:21:40 +04:00
static int EC_FLAGS_SKIP_DSDT_SCAN ; /* Not all BIOS survive early DSDT scan */
2009-02-21 20:18:13 +03:00
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Transaction Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2006-09-26 19:50:33 +04:00
static inline u8 acpi_ec_read_status ( struct acpi_ec * ec )
2005-04-17 02:20:36 +04:00
{
2007-11-21 03:23:26 +03:00
u8 x = inb ( ec - > command_addr ) ;
2008-01-24 06:33:06 +03:00
pr_debug ( PREFIX " ---> status = 0x%2.2x \n " , x ) ;
2007-11-21 03:23:26 +03:00
return x ;
2005-03-19 09:10:05 +03:00
}
2006-09-26 19:50:33 +04:00
static inline u8 acpi_ec_read_data ( struct acpi_ec * ec )
2006-09-26 19:50:33 +04:00
{
2007-11-21 03:23:26 +03:00
u8 x = inb ( ec - > data_addr ) ;
2008-01-24 06:33:06 +03:00
pr_debug ( PREFIX " ---> data = 0x%2.2x \n " , x ) ;
2008-09-25 21:00:31 +04:00
return x ;
2006-09-26 19:50:33 +04:00
}
2006-09-26 19:50:33 +04:00
static inline void acpi_ec_write_cmd ( struct acpi_ec * ec , u8 command )
2005-07-23 12:08:00 +04:00
{
2008-01-24 06:33:06 +03:00
pr_debug ( PREFIX " <--- command = 0x%2.2x \n " , command ) ;
2006-09-26 19:50:33 +04:00
outb ( command , ec - > command_addr ) ;
2005-07-23 12:08:00 +04:00
}
2006-09-26 19:50:33 +04:00
static inline void acpi_ec_write_data ( struct acpi_ec * ec , u8 data )
2005-07-23 12:08:00 +04:00
{
2008-01-24 06:33:06 +03:00
pr_debug ( PREFIX " <--- data = 0x%2.2x \n " , data ) ;
2006-09-26 19:50:33 +04:00
outb ( data , ec - > data_addr ) ;
2006-09-26 19:50:33 +04:00
}
2005-07-23 12:08:00 +04:00
2008-09-25 21:00:31 +04:00
static int ec_transaction_done ( struct acpi_ec * ec )
2006-09-26 19:50:33 +04:00
{
2008-09-25 21:00:31 +04:00
unsigned long flags ;
int ret = 0 ;
2008-09-26 00:54:28 +04:00
spin_lock_irqsave ( & ec - > curr_lock , flags ) ;
2008-11-08 21:42:30 +03:00
if ( ! ec - > curr | | ec - > curr - > done )
2008-09-25 21:00:31 +04:00
ret = 1 ;
2008-09-26 00:54:28 +04:00
spin_unlock_irqrestore ( & ec - > curr_lock , flags ) ;
2008-09-25 21:00:31 +04:00
return ret ;
2005-07-23 12:08:00 +04:00
}
2005-03-19 09:10:05 +03:00
2008-11-12 01:40:19 +03:00
static void start_transaction ( struct acpi_ec * ec )
{
ec - > curr - > irq_count = ec - > curr - > wi = ec - > curr - > ri = 0 ;
ec - > curr - > done = false ;
acpi_ec_write_cmd ( ec , ec - > curr - > command ) ;
}
2009-08-30 03:06:14 +04:00
static void advance_transaction ( struct acpi_ec * ec , u8 status )
2006-09-26 19:50:33 +04:00
{
2008-09-25 21:00:31 +04:00
unsigned long flags ;
2008-09-26 00:54:28 +04:00
spin_lock_irqsave ( & ec - > curr_lock , flags ) ;
if ( ! ec - > curr )
2008-09-25 21:00:31 +04:00
goto unlock ;
2008-11-12 01:40:19 +03:00
if ( ec - > curr - > wlen > ec - > curr - > wi ) {
if ( ( status & ACPI_EC_FLAG_IBF ) = = 0 )
acpi_ec_write_data ( ec ,
ec - > curr - > wdata [ ec - > curr - > wi + + ] ) ;
else
2008-11-08 21:42:30 +03:00
goto err ;
2008-11-12 01:40:19 +03:00
} else if ( ec - > curr - > rlen > ec - > curr - > ri ) {
2008-09-25 21:00:31 +04:00
if ( ( status & ACPI_EC_FLAG_OBF ) = = 1 ) {
2008-11-12 01:40:19 +03:00
ec - > curr - > rdata [ ec - > curr - > ri + + ] = acpi_ec_read_data ( ec ) ;
if ( ec - > curr - > rlen = = ec - > curr - > ri )
2008-11-08 21:42:30 +03:00
ec - > curr - > done = true ;
2008-09-25 21:00:31 +04:00
} else
2008-11-08 21:42:30 +03:00
goto err ;
2008-11-12 01:40:19 +03:00
} else if ( ec - > curr - > wlen = = ec - > curr - > wi & &
( status & ACPI_EC_FLAG_IBF ) = = 0 )
2008-11-08 21:42:30 +03:00
ec - > curr - > done = true ;
goto unlock ;
err :
/* false interrupt, state didn't change */
2008-11-13 12:00:03 +03:00
if ( in_interrupt ( ) )
+ + ec - > curr - > irq_count ;
2008-09-25 21:00:31 +04:00
unlock :
2008-09-26 00:54:28 +04:00
spin_unlock_irqrestore ( & ec - > curr_lock , flags ) ;
2008-03-21 17:07:03 +03:00
}
2008-01-24 06:28:34 +03:00
2008-09-25 21:00:31 +04:00
static void acpi_ec_gpe_query ( void * ec_cxt ) ;
static int ec_check_sci ( struct acpi_ec * ec , u8 state )
2006-09-26 19:50:33 +04:00
{
2008-09-25 21:00:31 +04:00
if ( state & ACPI_EC_FLAG_SCI ) {
if ( ! test_and_set_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) )
return acpi_os_execute ( OSL_EC_BURST_HANDLER ,
acpi_ec_gpe_query , ec ) ;
}
return 0 ;
}
static int ec_poll ( struct acpi_ec * ec )
{
2009-08-30 03:06:14 +04:00
unsigned long flags ;
int repeat = 2 ; /* number of command restarts */
while ( repeat - - ) {
unsigned long delay = jiffies +
msecs_to_jiffies ( ACPI_EC_DELAY ) ;
do {
/* don't sleep with disabled interrupts */
if ( EC_FLAGS_MSI | | irqs_disabled ( ) ) {
udelay ( ACPI_EC_MSI_UDELAY ) ;
if ( ec_transaction_done ( ec ) )
return 0 ;
} else {
if ( wait_event_timeout ( ec - > wait ,
ec_transaction_done ( ec ) ,
msecs_to_jiffies ( 1 ) ) )
return 0 ;
}
advance_transaction ( ec , acpi_ec_read_status ( ec ) ) ;
} while ( time_before ( jiffies , delay ) ) ;
2009-10-02 02:53:15 +04:00
if ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_IBF )
2009-08-30 03:06:14 +04:00
break ;
pr_debug ( PREFIX " controller reset, restart transaction \n " ) ;
spin_lock_irqsave ( & ec - > curr_lock , flags ) ;
start_transaction ( ec ) ;
spin_unlock_irqrestore ( & ec - > curr_lock , flags ) ;
2006-12-07 18:42:16 +03:00
}
2008-03-21 17:07:15 +03:00
return - ETIME ;
2005-04-17 02:20:36 +04:00
}
2008-09-26 00:54:28 +04:00
static int acpi_ec_transaction_unlocked ( struct acpi_ec * ec ,
2009-08-30 03:06:14 +04:00
struct transaction * t )
2005-07-23 12:08:00 +04:00
{
2008-09-25 21:00:31 +04:00
unsigned long tmp ;
int ret = 0 ;
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " transaction start \n " ) ;
2008-09-25 21:00:31 +04:00
/* disable GPE during transaction if storm is detected */
if ( test_bit ( EC_FLAGS_GPE_STORM , & ec - > flags ) ) {
2008-10-25 21:48:46 +04:00
acpi_disable_gpe ( NULL , ec - > gpe ) ;
2006-09-26 19:50:33 +04:00
}
2009-02-21 20:18:13 +03:00
if ( EC_FLAGS_MSI )
2009-08-30 03:06:14 +04:00
udelay ( ACPI_EC_MSI_UDELAY ) ;
2008-09-25 21:00:31 +04:00
/* start transaction */
2008-09-26 00:54:28 +04:00
spin_lock_irqsave ( & ec - > curr_lock , tmp ) ;
2008-09-25 21:00:31 +04:00
/* following two actions should be kept atomic */
2008-09-26 00:54:28 +04:00
ec - > curr = t ;
2008-11-12 01:40:19 +03:00
start_transaction ( ec ) ;
2008-09-26 00:54:28 +04:00
if ( ec - > curr - > command = = ACPI_EC_COMMAND_QUERY )
2007-10-22 14:18:30 +04:00
clear_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) ;
2008-09-26 00:54:28 +04:00
spin_unlock_irqrestore ( & ec - > curr_lock , tmp ) ;
2009-08-30 03:06:14 +04:00
ret = ec_poll ( ec ) ;
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " transaction end \n " ) ;
2008-09-26 00:54:28 +04:00
spin_lock_irqsave ( & ec - > curr_lock , tmp ) ;
ec - > curr = NULL ;
spin_unlock_irqrestore ( & ec - > curr_lock , tmp ) ;
2008-09-25 21:00:31 +04:00
if ( test_bit ( EC_FLAGS_GPE_STORM , & ec - > flags ) ) {
/* check if we received SCI during transaction */
ec_check_sci ( ec , acpi_ec_read_status ( ec ) ) ;
/* it is safe to enable GPE outside of transaction */
2008-10-25 21:48:46 +04:00
acpi_enable_gpe ( NULL , ec - > gpe ) ;
2009-08-30 03:06:14 +04:00
} else if ( t - > irq_count > ACPI_EC_STORM_THRESHOLD ) {
2008-11-01 14:05:26 +03:00
pr_info ( PREFIX " GPE storm detected, "
" transactions will use polling mode \n " ) ;
2008-09-25 21:00:31 +04:00
set_bit ( EC_FLAGS_GPE_STORM , & ec - > flags ) ;
}
return ret ;
}
static int ec_check_ibf0 ( struct acpi_ec * ec )
{
u8 status = acpi_ec_read_status ( ec ) ;
return ( status & ACPI_EC_FLAG_IBF ) = = 0 ;
2005-07-23 12:08:00 +04:00
}
2008-10-16 02:02:33 +04:00
static int ec_wait_ibf0 ( struct acpi_ec * ec )
{
unsigned long delay = jiffies + msecs_to_jiffies ( ACPI_EC_DELAY ) ;
/* interrupt wait manually if GPE mode is not active */
while ( time_before ( jiffies , delay ) )
2009-08-30 03:06:14 +04:00
if ( wait_event_timeout ( ec - > wait , ec_check_ibf0 ( ec ) ,
msecs_to_jiffies ( 1 ) ) )
2008-10-16 02:02:33 +04:00
return 0 ;
return - ETIME ;
2005-07-23 12:08:00 +04:00
}
2009-08-30 03:06:14 +04:00
static int acpi_ec_transaction ( struct acpi_ec * ec , struct transaction * t )
2005-04-17 02:20:36 +04:00
{
2006-09-05 20:12:24 +04:00
int status ;
2005-08-12 01:32:05 +04:00
u32 glk ;
2008-09-26 00:54:28 +04:00
if ( ! ec | | ( ! t ) | | ( t - > wlen & & ! t - > wdata ) | | ( t - > rlen & & ! t - > rdata ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2008-09-26 00:54:28 +04:00
if ( t - > rdata )
memset ( t - > rdata , 0 , t - > rlen ) ;
2006-12-07 18:42:17 +03:00
mutex_lock ( & ec - > lock ) ;
2006-09-26 19:50:33 +04:00
if ( ec - > global_lock ) {
2005-04-17 02:20:36 +04:00
status = acpi_acquire_global_lock ( ACPI_EC_UDELAY_GLK , & glk ) ;
2007-02-15 23:16:18 +03:00
if ( ACPI_FAILURE ( status ) ) {
2008-09-25 21:00:31 +04:00
status = - ENODEV ;
goto unlock ;
2007-02-15 23:16:18 +03:00
}
2005-04-17 02:20:36 +04:00
}
2008-10-16 02:02:33 +04:00
if ( ec_wait_ibf0 ( ec ) ) {
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " input buffer is not empty, "
" aborting transaction \n " ) ;
2008-09-25 21:00:31 +04:00
status = - ETIME ;
2005-03-19 09:10:05 +03:00
goto end ;
2005-08-10 09:40:00 +04:00
}
2009-08-30 03:06:14 +04:00
status = acpi_ec_transaction_unlocked ( ec , t ) ;
2008-09-25 21:00:31 +04:00
end :
2006-09-26 19:50:33 +04:00
if ( ec - > global_lock )
2005-04-17 02:20:36 +04:00
acpi_release_global_lock ( glk ) ;
2008-09-25 21:00:31 +04:00
unlock :
2006-12-07 18:42:17 +03:00
mutex_unlock ( & ec - > lock ) ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2008-12-09 22:45:30 +03:00
static int acpi_ec_burst_enable ( struct acpi_ec * ec )
2007-03-07 22:28:00 +03:00
{
u8 d ;
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = ACPI_EC_BURST_ENABLE ,
. wdata = NULL , . rdata = & d ,
. wlen = 0 , . rlen = 1 } ;
2009-08-30 03:06:14 +04:00
return acpi_ec_transaction ( ec , & t ) ;
2007-03-07 22:28:00 +03:00
}
2008-12-09 22:45:30 +03:00
static int acpi_ec_burst_disable ( struct acpi_ec * ec )
2007-03-07 22:28:00 +03:00
{
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = ACPI_EC_BURST_DISABLE ,
. wdata = NULL , . rdata = NULL ,
. wlen = 0 , . rlen = 0 } ;
2008-09-25 21:00:31 +04:00
return ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_BURST ) ?
2009-08-30 03:06:14 +04:00
acpi_ec_transaction ( ec , & t ) : 0 ;
2007-03-07 22:28:00 +03:00
}
2006-12-07 18:42:17 +03:00
static int acpi_ec_read ( struct acpi_ec * ec , u8 address , u8 * data )
2006-09-26 19:50:33 +04:00
{
int result ;
u8 d ;
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = ACPI_EC_COMMAND_READ ,
. wdata = & address , . rdata = & d ,
. wlen = 1 , . rlen = 1 } ;
2006-09-26 19:50:33 +04:00
2009-08-30 03:06:14 +04:00
result = acpi_ec_transaction ( ec , & t ) ;
2006-09-26 19:50:33 +04:00
* data = d ;
return result ;
}
2006-09-26 19:50:33 +04:00
2006-09-26 19:50:33 +04:00
static int acpi_ec_write ( struct acpi_ec * ec , u8 address , u8 data )
{
2006-12-07 18:42:17 +03:00
u8 wdata [ 2 ] = { address , data } ;
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = ACPI_EC_COMMAND_WRITE ,
. wdata = wdata , . rdata = NULL ,
. wlen = 2 , . rlen = 0 } ;
2009-08-30 03:06:14 +04:00
return acpi_ec_transaction ( ec , & t ) ;
2006-09-26 19:50:33 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Externally callable EC access functions . For now , assume 1 EC only
*/
2007-03-07 22:28:00 +03:00
int ec_burst_enable ( void )
{
if ( ! first_ec )
return - ENODEV ;
2007-03-07 22:28:00 +03:00
return acpi_ec_burst_enable ( first_ec ) ;
2007-03-07 22:28:00 +03:00
}
EXPORT_SYMBOL ( ec_burst_enable ) ;
int ec_burst_disable ( void )
{
if ( ! first_ec )
return - ENODEV ;
2007-03-07 22:28:00 +03:00
return acpi_ec_burst_disable ( first_ec ) ;
2007-03-07 22:28:00 +03:00
}
EXPORT_SYMBOL ( ec_burst_disable ) ;
2006-12-07 18:42:17 +03:00
int ec_read ( u8 addr , u8 * val )
2005-04-17 02:20:36 +04:00
{
int err ;
2006-09-26 19:50:33 +04:00
u8 temp_data ;
2005-04-17 02:20:36 +04:00
if ( ! first_ec )
return - ENODEV ;
2007-03-07 22:28:00 +03:00
err = acpi_ec_read ( first_ec , addr , & temp_data ) ;
2005-04-17 02:20:36 +04:00
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
{
int err ;
if ( ! first_ec )
return - ENODEV ;
2007-03-07 22:28:00 +03:00
err = acpi_ec_write ( first_ec , addr , val ) ;
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_write ) ;
2006-10-27 09:47:34 +04:00
int ec_transaction ( u8 command ,
2007-03-08 02:29:35 +03:00
const u8 * wdata , unsigned wdata_len ,
2007-05-04 16:16:19 +04:00
u8 * rdata , unsigned rdata_len ,
int force_poll )
2005-07-23 12:08:00 +04:00
{
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = command ,
. wdata = wdata , . rdata = rdata ,
. wlen = wdata_len , . rlen = rdata_len } ;
2006-09-05 20:12:24 +04:00
if ( ! first_ec )
return - ENODEV ;
2005-07-23 12:08:00 +04:00
2009-08-30 03:06:14 +04:00
return acpi_ec_transaction ( first_ec , & t ) ;
2005-07-23 12:08:00 +04:00
}
2005-04-17 02:20:36 +04:00
2006-10-04 06:49:00 +04:00
EXPORT_SYMBOL ( ec_transaction ) ;
2006-12-07 18:42:17 +03:00
static int acpi_ec_query ( struct acpi_ec * ec , u8 * data )
2006-09-26 19:50:33 +04:00
{
int result ;
2006-12-07 18:42:17 +03:00
u8 d ;
2008-09-26 00:54:28 +04:00
struct transaction t = { . command = ACPI_EC_COMMAND_QUERY ,
. wdata = NULL , . rdata = & d ,
. wlen = 0 , . rlen = 1 } ;
2006-12-07 18:42:17 +03:00
if ( ! ec | | ! data )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-12-07 18:42:17 +03: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-10 09:40:00 +04:00
2009-08-30 03:06:14 +04:00
result = acpi_ec_transaction ( ec , & t ) ;
2006-12-07 18:42:17 +03:00
if ( result )
return result ;
2005-04-17 02:20:36 +04:00
2006-12-07 18:42:17 +03:00
if ( ! d )
return - ENODATA ;
2005-04-17 02:20:36 +04:00
2006-12-07 18:42:17 +03:00
* data = d ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Event Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2007-05-29 16:43:02 +04:00
int acpi_ec_add_query_handler ( struct acpi_ec * ec , u8 query_bit ,
acpi_handle handle , acpi_ec_query_func func ,
void * data )
{
struct acpi_ec_query_handler * handler =
kzalloc ( sizeof ( struct acpi_ec_query_handler ) , GFP_KERNEL ) ;
if ( ! handler )
return - ENOMEM ;
handler - > query_bit = query_bit ;
handler - > handle = handle ;
handler - > func = func ;
handler - > data = data ;
mutex_lock ( & ec - > lock ) ;
2007-09-26 19:43:22 +04:00
list_add ( & handler - > node , & ec - > list ) ;
2007-05-29 16:43:02 +04:00
mutex_unlock ( & ec - > lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( acpi_ec_add_query_handler ) ;
void acpi_ec_remove_query_handler ( struct acpi_ec * ec , u8 query_bit )
{
2007-10-24 20:26:00 +04:00
struct acpi_ec_query_handler * handler , * tmp ;
2007-05-29 16:43:02 +04:00
mutex_lock ( & ec - > lock ) ;
2007-10-24 20:26:00 +04:00
list_for_each_entry_safe ( handler , tmp , & ec - > list , node ) {
2007-05-29 16:43:02 +04:00
if ( query_bit = = handler - > query_bit ) {
list_del ( & handler - > node ) ;
kfree ( handler ) ;
}
}
mutex_unlock ( & ec - > lock ) ;
}
EXPORT_SYMBOL_GPL ( acpi_ec_remove_query_handler ) ;
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-07-23 12:08:00 +04:00
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = ec_cxt ;
2006-09-26 19:50:33 +04:00
u8 value = 0 ;
2007-05-29 16:43:02 +04:00
struct acpi_ec_query_handler * handler , copy ;
2005-07-23 12:08:00 +04:00
2006-12-07 18:42:16 +03:00
if ( ! ec | | acpi_ec_query ( ec , & value ) )
2006-12-07 18:42:16 +03:00
return ;
2007-05-29 16:43:02 +04:00
mutex_lock ( & ec - > lock ) ;
list_for_each_entry ( handler , & ec - > list , node ) {
if ( value = = handler - > query_bit ) {
/* have custom handler for this bit */
memcpy ( & copy , handler , sizeof ( copy ) ) ;
mutex_unlock ( & ec - > lock ) ;
if ( copy . func ) {
copy . func ( copy . data ) ;
} else if ( copy . handle ) {
acpi_evaluate_object ( copy . handle , NULL , NULL , NULL ) ;
}
return ;
}
}
mutex_unlock ( & ec - > lock ) ;
2005-07-23 12:08:00 +04:00
}
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-04-17 02:20:36 +04:00
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = data ;
2008-09-25 21:00:31 +04:00
u8 status ;
2007-05-04 16:16:19 +04:00
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " ~~~> interrupt \n " ) ;
2008-09-25 21:00:31 +04:00
status = acpi_ec_read_status ( ec ) ;
2009-08-30 03:06:14 +04:00
advance_transaction ( ec , status ) ;
if ( ec_transaction_done ( ec ) & & ( status & ACPI_EC_FLAG_IBF ) = = 0 )
wake_up ( & ec - > wait ) ;
2008-09-25 21:00:31 +04:00
ec_check_sci ( ec , status ) ;
return ACPI_INTERRUPT_HANDLED ;
2008-03-21 17:07:03 +03:00
}
2005-04-17 02:20:36 +04:00
/* --------------------------------------------------------------------------
Address Space Management
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static acpi_status
2007-05-29 16:42:52 +04:00
acpi_ec_space_handler ( u32 function , acpi_physical_address address ,
u32 bits , acpi_integer * value ,
2005-08-12 01:32:05 +04:00
void * handler_context , void * region_context )
2005-04-17 02:20:36 +04:00
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = handler_context ;
2008-01-11 02:42:51 +03:00
int result = 0 , i ;
2007-05-29 16:42:52 +04:00
u8 temp = 0 ;
2005-04-17 02:20:36 +04:00
if ( ( address > 0xFF ) | | ! value | | ! handler_context )
2006-06-27 08:41:40 +04:00
return AE_BAD_PARAMETER ;
2005-04-17 02:20:36 +04:00
2007-05-29 16:42:52 +04:00
if ( function ! = ACPI_READ & & function ! = ACPI_WRITE )
2006-06-27 08:41:40 +04:00
return AE_BAD_PARAMETER ;
2005-04-17 02:20:36 +04:00
2007-05-29 16:42:52 +04:00
if ( bits ! = 8 & & acpi_strict )
return AE_BAD_PARAMETER ;
2005-04-17 02:20:36 +04:00
2009-08-28 23:29:44 +04:00
if ( EC_FLAGS_MSI )
acpi_ec_burst_enable ( ec ) ;
2008-01-11 02:42:57 +03:00
2008-01-11 02:42:51 +03:00
if ( function = = ACPI_READ ) {
result = acpi_ec_read ( ec , address , & temp ) ;
* value = temp ;
} else {
temp = 0xff & ( * value ) ;
result = acpi_ec_write ( ec , address , temp ) ;
}
for ( i = 8 ; unlikely ( bits - i > 0 ) ; i + = 8 ) {
+ + address ;
2007-05-29 16:42:52 +04:00
if ( function = = ACPI_READ ) {
result = acpi_ec_read ( ec , address , & temp ) ;
( * value ) | = ( ( acpi_integer ) temp ) < < i ;
} else {
temp = 0xff & ( ( * value ) > > i ) ;
result = acpi_ec_write ( ec , address , temp ) ;
}
2005-04-17 02:20:36 +04:00
}
2009-08-28 23:29:44 +04:00
if ( EC_FLAGS_MSI )
acpi_ec_burst_disable ( ec ) ;
2008-01-11 02:42:57 +03:00
2005-04-17 02:20:36 +04:00
switch ( result ) {
case - EINVAL :
2006-06-27 08:41:40 +04:00
return AE_BAD_PARAMETER ;
2005-04-17 02:20:36 +04:00
break ;
case - ENODEV :
2006-06-27 08:41:40 +04:00
return AE_NOT_FOUND ;
2005-04-17 02:20:36 +04:00
break ;
case - ETIME :
2006-06-27 08:41:40 +04:00
return AE_TIME ;
2005-04-17 02:20:36 +04:00
break ;
default :
2006-06-27 08:41:40 +04:00
return AE_OK ;
2005-04-17 02:20:36 +04:00
}
}
/* --------------------------------------------------------------------------
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
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = seq - > private ;
2005-04-17 02:20:36 +04:00
if ( ! ec )
goto end ;
2007-03-07 22:28:00 +03:00
seq_printf ( seq , " gpe: \t \t \t 0x%02x \n " , ( u32 ) ec - > gpe ) ;
seq_printf ( seq , " ports: \t \t \t 0x%02x, 0x%02x \n " ,
( unsigned ) ec - > command_addr , ( unsigned ) ec - > data_addr ) ;
seq_printf ( seq , " use global lock: \t %s \n " ,
2006-09-26 19:50:33 +04:00
ec - > global_lock ? " yes " : " no " ) ;
2005-08-12 01:32:05 +04:00
end :
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int acpi_ec_info_open_fs ( struct inode * inode , struct file * file )
{
return single_open ( file , acpi_ec_read_info , PDE ( inode ) - > data ) ;
}
2009-01-12 02:07:55 +03:00
static const 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
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 ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 12:02:27 +04:00
entry = proc_create_data ( ACPI_EC_FILE_INFO , S_IRUGO ,
acpi_device_dir ( device ) ,
& acpi_ec_info_ops , acpi_driver_data ( device ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! entry )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
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
{
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 ;
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* --------------------------------------------------------------------------
Driver Interface
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2007-03-07 22:28:00 +03:00
static acpi_status
ec_parse_io_ports ( struct acpi_resource * resource , void * context ) ;
static struct acpi_ec * make_acpi_ec ( void )
{
struct acpi_ec * ec = kzalloc ( sizeof ( struct acpi_ec ) , GFP_KERNEL ) ;
if ( ! ec )
return NULL ;
2007-10-22 14:18:30 +04:00
ec - > flags = 1 < < EC_FLAGS_QUERY_PENDING ;
2007-03-07 22:28:00 +03:00
mutex_init ( & ec - > lock ) ;
init_waitqueue_head ( & ec - > wait ) ;
2007-05-29 16:43:02 +04:00
INIT_LIST_HEAD ( & ec - > list ) ;
2008-09-26 00:54:28 +04:00
spin_lock_init ( & ec - > curr_lock ) ;
2007-03-07 22:28:00 +03:00
return ec ;
}
2007-05-29 16:43:02 +04:00
2007-08-14 09:03:42 +04:00
static acpi_status
acpi_ec_register_query_methods ( acpi_handle handle , u32 level ,
void * context , void * * return_value )
{
2008-12-16 11:46:12 +03:00
char node_name [ 5 ] ;
struct acpi_buffer buffer = { sizeof ( node_name ) , node_name } ;
2007-08-14 09:03:42 +04:00
struct acpi_ec * ec = context ;
int value = 0 ;
2008-12-16 11:46:12 +03:00
acpi_status status ;
status = acpi_get_name ( handle , ACPI_SINGLE_NAME , & buffer ) ;
if ( ACPI_SUCCESS ( status ) & & sscanf ( node_name , " _Q%x " , & value ) = = 1 ) {
2007-08-14 09:03:42 +04:00
acpi_ec_add_query_handler ( ec , value , handle , NULL , NULL ) ;
}
return AE_OK ;
}
2007-08-04 01:52:48 +04:00
static acpi_status
ec_parse_device ( acpi_handle handle , u32 Level , void * context , void * * retval )
2007-05-29 16:43:02 +04:00
{
2007-08-04 01:52:48 +04:00
acpi_status status ;
2008-11-03 22:26:40 +03:00
unsigned long long tmp = 0 ;
2007-08-04 01:52:48 +04:00
struct acpi_ec * ec = context ;
2009-04-01 09:33:15 +04:00
/* clear addr values, ec_parse_io_ports depend on it */
ec - > command_addr = ec - > data_addr = 0 ;
2007-08-04 01:52:48 +04:00
status = acpi_walk_resources ( handle , METHOD_NAME__CRS ,
ec_parse_io_ports , ec ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
2007-05-29 16:43:02 +04:00
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
2008-10-10 10:22:59 +04:00
status = acpi_evaluate_integer ( handle , " _GPE " , NULL , & tmp ) ;
2007-08-04 01:52:48 +04:00
if ( ACPI_FAILURE ( status ) )
return status ;
2008-10-10 10:22:59 +04:00
ec - > gpe = tmp ;
2007-05-29 16:43:02 +04:00
/* Use the global lock for all EC transactions? */
2008-11-03 22:26:40 +03:00
tmp = 0 ;
2008-10-10 10:22:59 +04:00
acpi_evaluate_integer ( handle , " _GLK " , NULL , & tmp ) ;
ec - > global_lock = tmp ;
2007-05-29 16:43:02 +04:00
ec - > handle = handle ;
2007-08-04 01:52:48 +04:00
return AE_CTRL_TERMINATE ;
2007-05-29 16:43:02 +04:00
}
2009-06-23 00:41:30 +04:00
static int ec_install_handlers ( struct acpi_ec * ec )
{
acpi_status status ;
if ( test_bit ( EC_FLAGS_HANDLERS_INSTALLED , & ec - > flags ) )
return 0 ;
status = acpi_install_gpe_handler ( NULL , ec - > gpe ,
ACPI_GPE_EDGE_TRIGGERED ,
& acpi_ec_gpe_handler , ec ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
acpi_set_gpe_type ( NULL , ec - > gpe , ACPI_GPE_TYPE_RUNTIME ) ;
acpi_enable_gpe ( NULL , ec - > gpe ) ;
status = acpi_install_address_space_handler ( ec - > handle ,
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ,
NULL , ec ) ;
if ( ACPI_FAILURE ( status ) ) {
if ( status = = AE_NOT_FOUND ) {
/*
* Maybe OS fails in evaluating the _REG object .
* The AE_NOT_FOUND error will be ignored and OS
* continue to initialize EC .
*/
printk ( KERN_ERR " Fail in evaluating the _REG object "
" of EC device. Broken bios is suspected. \n " ) ;
} else {
acpi_remove_gpe_handler ( NULL , ec - > gpe ,
& acpi_ec_gpe_handler ) ;
return - ENODEV ;
}
}
set_bit ( EC_FLAGS_HANDLERS_INSTALLED , & ec - > flags ) ;
return 0 ;
}
2007-09-06 03:56:38 +04:00
static void ec_remove_handlers ( struct acpi_ec * ec )
{
if ( ACPI_FAILURE ( acpi_remove_address_space_handler ( ec - > handle ,
ACPI_ADR_SPACE_EC , & acpi_ec_space_handler ) ) )
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " failed to remove space handler \n " ) ;
2007-09-06 03:56:38 +04:00
if ( ACPI_FAILURE ( acpi_remove_gpe_handler ( NULL , ec - > gpe ,
& acpi_ec_gpe_handler ) ) )
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " failed to remove gpe handler \n " ) ;
2008-09-25 21:00:31 +04:00
clear_bit ( EC_FLAGS_HANDLERS_INSTALLED , & ec - > flags ) ;
2007-09-06 03:56:38 +04:00
}
2006-09-26 19:50:33 +04:00
static int acpi_ec_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
2006-09-26 19:50:33 +04:00
struct acpi_ec * ec = NULL ;
2009-06-23 00:41:35 +04:00
int ret ;
2005-07-23 12:08:00 +04:00
2007-03-07 22:28:00 +03:00
strcpy ( acpi_device_name ( device ) , ACPI_EC_DEVICE_NAME ) ;
strcpy ( acpi_device_class ( device ) , ACPI_EC_CLASS ) ;
2007-09-06 03:56:38 +04:00
/* Check for boot EC */
2008-03-24 23:22:36 +03:00
if ( boot_ec & &
( boot_ec - > handle = = device - > handle | |
boot_ec - > handle = = ACPI_ROOT_OBJECT ) ) {
ec = boot_ec ;
boot_ec = NULL ;
} else {
ec = make_acpi_ec ( ) ;
if ( ! ec )
return - ENOMEM ;
2009-04-01 09:33:15 +04:00
}
if ( ec_parse_device ( device - > handle , 0 , ec , NULL ) ! =
AE_CTRL_TERMINATE ) {
2008-03-24 23:22:36 +03:00
kfree ( ec ) ;
return - EINVAL ;
2007-09-06 03:56:38 +04:00
}
2007-03-07 22:28:00 +03:00
ec - > handle = device - > handle ;
2008-03-24 23:22:36 +03:00
/* Find and register all query methods */
acpi_walk_namespace ( ACPI_TYPE_METHOD , ec - > handle , 1 ,
2009-11-13 05:06:08 +03:00
acpi_ec_register_query_methods , NULL , ec , NULL ) ;
2008-03-24 23:22:36 +03:00
2007-09-06 03:56:38 +04:00
if ( ! first_ec )
first_ec = ec ;
2008-09-23 01:37:34 +04:00
device - > driver_data = ec ;
2007-03-07 22:28:00 +03:00
acpi_ec_add_fs ( device ) ;
2007-11-21 03:23:26 +03:00
pr_info ( PREFIX " GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx \n " ,
2007-09-06 03:56:38 +04:00
ec - > gpe , ec - > command_addr , ec - > data_addr ) ;
2009-06-23 00:41:30 +04:00
ret = ec_install_handlers ( ec ) ;
/* EC is fully operational, allow queries */
clear_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
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
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec ;
2007-07-29 19:00:37 +04:00
struct acpi_ec_query_handler * handler , * tmp ;
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
ec = acpi_driver_data ( device ) ;
2009-06-23 00:41:40 +04:00
ec_remove_handlers ( ec ) ;
2007-05-29 16:43:02 +04:00
mutex_lock ( & ec - > lock ) ;
2007-07-29 19:00:37 +04:00
list_for_each_entry_safe ( handler , tmp , & ec - > list , node ) {
2007-05-29 16:43:02 +04:00
list_del ( & handler - > node ) ;
kfree ( handler ) ;
}
mutex_unlock ( & ec - > lock ) ;
2005-04-17 02:20:36 +04:00
acpi_ec_remove_fs ( device ) ;
2008-09-23 01:37:34 +04:00
device - > driver_data = NULL ;
2007-03-07 22:28:00 +03:00
if ( ec = = first_ec )
2007-03-07 22:28:00 +03:00
first_ec = NULL ;
2007-09-06 03:56:38 +04:00
kfree ( ec ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static acpi_status
2007-03-07 22:28:00 +03:00
ec_parse_io_ports ( struct acpi_resource * resource , void * context )
2005-04-17 02:20:36 +04:00
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = context ;
2005-04-17 02:20:36 +04:00
2007-03-07 22:28:00 +03:00
if ( resource - > type ! = ACPI_RESOURCE_TYPE_IO )
2005-04-17 02:20:36 +04:00
return AE_OK ;
/*
* The first address region returned is the data port , and
* the second address region returned is the status / command
* port .
*/
2007-03-07 22:28:00 +03:00
if ( ec - > data_addr = = 0 )
2006-09-26 19:50:33 +04:00
ec - > data_addr = resource - > data . io . minimum ;
2007-03-07 22:28:00 +03:00
else if ( ec - > command_addr = = 0 )
2006-09-26 19:50:33 +04:00
ec - > command_addr = resource - > data . io . minimum ;
2007-03-07 22:28:00 +03:00
else
2005-04-17 02:20:36 +04:00
return AE_CTRL_TERMINATE ;
return AE_OK ;
}
2008-01-01 22:12:55 +03:00
int __init acpi_boot_ec_enable ( void )
{
2008-09-25 21:00:31 +04:00
if ( ! boot_ec | | test_bit ( EC_FLAGS_HANDLERS_INSTALLED , & boot_ec - > flags ) )
2008-01-01 22:12:55 +03:00
return 0 ;
if ( ! ec_install_handlers ( boot_ec ) ) {
first_ec = boot_ec ;
return 0 ;
}
return - EFAULT ;
}
2008-03-21 17:07:21 +03:00
static const struct acpi_device_id ec_device_ids [ ] = {
{ " PNP0C09 " , 0 } ,
{ " " , 0 } ,
} ;
2009-10-02 20:21:40 +04:00
/* Some BIOS do not survive early DSDT scan, skip it */
static int ec_skip_dsdt_scan ( const struct dmi_system_id * id )
{
EC_FLAGS_SKIP_DSDT_SCAN = 1 ;
return 0 ;
}
2009-10-02 20:21:33 +04:00
/* ASUStek often supplies us with broken ECDT, validate it */
static int ec_validate_ecdt ( const struct dmi_system_id * id )
{
EC_FLAGS_VALIDATE_ECDT = 1 ;
return 0 ;
}
/* MSI EC needs special treatment, enable it */
static int ec_flag_msi ( const struct dmi_system_id * id )
{
2009-12-22 10:42:52 +03:00
printk ( KERN_DEBUG PREFIX " Detected MSI hardware, enabling workarounds. \n " ) ;
2009-10-02 20:21:33 +04:00
EC_FLAGS_MSI = 1 ;
EC_FLAGS_VALIDATE_ECDT = 1 ;
return 0 ;
}
static struct dmi_system_id __initdata ec_dmi_table [ ] = {
2009-10-02 20:21:40 +04:00
{
ec_skip_dsdt_scan , " Compal JFL92 " , {
DMI_MATCH ( DMI_BIOS_VENDOR , " COMPAL " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " JFL92 " ) } , NULL } ,
2009-10-02 20:21:33 +04:00
{
ec_flag_msi , " MSI hardware " , {
2009-12-22 10:42:52 +03:00
DMI_MATCH ( DMI_BIOS_VENDOR , " Micro-Star " ) } , NULL } ,
{
ec_flag_msi , " MSI hardware " , {
DMI_MATCH ( DMI_SYS_VENDOR , " Micro-Star " ) } , NULL } ,
{
ec_flag_msi , " MSI hardware " , {
DMI_MATCH ( DMI_CHASSIS_VENDOR , " MICRO-Star " ) } , NULL } ,
2009-10-02 20:21:33 +04:00
{
ec_validate_ecdt , " ASUS hardware " , {
DMI_MATCH ( DMI_BIOS_VENDOR , " ASUS " ) } , NULL } ,
{ } ,
} ;
2007-03-07 22:28:00 +03:00
int __init acpi_ec_ecdt_probe ( void )
{
acpi_status status ;
2009-01-14 02:57:53 +03:00
struct acpi_ec * saved_ec = NULL ;
2005-08-12 01:32:05 +04:00
struct acpi_table_ecdt * ecdt_ptr ;
2005-07-23 12:08:00 +04:00
2007-03-07 22:28:00 +03:00
boot_ec = make_acpi_ec ( ) ;
if ( ! boot_ec )
2007-03-07 22:28:00 +03:00
return - ENOMEM ;
/*
* Generate a boot ec context
*/
2009-10-02 20:21:33 +04:00
dmi_check_system ( ec_dmi_table ) ;
2007-02-02 19:48:22 +03:00
status = acpi_get_table ( ACPI_SIG_ECDT , 1 ,
( struct acpi_table_header * * ) & ecdt_ptr ) ;
2007-08-04 01:52:48 +04:00
if ( ACPI_SUCCESS ( status ) ) {
2007-11-21 03:23:26 +03:00
pr_info ( PREFIX " EC description table is found, configuring boot EC \n " ) ;
2007-08-04 01:52:48 +04:00
boot_ec - > command_addr = ecdt_ptr - > control . address ;
boot_ec - > data_addr = ecdt_ptr - > data . address ;
boot_ec - > gpe = ecdt_ptr - > gpe ;
2008-03-11 07:27:16 +03:00
boot_ec - > handle = ACPI_ROOT_OBJECT ;
2008-03-24 23:22:36 +03:00
acpi_get_handle ( ACPI_ROOT_OBJECT , ecdt_ptr - > id , & boot_ec - > handle ) ;
2009-01-14 02:57:53 +03:00
/* Don't trust ECDT, which comes from ASUSTek */
2009-10-02 20:21:33 +04:00
if ( ! EC_FLAGS_VALIDATE_ECDT )
2008-11-27 01:11:53 +03:00
goto install ;
2009-01-14 02:57:53 +03:00
saved_ec = kmalloc ( sizeof ( struct acpi_ec ) , GFP_KERNEL ) ;
if ( ! saved_ec )
return - ENOMEM ;
2009-04-01 09:33:15 +04:00
memcpy ( saved_ec , boot_ec , sizeof ( struct acpi_ec ) ) ;
2008-11-27 01:11:53 +03:00
/* fall through */
2007-08-04 01:52:48 +04:00
}
2009-10-02 20:21:33 +04:00
2009-10-02 20:21:40 +04:00
if ( EC_FLAGS_SKIP_DSDT_SCAN )
return - ENODEV ;
2008-11-27 01:11:53 +03:00
/* This workaround is needed only on some broken machines,
* which require early EC , but fail to provide ECDT */
printk ( KERN_DEBUG PREFIX " Look up EC in DSDT \n " ) ;
status = acpi_get_devices ( ec_device_ids [ 0 ] . id , ec_parse_device ,
boot_ec , NULL ) ;
/* Check that acpi_get_devices actually find something */
if ( ACPI_FAILURE ( status ) | | ! boot_ec - > handle )
goto error ;
2009-01-14 02:57:53 +03:00
if ( saved_ec ) {
/* try to find good ECDT from ASUSTek */
if ( saved_ec - > command_addr ! = boot_ec - > command_addr | |
saved_ec - > data_addr ! = boot_ec - > data_addr | |
saved_ec - > gpe ! = boot_ec - > gpe | |
saved_ec - > handle ! = boot_ec - > handle )
pr_info ( PREFIX " ASUSTek keeps feeding us with broken "
" ECDT tables, which are very hard to workaround. "
" Trying to use DSDT EC info instead. Please send "
" output of acpidump to linux-acpi@vger.kernel.org \n " ) ;
kfree ( saved_ec ) ;
saved_ec = NULL ;
} else {
/* We really need to limit this workaround, the only ASUS,
* which needs it , has fake EC . _INI method , so use it as flag .
* Keep boot_ec struct as it will be needed soon .
*/
acpi_handle dummy ;
if ( ! dmi_name_in_vendors ( " ASUS " ) | |
ACPI_FAILURE ( acpi_get_handle ( boot_ec - > handle , " _INI " ,
& dummy ) ) )
return - ENODEV ;
}
2008-11-27 01:11:53 +03:00
install :
if ( ! ec_install_handlers ( boot_ec ) ) {
2007-03-07 22:28:00 +03:00
first_ec = boot_ec ;
2007-03-07 22:28:00 +03:00
return 0 ;
2007-03-07 22:28:00 +03:00
}
2008-11-27 01:11:53 +03:00
error :
2007-03-07 22:28:00 +03:00
kfree ( boot_ec ) ;
boot_ec = NULL ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2008-03-21 17:07:21 +03:00
static int acpi_ec_suspend ( struct acpi_device * device , pm_message_t state )
{
struct acpi_ec * ec = acpi_driver_data ( device ) ;
/* Stop using GPE */
2008-10-25 21:48:46 +04:00
acpi_disable_gpe ( NULL , ec - > gpe ) ;
2008-03-21 17:07:21 +03:00
return 0 ;
}
static int acpi_ec_resume ( struct acpi_device * device )
{
struct acpi_ec * ec = acpi_driver_data ( device ) ;
/* Enable use of GPE back */
2008-10-25 21:48:46 +04:00
acpi_enable_gpe ( NULL , ec - > gpe ) ;
2008-03-21 17:07:21 +03:00
return 0 ;
}
static struct acpi_driver acpi_ec_driver = {
. name = " ec " ,
. class = ACPI_EC_CLASS ,
. ids = ec_device_ids ,
. ops = {
. add = acpi_ec_add ,
. remove = acpi_ec_remove ,
. suspend = acpi_ec_suspend ,
. resume = acpi_ec_resume ,
} ,
} ;
2009-03-25 01:49:48 +03:00
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_ec_dir = proc_mkdir ( ACPI_EC_CLASS , acpi_root_dir ) ;
if ( ! acpi_ec_dir )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
/* 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 ) ;
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 result ;
2005-04-17 02:20:36 +04:00
}
/* 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_bus_unregister_driver ( & acpi_ec_driver ) ;
remove_proc_entry ( ACPI_EC_CLASS , acpi_root_dir ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 14:18:43 +04:00
# endif /* 0 */