|
@@ -387,6 +387,7 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
static DEFINE_MUTEX(acpi_pm_notifier_lock);
|
|
|
+static DEFINE_MUTEX(acpi_pm_notifier_install_lock);
|
|
|
|
|
|
void acpi_pm_wakeup_event(struct device *dev)
|
|
|
{
|
|
@@ -443,24 +444,25 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
|
|
|
if (!dev && !func)
|
|
|
return AE_BAD_PARAMETER;
|
|
|
|
|
|
- mutex_lock(&acpi_pm_notifier_lock);
|
|
|
+ mutex_lock(&acpi_pm_notifier_install_lock);
|
|
|
|
|
|
if (adev->wakeup.flags.notifier_present)
|
|
|
goto out;
|
|
|
|
|
|
- adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
|
|
|
- adev->wakeup.context.dev = dev;
|
|
|
- adev->wakeup.context.func = func;
|
|
|
-
|
|
|
status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
|
|
|
acpi_pm_notify_handler, NULL);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
goto out;
|
|
|
|
|
|
+ mutex_lock(&acpi_pm_notifier_lock);
|
|
|
+ adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
|
|
|
+ adev->wakeup.context.dev = dev;
|
|
|
+ adev->wakeup.context.func = func;
|
|
|
adev->wakeup.flags.notifier_present = true;
|
|
|
+ mutex_unlock(&acpi_pm_notifier_lock);
|
|
|
|
|
|
out:
|
|
|
- mutex_unlock(&acpi_pm_notifier_lock);
|
|
|
+ mutex_unlock(&acpi_pm_notifier_install_lock);
|
|
|
return status;
|
|
|
}
|
|
|
|
|
@@ -472,7 +474,7 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
|
|
{
|
|
|
acpi_status status = AE_BAD_PARAMETER;
|
|
|
|
|
|
- mutex_lock(&acpi_pm_notifier_lock);
|
|
|
+ mutex_lock(&acpi_pm_notifier_install_lock);
|
|
|
|
|
|
if (!adev->wakeup.flags.notifier_present)
|
|
|
goto out;
|
|
@@ -483,14 +485,15 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
|
|
if (ACPI_FAILURE(status))
|
|
|
goto out;
|
|
|
|
|
|
+ mutex_lock(&acpi_pm_notifier_lock);
|
|
|
adev->wakeup.context.func = NULL;
|
|
|
adev->wakeup.context.dev = NULL;
|
|
|
wakeup_source_unregister(adev->wakeup.ws);
|
|
|
-
|
|
|
adev->wakeup.flags.notifier_present = false;
|
|
|
+ mutex_unlock(&acpi_pm_notifier_lock);
|
|
|
|
|
|
out:
|
|
|
- mutex_unlock(&acpi_pm_notifier_lock);
|
|
|
+ mutex_unlock(&acpi_pm_notifier_install_lock);
|
|
|
return status;
|
|
|
}
|
|
|
|
|
@@ -847,47 +850,48 @@ static int acpi_dev_pm_full_power(struct acpi_device *adev)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI.
|
|
|
+ * acpi_dev_suspend - Put device into a low-power state using ACPI.
|
|
|
* @dev: Device to put into a low-power state.
|
|
|
+ * @wakeup: Whether or not to enable wakeup for the device.
|
|
|
*
|
|
|
- * Put the given device into a runtime low-power state using the standard ACPI
|
|
|
+ * Put the given device into a low-power state using the standard ACPI
|
|
|
* mechanism. Set up remote wakeup if desired, choose the state to put the
|
|
|
* device into (this checks if remote wakeup is expected to work too), and set
|
|
|
* the power state of the device.
|
|
|
*/
|
|
|
-int acpi_dev_runtime_suspend(struct device *dev)
|
|
|
+int acpi_dev_suspend(struct device *dev, bool wakeup)
|
|
|
{
|
|
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
- bool remote_wakeup;
|
|
|
+ u32 target_state = acpi_target_system_state();
|
|
|
int error;
|
|
|
|
|
|
if (!adev)
|
|
|
return 0;
|
|
|
|
|
|
- remote_wakeup = acpi_device_can_wakeup(adev);
|
|
|
- if (remote_wakeup) {
|
|
|
- error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
|
|
|
+ if (wakeup && acpi_device_can_wakeup(adev)) {
|
|
|
+ error = acpi_device_wakeup_enable(adev, target_state);
|
|
|
if (error)
|
|
|
return -EAGAIN;
|
|
|
+ } else {
|
|
|
+ wakeup = false;
|
|
|
}
|
|
|
|
|
|
- error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
|
|
|
- if (error && remote_wakeup)
|
|
|
+ error = acpi_dev_pm_low_power(dev, adev, target_state);
|
|
|
+ if (error && wakeup)
|
|
|
acpi_device_wakeup_disable(adev);
|
|
|
|
|
|
return error;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend);
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_suspend);
|
|
|
|
|
|
/**
|
|
|
- * acpi_dev_runtime_resume - Put device into the full-power state using ACPI.
|
|
|
+ * acpi_dev_resume - Put device into the full-power state using ACPI.
|
|
|
* @dev: Device to put into the full-power state.
|
|
|
*
|
|
|
* Put the given device into the full-power state using the standard ACPI
|
|
|
- * mechanism at run time. Set the power state of the device to ACPI D0 and
|
|
|
- * disable remote wakeup.
|
|
|
+ * mechanism. Set the power state of the device to ACPI D0 and disable wakeup.
|
|
|
*/
|
|
|
-int acpi_dev_runtime_resume(struct device *dev)
|
|
|
+int acpi_dev_resume(struct device *dev)
|
|
|
{
|
|
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
int error;
|
|
@@ -899,7 +903,7 @@ int acpi_dev_runtime_resume(struct device *dev)
|
|
|
acpi_device_wakeup_disable(adev);
|
|
|
return error;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_resume);
|
|
|
|
|
|
/**
|
|
|
* acpi_subsys_runtime_suspend - Suspend device using ACPI.
|
|
@@ -911,7 +915,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
|
|
|
int acpi_subsys_runtime_suspend(struct device *dev)
|
|
|
{
|
|
|
int ret = pm_generic_runtime_suspend(dev);
|
|
|
- return ret ? ret : acpi_dev_runtime_suspend(dev);
|
|
|
+ return ret ? ret : acpi_dev_suspend(dev, true);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
|
|
|
|
|
@@ -924,68 +928,32 @@ EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
|
|
|
*/
|
|
|
int acpi_subsys_runtime_resume(struct device *dev)
|
|
|
{
|
|
|
- int ret = acpi_dev_runtime_resume(dev);
|
|
|
+ int ret = acpi_dev_resume(dev);
|
|
|
return ret ? ret : pm_generic_runtime_resume(dev);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume);
|
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
-/**
|
|
|
- * acpi_dev_suspend_late - Put device into a low-power state using ACPI.
|
|
|
- * @dev: Device to put into a low-power state.
|
|
|
- *
|
|
|
- * Put the given device into a low-power state during system transition to a
|
|
|
- * sleep state using the standard ACPI mechanism. Set up system wakeup if
|
|
|
- * desired, choose the state to put the device into (this checks if system
|
|
|
- * wakeup is expected to work too), and set the power state of the device.
|
|
|
- */
|
|
|
-int acpi_dev_suspend_late(struct device *dev)
|
|
|
+static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
|
|
|
{
|
|
|
- struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
- u32 target_state;
|
|
|
- bool wakeup;
|
|
|
- int error;
|
|
|
-
|
|
|
- if (!adev)
|
|
|
- return 0;
|
|
|
-
|
|
|
- target_state = acpi_target_system_state();
|
|
|
- wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
|
|
|
- if (wakeup) {
|
|
|
- error = acpi_device_wakeup_enable(adev, target_state);
|
|
|
- if (error)
|
|
|
- return error;
|
|
|
- }
|
|
|
+ u32 sys_target = acpi_target_system_state();
|
|
|
+ int ret, state;
|
|
|
|
|
|
- error = acpi_dev_pm_low_power(dev, adev, target_state);
|
|
|
- if (error && wakeup)
|
|
|
- acpi_device_wakeup_disable(adev);
|
|
|
+ if (device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
|
|
|
+ return true;
|
|
|
|
|
|
- return error;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(acpi_dev_suspend_late);
|
|
|
+ if (sys_target == ACPI_STATE_S0)
|
|
|
+ return false;
|
|
|
|
|
|
-/**
|
|
|
- * acpi_dev_resume_early - Put device into the full-power state using ACPI.
|
|
|
- * @dev: Device to put into the full-power state.
|
|
|
- *
|
|
|
- * Put the given device into the full-power state using the standard ACPI
|
|
|
- * mechanism during system transition to the working state. Set the power
|
|
|
- * state of the device to ACPI D0 and disable remote wakeup.
|
|
|
- */
|
|
|
-int acpi_dev_resume_early(struct device *dev)
|
|
|
-{
|
|
|
- struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
- int error;
|
|
|
+ if (adev->power.flags.dsw_present)
|
|
|
+ return true;
|
|
|
|
|
|
- if (!adev)
|
|
|
- return 0;
|
|
|
+ ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
|
|
|
+ if (ret)
|
|
|
+ return true;
|
|
|
|
|
|
- error = acpi_dev_pm_full_power(adev);
|
|
|
- acpi_device_wakeup_disable(adev);
|
|
|
- return error;
|
|
|
+ return state != adev->power.state;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
|
|
|
|
|
|
/**
|
|
|
* acpi_subsys_prepare - Prepare device for system transition to a sleep state.
|
|
@@ -994,29 +962,36 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
|
|
|
int acpi_subsys_prepare(struct device *dev)
|
|
|
{
|
|
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
- u32 sys_target;
|
|
|
- int ret, state;
|
|
|
+ int ret;
|
|
|
|
|
|
ret = pm_generic_prepare(dev);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- if (!adev || !pm_runtime_suspended(dev)
|
|
|
- || device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
|
|
|
+ if (!adev || !pm_runtime_suspended(dev))
|
|
|
return 0;
|
|
|
|
|
|
- sys_target = acpi_target_system_state();
|
|
|
- if (sys_target == ACPI_STATE_S0)
|
|
|
- return 1;
|
|
|
-
|
|
|
- if (adev->power.flags.dsw_present)
|
|
|
- return 0;
|
|
|
-
|
|
|
- ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
|
|
|
- return !ret && state == adev->power.state;
|
|
|
+ return !acpi_dev_needs_resume(dev, adev);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
|
|
|
|
|
|
+/**
|
|
|
+ * acpi_subsys_complete - Finalize device's resume during system resume.
|
|
|
+ * @dev: Device to handle.
|
|
|
+ */
|
|
|
+void acpi_subsys_complete(struct device *dev)
|
|
|
+{
|
|
|
+ pm_generic_complete(dev);
|
|
|
+ /*
|
|
|
+ * If the device had been runtime-suspended before the system went into
|
|
|
+ * the sleep state it is going out of and it has never been resumed till
|
|
|
+ * now, resume it in case the firmware powered it up.
|
|
|
+ */
|
|
|
+ if (dev->power.direct_complete && pm_resume_via_firmware())
|
|
|
+ pm_request_resume(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_complete);
|
|
|
+
|
|
|
/**
|
|
|
* acpi_subsys_suspend - Run the device driver's suspend callback.
|
|
|
* @dev: Device to handle.
|
|
@@ -1041,7 +1016,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
|
|
|
int acpi_subsys_suspend_late(struct device *dev)
|
|
|
{
|
|
|
int ret = pm_generic_suspend_late(dev);
|
|
|
- return ret ? ret : acpi_dev_suspend_late(dev);
|
|
|
+ return ret ? ret : acpi_dev_suspend(dev, device_may_wakeup(dev));
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
|
|
|
|
|
@@ -1055,7 +1030,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
|
|
|
*/
|
|
|
int acpi_subsys_resume_early(struct device *dev)
|
|
|
{
|
|
|
- int ret = acpi_dev_resume_early(dev);
|
|
|
+ int ret = acpi_dev_resume(dev);
|
|
|
return ret ? ret : pm_generic_resume_early(dev);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(acpi_subsys_resume_early);
|
|
@@ -1085,7 +1060,7 @@ static struct dev_pm_domain acpi_general_pm_domain = {
|
|
|
.runtime_resume = acpi_subsys_runtime_resume,
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
.prepare = acpi_subsys_prepare,
|
|
|
- .complete = pm_complete_with_resume_check,
|
|
|
+ .complete = acpi_subsys_complete,
|
|
|
.suspend = acpi_subsys_suspend,
|
|
|
.suspend_late = acpi_subsys_suspend_late,
|
|
|
.resume_early = acpi_subsys_resume_early,
|