diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2011-02-24 21:59:21 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-02-24 21:59:21 +0300 |
commit | 981858bd7a401aa9607d9f430d5de920025fc3ea (patch) | |
tree | ac68be4bb6d73e20e5e10dfc19af679214249f60 /drivers/acpi/acpica/evxfgpe.c | |
parent | 2949ad50711cc161721cf788711722eeeca33764 (diff) | |
download | linux-981858bd7a401aa9607d9f430d5de920025fc3ea.tar.xz |
ACPI / ACPICA: Implicit notify for multiple devices
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>
Diffstat (limited to 'drivers/acpi/acpica/evxfgpe.c')
-rw-r--r-- | drivers/acpi/acpica/evxfgpe.c | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 3b20a3401b64..52aaff3df562 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -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); @@ -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; |