]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
ACPI / PM: Split acpi_device_wakeup()
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 21 Jul 2017 12:40:49 +0000 (14:40 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 1 Aug 2017 12:05:03 +0000 (14:05 +0200)
To prepare for a subsequent change and make the code somewhat easier
to follow, do the following in the ACPI device wakeup handling code:

 * Replace wakeup.flags.enabled under struct acpi_device with
   wakeup.enable_count as that will be necessary going forward.

   For now, wakeup.enable_count is not allowed to grow beyond 1,
   so the current behavior is retained.

 * Split acpi_device_wakeup() into acpi_device_wakeup_enable()
   and acpi_device_wakeup_disable() and modify the callers of
   it accordingly.

 * Introduce a new acpi_wakeup_lock mutex to protect the wakeup
   enabling/disabling code from races in case it is executed
   more than once in parallel for the same device (which may
   happen for bridges theoretically).

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/acpi/device_pm.c
include/acpi/acpi_bus.h

index 2ed6935d4483bb0139e1ed9cc4cf27854c328c0d..4cd4bdab053d1b27178a22f7cf6ad4d14a49e184 100644 (file)
@@ -682,47 +682,74 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
        }
 }
 
+static DEFINE_MUTEX(acpi_wakeup_lock);
+
 /**
- * acpi_device_wakeup - Enable/disable wakeup functionality for device.
- * @adev: ACPI device to enable/disable wakeup functionality for.
+ * acpi_device_wakeup_enable - Enable wakeup functionality for device.
+ * @adev: ACPI device to enable 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
- * wakeup signals for the device in response to external (remote) events and
- * enable/disable device wakeup power.
+ * Enable the GPE associated with @adev so that it can generate wakeup signals
+ * for the device in response to external (remote) events and enable wakeup
+ * power for it.
  *
  * Callers must ensure that @adev is a valid ACPI device node before executing
  * this function.
  */
-static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state,
-                             bool enable)
+static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
 {
        struct acpi_device_wakeup *wakeup = &adev->wakeup;
+       acpi_status status;
+       int error = 0;
 
-       if (enable) {
-               acpi_status res;
-               int error;
+       mutex_lock(&acpi_wakeup_lock);
 
-               if (adev->wakeup.flags.enabled)
-                       return 0;
+       if (wakeup->enable_count > 0)
+               goto out;
 
-               error = acpi_enable_wakeup_device_power(adev, target_state);
-               if (error)
-                       return error;
+       error = acpi_enable_wakeup_device_power(adev, target_state);
+       if (error)
+               goto out;
 
-               res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
-               if (ACPI_FAILURE(res)) {
-                       acpi_disable_wakeup_device_power(adev);
-                       return -EIO;
-               }
-               adev->wakeup.flags.enabled = 1;
-       } else if (adev->wakeup.flags.enabled) {
-               acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+       status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+       if (ACPI_FAILURE(status)) {
                acpi_disable_wakeup_device_power(adev);
-               adev->wakeup.flags.enabled = 0;
+               error = -EIO;
+               goto out;
        }
-       return 0;
+
+       wakeup->enable_count++;
+
+out:
+       mutex_unlock(&acpi_wakeup_lock);
+       return error;
+}
+
+/**
+ * acpi_device_wakeup_disable - Disable wakeup functionality for device.
+ * @adev: ACPI device to disable wakeup functionality for.
+ *
+ * Disable the GPE associated with @adev and disable wakeup power for it.
+ *
+ * Callers must ensure that @adev is a valid ACPI device node before executing
+ * this function.
+ */
+static void acpi_device_wakeup_disable(struct acpi_device *adev)
+{
+       struct acpi_device_wakeup *wakeup = &adev->wakeup;
+
+       mutex_lock(&acpi_wakeup_lock);
+
+       if (!wakeup->enable_count)
+               goto out;
+
+       acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
+       acpi_disable_wakeup_device_power(adev);
+
+       wakeup->enable_count--;
+
+out:
+       mutex_unlock(&acpi_wakeup_lock);
 }
 
 /**
@@ -744,9 +771,15 @@ int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
        if (!acpi_device_can_wakeup(adev))
                return -EINVAL;
 
-       error = acpi_device_wakeup(adev, acpi_target_system_state(), enable);
+       if (!enable) {
+               acpi_device_wakeup_disable(adev);
+               dev_dbg(dev, "Wakeup disabled by ACPI\n");
+               return 0;
+       }
+
+       error = acpi_device_wakeup_enable(adev, acpi_target_system_state());
        if (!error)
-               dev_dbg(dev, "Wakeup %s by ACPI\n", enable ? "enabled" : "disabled");
+               dev_dbg(dev, "Wakeup enabled by ACPI\n");
 
        return error;
 }
@@ -800,13 +833,15 @@ 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_wakeup(adev, ACPI_STATE_S0, remote_wakeup);
-       if (remote_wakeup && error)
-               return -EAGAIN;
+       if (remote_wakeup) {
+               error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
+               if (error)
+                       return -EAGAIN;
+       }
 
        error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
-       if (error)
-               acpi_device_wakeup(adev, ACPI_STATE_S0, false);
+       if (error && remote_wakeup)
+               acpi_device_wakeup_disable(adev);
 
        return error;
 }
@@ -829,7 +864,7 @@ int acpi_dev_runtime_resume(struct device *dev)
                return 0;
 
        error = acpi_dev_pm_full_power(adev);
-       acpi_device_wakeup(adev, ACPI_STATE_S0, false);
+       acpi_device_wakeup_disable(adev);
        return error;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
@@ -884,13 +919,15 @@ int acpi_dev_suspend_late(struct device *dev)
 
        target_state = acpi_target_system_state();
        wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
-       error = acpi_device_wakeup(adev, target_state, wakeup);
-       if (wakeup && error)
-               return error;
+       if (wakeup) {
+               error = acpi_device_wakeup_enable(adev, target_state);
+               if (error)
+                       return error;
+       }
 
        error = acpi_dev_pm_low_power(dev, adev, target_state);
-       if (error)
-               acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
+       if (error && wakeup)
+               acpi_device_wakeup_disable(adev);
 
        return error;
 }
@@ -913,7 +950,7 @@ int acpi_dev_resume_early(struct device *dev)
                return 0;
 
        error = acpi_dev_pm_full_power(adev);
-       acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false);
+       acpi_device_wakeup_disable(adev);
        return error;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
@@ -1056,7 +1093,7 @@ static 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_wakeup(adev, ACPI_STATE_S0, false);
+                       acpi_device_wakeup_disable(adev);
                        acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
                }
        }
@@ -1100,7 +1137,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
        dev_pm_domain_set(dev, &acpi_general_pm_domain);
        if (power_on) {
                acpi_dev_pm_full_power(adev);
-               acpi_device_wakeup(adev, ACPI_STATE_S0, false);
+               acpi_device_wakeup_disable(adev);
        }
 
        dev->pm_domain->detach = acpi_dev_pm_detach;
index 68bc6be447fd6d97c4a90d78ed65aa89dead82be..b7df95dbe7e9789c67d65ece796743927797870f 100644 (file)
@@ -316,7 +316,6 @@ struct acpi_device_perf {
 struct acpi_device_wakeup_flags {
        u8 valid:1;             /* Can successfully enable wakeup? */
        u8 notifier_present:1;  /* Wake-up notify handler has been installed */
-       u8 enabled:1;           /* Enabled for wakeup */
 };
 
 struct acpi_device_wakeup_context {
@@ -333,6 +332,7 @@ struct acpi_device_wakeup {
        struct acpi_device_wakeup_context context;
        struct wakeup_source *ws;
        int prepare_count;
+       int enable_count;
 };
 
 struct acpi_device_physical_node {