|
@@ -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;
|
|
|
}
|
|
|
|