Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bba63a29 authored by Lin Ming's avatar Lin Ming Committed by Len Brown
Browse files

ACPICA: Implicit notify support



This feature provides an automatic device notification for wake devices
when a wakeup GPE occurs and there is no corresponding GPE method or
handler. Rather than ignoring such a GPE, an implicit AML Notify
operation is performed on the parent device object.
This feature is not part of the ACPI specification and is provided for
Windows compatibility only.

Signed-off-by: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 5a284cd7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -91,6 +91,8 @@ struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number,
						     struct acpi_gpe_block_info
						     *gpe_block);

acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info);

/*
 * evgpeblk - Upper-level GPE block support
 */
+1 −0
Original line number Diff line number Diff line
@@ -419,6 +419,7 @@ struct acpi_gpe_handler_info {
union acpi_gpe_dispatch_info {
	struct acpi_namespace_node *method_node;	/* Method node for this GPE level */
	struct acpi_gpe_handler_info *handler;  /* Installed GPE handler */
	struct acpi_namespace_node *device_node;        /* Parent _PRW device for implicit notify */
};

/*
+100 −72
Original line number Diff line number Diff line
@@ -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;
	}

	/* Do the correct dispatch - normal method or implicit notify */

	switch (local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) {
	case ACPI_GPE_DISPATCH_NOTIFY:

		/*
	 * Must check for control method type dispatch one more time to avoid a
	 * race with ev_gpe_install_handler
		 * 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.
		 */
	if ((local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
	    ACPI_GPE_DISPATCH_METHOD) {
		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.
	 */
	switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) {
	case ACPI_GPE_DISPATCH_HANDLER:
	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);
	}

	/*
		 * Invoke the installed handler (at interrupt level)
		 * Ignore return status for now.
		 * TBD: leave GPE disabled on error?
	 * 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.
	 */
		(void)gpe_event_info->dispatch.handler->address(gpe_device,
	switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) {
	case ACPI_GPE_DISPATCH_HANDLER:

		/* Invoke the installed handler (at interrupt level) */

		return_value =
		    gpe_event_info->dispatch.handler->address(gpe_device,
							      gpe_number,
							      gpe_event_info->
								dispatch.
								handler->
							      dispatch.handler->
							      context);

		/* It is now safe to clear level-triggered events. */
		/* If requested, clear (if level-triggered) and reenable the GPE */

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

+8 −3
Original line number Diff line number Diff line
@@ -472,9 +472,14 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
			gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j;
			gpe_event_info = &gpe_block->event_info[gpe_index];

			/* Ignore GPEs that have no corresponding _Lxx/_Exx method */

			if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD)
			/*
			 * Ignore GPEs that have no corresponding _Lxx/_Exx method
			 * and GPEs that are used to wake the system
			 */
			if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
			     ACPI_GPE_DISPATCH_NONE)
			    || ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)
				== ACPI_GPE_DISPATCH_HANDLER)
			    || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
				continue;
			}
+1 −0
Original line number Diff line number Diff line
@@ -415,6 +415,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
	 * Add the GPE information from above to the gpe_event_info block for
	 * use during dispatch of this GPE.
	 */
	gpe_event_info->flags &= ~(ACPI_GPE_DISPATCH_MASK);
	gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD);
	gpe_event_info->dispatch.method_node = method_node;

Loading