summaryrefslogtreecommitdiffstats
path: root/events/evgpe.c
diff options
context:
space:
mode:
Diffstat (limited to 'events/evgpe.c')
-rw-r--r--events/evgpe.c288
1 files changed, 213 insertions, 75 deletions
diff --git a/events/evgpe.c b/events/evgpe.c
index f7ab160..2ed4c3c 100644
--- a/events/evgpe.c
+++ b/events/evgpe.c
@@ -202,12 +202,13 @@ AcpiEvEnableGpe (
/*
- * 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 AcpiEvGpeDispatch 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
+ * AcpiEvGpeDispatch the first time it fires.
*/
- if (!(GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK))
+ if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
+ ACPI_GPE_DISPATCH_NONE)
{
return_ACPI_STATUS (AE_NO_HANDLER);
}
@@ -229,6 +230,104 @@ AcpiEvEnableGpe (
/*******************************************************************************
*
+ * FUNCTION: AcpiEvAddGpeReference
+ *
+ * PARAMETERS: GpeEventInfo - Add a reference to this GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
+ * hardware-enabled.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEvAddGpeReference (
+ ACPI_GPE_EVENT_INFO *GpeEventInfo)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (EvAddGpeReference);
+
+
+ if (GpeEventInfo->RuntimeCount == ACPI_UINT8_MAX)
+ {
+ return_ACPI_STATUS (AE_LIMIT);
+ }
+
+ GpeEventInfo->RuntimeCount++;
+ if (GpeEventInfo->RuntimeCount == 1)
+ {
+ /* Enable on first reference */
+
+ Status = AcpiEvUpdateGpeEnableMask (GpeEventInfo);
+ if (ACPI_SUCCESS (Status))
+ {
+ Status = AcpiEvEnableGpe (GpeEventInfo);
+ }
+
+ if (ACPI_FAILURE (Status))
+ {
+ GpeEventInfo->RuntimeCount--;
+ }
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvRemoveGpeReference
+ *
+ * PARAMETERS: GpeEventInfo - Remove a reference to this GPE
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Remove a reference to a GPE. When the last reference is
+ * removed, the GPE is hardware-disabled.
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+AcpiEvRemoveGpeReference (
+ ACPI_GPE_EVENT_INFO *GpeEventInfo)
+{
+ ACPI_STATUS Status = AE_OK;
+
+
+ ACPI_FUNCTION_TRACE (EvRemoveGpeReference);
+
+
+ if (!GpeEventInfo->RuntimeCount)
+ {
+ return_ACPI_STATUS (AE_LIMIT);
+ }
+
+ GpeEventInfo->RuntimeCount--;
+ if (!GpeEventInfo->RuntimeCount)
+ {
+ /* Disable on last reference */
+
+ Status = AcpiEvUpdateGpeEnableMask (GpeEventInfo);
+ if (ACPI_SUCCESS (Status))
+ {
+ Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
+ }
+
+ if (ACPI_FAILURE (Status))
+ {
+ GpeEventInfo->RuntimeCount++;
+ }
+ }
+
+ return_ACPI_STATUS (Status);
+}
+
+
+/*******************************************************************************
+ *
* FUNCTION: AcpiEvLowGetGpeInfo
*
* PARAMETERS: GpeNumber - Raw GPE number
@@ -412,7 +511,7 @@ AcpiEvGpeDetect (
}
ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS,
- "Read GPE Register at GPE%X: Status=%02X, Enable=%02X\n",
+ "Read GPE Register at GPE%02X: Status=%02X, Enable=%02X\n",
GpeRegisterInfo->BaseGpeNumber, StatusReg, EnableReg));
/* Check if there is anything active at all in this register */
@@ -437,7 +536,7 @@ AcpiEvGpeDetect (
* Found an active GPE. Dispatch the event to a handler
* or method.
*/
- IntStatus |= AcpiEvGpeDispatch (
+ IntStatus |= AcpiEvGpeDispatch (GpeBlock->Node,
&GpeBlock->EventInfo[((ACPI_SIZE) i *
ACPI_GPE_REGISTER_WIDTH) + j],
j + GpeRegisterInfo->BaseGpeNumber);
@@ -521,13 +620,27 @@ AcpiEvAsynchExecuteGpeMethod (
return_VOID;
}
- /*
- * Must check for control method type dispatch one more time to avoid a
- * race with EvGpeInstallHandler
- */
- if ((LocalGpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
- ACPI_GPE_DISPATCH_METHOD)
+ /* Do the correct dispatch - normal method or implicit notify */
+
+ switch (LocalGpeEventInfo->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 = AcpiEvQueueNotifyRequest (
+ LocalGpeEventInfo->Dispatch.DeviceNode,
+ ACPI_NOTIFY_DEVICE_WAKE);
+ break;
+
+ case ACPI_GPE_DISPATCH_METHOD:
+
/* Allocate the evaluation information block */
Info = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_EVALUATE_INFO));
@@ -538,8 +651,8 @@ AcpiEvAsynchExecuteGpeMethod (
else
{
/*
- * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the _Lxx/_Exx
- * control method that corresponds to this GPE
+ * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the
+ * _Lxx/_Exx control method that corresponds to this GPE
*/
Info->PrefixNode = LocalGpeEventInfo->Dispatch.MethodNode;
Info->Flags = ACPI_IGNORE_RETURN_VALUE;
@@ -554,6 +667,11 @@ AcpiEvAsynchExecuteGpeMethod (
"while evaluating GPE method [%4.4s]",
AcpiUtGetNodeName (LocalGpeEventInfo->Dispatch.MethodNode)));
}
+
+ break;
+
+ default:
+ return_VOID; /* Should never happen */
}
/* Defer enabling of GPE until all notify handlers are done */
@@ -573,6 +691,7 @@ AcpiEvAsynchExecuteGpeMethod (
* FUNCTION: AcpiEvAsynchEnableGpe
*
* PARAMETERS: Context (GpeEventInfo) - Info for this GPE
+ * Callback from AcpiOsExecute
*
* RETURN: None
*
@@ -586,6 +705,32 @@ AcpiEvAsynchEnableGpe (
void *Context)
{
ACPI_GPE_EVENT_INFO *GpeEventInfo = Context;
+
+
+ (void) AcpiEvFinishGpe (GpeEventInfo);
+
+ ACPI_FREE (GpeEventInfo);
+ return;
+}
+
+
+/*******************************************************************************
+ *
+ * FUNCTION: AcpiEvFinishGpe
+ *
+ * PARAMETERS: GpeEventInfo - 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
+AcpiEvFinishGpe (
+ ACPI_GPE_EVENT_INFO *GpeEventInfo)
+{
ACPI_STATUS Status;
@@ -593,25 +738,23 @@ AcpiEvAsynchEnableGpe (
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 = AcpiHwClearGpe (GpeEventInfo);
if (ACPI_FAILURE (Status))
{
- goto Exit;
+ return (Status);
}
}
/*
- * Enable this GPE, conditionally. This means that the GPE will only be
- * physically enabled if the EnableForRun bit is set in the EventInfo
+ * Enable this GPE, conditionally. This means that the GPE will
+ * only be physically enabled if the EnableForRun bit is set
+ * in the EventInfo.
*/
(void) AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_CONDITIONAL_ENABLE);
-
-Exit:
- ACPI_FREE (GpeEventInfo);
- return;
+ return (AE_OK);
}
@@ -619,8 +762,9 @@ Exit:
*
* FUNCTION: AcpiEvGpeDispatch
*
- * PARAMETERS: GpeEventInfo - Info for this GPE
- * GpeNumber - Number relative to the parent GPE block
+ * PARAMETERS: GpeDevice - Device node. NULL for GPE0/GPE1
+ * GpeEventInfo - Info for this GPE
+ * GpeNumber - Number relative to the parent GPE block
*
* RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
*
@@ -633,16 +777,25 @@ Exit:
UINT32
AcpiEvGpeDispatch (
+ ACPI_NAMESPACE_NODE *GpeDevice,
ACPI_GPE_EVENT_INFO *GpeEventInfo,
UINT32 GpeNumber)
{
ACPI_STATUS Status;
+ UINT32 ReturnValue;
ACPI_FUNCTION_TRACE (EvGpeDispatch);
+ /* Invoke global event handler if present */
+
AcpiGpeCount++;
+ if (AcpiGbl_GlobalEventHandler)
+ {
+ AcpiGbl_GlobalEventHandler (ACPI_EVENT_TYPE_GPE, GpeDevice,
+ GpeNumber, AcpiGbl_GlobalEventHandlerContext);
+ }
/*
* If edge-triggered, clear the GPE status bit now. Note that
@@ -655,58 +808,55 @@ AcpiEvGpeDispatch (
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to clear GPE[0x%2X]", GpeNumber));
+ "Unable to clear GPE%02X", GpeNumber));
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, 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 = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
+ if (ACPI_FAILURE (Status))
+ {
+ ACPI_EXCEPTION ((AE_INFO, Status,
+ "Unable to disable GPE%02X", GpeNumber));
+ 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 (GpeEventInfo->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) GpeEventInfo->Dispatch.Handler->Address (
- GpeEventInfo->Dispatch.Handler->Context);
+ /* Invoke the installed handler (at interrupt level) */
- /* It is now safe to clear level-triggered events. */
+ ReturnValue = GpeEventInfo->Dispatch.Handler->Address (
+ GpeDevice, GpeNumber,
+ GpeEventInfo->Dispatch.Handler->Context);
- if ((GpeEventInfo->Flags & ACPI_GPE_XRUPT_TYPE_MASK) ==
- ACPI_GPE_LEVEL_TRIGGERED)
+ /* If requested, clear (if level-triggered) and reenable the GPE */
+
+ if (ReturnValue & ACPI_REENABLE_GPE)
{
- Status = AcpiHwClearGpe (GpeEventInfo);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to clear GPE[0x%2X]", GpeNumber));
- return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
- }
+ (void) AcpiEvFinishGpe (GpeEventInfo);
}
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 = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to disable GPE[0x%2X]", GpeNumber));
- return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
- }
+ case ACPI_GPE_DISPATCH_NOTIFY:
/*
* Execute the method associated with the GPE
@@ -717,7 +867,7 @@ AcpiEvGpeDispatch (
if (ACPI_FAILURE (Status))
{
ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to queue handler for GPE[0x%2X] - event disabled",
+ "Unable to queue handler for GPE%02X - event disabled",
GpeNumber));
}
break;
@@ -730,20 +880,8 @@ AcpiEvGpeDispatch (
* a GPE to be enabled if it has no handler or method.
*/
ACPI_ERROR ((AE_INFO,
- "No handler or method for GPE[0x%2X], disabling event",
+ "No handler or method for GPE%02X, disabling event",
GpeNumber));
-
- /*
- * Disable the GPE. The GPE will remain disabled until a handler
- * is installed or ACPICA is restarted.
- */
- Status = AcpiHwLowSetGpe (GpeEventInfo, ACPI_GPE_DISABLE);
- if (ACPI_FAILURE (Status))
- {
- ACPI_EXCEPTION ((AE_INFO, Status,
- "Unable to disable GPE[0x%2X]", GpeNumber));
- return_UINT32 (ACPI_INTERRUPT_NOT_HANDLED);
- }
break;
}
OpenPOWER on IntegriCloud