]> err.no Git - linux-2.6/blobdiff - drivers/acpi/ec.c
Merge branch 'audit.b49' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
[linux-2.6] / drivers / acpi / ec.c
index 97dc16155a556d13c373535f767bc8faba76514f..e7e197e3a4ffce2151d61f5e6b48f89c98087dfa 100644 (file)
@@ -26,6 +26,9 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
+/* Uncomment next line to get verbose print outs*/
+/* #define DEBUG */
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -47,9 +50,6 @@
 #undef PREFIX
 #define PREFIX                         "ACPI: EC: "
 
-/* Uncomment next line to get verbose print outs*/
-/* #define DEBUG */
-
 /* EC status register */
 #define ACPI_EC_FLAG_OBF       0x01    /* Output buffer full */
 #define ACPI_EC_FLAG_IBF       0x02    /* Input buffer full */
@@ -82,6 +82,7 @@ enum {
        EC_FLAGS_ADDRESS,               /* Address is being written */
        EC_FLAGS_NO_WDATA_GPE,          /* Don't expect WDATA GPE event */
        EC_FLAGS_WDATA,                 /* Data is being written */
+       EC_FLAGS_NO_OBF1_GPE,           /* Don't expect GPE before read */
 };
 
 static int acpi_ec_remove(struct acpi_device *device, int type);
@@ -128,6 +129,7 @@ static struct acpi_ec {
        struct mutex lock;
        wait_queue_head_t wait;
        struct list_head list;
+       atomic_t irq_count;
        u8 handlers_installed;
 } *boot_ec, *first_ec;
 
@@ -138,26 +140,26 @@ static struct acpi_ec {
 static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
 {
        u8 x = inb(ec->command_addr);
-       pr_debug(PREFIX "---> status = 0x%2x\n", x);
+       pr_debug(PREFIX "---> status = 0x%2.2x\n", x);
        return x;
 }
 
 static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
 {
        u8 x = inb(ec->data_addr);
-       pr_debug(PREFIX "---> data = 0x%2x\n", x);
+       pr_debug(PREFIX "---> data = 0x%2.2x\n", x);
        return inb(ec->data_addr);
 }
 
 static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
 {
-       pr_debug(PREFIX "<--- command = 0x%2x\n", command);
+       pr_debug(PREFIX "<--- command = 0x%2.2x\n", command);
        outb(command, ec->command_addr);
 }
 
 static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
 {
-       pr_debug(PREFIX "<--- data = 0x%2x\n", data);
+       pr_debug(PREFIX "<--- data = 0x%2.2x\n", data);
        outb(data, ec->data_addr);
 }
 
@@ -179,6 +181,12 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event)
 static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
 {
        int ret = 0;
+
+       atomic_set(&ec->irq_count, 0);
+
+       if (unlikely(event == ACPI_EC_EVENT_OBF_1 &&
+                    test_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags)))
+               force_poll = 1;
        if (unlikely(test_bit(EC_FLAGS_ADDRESS, &ec->flags) &&
                     test_bit(EC_FLAGS_NO_ADDRESS_GPE, &ec->flags)))
                force_poll = 1;
@@ -192,7 +200,12 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
                        goto end;
                clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
                if (acpi_ec_check_status(ec, event)) {
-                       if (test_bit(EC_FLAGS_ADDRESS, &ec->flags)) {
+                       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)) {
                                /* miss address GPE, don't expect it anymore */
                                pr_info(PREFIX "missing address confirmation, "
                                        "don't expect it any longer.\n");
@@ -217,6 +230,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
                while (time_before(jiffies, delay)) {
                        if (acpi_ec_check_status(ec, event))
                                goto end;
+                       msleep(5);
                }
        }
        pr_err(PREFIX "acpi_ec_wait timeout,"
@@ -519,6 +533,13 @@ static u32 acpi_ec_gpe_handler(void *data)
        struct acpi_ec *ec = data;
 
        pr_debug(PREFIX "~~~> interrupt\n");
+       atomic_inc(&ec->irq_count);
+       if (atomic_read(&ec->irq_count) > 5) {
+               pr_err(PREFIX "GPE storm detected, disabling EC GPE\n");
+               acpi_disable_gpe(NULL, ec->gpe, ACPI_ISR);
+               clear_bit(EC_FLAGS_GPE_MODE, &ec->flags);
+               return ACPI_INTERRUPT_HANDLED;
+       }
        clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
        if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
                wake_up(&ec->wait);
@@ -563,7 +584,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
                      void *handler_context, void *region_context)
 {
        struct acpi_ec *ec = handler_context;
-       int result = 0, i = 0;
+       int result = 0, i;
        u8 temp = 0;
 
        if ((address > 0xFF) || !value || !handler_context)
@@ -575,7 +596,18 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
        if (bits != 8 && acpi_strict)
                return AE_BAD_PARAMETER;
 
-       while (bits - i > 0) {
+       acpi_ec_burst_enable(ec);
+
+       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;
                if (function == ACPI_READ) {
                        result = acpi_ec_read(ec, address, &temp);
                        (*value) |= ((acpi_integer)temp) << i;
@@ -583,10 +615,10 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
                        temp = 0xff & ((*value) >> i);
                        result = acpi_ec_write(ec, address, temp);
                }
-               i += 8;
-               ++address;
        }
 
+       acpi_ec_burst_disable(ec);
+
        switch (result) {
        case -EINVAL:
                return AE_BAD_PARAMETER;