]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ACPI / ACPICA: Fix low-level GPE manipulation code
authorRafael J. Wysocki <rjw@sisk.pl>
Tue, 8 Jun 2010 08:49:08 +0000 (10:49 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 2 Aug 2010 17:30:20 +0000 (10:30 -0700)
commit fd247447c1d94a79d5cfc647430784306b3a8323 upstream.

ACPICA uses acpi_ev_enable_gpe() for enabling GPEs at the low level,
which is incorrect, because this function only enables the GPE if the
corresponding bit in its enable register's enable_for_run mask is set.
This causes acpi_set_gpe() to work incorrectly if used for enabling
GPEs that were not previously enabled with acpi_enable_gpe().  As a
result, among other things, wakeup-only GPEs are never enabled by
acpi_enable_wakeup_device(), so the devices that use them are unable
to wake up the system.

To fix this issue remove acpi_ev_enable_gpe() and its counterpart
acpi_ev_disable_gpe() and replace acpi_hw_low_disable_gpe() with
acpi_hw_low_set_gpe() that will be used instead to manipulate GPE
enable bits at the low level.  Make the users of acpi_ev_enable_gpe()
and acpi_ev_disable_gpe() call acpi_hw_low_set_gpe() instead and
make sure that GPE enable masks are only updated by acpi_enable_gpe()
and acpi_disable_gpe() when GPE reference counters change from 0
to 1 and from 1 to 0, respectively.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/acpi/acpica/acevents.h
drivers/acpi/acpica/achware.h
drivers/acpi/acpica/evgpe.c
drivers/acpi/acpica/evxfevnt.c
drivers/acpi/acpica/hwgpe.c
include/acpi/acexcep.h

index 3e6ba99e4053a0824252de17989e79f72193021f..af38dfb578ef3054446a3c4b0cd507d633efa96f 100644 (file)
@@ -78,10 +78,6 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
 acpi_status
 acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info);
 
-acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
-
-acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);
-
 struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
                                                       u32 gpe_number);
 
index c46277d179f006fef3208db917f8cfdec375eeb7..32391588e163c5cb650deaf6c302a97b2efae1fd 100644 (file)
@@ -93,7 +93,8 @@ acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width);
 u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info,
                             struct acpi_gpe_register_info *gpe_register_info);
 
-acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);
+acpi_status
+acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action);
 
 acpi_status
 acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info);
index 3c6b91a5b878211df9ba9784fa331d0e3c3a4688..e2dc53d1e11bd2d2b1fb1d2a9ae61176a380136c 100644 (file)
@@ -92,76 +92,6 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info)
        return_ACPI_STATUS(AE_OK);
 }
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_ev_enable_gpe
- *
- * PARAMETERS:  gpe_event_info          - GPE to enable
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Enable a GPE based on the GPE type
- *
- ******************************************************************************/
-
-acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
-{
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(ev_enable_gpe);
-
-       /* Make sure HW enable masks are updated */
-
-       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
-       if (ACPI_FAILURE(status))
-               return_ACPI_STATUS(status);
-
-       /* Clear the GPE (of stale events), then enable it */
-       status = acpi_hw_clear_gpe(gpe_event_info);
-       if (ACPI_FAILURE(status))
-               return_ACPI_STATUS(status);
-
-       /* Enable the requested GPE */
-       status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
-       return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
- * FUNCTION:    acpi_ev_disable_gpe
- *
- * PARAMETERS:  gpe_event_info          - GPE to disable
- *
- * RETURN:      Status
- *
- * DESCRIPTION: Disable a GPE based on the GPE type
- *
- ******************************************************************************/
-
-acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
-{
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE(ev_disable_gpe);
-
-       /* Make sure HW enable masks are updated */
-
-       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
-       if (ACPI_FAILURE(status))
-               return_ACPI_STATUS(status);
-
-       /*
-        * Even if we don't know the GPE type, make sure that we always
-        * disable it. low_disable_gpe will just clear the enable bit for this
-        * GPE and write it. It will not write out the current GPE enable mask,
-        * since this may inadvertently enable GPEs too early, if a rogue GPE has
-        * come in during ACPICA initialization - possibly as a result of AML or
-        * other code that has enabled the GPE.
-        */
-       status = acpi_hw_low_disable_gpe(gpe_event_info);
-       return_ACPI_STATUS(status);
-}
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ev_get_gpe_event_info
@@ -388,10 +318,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
                return_VOID;
        }
 
-       /* Set the GPE flags for return to enabled state */
-
-       (void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
-
        /*
         * Take a snapshot of the GPE info for this level - we copy the info to
         * prevent a race condition with remove_handler/remove_block.
@@ -544,7 +470,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
                 * Disable the GPE, so it doesn't keep firing before the method has a
                 * chance to run (it runs asynchronously with interrupts enabled).
                 */
-               status = acpi_ev_disable_gpe(gpe_event_info);
+               status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
                if (ACPI_FAILURE(status)) {
                        ACPI_EXCEPTION((AE_INFO, status,
                                        "Unable to disable GPE[%2X]",
@@ -578,7 +504,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
                 * Disable the GPE. The GPE will remain disabled until the ACPICA
                 * Core Subsystem is restarted, or a handler is installed.
                 */
-               status = acpi_ev_disable_gpe(gpe_event_info);
+               status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
                if (ACPI_FAILURE(status)) {
                        ACPI_EXCEPTION((AE_INFO, status,
                                        "Unable to disable GPE[%2X]",
index 5ff32c78ea2db417ba2e303fbb9b401454701508..47c1aac07202fbb87b4f7c8bb5e01323cd17be09 100644 (file)
@@ -199,6 +199,44 @@ acpi_status acpi_enable_event(u32 event, u32 flags)
 
 ACPI_EXPORT_SYMBOL(acpi_enable_event)
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_clear_and_enable_gpe
+ *
+ * PARAMETERS:  gpe_event_info  - GPE to enable
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Clear the given GPE from stale events and enable it.
+ *
+ ******************************************************************************/
+static acpi_status
+acpi_clear_and_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+       acpi_status status;
+
+       /*
+        * We will only allow a GPE to be enabled if it has either an
+        * associated method (_Lxx/_Exx) or a handler. Otherwise, the
+        * GPE will be immediately disabled by acpi_ev_gpe_dispatch the
+        * first time it fires.
+        */
+       if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) {
+               return_ACPI_STATUS(AE_NO_HANDLER);
+       }
+
+       /* Clear the GPE (of stale events) */
+       status = acpi_hw_clear_gpe(gpe_event_info);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Enable the requested GPE */
+       status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
+
+       return_ACPI_STATUS(status);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_set_gpe
@@ -235,11 +273,11 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
 
        switch (action) {
        case ACPI_GPE_ENABLE:
-               status = acpi_ev_enable_gpe(gpe_event_info);
+               status = acpi_clear_and_enable_gpe(gpe_event_info);
                break;
 
        case ACPI_GPE_DISABLE:
-               status = acpi_ev_disable_gpe(gpe_event_info);
+               status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
                break;
 
        default:
@@ -291,9 +329,13 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
 
        if (type & ACPI_GPE_TYPE_RUNTIME) {
                if (++gpe_event_info->runtime_count == 1) {
-                       status = acpi_ev_enable_gpe(gpe_event_info);
-                       if (ACPI_FAILURE(status))
+                       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
+                       if (ACPI_SUCCESS(status)) {
+                               status = acpi_clear_and_enable_gpe(gpe_event_info);
+                       }
+                       if (ACPI_FAILURE(status)) {
                                gpe_event_info->runtime_count--;
+                       }
                }
        }
 
@@ -308,7 +350,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
                 * system into a sleep state.
                 */
                if (++gpe_event_info->wakeup_count == 1)
-                       acpi_ev_update_gpe_enable_masks(gpe_event_info);
+                       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
        }
 
 unlock_and_exit:
@@ -351,8 +393,16 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
        }
 
        if ((type & ACPI_GPE_TYPE_RUNTIME) && gpe_event_info->runtime_count) {
-               if (--gpe_event_info->runtime_count == 0)
-                       status = acpi_ev_disable_gpe(gpe_event_info);
+               if (--gpe_event_info->runtime_count == 0) {
+                       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
+                       if (ACPI_SUCCESS(status)) {
+                               status = acpi_hw_low_set_gpe(gpe_event_info,
+                                                            ACPI_GPE_DISABLE);
+                       }
+                       if (ACPI_FAILURE(status)) {
+                               gpe_event_info->runtime_count++;
+                       }
+               }
        }
 
        if ((type & ACPI_GPE_TYPE_WAKE) && gpe_event_info->wakeup_count) {
@@ -361,7 +411,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 type)
                 * states, so we don't need to disable them here.
                 */
                if (--gpe_event_info->wakeup_count == 0)
-                       acpi_ev_update_gpe_enable_masks(gpe_event_info);
+                       status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
        }
 
 unlock_and_exit:
index d989b8e786cc9839417998e545776df1c769c8e8..0070328466603fbaae5ebcf6ecaed9e0e8574d87 100644 (file)
@@ -78,23 +78,27 @@ u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info,
 
 /******************************************************************************
  *
- * FUNCTION:   acpi_hw_low_disable_gpe
+ * FUNCTION:   acpi_hw_low_set_gpe
  *
  * PARAMETERS: gpe_event_info      - Info block for the GPE to be disabled
+ *             action              - Enable or disable
  *
  * RETURN:     Status
  *
- * DESCRIPTION: Disable a single GPE in the enable register.
+ * DESCRIPTION: Enable or disable a single GPE in its enable register.
  *
  ******************************************************************************/
 
-acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
+acpi_status
+acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action)
 {
        struct acpi_gpe_register_info *gpe_register_info;
        acpi_status status;
        u32 enable_mask;
        u32 register_bit;
 
+       ACPI_FUNCTION_ENTRY();
+
        /* Get the info block for the entire GPE register */
 
        gpe_register_info = gpe_event_info->register_info;
@@ -109,11 +113,23 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
                return (status);
        }
 
-       /* Clear just the bit that corresponds to this GPE */
+       /* Set or clear just the bit that corresponds to this GPE */
 
        register_bit = acpi_hw_gpe_register_bit(gpe_event_info,
                                                gpe_register_info);
-       ACPI_CLEAR_BIT(enable_mask, register_bit);
+       switch (action) {
+       case ACPI_GPE_ENABLE:
+               ACPI_SET_BIT(enable_mask, register_bit);
+               break;
+
+       case ACPI_GPE_DISABLE:
+               ACPI_CLEAR_BIT(enable_mask, register_bit);
+               break;
+
+       default:
+               ACPI_ERROR((AE_INFO, "Invalid action\n"));
+               return (AE_BAD_PARAMETER);
+       }
 
        /* Write the updated enable mask */
 
index 5b2e5e80ecb07bb211ec3e13a607ef37dbd8323c..5958d7845bd5ac86397506bd54e872a262ef6135 100644 (file)
@@ -87,7 +87,7 @@
 #define AE_NO_GLOBAL_LOCK               (acpi_status) (0x0017 | AE_CODE_ENVIRONMENTAL)
 #define AE_ABORT_METHOD                 (acpi_status) (0x0018 | AE_CODE_ENVIRONMENTAL)
 #define AE_SAME_HANDLER                 (acpi_status) (0x0019 | AE_CODE_ENVIRONMENTAL)
-#define AE_WAKE_ONLY_GPE                (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL)
+#define AE_NO_HANDLER                   (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL)
 #define AE_OWNER_ID_LIMIT               (acpi_status) (0x001B | AE_CODE_ENVIRONMENTAL)
 
 #define AE_CODE_ENV_MAX                 0x001B