summaryrefslogtreecommitdiff
path: root/drivers/acpi/acpica/evgpe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/acpica/evgpe.c')
-rw-r--r--drivers/acpi/acpica/evgpe.c172
1 files changed, 100 insertions, 72 deletions
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index c25999546c8c..2dbca95979ce 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -115,12 +115,13 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
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.
+ * We will only allow a GPE to be enabled if it has either an associated
+ * method (_Lxx/_Exx) or a handler, or is using the implicit notify
+ * feature. 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)) {
+ if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
+ ACPI_GPE_DISPATCH_NONE) {
return_ACPI_STATUS(AE_NO_HANDLER);
}
@@ -486,12 +487,26 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
return_VOID;
}
- /*
- * Must check for control method type dispatch one more time to avoid a
- * race with ev_gpe_install_handler
- */
- if ((local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_METHOD) {
+ /* Do the correct dispatch - normal method or implicit notify */
+
+ switch (local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) {
+ case ACPI_GPE_DISPATCH_NOTIFY:
+
+ /*
+ * Implicit notify.
+ * Dispatch a DEVICE_WAKE notify to the appropriate handler.
+ * NOTE: the request is queued for execution after this method
+ * completes. The notify handlers are NOT invoked synchronously
+ * from this thread -- because handlers may in turn run other
+ * control methods.
+ */
+ status =
+ acpi_ev_queue_notify_request(local_gpe_event_info->dispatch.
+ device_node,
+ ACPI_NOTIFY_DEVICE_WAKE);
+ break;
+
+ case ACPI_GPE_DISPATCH_METHOD:
/* Allocate the evaluation information block */
@@ -518,6 +533,11 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
(local_gpe_event_info->dispatch.
method_node)));
}
+
+ break;
+
+ default:
+ return_VOID; /* Should never happen */
}
/* Defer enabling of GPE until all notify handlers are done */
@@ -531,6 +551,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
return_VOID;
}
+
/*******************************************************************************
*
* FUNCTION: acpi_ev_asynch_enable_gpe
@@ -541,38 +562,60 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
* RETURN: None
*
* DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to
- * complete.
+ * complete (i.e., finish execution of Notify)
*
******************************************************************************/
static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context)
{
struct acpi_gpe_event_info *gpe_event_info = context;
+
+ (void)acpi_ev_finish_gpe(gpe_event_info);
+
+ ACPI_FREE(gpe_event_info);
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_finish_gpe
+ *
+ * PARAMETERS: gpe_event_info - Info for this GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Clear/Enable a GPE. Common code that is used after execution
+ * of a GPE method or a synchronous or asynchronous GPE handler.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
acpi_status status;
if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
ACPI_GPE_LEVEL_TRIGGERED) {
/*
- * GPE is level-triggered, we clear the GPE status bit after handling
- * the event.
+ * GPE is level-triggered, we clear the GPE status bit after
+ * handling the event.
*/
status = acpi_hw_clear_gpe(gpe_event_info);
if (ACPI_FAILURE(status)) {
- goto exit;
+ return (status);
}
}
/*
- * Enable this GPE, conditionally. This means that the GPE will only be
- * physically enabled if the enable_for_run bit is set in the event_info
+ * Enable this GPE, conditionally. This means that the GPE will
+ * only be physically enabled if the enable_for_run bit is set
+ * in the event_info.
*/
(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE);
-
-exit:
- ACPI_FREE(gpe_event_info);
- return;
+ return (AE_OK);
}
+
/*******************************************************************************
*
* FUNCTION: acpi_ev_gpe_dispatch
@@ -595,6 +638,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
{
acpi_status status;
+ u32 return_value;
ACPI_FUNCTION_TRACE(ev_gpe_dispatch);
@@ -616,54 +660,49 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
}
/*
- * Dispatch the GPE to either an installed handler, or the control method
- * associated with this GPE (_Lxx or _Exx). If a handler exists, we invoke
- * it and do not attempt to run the method. If there is neither a handler
- * nor a method, we disable this GPE to prevent further such pointless
- * events from firing.
+ * Always disable the GPE so that it does not keep firing before
+ * any asynchronous activity completes (either from the execution
+ * of a GPE method or an asynchronous GPE handler.)
+ *
+ * If there is no handler or method to run, just disable the
+ * GPE and leave it disabled permanently to prevent further such
+ * pointless events from firing.
+ */
+ 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%02X", gpe_number));
+ return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
+ }
+
+ /*
+ * Dispatch the GPE to either an installed handler or the control
+ * method associated with this GPE (_Lxx or _Exx). If a handler
+ * exists, we invoke it and do not attempt to run the method.
+ * If there is neither a handler nor a method, leave the GPE
+ * disabled.
*/
switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) {
case ACPI_GPE_DISPATCH_HANDLER:
- /*
- * Invoke the installed handler (at interrupt level)
- * Ignore return status for now.
- * TBD: leave GPE disabled on error?
- */
- (void)gpe_event_info->dispatch.handler->address(gpe_device,
- gpe_number,
- gpe_event_info->
- dispatch.
- handler->
- context);
-
- /* It is now safe to clear level-triggered events. */
-
- if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
- ACPI_GPE_LEVEL_TRIGGERED) {
- status = acpi_hw_clear_gpe(gpe_event_info);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "Unable to clear GPE[0x%2X]",
- gpe_number));
- return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
- }
+ /* Invoke the installed handler (at interrupt level) */
+
+ return_value =
+ gpe_event_info->dispatch.handler->address(gpe_device,
+ gpe_number,
+ gpe_event_info->
+ dispatch.handler->
+ context);
+
+ /* If requested, clear (if level-triggered) and reenable the GPE */
+
+ if (return_value & ACPI_REENABLE_GPE) {
+ (void)acpi_ev_finish_gpe(gpe_event_info);
}
break;
case ACPI_GPE_DISPATCH_METHOD:
-
- /*
- * 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_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]",
- gpe_number));
- return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
- }
+ case ACPI_GPE_DISPATCH_NOTIFY:
/*
* Execute the method associated with the GPE
@@ -690,17 +729,6 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
"No handler or method for GPE[0x%2X], disabling event",
gpe_number));
- /*
- * Disable the GPE. The GPE will remain disabled a handler
- * is installed or ACPICA is restarted.
- */
- 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]",
- gpe_number));
- return_UINT32(ACPI_INTERRUPT_NOT_HANDLED);
- }
break;
}