ACPICA: Add support for implicit notify on multiple devices
authorBob Moore <robert.moore@intel.com>
Fri, 29 Jun 2012 02:04:17 +0000 (10:04 +0800)
committerLen Brown <len.brown@intel.com>
Sat, 14 Jul 2012 15:17:29 +0000 (11:17 -0400)
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>
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/evgpe.c
drivers/acpi/acpica/evgpeutil.c
drivers/acpi/acpica/evxfgpe.c

index af7330f4a52d06ab4859b9f1d1cd27615ad22613..6b225e810f3a2a7851d15a56804e80d7f258e2fd 100644 (file)
@@ -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 */
index 8ba0e5f170916a55d81244ef679eaa7715365972..afbd5cb391f671eeedef586c3c6aa515fd8eba62 100644 (file)
@@ -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,
-                               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 = 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 = notify->next;
                }
 
                break;
index 3c43796b8361dd67447162f2c39ac2ca45e1d856..0c33c62bd9eb41f2823b769dfb43caac7a6da90f 100644 (file)
@@ -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;
                        }
                }
        }
index 86f9b343ebd404c45833eda4b88fb37a4f6ec5f7..2ce44099eb840109b59d26cf386af604eee9018c 100644 (file)
@@ -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;
-       }
-
-       /* 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;
+       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);
        }
 
-       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. */
-
-               notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
-               if (!notify_object) {
-                       status = AE_NO_MEMORY;
-                       goto unlock_and_exit;
+       /*
+        * 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) {
+
+               /* 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;
                }
 
-               notify_object->node = device_node;
-               notify_object->next = gpe_event_info->dispatch.device.next;
-               gpe_event_info->dispatch.device.next = notify_object;
+               /* Add this device to the notify list for this GPE */
+
+               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;
        status = AE_OK;
 
- unlock_and_exit:
+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)