]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/acpi/ec.c
Merge branch 'drm-fixes-4.4' of git://people.freedesktop.org/~agd5f/linux into drm...
[karo-tx-linux.git] / drivers / acpi / ec.c
index 42c66b64c12cefd8c1491e7b91af138b86ddf5af..b420fb46669dd698c4d346559c325c0f4afeaf4d 100644 (file)
@@ -441,17 +441,31 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
 
 static bool acpi_ec_guard_event(struct acpi_ec *ec)
 {
+       bool guarded = true;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ec->lock, flags);
+       /*
+        * If firmware SCI_EVT clearing timing is "event", we actually
+        * don't know when the SCI_EVT will be cleared by firmware after
+        * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an
+        * acceptable period.
+        *
+        * The guarding period begins when EC_FLAGS_QUERY_PENDING is
+        * flagged, which means SCI_EVT check has just been performed.
+        * But if the current transaction is ACPI_EC_COMMAND_QUERY, the
+        * guarding should have already been performed (via
+        * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the
+        * ACPI_EC_COMMAND_QUERY transaction can be transitioned into
+        * ACPI_EC_COMMAND_POLL state immediately.
+        */
        if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
            ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY ||
            !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) ||
            (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY))
-               return false;
-
-       /*
-        * Postpone the query submission to allow the firmware to proceed,
-        * we shouldn't check SCI_EVT before the firmware reflagging it.
-        */
-       return true;
+               guarded = false;
+       spin_unlock_irqrestore(&ec->lock, flags);
+       return guarded;
 }
 
 static int ec_transaction_polled(struct acpi_ec *ec)
@@ -597,6 +611,7 @@ static int ec_guard(struct acpi_ec *ec)
        unsigned long guard = usecs_to_jiffies(ec_polling_guard);
        unsigned long timeout = ec->timestamp + guard;
 
+       /* Ensure guarding period before polling EC status */
        do {
                if (ec_busy_polling) {
                        /* Perform busy polling */
@@ -606,11 +621,13 @@ static int ec_guard(struct acpi_ec *ec)
                } else {
                        /*
                         * Perform wait polling
-                        *
-                        * For SCI_EVT clearing timing of "event",
-                        * performing guarding before re-checking the
-                        * SCI_EVT. Otherwise, such guarding is not needed
-                        * due to the old practices.
+                        * 1. Wait the transaction to be completed by the
+                        *    GPE handler after the transaction enters
+                        *    ACPI_EC_COMMAND_POLL state.
+                        * 2. A special guarding logic is also required
+                        *    for event clearing mode "event" before the
+                        *    transaction enters ACPI_EC_COMMAND_POLL
+                        *    state.
                         */
                        if (!ec_transaction_polled(ec) &&
                            !acpi_ec_guard_event(ec))
@@ -620,7 +637,6 @@ static int ec_guard(struct acpi_ec *ec)
                                               guard))
                                return 0;
                }
-               /* Guard the register accesses for the polling modes */
        } while (time_before(jiffies, timeout));
        return -ETIME;
 }
@@ -929,6 +945,23 @@ acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler)
        return handler;
 }
 
+static struct acpi_ec_query_handler *
+acpi_ec_get_query_handler_by_value(struct acpi_ec *ec, u8 value)
+{
+       struct acpi_ec_query_handler *handler;
+       bool found = false;
+
+       mutex_lock(&ec->mutex);
+       list_for_each_entry(handler, &ec->list, node) {
+               if (value == handler->query_bit) {
+                       found = true;
+                       break;
+               }
+       }
+       mutex_unlock(&ec->mutex);
+       return found ? acpi_ec_get_query_handler(handler) : NULL;
+}
+
 static void acpi_ec_query_handler_release(struct kref *kref)
 {
        struct acpi_ec_query_handler *handler =
@@ -964,14 +997,15 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
 }
 EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler);
 
-void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
+static void acpi_ec_remove_query_handlers(struct acpi_ec *ec,
+                                         bool remove_all, u8 query_bit)
 {
        struct acpi_ec_query_handler *handler, *tmp;
        LIST_HEAD(free_list);
 
        mutex_lock(&ec->mutex);
        list_for_each_entry_safe(handler, tmp, &ec->list, node) {
-               if (query_bit == handler->query_bit) {
+               if (remove_all || query_bit == handler->query_bit) {
                        list_del_init(&handler->node);
                        list_add(&handler->node, &free_list);
                }
@@ -980,6 +1014,11 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
        list_for_each_entry_safe(handler, tmp, &free_list, node)
                acpi_ec_put_query_handler(handler);
 }
+
+void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
+{
+       acpi_ec_remove_query_handlers(ec, false, query_bit);
+}
 EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
 
 static struct acpi_ec_query *acpi_ec_create_query(u8 *pval)
@@ -1025,7 +1064,6 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
 {
        u8 value = 0;
        int result;
-       struct acpi_ec_query_handler *handler;
        struct acpi_ec_query *q;
 
        q = acpi_ec_create_query(&value);
@@ -1043,28 +1081,29 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
        if (result)
                goto err_exit;
 
-       mutex_lock(&ec->mutex);
-       result = -ENODATA;
-       list_for_each_entry(handler, &ec->list, node) {
-               if (value == handler->query_bit) {
-                       result = 0;
-                       q->handler = acpi_ec_get_query_handler(handler);
-                       ec_dbg_evt("Query(0x%02x) scheduled",
-                                  q->handler->query_bit);
-                       /*
-                        * It is reported that _Qxx are evaluated in a
-                        * parallel way on Windows:
-                        * https://bugzilla.kernel.org/show_bug.cgi?id=94411
-                        */
-                       if (!schedule_work(&q->work))
-                               result = -EBUSY;
-                       break;
-               }
+       q->handler = acpi_ec_get_query_handler_by_value(ec, value);
+       if (!q->handler) {
+               result = -ENODATA;
+               goto err_exit;
+       }
+
+       /*
+        * It is reported that _Qxx are evaluated in a parallel way on
+        * Windows:
+        * https://bugzilla.kernel.org/show_bug.cgi?id=94411
+        *
+        * Put this log entry before schedule_work() in order to make
+        * it appearing before any other log entries occurred during the
+        * work queue execution.
+        */
+       ec_dbg_evt("Query(0x%02x) scheduled", value);
+       if (!schedule_work(&q->work)) {
+               ec_dbg_evt("Query(0x%02x) overlapped", value);
+               result = -EBUSY;
        }
-       mutex_unlock(&ec->mutex);
 
 err_exit:
-       if (result && q)
+       if (result)
                acpi_ec_delete_query(q);
        if (data)
                *data = value;
@@ -1354,19 +1393,13 @@ static int acpi_ec_add(struct acpi_device *device)
 static int acpi_ec_remove(struct acpi_device *device)
 {
        struct acpi_ec *ec;
-       struct acpi_ec_query_handler *handler, *tmp;
 
        if (!device)
                return -EINVAL;
 
        ec = acpi_driver_data(device);
        ec_remove_handlers(ec);
-       mutex_lock(&ec->mutex);
-       list_for_each_entry_safe(handler, tmp, &ec->list, node) {
-               list_del(&handler->node);
-               kfree(handler);
-       }
-       mutex_unlock(&ec->mutex);
+       acpi_ec_remove_query_handlers(ec, true, 0);
        release_region(ec->data_addr, 1);
        release_region(ec->command_addr, 1);
        device->driver_data = NULL;