diff options
author | Jeff Garzik <jgarzik@pobox.com> | 2005-08-10 19:46:28 +0200 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-08-10 19:46:28 +0200 |
commit | 2f058256cb64e346f4fb4499ff4e0f1c2791a4b4 (patch) | |
tree | 91e06602f4d3abb6812ea8c9bc9ba4501e14c84e /drivers/acpi/ec.c | |
parent | libata: Update 'passthru' branch for latest libata (diff) | |
parent | [PATCH] Fix ide-disk.c oops caused by hwif == NULL (diff) | |
download | linux-2f058256cb64e346f4fb4499ff4e0f1c2791a4b4.tar.xz linux-2f058256cb64e346f4fb4499ff4e0f1c2791a4b4.zip |
Merge /spare/repo/linux-2.6/
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 1004 |
1 files changed, 857 insertions, 147 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fdf143b405be..1ac5731d45e5 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/interrupt.h> #include <asm/io.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -49,60 +50,149 @@ ACPI_MODULE_NAME ("acpi_ec") #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ +#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #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 */ -#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ -#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ +#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ +#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ +#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ + #define ACPI_EC_COMMAND_READ 0x80 #define ACPI_EC_COMMAND_WRITE 0x81 +#define ACPI_EC_BURST_ENABLE 0x82 +#define ACPI_EC_BURST_DISABLE 0x83 #define ACPI_EC_COMMAND_QUERY 0x84 -static int acpi_ec_add (struct acpi_device *device); +#define EC_POLLING 0xFF +#define EC_BURST 0x00 + + 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); static struct acpi_driver acpi_ec_driver = { .name = ACPI_EC_DRIVER_NAME, .class = ACPI_EC_CLASS, .ids = ACPI_EC_HID, .ops = { - .add = acpi_ec_add, + .add = acpi_ec_polling_add, .remove = acpi_ec_remove, .start = acpi_ec_start, .stop = acpi_ec_stop, }, }; - -struct acpi_ec { - 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; +union acpi_ec { + struct { + 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; + } common; + + struct { + 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; + + struct { + 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; }; +static int acpi_ec_polling_wait ( union acpi_ec *ec, u8 event); +static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event); +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); +static acpi_status __init +acpi_fake_ecdt_polling_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval); + +static acpi_status __init +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); /* If we find an EC via the ECDT, we need to keep a ptr to its context */ -static struct acpi_ec *ec_ecdt; +static union acpi_ec *ec_ecdt; /* External interfaces use first EC only, so remember */ static struct acpi_device *first_ec; +static int acpi_ec_polling_mode = EC_POLLING; /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ +static inline u32 acpi_ec_read_status(union acpi_ec *ec) +{ + u32 status = 0; + + acpi_hw_low_level_read(8, &status, &ec->common.status_addr); + return status; +} + static int acpi_ec_wait ( - struct acpi_ec *ec, + union acpi_ec *ec, + u8 event) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_wait (ec, event); + else + return acpi_ec_burst_wait (ec, event); +} + +static int +acpi_ec_polling_wait ( + union acpi_ec *ec, u8 event) { u32 acpi_ec_status = 0; @@ -115,7 +205,7 @@ acpi_ec_wait ( switch (event) { case ACPI_EC_EVENT_OBF: do { - acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr); if (acpi_ec_status & ACPI_EC_FLAG_OBF) return 0; udelay(ACPI_EC_UDELAY); @@ -123,7 +213,7 @@ acpi_ec_wait ( break; case ACPI_EC_EVENT_IBE: do { - acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr); if (!(acpi_ec_status & ACPI_EC_FLAG_IBF)) return 0; udelay(ACPI_EC_UDELAY); @@ -135,11 +225,129 @@ acpi_ec_wait ( return -ETIME; } +static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_wait"); + + ec->burst.expect_event = event; + smp_mb(); + + result = wait_event_interruptible_timeout(ec->burst.wait, + !ec->burst.expect_event, + msecs_to_jiffies(ACPI_EC_DELAY)); + + ec->burst.expect_event = 0; + smp_mb(); + + if (result < 0){ + ACPI_DEBUG_PRINT((ACPI_DB_ERROR," result = %d ", result)); + return_VALUE(result); + } + + /* + * 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. + */ + switch (event) { + case ACPI_EC_EVENT_OBF: + if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_OBF) + return_VALUE(0); + break; + + case ACPI_EC_EVENT_IBE: + if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) + return_VALUE(0); + break; + } + + return_VALUE(-ETIME); +} + + + +static int +acpi_ec_enter_burst_mode ( + union acpi_ec *ec) +{ + u32 tmp = 0; + int status = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_enter_burst_mode"); + + status = acpi_ec_read_status(ec); + if (status != -EINVAL && + !(status & ACPI_EC_FLAG_BURST)){ + acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (status){ + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + return_VALUE(-EINVAL); + } + acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + if(tmp != 0x90 ) {/* Burst ACK byte*/ + return_VALUE(-EINVAL); + } + } + + atomic_set(&ec->burst.leaving_burst , 0); + return_VALUE(0); +} +static int +acpi_ec_leave_burst_mode ( + union acpi_ec *ec) +{ + int status =0; + + ACPI_FUNCTION_TRACE("acpi_ec_leave_burst_mode"); + + atomic_set(&ec->burst.leaving_burst , 1); + status = acpi_ec_read_status(ec); + if (status != -EINVAL && + (status & ACPI_EC_FLAG_BURST)){ + acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); + if (status){ + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"------->wait fail\n")); + return_VALUE(-EINVAL); + } + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + status = acpi_ec_read_status(ec); + } + + return_VALUE(0); +} static int acpi_ec_read ( - struct acpi_ec *ec, + union acpi_ec *ec, + u8 address, + u32 *data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_read(ec, address, data); + else + return acpi_ec_burst_read(ec, address, data); +} +static int +acpi_ec_write ( + union acpi_ec *ec, + u8 address, + u8 data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_write(ec, address, data); + else + return acpi_ec_burst_write(ec, address, data); +} +static int +acpi_ec_polling_read ( + union acpi_ec *ec, u8 address, u32 *data) { @@ -155,34 +363,33 @@ acpi_ec_read ( *data = 0; - if (ec->global_lock) { + 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->lock, flags); - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr); + spin_lock_irqsave(&ec->polling.lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr); result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); if (result) goto end; - acpi_hw_low_level_write(8, address, &ec->data_addr); + 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->data_addr); + acpi_hw_low_level_read(8, data, &ec->common.data_addr); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", *data, address)); - + end: - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock_irqrestore(&ec->polling.lock, flags); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); return_VALUE(result); @@ -190,8 +397,8 @@ end: static int -acpi_ec_write ( - struct acpi_ec *ec, +acpi_ec_polling_write ( + union acpi_ec *ec, u8 address, u8 data) { @@ -205,25 +412,25 @@ acpi_ec_write ( if (!ec) return_VALUE(-EINVAL); - if (ec->global_lock) { + 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->lock, flags); + spin_lock_irqsave(&ec->polling.lock, flags); - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr); result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); if (result) goto end; - acpi_hw_low_level_write(8, address, &ec->data_addr); + 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->data_addr); + acpi_hw_low_level_write(8, data, &ec->common.data_addr); result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); if (result) goto end; @@ -232,21 +439,172 @@ acpi_ec_write ( data, address)); end: - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock_irqrestore(&ec->polling.lock, flags); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); return_VALUE(result); } +static int +acpi_ec_burst_read ( + union acpi_ec *ec, + u8 address, + u32 *data) +{ + int status = 0; + u32 glk; + + ACPI_FUNCTION_TRACE("acpi_ec_read"); + + if (!ec || !data) + return_VALUE(-EINVAL); + +retry: + *data = 0; + + if (ec->common.global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + WARN_ON(in_interrupt()); + down(&ec->burst.sem); + + if(acpi_ec_enter_burst_mode(ec)) + goto end; + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + if (status) { + goto end; + } + + acpi_hw_low_level_write(8, address, &ec->common.data_addr); + status= acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (status){ + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + goto end; + } + + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", + *data, address)); + +end: + acpi_ec_leave_burst_mode(ec); + up(&ec->burst.sem); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + if(atomic_read(&ec->burst.leaving_burst) == 2){ + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); + while(atomic_read(&ec->burst.pending_gpe)){ + msleep(1); + } + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + goto retry; + } + + return_VALUE(status); +} + + +static int +acpi_ec_burst_write ( + union acpi_ec *ec, + u8 address, + u8 data) +{ + int status = 0; + u32 glk; + u32 tmp; + + ACPI_FUNCTION_TRACE("acpi_ec_write"); + + if (!ec) + return_VALUE(-EINVAL); +retry: + if (ec->common.global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + WARN_ON(in_interrupt()); + down(&ec->burst.sem); + + if(acpi_ec_enter_burst_mode(ec)) + goto end; + + status = acpi_ec_read_status(ec); + if (status != -EINVAL && + !(status & ACPI_EC_FLAG_BURST)){ + acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (status) + goto end; + acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr); + if(tmp != 0x90 ) /* Burst ACK byte*/ + goto end; + } + /*Now we are in burst mode*/ + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + if (status){ + goto end; + } + + acpi_hw_low_level_write(8, address, &ec->common.data_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (status){ + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + goto end; + } + + acpi_hw_low_level_write(8, data, &ec->common.data_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + if (status) + goto end; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", + data, address)); + +end: + acpi_ec_leave_burst_mode(ec); + up(&ec->burst.sem); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + if(atomic_read(&ec->burst.leaving_burst) == 2){ + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); + while(atomic_read(&ec->burst.pending_gpe)){ + msleep(1); + } + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + goto retry; + } + + return_VALUE(status); +} + /* * Externally callable EC access functions. For now, assume 1 EC only */ int ec_read(u8 addr, u8 *val) { - struct acpi_ec *ec; + union acpi_ec *ec; int err; u32 temp_data; @@ -269,7 +627,7 @@ EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { - struct acpi_ec *ec; + union acpi_ec *ec; int err; if (!first_ec) @@ -283,10 +641,19 @@ ec_write(u8 addr, u8 val) } EXPORT_SYMBOL(ec_write); - static int acpi_ec_query ( - struct acpi_ec *ec, + union acpi_ec *ec, + u32 *data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_query(ec, data); + else + return acpi_ec_burst_query(ec, data); +} +static int +acpi_ec_polling_query ( + union acpi_ec *ec, u32 *data) { int result = 0; @@ -301,7 +668,7 @@ acpi_ec_query ( *data = 0; - if (ec->global_lock) { + if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); @@ -312,32 +679,86 @@ acpi_ec_query ( * 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->lock, flags); + spin_lock_irqsave(&ec->polling.lock, flags); - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr); result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); if (result) goto end; - - acpi_hw_low_level_read(8, data, &ec->data_addr); + + acpi_hw_low_level_read(8, data, &ec->common.data_addr); if (!*data) result = -ENODATA; end: - spin_unlock_irqrestore(&ec->lock, flags); + spin_unlock_irqrestore(&ec->polling.lock, flags); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); return_VALUE(result); } +static int +acpi_ec_burst_query ( + union acpi_ec *ec, + u32 *data) +{ + int status = 0; + u32 glk; + + 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); + } + + down(&ec->burst.sem); + if(acpi_ec_enter_burst_mode(ec)) + goto end; + /* + * 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). + */ + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (status){ + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + goto end; + } + + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + if (!*data) + status = -ENODATA; + +end: + acpi_ec_leave_burst_mode(ec); + up(&ec->burst.sem); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + if(atomic_read(&ec->burst.leaving_burst) == 2){ + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); + status = -ENODATA; + } + return_VALUE(status); +} /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ -struct acpi_ec_query_data { +union acpi_ec_query_data { acpi_handle handle; u8 data; }; @@ -346,7 +767,17 @@ static void acpi_ec_gpe_query ( void *ec_cxt) { - struct acpi_ec *ec = (struct acpi_ec *) ec_cxt; + if (acpi_ec_polling_mode) + acpi_ec_gpe_polling_query(ec_cxt); + else + acpi_ec_gpe_burst_query(ec_cxt); +} + +static void +acpi_ec_gpe_polling_query ( + void *ec_cxt) +{ + 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'}; @@ -356,11 +787,11 @@ acpi_ec_gpe_query ( ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); if (!ec_cxt) - goto end; + goto end; - spin_lock_irqsave(&ec->lock, flags); - acpi_hw_low_level_read(8, &value, &ec->command_addr); - spin_unlock_irqrestore(&ec->lock, flags); + 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 @@ -373,29 +804,67 @@ acpi_ec_gpe_query ( 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->handle, object_name, NULL, NULL); + acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL); -end: - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); +end: + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); +} +static void +acpi_ec_gpe_burst_query ( + void *ec_cxt) +{ + 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'}; + + ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); + + if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI) + result = acpi_ec_query(ec, &value); + + if (result) + 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); +end: + atomic_dec(&ec->burst.pending_gpe); + return; } static u32 acpi_ec_gpe_handler ( void *data) { + if (acpi_ec_polling_mode) + return acpi_ec_gpe_polling_handler(data); + else + return acpi_ec_gpe_burst_handler(data); +} +static u32 +acpi_ec_gpe_polling_handler ( + void *data) +{ acpi_status status = AE_OK; - struct acpi_ec *ec = (struct acpi_ec *) data; + union acpi_ec *ec = (union acpi_ec *) data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; - acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); + acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, acpi_ec_gpe_query, ec); @@ -405,6 +874,55 @@ acpi_ec_gpe_handler ( else return ACPI_INTERRUPT_NOT_HANDLED; } +static u32 +acpi_ec_gpe_burst_handler ( + void *data) +{ + acpi_status status = AE_OK; + u32 value; + union acpi_ec *ec = (union acpi_ec *) data; + + if (!ec) + return ACPI_INTERRUPT_NOT_HANDLED; + + acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + + value = acpi_ec_read_status(ec); + + if((value & ACPI_EC_FLAG_IBF) && + !(value & ACPI_EC_FLAG_BURST) && + (atomic_read(&ec->burst.leaving_burst) == 0)) { + /* + * the embedded controller disables + * burst mode for any reason other + * than the burst disable command + * to process critical event. + */ + atomic_set(&ec->burst.leaving_burst , 2); /* block current pending transaction + and retry */ + wake_up(&ec->burst.wait); + }else { + if ((ec->burst.expect_event == ACPI_EC_EVENT_OBF && + (value & ACPI_EC_FLAG_OBF)) || + (ec->burst.expect_event == ACPI_EC_EVENT_IBE && + !(value & ACPI_EC_FLAG_IBF))) { + ec->burst.expect_event = 0; + wake_up(&ec->burst.wait); + return ACPI_INTERRUPT_HANDLED; + } + } + + if (value & ACPI_EC_FLAG_SCI){ + atomic_add(1, &ec->burst.pending_gpe) ; + status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, + acpi_ec_gpe_query, ec); + return status == AE_OK ? + ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; + } + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + return status == AE_OK ? + ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; +} /* -------------------------------------------------------------------------- Address Space Management @@ -421,10 +939,8 @@ acpi_ec_space_setup ( * The EC object is in the handler context and is needed * when calling the acpi_ec_space_handler. */ - if(function == ACPI_REGION_DEACTIVATE) - *return_context = NULL; - else - *return_context = handler_context; + *return_context = (function != ACPI_REGION_DEACTIVATE) ? + handler_context : NULL; return AE_OK; } @@ -440,8 +956,8 @@ acpi_ec_space_handler ( void *region_context) { int result = 0; - struct acpi_ec *ec = NULL; - u32 temp = 0; + union acpi_ec *ec = NULL; + u64 temp = *value; acpi_integer f_v = 0; int i = 0; @@ -450,22 +966,21 @@ acpi_ec_space_handler ( if ((address > 0xFF) || !value || !handler_context) return_VALUE(AE_BAD_PARAMETER); - if(bit_width != 8) { + if (bit_width != 8 && acpi_strict) { printk(KERN_WARNING PREFIX "acpi_ec_space_handler: bit_width should be 8\n"); - if (acpi_strict) - return_VALUE(AE_BAD_PARAMETER); + return_VALUE(AE_BAD_PARAMETER); } - ec = (struct acpi_ec *) handler_context; + ec = (union acpi_ec *) handler_context; next_byte: switch (function) { case ACPI_READ: - result = acpi_ec_read(ec, (u8) address, &temp); - *value = (acpi_integer) temp; + temp = 0; + result = acpi_ec_read(ec, (u8) address, (u32 *)&temp); break; case ACPI_WRITE: - result = acpi_ec_write(ec, (u8) address, (u8) *value); + result = acpi_ec_write(ec, (u8) address, (u8) temp); break; default: result = -EINVAL; @@ -474,19 +989,18 @@ next_byte: } bit_width -= 8; - if(bit_width){ - - if(function == ACPI_READ) - f_v |= (acpi_integer) (*value) << 8*i; - if(function == ACPI_WRITE) - (*value) >>=8; + if (bit_width) { + if (function == ACPI_READ) + f_v |= temp << 8 * i; + if (function == ACPI_WRITE) + temp >>= 8; i++; + address++; goto next_byte; } - - if(function == ACPI_READ){ - f_v |= (acpi_integer) (*value) << 8*i; + if (function == ACPI_READ) { + f_v |= temp << 8 * i; *value = f_v; } @@ -505,8 +1019,6 @@ out: default: return_VALUE(AE_OK); } - - } @@ -520,7 +1032,7 @@ static struct proc_dir_entry *acpi_ec_dir; static int acpi_ec_read_info (struct seq_file *seq, void *offset) { - struct acpi_ec *ec = (struct acpi_ec *) seq->private; + union acpi_ec *ec = (union acpi_ec *) seq->private; ACPI_FUNCTION_TRACE("acpi_ec_read_info"); @@ -528,11 +1040,12 @@ acpi_ec_read_info (struct seq_file *seq, void *offset) goto end; seq_printf(seq, "gpe bit: 0x%02x\n", - (u32) ec->gpe_bit); + (u32) ec->common.gpe_bit); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->status_addr.address, (u32) ec->data_addr.address); + (u32) ec->common.status_addr.address, (u32) ec->common.data_addr.address); seq_printf(seq, "use global lock: %s\n", - ec->global_lock?"yes":"no"); + ec->common.global_lock?"yes":"no"); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); end: return_VALUE(0); @@ -602,13 +1115,14 @@ acpi_ec_remove_fs ( Driver Interface -------------------------------------------------------------------------- */ + static int -acpi_ec_add ( +acpi_ec_polling_add ( struct acpi_device *device) { int result = 0; acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; + union acpi_ec *ec = NULL; unsigned long uid; ACPI_FUNCTION_TRACE("acpi_ec_add"); @@ -616,36 +1130,107 @@ acpi_ec_add ( if (!device) return_VALUE(-EINVAL); - ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec) return_VALUE(-ENOMEM); - memset(ec, 0, sizeof(struct acpi_ec)); + memset(ec, 0, sizeof(union acpi_ec)); - ec->handle = device->handle; - ec->uid = -1; - spin_lock_init(&ec->lock); + 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? */ - acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); + acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); /* If our UID matches the UID for the ECDT-enumerated EC, we now have the *real* EC info, so kill the makeshift one.*/ - acpi_evaluate_integer(ec->handle, "_UID", NULL, &uid); - if (ec_ecdt && ec_ecdt->uid == uid) { + 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, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); + + kfree(ec_ecdt); + } + + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error obtaining GPE bit assignment\n")); + result = -ENODEV; + goto end; + } + + result = acpi_ec_add_fs(device); + if (result) + goto end; + + printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n", + acpi_device_name(device), acpi_device_bid(device), + (u32) ec->common.gpe_bit); + + if (!first_ec) + first_ec = device; + +end: + if (result) + kfree(ec); + + return_VALUE(result); +} +static int +acpi_ec_burst_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + union acpi_ec *ec = NULL; + unsigned long uid; + + 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; + 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); + 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? */ + acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); + + /* If our UID matches the UID for the ECDT-enumerated EC, + we now have the *real* EC info, so kill the makeshift one.*/ + 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, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); + + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); kfree(ec_ecdt); } /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ - status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe_bit); + status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error obtaining GPE bit assignment\n")); @@ -659,7 +1244,7 @@ acpi_ec_add ( printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n", acpi_device_name(device), acpi_device_bid(device), - (u32) ec->gpe_bit); + (u32) ec->common.gpe_bit); if (!first_ec) first_ec = device; @@ -677,7 +1262,7 @@ acpi_ec_remove ( struct acpi_device *device, int type) { - struct acpi_ec *ec = NULL; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_remove"); @@ -699,7 +1284,7 @@ acpi_ec_io_ports ( struct acpi_resource *resource, void *context) { - struct acpi_ec *ec = (struct acpi_ec *) context; + union acpi_ec *ec = (union acpi_ec *) context; struct acpi_generic_address *addr; if (resource->id != ACPI_RSTYPE_IO) { @@ -711,10 +1296,10 @@ acpi_ec_io_ports ( * the second address region returned is the status/command * port. */ - if (ec->data_addr.register_bit_width == 0) { - addr = &ec->data_addr; - } else if (ec->command_addr.register_bit_width == 0) { - addr = &ec->command_addr; + 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; } else { return AE_CTRL_TERMINATE; } @@ -733,7 +1318,7 @@ acpi_ec_start ( struct acpi_device *device) { acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_start"); @@ -748,35 +1333,36 @@ acpi_ec_start ( /* * Get I/O port addresses. Convert to GAS format. */ - status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, + status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec); - if (ACPI_FAILURE(status) || ec->command_addr.register_bit_width == 0) { + if (ACPI_FAILURE(status) || ec->common.command_addr.register_bit_width == 0) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error getting I/O port addresses")); return_VALUE(-ENODEV); } - ec->status_addr = ec->command_addr; + ec->common.status_addr = ec->common.command_addr; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n", - (u32) ec->gpe_bit, (u32) ec->command_addr.address, - (u32) ec->data_addr.address)); + (u32) ec->common.gpe_bit, (u32) ec->common.command_addr.address, + (u32) ec->common.data_addr.address)); + /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) { return_VALUE(-ENODEV); } - acpi_set_gpe_type (NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe (NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type (NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe (NULL, ec->common.gpe_bit, ACPI_NOT_ISR); - status = acpi_install_address_space_handler (ec->handle, + status = acpi_install_address_space_handler (ec->common.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_bit, &acpi_ec_gpe_handler); + acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler); return_VALUE(-ENODEV); } @@ -790,7 +1376,7 @@ acpi_ec_stop ( int type) { acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_stop"); @@ -799,12 +1385,12 @@ acpi_ec_stop ( ec = acpi_driver_data(device); - status = acpi_remove_address_space_handler(ec->handle, + status = acpi_remove_address_space_handler(ec->common.handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); - status = acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); + status = acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); @@ -818,27 +1404,76 @@ acpi_fake_ecdt_callback ( void *context, void **retval) { + + if (acpi_ec_polling_mode) + return acpi_fake_ecdt_polling_callback(handle, + Level, context, retval); + else + return acpi_fake_ecdt_burst_callback(handle, + Level, context, retval); +} + +static acpi_status __init +acpi_fake_ecdt_polling_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ + acpi_status status; + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_ec_io_ports, ec_ecdt); + 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); + + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit); + if (ACPI_FAILURE(status)) + return status; + spin_lock_init(&ec_ecdt->polling.lock); + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.handle = handle; + + 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); + + return AE_CTRL_TERMINATE; +} + +static acpi_status __init +acpi_fake_ecdt_burst_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ acpi_status status; + init_MUTEX(&ec_ecdt->burst.sem); + init_waitqueue_head(&ec_ecdt->burst.wait); status = acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec_ecdt); if (ACPI_FAILURE(status)) return status; - ec_ecdt->status_addr = ec_ecdt->command_addr; + ec_ecdt->common.status_addr = ec_ecdt->common.command_addr; - ec_ecdt->uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); + ec_ecdt->common.uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid); - status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit); if (ACPI_FAILURE(status)) return status; - spin_lock_init(&ec_ecdt->lock); - ec_ecdt->global_lock = TRUE; - ec_ecdt->handle = handle; + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.handle = handle; printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", - (u32) ec_ecdt->gpe_bit, (u32) ec_ecdt->command_addr.address, - (u32) ec_ecdt->data_addr.address); + (u32) ec_ecdt->common.gpe_bit, (u32) ec_ecdt->common.command_addr.address, + (u32) ec_ecdt->common.data_addr.address); return AE_CTRL_TERMINATE; } @@ -861,12 +1496,12 @@ acpi_ec_fake_ecdt(void) printk(KERN_INFO PREFIX "Try to make an fake ECDT\n"); - ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec_ecdt) { ret = -ENOMEM; goto error; } - memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + memset(ec_ecdt, 0, sizeof(union acpi_ec)); status = acpi_get_devices (ACPI_EC_HID, acpi_fake_ecdt_callback, @@ -887,6 +1522,15 @@ error: static int __init acpi_ec_get_real_ecdt(void) { + if (acpi_ec_polling_mode) + return acpi_ec_polling_get_real_ecdt(); + else + return acpi_ec_burst_get_real_ecdt(); +} + +static int __init +acpi_ec_polling_get_real_ecdt(void) +{ acpi_status status; struct acpi_table_ecdt *ecdt_ptr; @@ -900,21 +1544,67 @@ acpi_ec_get_real_ecdt(void) /* * Generate a temporary ec context to use until the namespace is scanned */ - ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec_ecdt) return -ENOMEM; - memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + 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; + + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); + if (ACPI_FAILURE(status)) { + goto error; + } + + return 0; +error: + printk(KERN_ERR PREFIX "Could not use ECDT\n"); + kfree(ec_ecdt); + ec_ecdt = NULL; + + return -ENODEV; +} + + +static int __init +acpi_ec_burst_get_real_ecdt(void) +{ + acpi_status status; + struct acpi_table_ecdt *ecdt_ptr; + + status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) &ecdt_ptr); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk(KERN_INFO PREFIX "Found ECDT\n"); - ec_ecdt->command_addr = ecdt_ptr->ec_control; - ec_ecdt->status_addr = ecdt_ptr->ec_control; - ec_ecdt->data_addr = ecdt_ptr->ec_data; - ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; - spin_lock_init(&ec_ecdt->lock); + /* + * 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)); + + init_MUTEX(&ec_ecdt->burst.sem); + init_waitqueue_head(&ec_ecdt->burst.wait); + 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; /* use the GL just to be safe */ - ec_ecdt->global_lock = TRUE; - ec_ecdt->uid = ecdt_ptr->uid; + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.uid = ecdt_ptr->uid; - status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); if (ACPI_FAILURE(status)) { goto error; } @@ -947,20 +1637,20 @@ acpi_ec_ecdt_probe (void) /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec_ecdt); if (ACPI_FAILURE(status)) { goto error; } - acpi_set_gpe_type (NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe (NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR); + 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); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); goto error; } @@ -1021,4 +1711,24 @@ static int __init acpi_fake_ecdt_setup(char *str) acpi_fake_ecdt_enabled = 1; return 0; } + __setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); +static int __init acpi_ec_set_polling_mode(char *str) +{ + 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; + } + printk(KERN_INFO PREFIX "EC %s mode.\n", + burst ? "burst": "polling"); + return 0; +} +__setup("ec_burst=", acpi_ec_set_polling_mode); |