Commit 99d8845e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

ACPI / PM: Split acpi_device_wakeup()

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: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent baecc470
...@@ -682,47 +682,74 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context) ...@@ -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. * acpi_device_wakeup_enable - Enable wakeup functionality for device.
* @adev: ACPI device to enable/disable wakeup functionality for. * @adev: ACPI device to enable wakeup functionality for.
* @target_state: State the system is transitioning into. * @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 * Enable the GPE associated with @adev so that it can generate wakeup signals
* wakeup signals for the device in response to external (remote) events and * for the device in response to external (remote) events and enable wakeup
* enable/disable device wakeup power. * power for it.
* *
* Callers must ensure that @adev is a valid ACPI device node before executing * Callers must ensure that @adev is a valid ACPI device node before executing
* this function. * this function.
*/ */
static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state, static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
bool enable)
{ {
struct acpi_device_wakeup *wakeup = &adev->wakeup; struct acpi_device_wakeup *wakeup = &adev->wakeup;
acpi_status status;
int error = 0;
if (enable) { mutex_lock(&acpi_wakeup_lock);
acpi_status res;
int error;
if (adev->wakeup.flags.enabled) if (wakeup->enable_count > 0)
return 0; goto out;
error = acpi_enable_wakeup_device_power(adev, target_state); error = acpi_enable_wakeup_device_power(adev, target_state);
if (error) if (error)
return error; goto out;
res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
if (ACPI_FAILURE(res)) { if (ACPI_FAILURE(status)) {
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);
acpi_disable_wakeup_device_power(adev); 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) ...@@ -744,9 +771,15 @@ int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
if (!acpi_device_can_wakeup(adev)) if (!acpi_device_can_wakeup(adev))
return -EINVAL; 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) if (!error)
dev_dbg(dev, "Wakeup %s by ACPI\n", enable ? "enabled" : "disabled"); dev_dbg(dev, "Wakeup enabled by ACPI\n");
return error; return error;
} }
...@@ -800,13 +833,15 @@ int acpi_dev_runtime_suspend(struct device *dev) ...@@ -800,13 +833,15 @@ int acpi_dev_runtime_suspend(struct device *dev)
remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
PM_QOS_FLAGS_NONE; PM_QOS_FLAGS_NONE;
error = acpi_device_wakeup(adev, ACPI_STATE_S0, remote_wakeup); if (remote_wakeup) {
if (remote_wakeup && error) error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
return -EAGAIN; if (error)
return -EAGAIN;
}
error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
if (error) if (error && remote_wakeup)
acpi_device_wakeup(adev, ACPI_STATE_S0, false); acpi_device_wakeup_disable(adev);
return error; return error;
} }
...@@ -829,7 +864,7 @@ int acpi_dev_runtime_resume(struct device *dev) ...@@ -829,7 +864,7 @@ int acpi_dev_runtime_resume(struct device *dev)
return 0; return 0;
error = acpi_dev_pm_full_power(adev); error = acpi_dev_pm_full_power(adev);
acpi_device_wakeup(adev, ACPI_STATE_S0, false); acpi_device_wakeup_disable(adev);
return error; return error;
} }
EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
...@@ -884,13 +919,15 @@ int acpi_dev_suspend_late(struct device *dev) ...@@ -884,13 +919,15 @@ int acpi_dev_suspend_late(struct device *dev)
target_state = acpi_target_system_state(); target_state = acpi_target_system_state();
wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
error = acpi_device_wakeup(adev, target_state, wakeup); if (wakeup) {
if (wakeup && error) error = acpi_device_wakeup_enable(adev, target_state);
return error; if (error)
return error;
}
error = acpi_dev_pm_low_power(dev, adev, target_state); error = acpi_dev_pm_low_power(dev, adev, target_state);
if (error) if (error && wakeup)
acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); acpi_device_wakeup_disable(adev);
return error; return error;
} }
...@@ -913,7 +950,7 @@ int acpi_dev_resume_early(struct device *dev) ...@@ -913,7 +950,7 @@ int acpi_dev_resume_early(struct device *dev)
return 0; return 0;
error = acpi_dev_pm_full_power(adev); error = acpi_dev_pm_full_power(adev);
acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); acpi_device_wakeup_disable(adev);
return error; return error;
} }
EXPORT_SYMBOL_GPL(acpi_dev_resume_early); EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
...@@ -1056,7 +1093,7 @@ static void acpi_dev_pm_detach(struct device *dev, bool power_off) ...@@ -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_latency_limit(dev);
dev_pm_qos_hide_flags(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); 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) ...@@ -1100,7 +1137,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
dev_pm_domain_set(dev, &acpi_general_pm_domain); dev_pm_domain_set(dev, &acpi_general_pm_domain);
if (power_on) { if (power_on) {
acpi_dev_pm_full_power(adev); 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; dev->pm_domain->detach = acpi_dev_pm_detach;
......
...@@ -316,7 +316,6 @@ struct acpi_device_perf { ...@@ -316,7 +316,6 @@ struct acpi_device_perf {
struct acpi_device_wakeup_flags { struct acpi_device_wakeup_flags {
u8 valid:1; /* Can successfully enable wakeup? */ u8 valid:1; /* Can successfully enable wakeup? */
u8 notifier_present:1; /* Wake-up notify handler has been installed */ u8 notifier_present:1; /* Wake-up notify handler has been installed */
u8 enabled:1; /* Enabled for wakeup */
}; };
struct acpi_device_wakeup_context { struct acpi_device_wakeup_context {
...@@ -333,6 +332,7 @@ struct acpi_device_wakeup { ...@@ -333,6 +332,7 @@ struct acpi_device_wakeup {
struct acpi_device_wakeup_context context; struct acpi_device_wakeup_context context;
struct wakeup_source *ws; struct wakeup_source *ws;
int prepare_count; int prepare_count;
int enable_count;
}; };
struct acpi_device_physical_node { struct acpi_device_physical_node {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment