2005-04-17 02:20:36 +04:00
/*
2007-03-07 22:28:00 +03:00
* ec . c - ACPI Embedded Controller Driver ( v2 .0 )
2005-04-17 02:20:36 +04:00
*
2007-03-07 22:28:00 +03:00
* Copyright ( C ) 2006 , 2007 Alexey Starikovskiy < alexey . y . starikovskiy @ intel . com >
* 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 .
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*/
# 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>
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 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
# undef PREFIX
# define PREFIX "ACPI: EC: "
2007-05-29 16:42:57 +04:00
2007-11-21 03:23:26 +03:00
/* Uncomment next line to get verbose print outs*/
/* #define DEBUG */
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-09-26 19:50:33 +04:00
/* EC events */
2006-12-07 18:42:17 +03:00
enum ec_event {
2006-09-26 19:50:33 +04:00
ACPI_EC_EVENT_OBF_1 = 1 , /* Output buffer full */
2007-10-22 14:18:30 +04:00
ACPI_EC_EVENT_IBF_0 , /* Input buffer empty */
2006-09-26 19:50:33 +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 */
2007-10-22 14:18:30 +04:00
enum {
EC_FLAGS_WAIT_GPE = 0 , /* Don't check status until GPE arrives */
EC_FLAGS_QUERY_PENDING , /* Query is pending */
2007-10-22 14:18:43 +04:00
EC_FLAGS_GPE_MODE , /* Expect GPE to be sent for status change */
2007-11-19 01:37:03 +03:00
EC_FLAGS_NO_ADDRESS_GPE , /* Expect GPE only for non-address event */
EC_FLAGS_ADDRESS , /* Address is being written */
2007-11-21 03:23:32 +03:00
EC_FLAGS_NO_WDATA_GPE , /* Don't expect WDATA GPE event */
EC_FLAGS_WDATA , /* Data is being written */
2008-01-24 06:28:34 +03:00
EC_FLAGS_NO_OBF1_GPE , /* Don't expect GPE before read */
2007-10-22 14:18:30 +04:00
} ;
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 ) ;
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
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id ec_device_ids [ ] = {
{ " PNP0C09 " , 0 } ,
{ " " , 0 } ,
} ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_ec_driver = {
2007-02-13 07:33:40 +03:00
. name = " ec " ,
2005-08-12 01:32:05 +04:00
. class = ACPI_EC_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = ec_device_ids ,
2005-08-12 01:32:05 +04:00
. ops = {
2006-09-26 19:50:33 +04:00
. add = acpi_ec_add ,
2005-08-12 01:32:05 +04:00
. remove = acpi_ec_remove ,
. start = acpi_ec_start ,
. stop = acpi_ec_stop ,
} ,
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 ;
} ;
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 ;
2007-09-06 03:56:38 +04:00
u8 handlers_installed ;
2007-03-07 22:28:00 +03:00
} * boot_ec , * first_ec ;
2006-09-26 19:50:33 +04: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 ) ;
2006-09-26 19:50:33 +04:00
return inb ( ec - > data_addr ) ;
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
2007-10-22 14:18:30 +04:00
static inline int acpi_ec_check_status ( struct acpi_ec * ec , enum ec_event event )
2006-09-26 19:50:33 +04:00
{
2007-10-22 14:18:30 +04:00
if ( test_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) )
2007-03-08 02:29:35 +03:00
return 0 ;
2006-12-07 18:42:17 +03:00
if ( event = = ACPI_EC_EVENT_OBF_1 ) {
2007-10-22 14:18:30 +04:00
if ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_OBF )
2006-09-26 19:50:33 +04:00
return 1 ;
2006-12-07 18:42:17 +03:00
} else if ( event = = ACPI_EC_EVENT_IBF_0 ) {
2007-10-22 14:18:30 +04:00
if ( ! ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_IBF ) )
2006-09-26 19:50:33 +04:00
return 1 ;
2005-07-23 12:08:00 +04:00
}
2006-09-26 19:50:33 +04:00
return 0 ;
2005-07-23 12:08:00 +04:00
}
2005-03-19 09:10:05 +03:00
2007-10-22 14:18:30 +04:00
static int acpi_ec_wait ( struct acpi_ec * ec , enum ec_event event , int force_poll )
2006-09-26 19:50:33 +04:00
{
2007-11-19 01:37:03 +03:00
int ret = 0 ;
2008-01-24 06:28:34 +03:00
if ( unlikely ( event = = ACPI_EC_EVENT_OBF_1 & &
test_bit ( EC_FLAGS_NO_OBF1_GPE , & ec - > flags ) ) )
force_poll = 1 ;
2007-11-19 01:37:03 +03:00
if ( unlikely ( test_bit ( EC_FLAGS_ADDRESS , & ec - > flags ) & &
test_bit ( EC_FLAGS_NO_ADDRESS_GPE , & ec - > flags ) ) )
force_poll = 1 ;
2007-11-21 03:23:32 +03:00
if ( unlikely ( test_bit ( EC_FLAGS_WDATA , & ec - > flags ) & &
test_bit ( EC_FLAGS_NO_WDATA_GPE , & ec - > flags ) ) )
force_poll = 1 ;
2007-10-22 14:18:43 +04:00
if ( likely ( test_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) ) & &
likely ( ! force_poll ) ) {
2007-10-22 14:18:30 +04:00
if ( wait_event_timeout ( ec - > wait , acpi_ec_check_status ( ec , event ) ,
msecs_to_jiffies ( ACPI_EC_DELAY ) ) )
2007-11-19 01:37:03 +03:00
goto end ;
2007-10-22 14:18:30 +04:00
clear_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
if ( acpi_ec_check_status ( ec , event ) ) {
2008-01-24 06:28:34 +03:00
if ( event = = ACPI_EC_EVENT_OBF_1 ) {
/* miss OBF_1 GPE, don't expect it */
pr_info ( PREFIX " missing OBF confirmation, "
" don't expect it any longer. \n " ) ;
set_bit ( EC_FLAGS_NO_OBF1_GPE , & ec - > flags ) ;
} else if ( test_bit ( EC_FLAGS_ADDRESS , & ec - > flags ) ) {
2007-11-19 01:37:03 +03:00
/* miss address GPE, don't expect it anymore */
2007-11-21 03:23:32 +03:00
pr_info ( PREFIX " missing address confirmation, "
2007-11-19 01:37:03 +03:00
" don't expect it any longer. \n " ) ;
set_bit ( EC_FLAGS_NO_ADDRESS_GPE , & ec - > flags ) ;
2007-11-21 03:23:32 +03:00
} else if ( test_bit ( EC_FLAGS_WDATA , & ec - > flags ) ) {
/* miss write data GPE, don't expect it */
pr_info ( PREFIX " missing write data confirmation, "
" don't expect it any longer. \n " ) ;
set_bit ( EC_FLAGS_NO_WDATA_GPE , & ec - > flags ) ;
2007-10-22 14:19:03 +04:00
} else {
2007-10-22 14:18:56 +04:00
/* missing GPEs, switch back to poll mode */
2007-11-21 03:23:26 +03:00
if ( printk_ratelimit ( ) )
2007-11-21 03:23:32 +03:00
pr_info ( PREFIX " missing confirmations, "
2007-11-21 03:23:26 +03:00
" switch off interrupt mode. \n " ) ;
2007-10-22 14:18:56 +04:00
clear_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) ;
2007-10-22 14:19:03 +04:00
}
2007-11-19 01:37:03 +03:00
goto end ;
2006-09-26 19:50:33 +04:00
}
2007-10-22 14:18:43 +04:00
} else {
unsigned long delay = jiffies + msecs_to_jiffies ( ACPI_EC_DELAY ) ;
clear_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
while ( time_before ( jiffies , delay ) ) {
if ( acpi_ec_check_status ( ec , event ) )
2007-11-19 01:37:03 +03:00
goto end ;
2007-10-22 14:18:43 +04:00
}
2006-12-07 18:42:16 +03:00
}
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " acpi_ec_wait timeout, "
2007-10-22 14:18:30 +04:00
" status = %d, expect_event = %d \n " ,
acpi_ec_read_status ( ec ) , event ) ;
2007-11-19 01:37:03 +03:00
ret = - ETIME ;
end :
clear_bit ( EC_FLAGS_ADDRESS , & ec - > flags ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-09-26 19:50:33 +04:00
static int acpi_ec_transaction_unlocked ( struct acpi_ec * ec , u8 command ,
2006-12-07 18:42:17 +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
{
2006-12-07 18:42:16 +03:00
int result = 0 ;
2007-10-22 14:18:30 +04:00
set_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
2006-09-26 19:50:33 +04:00
acpi_ec_write_cmd ( ec , command ) ;
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " transaction start \n " ) ;
2006-12-07 18:42:17 +03:00
for ( ; wdata_len > 0 ; - - wdata_len ) {
2007-10-22 14:18:30 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBF_0 , force_poll ) ;
2006-12-07 18:42:16 +03:00
if ( result ) {
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX
2006-12-07 18:42:17 +03:00
" write_cmd timeout, command = %d \n " , command ) ;
2006-12-07 18:42:16 +03:00
goto end ;
}
2007-11-19 01:37:03 +03:00
/* mark the address byte written to EC */
if ( rdata_len + wdata_len > 1 )
set_bit ( EC_FLAGS_ADDRESS , & ec - > flags ) ;
2007-10-22 14:18:30 +04:00
set_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
2006-09-26 19:50:33 +04:00
acpi_ec_write_data ( ec , * ( wdata + + ) ) ;
2006-09-26 19:50:33 +04:00
}
2005-07-23 12:08:00 +04:00
2006-12-07 18:42:16 +03:00
if ( ! rdata_len ) {
2007-11-21 03:23:32 +03:00
set_bit ( EC_FLAGS_WDATA , & ec - > flags ) ;
2007-10-22 14:18:30 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBF_0 , force_poll ) ;
2006-12-07 18:42:16 +03:00
if ( result ) {
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX
2006-12-07 18:42:17 +03:00
" finish-write timeout, command = %d \n " , command ) ;
2006-12-07 18:42:16 +03:00
goto end ;
}
2007-10-22 14:18:30 +04:00
} else if ( command = = ACPI_EC_COMMAND_QUERY )
clear_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) ;
2005-07-23 12:08:00 +04:00
2006-12-07 18:42:17 +03:00
for ( ; rdata_len > 0 ; - - rdata_len ) {
2007-10-22 14:18:30 +04:00
result = acpi_ec_wait ( ec , ACPI_EC_EVENT_OBF_1 , force_poll ) ;
2006-12-07 18:42:16 +03:00
if ( result ) {
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " read timeout, command = %d \n " , command ) ;
2006-12-07 18:42:16 +03:00
goto end ;
}
2007-10-22 14:18:36 +04:00
/* Don't expect GPE after last read */
if ( rdata_len > 1 )
set_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
2006-09-26 19:50:33 +04:00
* ( rdata + + ) = acpi_ec_read_data ( ec ) ;
2006-09-26 19:50:33 +04:00
}
2006-12-07 18:42:16 +03:00
end :
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " transaction end \n " ) ;
2006-12-07 18:42:16 +03:00
return result ;
2005-07-23 12:08:00 +04:00
}
2006-09-26 19:50:33 +04:00
static int acpi_ec_transaction ( struct acpi_ec * ec , u8 command ,
2006-12-07 18:42:17 +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-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 ;
2005-04-17 02:20:36 +04:00
2006-09-05 20:12:24 +04:00
if ( ! ec | | ( wdata_len & & ! wdata ) | | ( rdata_len & & ! rdata ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-12-07 18:42:17 +03:00
if ( rdata )
memset ( rdata , 0 , rdata_len ) ;
2005-04-17 02:20:36 +04:00
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 ) ) {
mutex_unlock ( & ec - > lock ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2007-02-15 23:16:18 +03:00
}
2005-04-17 02:20:36 +04:00
}
2005-03-19 09:10:05 +03:00
2007-10-22 14:18:30 +04:00
status = acpi_ec_wait ( ec , ACPI_EC_EVENT_IBF_0 , 0 ) ;
2005-08-10 09:40:00 +04:00
if ( status ) {
2007-11-21 03:23:26 +03:00
pr_err ( PREFIX " input buffer is not empty, "
" aborting transaction \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
2006-12-07 18:42:17 +03:00
status = acpi_ec_transaction_unlocked ( ec , command ,
wdata , wdata_len ,
2007-05-04 16:16:19 +04:00
rdata , rdata_len ,
force_poll ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 18:42:17 +03:00
end :
2005-04-17 02:20:36 +04:00
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 ) ;
2006-12-07 18:42:17 +03:00
mutex_unlock ( & ec - > lock ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
2007-03-07 22:28:00 +03:00
/*
* Note : samsung nv5000 doesn ' t work with ec burst mode .
* http : //bugzilla.kernel.org/show_bug.cgi?id=4980
*/
int acpi_ec_burst_enable ( struct acpi_ec * ec )
{
u8 d ;
2007-05-04 16:16:19 +04:00
return acpi_ec_transaction ( ec , ACPI_EC_BURST_ENABLE , NULL , 0 , & d , 1 , 0 ) ;
2007-03-07 22:28:00 +03:00
}
int acpi_ec_burst_disable ( struct acpi_ec * ec )
{
2007-05-04 16:16:19 +04:00
return acpi_ec_transaction ( ec , ACPI_EC_BURST_DISABLE , NULL , 0 , NULL , 0 , 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 ;
result = acpi_ec_transaction ( ec , ACPI_EC_COMMAND_READ ,
2007-05-04 16:16:19 +04:00
& address , 1 , & d , 1 , 0 ) ;
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 } ;
return acpi_ec_transaction ( ec , ACPI_EC_COMMAND_WRITE ,
2007-05-04 16:16:19 +04:00
wdata , 2 , NULL , 0 , 0 ) ;
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
{
2006-09-05 20:12:24 +04:00
if ( ! first_ec )
return - ENODEV ;
2005-07-23 12:08:00 +04:00
2007-03-07 22:28:00 +03:00
return acpi_ec_transaction ( first_ec , command , wdata ,
2007-05-04 16:16:19 +04:00
wdata_len , rdata , rdata_len ,
force_poll ) ;
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 ;
2005-04-17 02:20:36 +04:00
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
2007-05-04 16:16:19 +04:00
result = acpi_ec_transaction ( ec , ACPI_EC_COMMAND_QUERY , NULL , 0 , & d , 1 , 0 ) ;
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
{
2005-08-12 01:32:05 +04:00
acpi_status status = AE_OK ;
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec = data ;
2007-05-04 16:16:19 +04:00
2007-11-21 03:23:26 +03:00
pr_debug ( PREFIX " ~~~> interrupt \n " ) ;
2007-10-22 14:18:30 +04:00
clear_bit ( EC_FLAGS_WAIT_GPE , & ec - > flags ) ;
2007-10-22 14:18:43 +04:00
if ( test_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) )
2006-12-07 18:42:16 +03:00
wake_up ( & ec - > wait ) ;
2005-03-19 09:10:05 +03:00
2007-10-22 14:18:30 +04:00
if ( acpi_ec_read_status ( ec ) & ACPI_EC_FLAG_SCI ) {
if ( ! test_and_set_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) )
status = acpi_os_execute ( OSL_EC_BURST_HANDLER ,
acpi_ec_gpe_query , ec ) ;
2007-10-22 14:19:03 +04:00
} else if ( unlikely ( ! test_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) ) ) {
/* this is non-query, must be confirmation */
2007-11-21 03:23:26 +03:00
if ( printk_ratelimit ( ) )
pr_info ( PREFIX " non-query interrupt received, "
" switching to interrupt mode \n " ) ;
2007-10-22 14:18:43 +04:00
set_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) ;
2007-10-22 14:19:03 +04:00
}
2006-12-07 18:42:16 +03:00
2007-10-22 14:18:43 +04:00
return ACPI_SUCCESS ( status ) ?
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
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 ;
2007-05-29 16:42:52 +04:00
int result = 0 , i = 0 ;
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
2007-05-29 16:42:52 +04:00
while ( bits - i > 0 ) {
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 ) ;
}
i + = 8 ;
+ + address ;
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 ) ;
}
2006-09-26 19:50:33 +04:00
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
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
}
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 )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
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 ;
}
2006-06-27 08:41:40 +04:00
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 ) ;
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 )
{
struct acpi_namespace_node * node = handle ;
struct acpi_ec * ec = context ;
int value = 0 ;
if ( sscanf ( node - > name . ascii , " _Q%x " , & value ) = = 1 ) {
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 ;
struct acpi_ec * ec = context ;
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 */
2007-08-04 01:52:48 +04:00
status = acpi_evaluate_integer ( handle , " _GPE " , NULL , & ec - > gpe ) ;
if ( ACPI_FAILURE ( status ) )
return status ;
2007-08-14 09:03:42 +04:00
/* Find and register all query methods */
acpi_walk_namespace ( ACPI_TYPE_METHOD , handle , 1 ,
acpi_ec_register_query_methods , ec , NULL ) ;
2007-05-29 16:43:02 +04:00
/* Use the global lock for all EC transactions? */
acpi_evaluate_integer ( handle , " _GLK " , NULL , & ec - > global_lock ) ;
ec - > handle = handle ;
2007-08-04 01:52:48 +04:00
return AE_CTRL_TERMINATE ;
2007-05-29 16:43:02 +04:00
}
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 " ) ;
2007-09-06 03:56:38 +04:00
ec - > handlers_installed = 0 ;
}
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 ;
2005-07-23 12:08:00 +04:00
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
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 */
if ( boot_ec ) {
if ( boot_ec - > handle = = device - > handle ) {
/* Pre-loaded EC from DSDT, just move pointer */
ec = boot_ec ;
boot_ec = NULL ;
goto end ;
} else if ( boot_ec - > handle = = ACPI_ROOT_OBJECT ) {
/* ECDT-based EC, time to shut it down */
ec_remove_handlers ( boot_ec ) ;
kfree ( boot_ec ) ;
first_ec = boot_ec = NULL ;
}
}
2007-03-07 22:28:00 +03:00
ec = make_acpi_ec ( ) ;
2005-07-23 12:08:00 +04:00
if ( ! ec )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2006-09-26 19:50:33 +04:00
2007-08-04 01:52:48 +04:00
if ( ec_parse_device ( device - > handle , 0 , ec , NULL ) ! =
AE_CTRL_TERMINATE ) {
2007-03-07 22:28:00 +03:00
kfree ( ec ) ;
return - EINVAL ;
2005-07-23 12:08:00 +04:00
}
2007-03-07 22:28:00 +03:00
ec - > handle = device - > handle ;
2007-09-06 03:56:38 +04:00
end :
if ( ! first_ec )
first_ec = ec ;
2007-03-07 22:28:00 +03:00
acpi_driver_data ( device ) = ec ;
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 ) ;
2007-11-21 03:23:26 +03:00
pr_info ( PREFIX " driver started in %s mode \n " ,
2007-10-22 14:19:03 +04:00
( test_bit ( EC_FLAGS_GPE_MODE , & ec - > flags ) ) ? " interrupt " : " poll " ) ;
2007-03-07 22:28:00 +03:00
return 0 ;
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 ) ;
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 ) ;
2007-03-07 22:28:00 +03:00
acpi_driver_data ( device ) = 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 ;
}
2007-03-07 22:28:00 +03:00
static int ec_install_handlers ( struct acpi_ec * ec )
{
2007-03-07 22:28:00 +03:00
acpi_status status ;
2007-09-06 03:56:38 +04:00
if ( ec - > handlers_installed )
return 0 ;
2007-03-07 22:28:00 +03:00
status = acpi_install_gpe_handler ( NULL , ec - > gpe ,
ACPI_GPE_EDGE_TRIGGERED ,
& acpi_ec_gpe_handler , ec ) ;
2007-03-07 22:28:00 +03:00
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
2007-03-07 22:28:00 +03:00
2007-03-07 22:28:00 +03:00
acpi_set_gpe_type ( NULL , ec - > gpe , ACPI_GPE_TYPE_RUNTIME ) ;
acpi_enable_gpe ( NULL , ec - > gpe , ACPI_NOT_ISR ) ;
status = acpi_install_address_space_handler ( ec - > handle ,
ACPI_ADR_SPACE_EC ,
& acpi_ec_space_handler ,
& acpi_ec_space_setup , ec ) ;
if ( ACPI_FAILURE ( status ) ) {
acpi_remove_gpe_handler ( NULL , ec - > gpe , & acpi_ec_gpe_handler ) ;
return - ENODEV ;
}
2007-09-06 03:56:38 +04:00
ec - > handlers_installed = 1 ;
2007-03-07 22:28:00 +03:00
return 0 ;
}
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
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec ;
2007-05-29 16:43:02 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
ec = acpi_driver_data ( device ) ;
if ( ! ec )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-09-06 03:56:38 +04:00
ret = ec_install_handlers ( ec ) ;
2007-05-29 16:43:02 +04:00
/* EC is fully operational, allow queries */
2007-10-22 14:18:30 +04:00
clear_bit ( EC_FLAGS_QUERY_PENDING , & ec - > flags ) ;
2007-05-29 16:43:02 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
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
{
2007-03-07 22:28:00 +03:00
struct acpi_ec * ec ;
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 ) ;
2007-03-07 22:28:00 +03:00
if ( ! ec )
return - EINVAL ;
2007-09-06 03:56:38 +04:00
ec_remove_handlers ( ec ) ;
2007-08-24 08:10:11 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-01-01 22:12:55 +03:00
int __init acpi_boot_ec_enable ( void )
{
if ( ! boot_ec | | boot_ec - > handlers_installed )
return 0 ;
if ( ! ec_install_handlers ( boot_ec ) ) {
first_ec = boot_ec ;
return 0 ;
}
return - EFAULT ;
}
2007-03-07 22:28:00 +03:00
int __init acpi_ec_ecdt_probe ( void )
{
int ret ;
acpi_status status ;
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
*/
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 ;
boot_ec - > handle = ACPI_ROOT_OBJECT ;
} else {
2007-11-15 21:52:47 +03:00
/* This workaround is needed only on some broken machines,
* which require early EC , but fail to provide ECDT */
acpi_handle x ;
2007-08-04 01:52:48 +04:00
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 ) ;
2007-08-31 09:05:26 +04:00
/* Check that acpi_get_devices actually find something */
if ( ACPI_FAILURE ( status ) | | ! boot_ec - > handle )
2007-08-04 01:52:48 +04:00
goto error ;
2007-11-15 21:52:47 +03:00
/* We really need to limit this workaround, the only ASUS,
* which needs it , has fake EC . _INI method , so use it as flag .
2008-01-01 22:12:55 +03:00
* Keep boot_ec struct as it will be needed soon .
2007-11-15 21:52:47 +03:00
*/
if ( ACPI_FAILURE ( acpi_get_handle ( boot_ec - > handle , " _INI " , & x ) ) )
2008-01-01 22:12:55 +03:00
return - ENODEV ;
2007-08-04 01:52:48 +04:00
}
2005-04-17 02:20:36 +04:00
2007-03-07 22:28:00 +03:00
ret = ec_install_handlers ( boot_ec ) ;
2007-03-07 22:28:00 +03:00
if ( ! ret ) {
first_ec = boot_ec ;
2007-03-07 22:28:00 +03:00
return 0 ;
2007-03-07 22:28:00 +03:00
}
2007-03-07 22:28:00 +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 ;
}
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
if ( acpi_disabled )
2006-06-27 08:41:40 +04:00
return 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
}
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_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 */