There are cases that BIOS doesn't provide _Qxx handler for the returned
query value, in this case, acpi_set_gpe(ACPI_GPE_DISABLE) need to be
invoked to prevent event IRQ storms.

This patch implements such storm prevention using new GPE APIs.

Signed-off-by: Lv Zheng <lv.zh...@intel.com>
---
 drivers/acpi/ec.c |   50 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 936424a..b98474f 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -80,6 +80,10 @@ enum {
                                         * OpReg are installed */
        EC_FLAGS_STARTED,               /* Driver is started */
        EC_FLAGS_STOPPED,               /* Driver is stopped */
+       EC_FLAGS_COMMAND_STORM,         /* GPE storms occurred to the
+                                        * current command processing */
+       EC_FLAGS_EVENT_STORM,           /* GPE storms occurred to the
+                                        * current event processing */
 };
 
 #define ACPI_EC_COMMAND_POLL           0x01 /* Available for command byte */
@@ -158,6 +162,31 @@ static void acpi_ec_disable_gpe(struct acpi_ec *ec)
                wake_up(&ec->wait);
 }
 
+static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag)
+{
+       if (!test_bit(flag, &ec->flags)) {
+               if (!test_bit(EC_FLAGS_EVENT_STORM, &ec->flags) &&
+                   !test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) {
+                       acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
+                       pr_debug("+++++ Polling enabled +++++\n");
+               }
+               set_bit(flag, &ec->flags);
+       }
+}
+
+
+static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag)
+{
+       if (test_bit(flag, &ec->flags)) {
+               clear_bit(flag, &ec->flags);
+               if (!test_bit(EC_FLAGS_EVENT_STORM, &ec->flags) &&
+                   !test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) {
+                       acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
+                       pr_debug("+++++ Polling disabled +++++\n");
+               }
+       }
+}
+
 /* --------------------------------------------------------------------------
                              Transaction Management
    -------------------------------------------------------------------------- 
*/
@@ -276,10 +305,8 @@ err:
                if (in_interrupt() && t) {
                        if (t->irq_count < ec_storm_threshold)
                                ++t->irq_count;
-                       if (t->irq_count == ec_storm_threshold) {
-                               pr_debug("+++++ Polling enabled +++++\n");
-                               acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
-                       }
+                       if (t->irq_count == ec_storm_threshold)
+                               acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM);
                }
        }
        return wakeup;
@@ -362,10 +389,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
        spin_unlock_irqrestore(&ec->lock, tmp);
        ret = ec_poll(ec);
        spin_lock_irqsave(&ec->lock, tmp);
-       if (t->irq_count == ec_storm_threshold) {
-               acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE);
-               pr_debug("+++++ Polling disabled +++++\n");
-       }
+       if (t->irq_count == ec_storm_threshold)
+               acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
        pr_debug("***** Command(%s) stopped *****\n",
                 acpi_ec_cmd_string(t->command));
        /* Disable GPE for command processing (IBF=0/OBF=1) */
@@ -563,6 +588,8 @@ static void acpi_ec_stop(struct acpi_ec *ec)
                spin_unlock_irqrestore(&ec->lock, flags);
                wait_event(ec->wait, acpi_ec_stopped(ec));
                spin_lock_irqsave(&ec->lock, flags);
+               /* Event storm may still be indicated */
+               acpi_ec_clear_storm(ec, EC_FLAGS_EVENT_STORM);
                /* Disable GPE for event processing (EVT_SCI=1) */
                acpi_ec_disable_gpe(ec);
                clear_bit(EC_FLAGS_STARTED, &ec->flags);
@@ -714,10 +741,14 @@ static void acpi_ec_run(void *cxt)
 
 static int acpi_ec_notify_query_handlers(struct acpi_ec *ec, u8 query_bit)
 {
+       unsigned long flags;
        struct acpi_ec_query_handler *handler;
 
        list_for_each_entry(handler, &ec->list, node) {
                if (query_bit == handler->query_bit) {
+                       spin_lock_irqsave(&ec->lock, flags);
+                       acpi_ec_clear_storm(ec, EC_FLAGS_EVENT_STORM);
+                       spin_unlock_irqrestore(&ec->lock, flags);
                        /* have custom handler for this bit */
                        handler = acpi_ec_get_query_handler(handler);
                        pr_debug("##### Query(0x%02x) scheduled #####\n",
@@ -728,6 +759,9 @@ static int acpi_ec_notify_query_handlers(struct acpi_ec 
*ec, u8 query_bit)
                }
        }
        pr_warn_once("BIOS bug: no handler for query (0x%02x)\n", query_bit);
+       spin_lock_irqsave(&ec->lock, flags);
+       acpi_ec_set_storm(ec, EC_FLAGS_EVENT_STORM);
+       spin_unlock_irqrestore(&ec->lock, flags);
        return 0;
 }
 
-- 
1.7.10

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to