Skip to content

Commit

Permalink
ACPI / ACPICA: Implicit notify for multiple devices
Browse files Browse the repository at this point in the history
Commit bba63a2 (ACPICA: Implicit notify support) introduced a
mechanism that causes a notify request of type
ACPI_NOTIFY_DEVICE_WAKE to be queued automatically by
acpi_ev_asynch_execute_gpe_method() for the device whose _PRW points
to the GPE being handled if that GPE is not associated with an
_Lxx/_Exx method.  However, it turns out that on some systems there
are multiple devices with _PRW pointing to the same GPE without
_Lxx/_Exx and the mechanism introduced by commit bba63a2 needs to be
extended so that "implicit" notify requests of type
ACPI_NOTIFY_DEVICE_WAKE can be queued automatically for all those
devices at the same time.

Reported-and-tested-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
  • Loading branch information
Rafael J. Wysocki committed Feb 24, 2011
1 parent 2949ad5 commit 981858b
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 14 deletions.
7 changes: 6 additions & 1 deletion drivers/acpi/acpica/aclocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,15 @@ struct acpi_gpe_handler_info {
u8 originally_enabled; /* True if GPE was originally enabled */
};

struct acpi_gpe_notify_object {
struct acpi_namespace_node *node;
struct acpi_gpe_notify_object *next;
};

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 */
struct acpi_gpe_notify_object device; /* List of _PRW devices for implicit notify */
};

/*
Expand Down
17 changes: 13 additions & 4 deletions drivers/acpi/acpica/evgpe.c
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,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;

ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method);

Expand Down Expand Up @@ -508,10 +509,18 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
* 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);
status = acpi_ev_queue_notify_request(
local_gpe_event_info->dispatch.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;
}

break;

case ACPI_GPE_DISPATCH_METHOD:
Expand Down
42 changes: 33 additions & 9 deletions drivers/acpi/acpica/evxfgpe.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
acpi_status status = AE_BAD_PARAMETER;
struct acpi_gpe_event_info *gpe_event_info;
struct acpi_namespace_node *device_node;
struct acpi_gpe_notify_object *notify_object;
acpi_cpu_flags flags;
u8 gpe_dispatch_mask;

ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);

Expand All @@ -221,27 +223,49 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
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
* level-triggered (for windows compatibility).
*/
if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
ACPI_GPE_DISPATCH_NONE) && (wake_device != ACPI_ROOT_OBJECT)) {
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;
}

/* Validate wake_device is of type Device */
/* Validate wake_device is of type Device */

device_node = ACPI_CAST_PTR(struct acpi_namespace_node,
wake_device);
if (device_node->type != ACPI_TYPE_DEVICE) {
goto unlock_and_exit;
}
device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
if (device_node->type != ACPI_TYPE_DEVICE) {
goto unlock_and_exit;
}

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.node = device_node;
gpe_event_info->dispatch.device.next = NULL;
} else {
/* There are multiple devices to notify implicitly. */

notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
if (!notify_object) {
status = AE_NO_MEMORY;
goto unlock_and_exit;
}

notify_object->node = device_node;
notify_object->next = gpe_event_info->dispatch.device.next;
gpe_event_info->dispatch.device.next = notify_object;
}

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

Expand Down

0 comments on commit 981858b

Please sign in to comment.