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

Commit 5816b343 authored by Bob Moore's avatar Bob Moore Committed by Len Brown
Browse files

ACPICA: Add support for implicit notify on multiple devices



Adds basic support to allow multiple devices to be implicitly
notified.

This change is partially derived from original commit 981858bd("ACPI /
ACPICA: Implicit notify for multiple devices") by Rafael.

Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarJung-uk Kim <jkim@freebsd.org>
Signed-off-by: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent e40d5940
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -404,6 +404,13 @@ struct acpi_gpe_handler_info {
	u8 originally_enabled;  /* True if GPE was originally enabled */
};

/* Notify info for implicit notify, multiple device objects */

struct acpi_gpe_notify_info {
	struct acpi_namespace_node *device_node;	/* Device to be notified */
	struct acpi_gpe_notify_info *next;
};

struct acpi_gpe_notify_object {
	struct acpi_namespace_node *node;
	struct acpi_gpe_notify_object *next;
@@ -412,7 +419,7 @@ struct acpi_gpe_notify_object {
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_gpe_notify_object device;   /* List of _PRW devices for implicit notify */
	struct acpi_gpe_notify_info *notify_list;	/* List of _PRW devices for implicit notifies */
};

/*
@@ -420,7 +427,7 @@ union acpi_gpe_dispatch_info {
 * NOTE: Important to keep this struct as small as possible.
 */
struct acpi_gpe_event_info {
	union acpi_gpe_dispatch_info dispatch;	/* Either Method or Handler */
	union acpi_gpe_dispatch_info dispatch;	/* Either Method, Handler, or notify_list */
	struct acpi_gpe_register_info *register_info;	/* Backpointer to register info */
	u8 flags;		/* Misc info about this GPE */
	u8 gpe_number;		/* This GPE */
+11 −11
Original line number Diff line number Diff line
@@ -466,7 +466,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
	acpi_status status;
	struct acpi_gpe_event_info *local_gpe_event_info;
	struct acpi_evaluate_info *info;
	struct acpi_gpe_notify_object *notify_object;
	struct acpi_gpe_notify_info *notify;

	ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method);

@@ -517,17 +517,17 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
		 * completes. The notify handlers are NOT invoked synchronously
		 * from this thread -- because handlers may in turn run other
		 * control methods.
		 *
		 * June 2012: Expand implicit notify mechanism to support
		 * notifies on multiple device objects.
		 */
		status = acpi_ev_queue_notify_request(
				local_gpe_event_info->dispatch.device.node,
		notify = local_gpe_event_info->dispatch.notify_list;
		while (ACPI_SUCCESS(status) && notify) {
			status =
			    acpi_ev_queue_notify_request(notify->device_node,
							 ACPI_NOTIFY_DEVICE_WAKE);

		notify_object = local_gpe_event_info->dispatch.device.next;
		while (ACPI_SUCCESS(status) && notify_object) {
			status = acpi_ev_queue_notify_request(
					notify_object->node,
					ACPI_NOTIFY_DEVICE_WAKE);
			notify_object = notify_object->next;
			notify = notify->next;
		}

		break;
+20 −0
Original line number Diff line number Diff line
@@ -347,6 +347,8 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
			    void *context)
{
	struct acpi_gpe_event_info *gpe_event_info;
	struct acpi_gpe_notify_info *notify;
	struct acpi_gpe_notify_info *next;
	u32 i;
	u32 j;

@@ -365,10 +367,28 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,

			if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
			    ACPI_GPE_DISPATCH_HANDLER) {

				/* Delete an installed handler block */

				ACPI_FREE(gpe_event_info->dispatch.handler);
				gpe_event_info->dispatch.handler = NULL;
				gpe_event_info->flags &=
				    ~ACPI_GPE_DISPATCH_MASK;
			} else if ((gpe_event_info->
				 flags & ACPI_GPE_DISPATCH_MASK) ==
				ACPI_GPE_DISPATCH_NOTIFY) {

				/* Delete the implicit notification device list */

				notify = gpe_event_info->dispatch.notify_list;
				while (notify) {
					next = notify->next;
					ACPI_FREE(notify);
					notify = next;
				}
				gpe_event_info->dispatch.notify_list = NULL;
				gpe_event_info->flags &=
				    ~ACPI_GPE_DISPATCH_MASK;
			}
		}
	}
+69 −37
Original line number Diff line number Diff line
@@ -197,12 +197,12 @@ acpi_status
acpi_setup_gpe_for_wake(acpi_handle wake_device,
			acpi_handle gpe_device, u32 gpe_number)
{
	acpi_status status = AE_BAD_PARAMETER;
	acpi_status status;
	struct acpi_gpe_event_info *gpe_event_info;
	struct acpi_namespace_node *device_node;
	struct acpi_gpe_notify_object *notify_object;
	struct acpi_gpe_notify_info *notify;
	struct acpi_gpe_notify_info *new_notify;
	acpi_cpu_flags flags;
	u8 gpe_dispatch_mask;

	ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);

@@ -216,63 +216,95 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	/* Handle root object case */

	if (wake_device == ACPI_ROOT_OBJECT) {
		device_node = acpi_gbl_root_node;
	} else {
		device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
	}

	/* Validate WakeDevice is of type Device */

	if (device_node->type != ACPI_TYPE_DEVICE) {
		return_ACPI_STATUS (AE_BAD_PARAMETER);
	}

	/*
	 * Allocate a new notify object up front, in case it is needed.
	 * Memory allocation while holding a spinlock is a big no-no
	 * on some hosts.
	 */
	new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info));
	if (!new_notify) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);

	/* Ensure that we have a valid GPE number */

	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
	if (!gpe_event_info) {
		status = AE_BAD_PARAMETER;
		goto unlock_and_exit;
	}

	if (wake_device == ACPI_ROOT_OBJECT) {
		goto out;
	}

	/*
	 * If there is no method or handler for this GPE, then the
	 * wake_device will be notified whenever this GPE fires (aka
	 * "implicit notify") Note: The GPE is assumed to be
	 * wake_device will be notified whenever this GPE fires. This is
	 * known as an "implicit notify". Note: The GPE is assumed to be
	 * level-triggered (for windows compatibility).
	 */
	gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK;
	if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE
	    && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) {
		goto out;
	if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
	    ACPI_GPE_DISPATCH_NONE) {
		/*
		 * This is the first device for implicit notify on this GPE.
		 * Just set the flags here, and enter the NOTIFY block below.
		 */
		gpe_event_info->flags =
		    (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
	}

	/* Validate wake_device is of type Device */
	/*
	 * If we already have an implicit notify on this GPE, add
	 * this device to the notify list.
	 */
	if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
	    ACPI_GPE_DISPATCH_NOTIFY) {

	device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
	if (device_node->type != ACPI_TYPE_DEVICE) {
		/* Ensure that the device is not already in the list */

		notify = gpe_event_info->dispatch.notify_list;
		while (notify) {
			if (notify->device_node == device_node) {
				status = AE_ALREADY_EXISTS;
				goto unlock_and_exit;
			}
			notify = notify->next;
		}

	if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) {
		gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY |
					 ACPI_GPE_LEVEL_TRIGGERED);
		gpe_event_info->dispatch.device.node = device_node;
		gpe_event_info->dispatch.device.next = NULL;
	} else {
		/* There are multiple devices to notify implicitly. */
		/* Add this device to the notify list for this GPE */

		notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
		if (!notify_object) {
			status = AE_NO_MEMORY;
			goto unlock_and_exit;
		new_notify->device_node = device_node;
		new_notify->next = gpe_event_info->dispatch.notify_list;
		gpe_event_info->dispatch.notify_list = new_notify;
		new_notify = NULL;
	}

		notify_object->node = device_node;
		notify_object->next = gpe_event_info->dispatch.device.next;
		gpe_event_info->dispatch.device.next = notify_object;
	}
	/* Mark the GPE as a possible wake event */

 out:
	gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
	status = AE_OK;

unlock_and_exit:
	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);

	/* Delete the notify object if it was not used above */

	if (new_notify) {
		ACPI_FREE(new_notify);
	}
	return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)