382 lines
13 KiB
Diff
382 lines
13 KiB
Diff
From: Rafael J. Wysocki <rjw@sisk.pl>
|
|
Date: Tue, 8 Jun 2010 08:49:08 +0000 (+0200)
|
|
Subject: ACPI / ACPICA: Fix low-level GPE manipulation code
|
|
X-Git-Tag: v2.6.35-rc4~72^2~6^2~3
|
|
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=fd247447c1d94a79d5cfc647430784306b3a8323
|
|
|
|
ACPI / ACPICA: Fix low-level GPE manipulation code
|
|
|
|
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>
|
|
---
|
|
|
|
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
|
|
index 5e094a2..138bbb5 100644
|
|
--- a/drivers/acpi/acpica/acevents.h
|
|
+++ b/drivers/acpi/acpica/acevents.h
|
|
@@ -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);
|
|
|
|
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
|
|
index c46277d..3239158 100644
|
|
--- a/drivers/acpi/acpica/achware.h
|
|
+++ b/drivers/acpi/acpica/achware.h
|
|
@@ -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);
|
|
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
|
|
index 57eeb3b..66cd038 100644
|
|
--- a/drivers/acpi/acpica/evgpe.c
|
|
+++ b/drivers/acpi/acpica/evgpe.c
|
|
@@ -99,106 +99,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: Hardware-enable a GPE. Always enables the GPE, regardless
|
|
- * of type or number of references.
|
|
- *
|
|
- * Note: The GPE lock should be already acquired when this function is called.
|
|
- *
|
|
- ******************************************************************************/
|
|
-
|
|
-acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
|
|
-{
|
|
- acpi_status status;
|
|
-
|
|
-
|
|
- ACPI_FUNCTION_TRACE(ev_enable_gpe);
|
|
-
|
|
-
|
|
- /*
|
|
- * 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);
|
|
- }
|
|
-
|
|
- /* Ensure the HW enable masks are current */
|
|
-
|
|
- status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
- if (ACPI_FAILURE(status)) {
|
|
- return_ACPI_STATUS(status);
|
|
- }
|
|
-
|
|
- /* 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_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: Hardware-disable a GPE. Always disables the requested GPE,
|
|
- * regardless of the type or number of references.
|
|
- *
|
|
- * Note: The GPE lock should be already acquired when this function is called.
|
|
- *
|
|
- ******************************************************************************/
|
|
-
|
|
-acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
|
|
-{
|
|
- acpi_status status;
|
|
-
|
|
- ACPI_FUNCTION_TRACE(ev_disable_gpe);
|
|
-
|
|
-
|
|
- /*
|
|
- * Note: Always disable the GPE, even if we think that that it is already
|
|
- * disabled. It is possible that the AML or some other code has enabled
|
|
- * the GPE behind our back.
|
|
- */
|
|
-
|
|
- /* Ensure the HW enable masks are current */
|
|
-
|
|
- status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
- if (ACPI_FAILURE(status)) {
|
|
- return_ACPI_STATUS(status);
|
|
- }
|
|
-
|
|
- /*
|
|
- * Always H/W disable this GPE, even if we don't know the GPE type.
|
|
- * Simply clear the enable bit for this particular GPE, but do not
|
|
- * write out the current GPE enable mask since this may inadvertently
|
|
- * enable GPEs too early. An example is a rogue GPE that has arrived
|
|
- * during ACPICA initialization - possibly because AML or other code
|
|
- * has enabled the GPE.
|
|
- */
|
|
- status = acpi_hw_low_disable_gpe(gpe_event_info);
|
|
- return_ACPI_STATUS(status);
|
|
-}
|
|
-
|
|
|
|
/*******************************************************************************
|
|
*
|
|
@@ -450,10 +350,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
|
|
return_VOID;
|
|
}
|
|
|
|
- /* Update the GPE register masks 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.
|
|
@@ -606,7 +502,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[0x%2X]",
|
|
@@ -643,7 +539,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
|
|
* Disable the GPE. The GPE will remain disabled a handler
|
|
* is installed or ACPICA is restarted.
|
|
*/
|
|
- 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[0x%2X]",
|
|
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
|
|
index 7c7bbb4..e3d9f5c 100644
|
|
--- a/drivers/acpi/acpica/evxfevnt.c
|
|
+++ b/drivers/acpi/acpica/evxfevnt.c
|
|
@@ -201,6 +201,44 @@ 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
|
|
*
|
|
* PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
|
|
@@ -240,11 +278,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:
|
|
@@ -307,7 +345,11 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type)
|
|
|
|
gpe_event_info->runtime_count++;
|
|
if (gpe_event_info->runtime_count == 1) {
|
|
- status = acpi_ev_enable_gpe(gpe_event_info);
|
|
+ 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--;
|
|
goto unlock_and_exit;
|
|
@@ -334,7 +376,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type)
|
|
*/
|
|
gpe_event_info->wakeup_count++;
|
|
if (gpe_event_info->wakeup_count == 1) {
|
|
- (void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
+ status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
}
|
|
}
|
|
|
|
@@ -394,7 +436,12 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type
|
|
|
|
gpe_event_info->runtime_count--;
|
|
if (!gpe_event_info->runtime_count) {
|
|
- status = acpi_ev_disable_gpe(gpe_event_info);
|
|
+ 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++;
|
|
goto unlock_and_exit;
|
|
@@ -415,7 +462,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type
|
|
|
|
gpe_event_info->wakeup_count--;
|
|
if (!gpe_event_info->wakeup_count) {
|
|
- (void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
+ status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
|
|
index d989b8e..40388e2 100644
|
|
--- a/drivers/acpi/acpica/hwgpe.c
|
|
+++ b/drivers/acpi/acpica/hwgpe.c
|
|
@@ -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 ot 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 */
|
|
|
|
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
|
|
index de5e99a..6881f5b 100644
|
|
--- a/include/acpi/actypes.h
|
|
+++ b/include/acpi/actypes.h
|
|
@@ -663,7 +663,7 @@ typedef u32 acpi_event_status;
|
|
#define ACPI_GPE_MAX 0xFF
|
|
#define ACPI_NUM_GPE 256
|
|
|
|
-/* Actions for acpi_set_gpe */
|
|
+/* Actions for acpi_set_gpe and acpi_hw_low_set_gpe */
|
|
|
|
#define ACPI_GPE_ENABLE 0
|
|
#define ACPI_GPE_DISABLE 1
|