]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/acpi/device_pm.c
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
[mirror_ubuntu-artful-kernel.git] / drivers / acpi / device_pm.c
index 49a51277f81d17dad2d612cba4d3efe3f7b18bf2..67075f800e34cb69b3a28d852b9bc15b00ffa702 100644 (file)
@@ -367,29 +367,61 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
 #ifdef CONFIG_PM
 static DEFINE_MUTEX(acpi_pm_notifier_lock);
 
+static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
+{
+       struct acpi_device *adev;
+
+       if (val != ACPI_NOTIFY_DEVICE_WAKE)
+               return;
+
+       adev = acpi_bus_get_acpi_device(handle);
+       if (!adev)
+               return;
+
+       mutex_lock(&acpi_pm_notifier_lock);
+
+       if (adev->wakeup.flags.notifier_present) {
+               __pm_wakeup_event(adev->wakeup.ws, 0);
+               if (adev->wakeup.context.work.func)
+                       queue_pm_work(&adev->wakeup.context.work);
+       }
+
+       mutex_unlock(&acpi_pm_notifier_lock);
+
+       acpi_bus_put_acpi_device(adev);
+}
+
 /**
- * acpi_add_pm_notifier - Register PM notifier for given ACPI device.
- * @adev: ACPI device to add the notifier for.
- * @context: Context information to pass to the notifier routine.
+ * acpi_add_pm_notifier - Register PM notify handler for given ACPI device.
+ * @adev: ACPI device to add the notify handler for.
+ * @dev: Device to generate a wakeup event for while handling the notification.
+ * @work_func: Work function to execute when handling the notification.
  *
  * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of
  * PM wakeup events.  For example, wakeup events may be generated for bridges
  * if one of the devices below the bridge is signaling wakeup, even if the
  * bridge itself doesn't have a wakeup GPE associated with it.
  */
-acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
-                                acpi_notify_handler handler, void *context)
+acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
+                                void (*work_func)(struct work_struct *work))
 {
        acpi_status status = AE_ALREADY_EXISTS;
 
+       if (!dev && !work_func)
+               return AE_BAD_PARAMETER;
+
        mutex_lock(&acpi_pm_notifier_lock);
 
        if (adev->wakeup.flags.notifier_present)
                goto out;
 
-       status = acpi_install_notify_handler(adev->handle,
-                                            ACPI_SYSTEM_NOTIFY,
-                                            handler, context);
+       adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
+       adev->wakeup.context.dev = dev;
+       if (work_func)
+               INIT_WORK(&adev->wakeup.context.work, work_func);
+
+       status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
+                                            acpi_pm_notify_handler, NULL);
        if (ACPI_FAILURE(status))
                goto out;
 
@@ -404,8 +436,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
  * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device.
  * @adev: ACPI device to remove the notifier from.
  */
-acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
-                                   acpi_notify_handler handler)
+acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
 {
        acpi_status status = AE_BAD_PARAMETER;
 
@@ -416,10 +447,17 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
 
        status = acpi_remove_notify_handler(adev->handle,
                                            ACPI_SYSTEM_NOTIFY,
-                                           handler);
+                                           acpi_pm_notify_handler);
        if (ACPI_FAILURE(status))
                goto out;
 
+       if (adev->wakeup.context.work.func) {
+               cancel_work_sync(&adev->wakeup.context.work);
+               adev->wakeup.context.work.func = NULL;
+       }
+       adev->wakeup.context.dev = NULL;
+       wakeup_source_unregister(adev->wakeup.ws);
+
        adev->wakeup.flags.notifier_present = false;
 
  out:
@@ -558,7 +596,6 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
  */
 int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
 {
-       acpi_handle handle = ACPI_HANDLE(dev);
        struct acpi_device *adev;
        int ret, d_min, d_max;
 
@@ -573,8 +610,9 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
                        d_max_in = ACPI_STATE_D3_HOT;
        }
 
-       if (!handle || acpi_bus_get_device(handle, &adev)) {
-               dev_dbg(dev, "ACPI handle without context in %s!\n", __func__);
+       adev = ACPI_COMPANION(dev);
+       if (!adev) {
+               dev_dbg(dev, "ACPI companion missing in %s!\n", __func__);
                return -ENODEV;
        }
 
@@ -600,26 +638,25 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
 }
 EXPORT_SYMBOL(acpi_pm_device_sleep_state);
 
-#ifdef CONFIG_PM_RUNTIME
 /**
- * acpi_wakeup_device - Wakeup notification handler for ACPI devices.
- * @handle: ACPI handle of the device the notification is for.
- * @event: Type of the signaled event.
- * @context: Device corresponding to @handle.
+ * acpi_pm_notify_work_func - ACPI devices wakeup notification work function.
+ * @work: Work item to handle.
  */
-static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context)
+static void acpi_pm_notify_work_func(struct work_struct *work)
 {
-       struct device *dev = context;
+       struct device *dev;
 
-       if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) {
+       dev = container_of(work, struct acpi_device_wakeup_context, work)->dev;
+       if (dev) {
                pm_wakeup_event(dev, 0);
                pm_runtime_resume(dev);
        }
 }
 
 /**
- * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device.
- * @adev: ACPI device to enable/disable the remote wakeup for.
+ * acpi_device_wakeup - Enable/disable wakeup functionality for device.
+ * @adev: ACPI device to enable/disable wakeup functionality for.
+ * @target_state: State the system is transitioning into.
  * @enable: Whether to enable or disable the wakeup functionality.
  *
  * Enable/disable the GPE associated with @adev so that it can generate
@@ -629,7 +666,8 @@ static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context)
  * Callers must ensure that @adev is a valid ACPI device node before executing
  * this function.
  */
-int __acpi_device_run_wake(struct acpi_device *adev, bool enable)
+static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state,
+                             bool enable)
 {
        struct acpi_device_wakeup *wakeup = &adev->wakeup;
 
@@ -637,7 +675,7 @@ int __acpi_device_run_wake(struct acpi_device *adev, bool enable)
                acpi_status res;
                int error;
 
-               error = acpi_enable_wakeup_device_power(adev, ACPI_STATE_S0);
+               error = acpi_enable_wakeup_device_power(adev, target_state);
                if (error)
                        return error;
 
@@ -653,6 +691,7 @@ int __acpi_device_run_wake(struct acpi_device *adev, bool enable)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
 /**
  * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device.
  * @dev: Device to enable/disable the platform to wake up.
@@ -661,41 +700,22 @@ int __acpi_device_run_wake(struct acpi_device *adev, bool enable)
 int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
 {
        struct acpi_device *adev;
-       acpi_handle handle;
 
        if (!device_run_wake(phys_dev))
                return -EINVAL;
 
-       handle = ACPI_HANDLE(phys_dev);
-       if (!handle || acpi_bus_get_device(handle, &adev)) {
-               dev_dbg(phys_dev, "ACPI handle without context in %s!\n",
-                       __func__);
+       adev = ACPI_COMPANION(phys_dev);
+       if (!adev) {
+               dev_dbg(phys_dev, "ACPI companion missing in %s!\n", __func__);
                return -ENODEV;
        }
 
-       return __acpi_device_run_wake(adev, enable);
+       return acpi_device_wakeup(adev, enable, ACPI_STATE_S0);
 }
 EXPORT_SYMBOL(acpi_pm_device_run_wake);
-#else
-static inline void acpi_wakeup_device(acpi_handle handle, u32 event,
-                                     void *context) {}
 #endif /* CONFIG_PM_RUNTIME */
 
 #ifdef CONFIG_PM_SLEEP
-/**
- * __acpi_device_sleep_wake - Enable or disable device to wake up the system.
- * @dev: Device to enable/desible to wake up the system.
- * @target_state: System state the device is supposed to wake up from.
- * @enable: Whether to enable or disable @dev to wake up the system.
- */
-int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state,
-                            bool enable)
-{
-       return enable ?
-               acpi_enable_wakeup_device_power(adev, target_state) :
-               acpi_disable_wakeup_device_power(adev);
-}
-
 /**
  * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system.
  * @dev: Device to enable/desible to wake up the system from sleep states.
@@ -703,21 +723,19 @@ int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state,
  */
 int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
 {
-       acpi_handle handle;
        struct acpi_device *adev;
        int error;
 
        if (!device_can_wakeup(dev))
                return -EINVAL;
 
-       handle = ACPI_HANDLE(dev);
-       if (!handle || acpi_bus_get_device(handle, &adev)) {
-               dev_dbg(dev, "ACPI handle without context in %s!\n", __func__);
+       adev = ACPI_COMPANION(dev);
+       if (!adev) {
+               dev_dbg(dev, "ACPI companion missing in %s!\n", __func__);
                return -ENODEV;
        }
 
-       error = __acpi_device_sleep_wake(adev, acpi_target_system_state(),
-                                        enable);
+       error = acpi_device_wakeup(adev, acpi_target_system_state(), enable);
        if (!error)
                dev_info(dev, "System wakeup %s by ACPI\n",
                                enable ? "enabled" : "disabled");
@@ -775,13 +793,13 @@ int acpi_dev_runtime_suspend(struct device *dev)
 
        remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
                                PM_QOS_FLAGS_NONE;
-       error = __acpi_device_run_wake(adev, remote_wakeup);
+       error = acpi_device_wakeup(adev, ACPI_STATE_S0, remote_wakeup);
        if (remote_wakeup && error)
                return -EAGAIN;
 
        error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
        if (error)
-               __acpi_device_run_wake(adev, false);
+               acpi_device_wakeup(adev, ACPI_STATE_S0, false);
 
        return error;
 }
@@ -804,7 +822,7 @@ int acpi_dev_runtime_resume(struct device *dev)
                return 0;
 
        error = acpi_dev_pm_full_power(adev);
-       __acpi_device_run_wake(adev, false);
+       acpi_device_wakeup(adev, ACPI_STATE_S0, false);
        return error;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
@@ -860,13 +878,13 @@ int acpi_dev_suspend_late(struct device *dev)
 
        target_state = acpi_target_system_state();
        wakeup = device_may_wakeup(dev);
-       error = __acpi_device_sleep_wake(adev, target_state, wakeup);
+       error = acpi_device_wakeup(adev, target_state, wakeup);
        if (wakeup && error)
                return error;
 
        error = acpi_dev_pm_low_power(dev, adev, target_state);
        if (error)
-               __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false);
+               acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
 
        return error;
 }
@@ -889,7 +907,7 @@ int acpi_dev_resume_early(struct device *dev)
                return 0;
 
        error = acpi_dev_pm_full_power(adev);
-       __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false);
+       acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
        return error;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
@@ -1048,11 +1066,11 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
        if (dev->pm_domain)
                return -EEXIST;
 
-       acpi_add_pm_notifier(adev, acpi_wakeup_device, dev);
+       acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func);
        dev->pm_domain = &acpi_general_pm_domain;
        if (power_on) {
                acpi_dev_pm_full_power(adev);
-               __acpi_device_run_wake(adev, false);
+               acpi_device_wakeup(adev, ACPI_STATE_S0, false);
        }
        return 0;
 }
@@ -1076,7 +1094,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off)
 
        if (adev && dev->pm_domain == &acpi_general_pm_domain) {
                dev->pm_domain = NULL;
-               acpi_remove_pm_notifier(adev, acpi_wakeup_device);
+               acpi_remove_pm_notifier(adev);
                if (power_off) {
                        /*
                         * If the device's PM QoS resume latency limit or flags
@@ -1086,7 +1104,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off)
                         */
                        dev_pm_qos_hide_latency_limit(dev);
                        dev_pm_qos_hide_flags(dev);
-                       __acpi_device_run_wake(adev, false);
+                       acpi_device_wakeup(adev, ACPI_STATE_S0, false);
                        acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
                }
        }