mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-14 17:14:09 +00:00
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 981858b("ACPI / ACPICA: Implicit notify for multiple devices") by Rafael. Signed-off-by: Bob Moore <robert.moore@intel.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jung-uk Kim <jkim@freebsd.org> Signed-off-by: Lin Ming <ming.m.lin@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
e40d594039
commit
5816b3430c
@ -404,6 +404,13 @@ struct acpi_gpe_handler_info {
|
|||||||
u8 originally_enabled; /* True if GPE was originally enabled */
|
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_gpe_notify_object {
|
||||||
struct acpi_namespace_node *node;
|
struct acpi_namespace_node *node;
|
||||||
struct acpi_gpe_notify_object *next;
|
struct acpi_gpe_notify_object *next;
|
||||||
@ -412,7 +419,7 @@ struct acpi_gpe_notify_object {
|
|||||||
union acpi_gpe_dispatch_info {
|
union acpi_gpe_dispatch_info {
|
||||||
struct acpi_namespace_node *method_node; /* Method node for this GPE level */
|
struct acpi_namespace_node *method_node; /* Method node for this GPE level */
|
||||||
struct acpi_gpe_handler_info *handler; /* Installed GPE handler */
|
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.
|
* NOTE: Important to keep this struct as small as possible.
|
||||||
*/
|
*/
|
||||||
struct acpi_gpe_event_info {
|
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 */
|
struct acpi_gpe_register_info *register_info; /* Backpointer to register info */
|
||||||
u8 flags; /* Misc info about this GPE */
|
u8 flags; /* Misc info about this GPE */
|
||||||
u8 gpe_number; /* This GPE */
|
u8 gpe_number; /* This GPE */
|
||||||
|
@ -466,7 +466,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
|
|||||||
acpi_status status;
|
acpi_status status;
|
||||||
struct acpi_gpe_event_info *local_gpe_event_info;
|
struct acpi_gpe_event_info *local_gpe_event_info;
|
||||||
struct acpi_evaluate_info *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);
|
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
|
* completes. The notify handlers are NOT invoked synchronously
|
||||||
* from this thread -- because handlers may in turn run other
|
* from this thread -- because handlers may in turn run other
|
||||||
* control methods.
|
* control methods.
|
||||||
|
*
|
||||||
|
* June 2012: Expand implicit notify mechanism to support
|
||||||
|
* notifies on multiple device objects.
|
||||||
*/
|
*/
|
||||||
status = acpi_ev_queue_notify_request(
|
notify = local_gpe_event_info->dispatch.notify_list;
|
||||||
local_gpe_event_info->dispatch.device.node,
|
while (ACPI_SUCCESS(status) && notify) {
|
||||||
ACPI_NOTIFY_DEVICE_WAKE);
|
status =
|
||||||
|
acpi_ev_queue_notify_request(notify->device_node,
|
||||||
|
ACPI_NOTIFY_DEVICE_WAKE);
|
||||||
|
|
||||||
notify_object = local_gpe_event_info->dispatch.device.next;
|
notify = notify->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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -347,6 +347,8 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
|
|||||||
void *context)
|
void *context)
|
||||||
{
|
{
|
||||||
struct acpi_gpe_event_info *gpe_event_info;
|
struct acpi_gpe_event_info *gpe_event_info;
|
||||||
|
struct acpi_gpe_notify_info *notify;
|
||||||
|
struct acpi_gpe_notify_info *next;
|
||||||
u32 i;
|
u32 i;
|
||||||
u32 j;
|
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) ==
|
if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
|
||||||
ACPI_GPE_DISPATCH_HANDLER) {
|
ACPI_GPE_DISPATCH_HANDLER) {
|
||||||
|
|
||||||
|
/* Delete an installed handler block */
|
||||||
|
|
||||||
ACPI_FREE(gpe_event_info->dispatch.handler);
|
ACPI_FREE(gpe_event_info->dispatch.handler);
|
||||||
gpe_event_info->dispatch.handler = NULL;
|
gpe_event_info->dispatch.handler = NULL;
|
||||||
gpe_event_info->flags &=
|
gpe_event_info->flags &=
|
||||||
~ACPI_GPE_DISPATCH_MASK;
|
~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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,12 +197,12 @@ acpi_status
|
|||||||
acpi_setup_gpe_for_wake(acpi_handle wake_device,
|
acpi_setup_gpe_for_wake(acpi_handle wake_device,
|
||||||
acpi_handle gpe_device, u32 gpe_number)
|
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_gpe_event_info *gpe_event_info;
|
||||||
struct acpi_namespace_node *device_node;
|
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;
|
acpi_cpu_flags flags;
|
||||||
u8 gpe_dispatch_mask;
|
|
||||||
|
|
||||||
ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);
|
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);
|
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);
|
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
|
||||||
|
|
||||||
/* Ensure that we have a valid GPE number */
|
/* Ensure that we have a valid GPE number */
|
||||||
|
|
||||||
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
|
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
|
||||||
if (!gpe_event_info) {
|
if (!gpe_event_info) {
|
||||||
|
status = AE_BAD_PARAMETER;
|
||||||
goto unlock_and_exit;
|
goto unlock_and_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wake_device == ACPI_ROOT_OBJECT) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is no method or handler for this GPE, then the
|
* If there is no method or handler for this GPE, then the
|
||||||
* wake_device will be notified whenever this GPE fires (aka
|
* wake_device will be notified whenever this GPE fires. This is
|
||||||
* "implicit notify") Note: The GPE is assumed to be
|
* known as an "implicit notify". Note: The GPE is assumed to be
|
||||||
* level-triggered (for windows compatibility).
|
* level-triggered (for windows compatibility).
|
||||||
*/
|
*/
|
||||||
gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK;
|
if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
|
||||||
if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE
|
ACPI_GPE_DISPATCH_NONE) {
|
||||||
&& gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) {
|
/*
|
||||||
goto out;
|
* 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);
|
/* Ensure that the device is not already in the list */
|
||||||
if (device_node->type != ACPI_TYPE_DEVICE) {
|
|
||||||
goto unlock_and_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) {
|
notify = gpe_event_info->dispatch.notify_list;
|
||||||
gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY |
|
while (notify) {
|
||||||
ACPI_GPE_LEVEL_TRIGGERED);
|
if (notify->device_node == device_node) {
|
||||||
gpe_event_info->dispatch.device.node = device_node;
|
status = AE_ALREADY_EXISTS;
|
||||||
gpe_event_info->dispatch.device.next = NULL;
|
goto unlock_and_exit;
|
||||||
} else {
|
}
|
||||||
/* There are multiple devices to notify implicitly. */
|
notify = notify->next;
|
||||||
|
|
||||||
notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
|
|
||||||
if (!notify_object) {
|
|
||||||
status = AE_NO_MEMORY;
|
|
||||||
goto unlock_and_exit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notify_object->node = device_node;
|
/* Add this device to the notify list for this GPE */
|
||||||
notify_object->next = gpe_event_info->dispatch.device.next;
|
|
||||||
gpe_event_info->dispatch.device.next = notify_object;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
/* Mark the GPE as a possible wake event */
|
||||||
|
|
||||||
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
|
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
|
||||||
status = AE_OK;
|
status = AE_OK;
|
||||||
|
|
||||||
unlock_and_exit:
|
unlock_and_exit:
|
||||||
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
|
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);
|
return_ACPI_STATUS(status);
|
||||||
}
|
}
|
||||||
ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)
|
ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user