|
@@ -224,6 +224,22 @@ 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.
|
|
|
+ */
|
|
|
+static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context)
|
|
|
+{
|
|
|
+ struct device *dev = context;
|
|
|
+
|
|
|
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && 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.
|
|
@@ -283,6 +299,9 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
|
|
|
return __acpi_device_run_wake(adev, enable);
|
|
|
}
|
|
|
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
|
|
@@ -329,3 +348,301 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
|
|
|
return error;
|
|
|
}
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_pm_get_node - Get ACPI device node for the given physical device.
|
|
|
+ * @dev: Device to get the ACPI node for.
|
|
|
+ */
|
|
|
+static struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
|
|
|
+{
|
|
|
+ acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
|
|
|
+ struct acpi_device *adev;
|
|
|
+
|
|
|
+ return handle && ACPI_SUCCESS(acpi_bus_get_device(handle, &adev)) ?
|
|
|
+ adev : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
|
|
|
+ * @dev: Device to put into a low-power state.
|
|
|
+ * @adev: ACPI device node corresponding to @dev.
|
|
|
+ * @system_state: System state to choose the device state for.
|
|
|
+ */
|
|
|
+static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev,
|
|
|
+ u32 system_state)
|
|
|
+{
|
|
|
+ int power_state;
|
|
|
+
|
|
|
+ if (!acpi_device_power_manageable(adev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ power_state = acpi_device_power_state(dev, adev, system_state,
|
|
|
+ ACPI_STATE_D3, NULL);
|
|
|
+ if (power_state < ACPI_STATE_D0 || power_state > ACPI_STATE_D3)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return acpi_device_set_power(adev, power_state);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_pm_full_power - Put ACPI device into the full-power state.
|
|
|
+ * @adev: ACPI device node to put into the full-power state.
|
|
|
+ */
|
|
|
+static int acpi_dev_pm_full_power(struct acpi_device *adev)
|
|
|
+{
|
|
|
+ return acpi_device_power_manageable(adev) ?
|
|
|
+ acpi_device_set_power(adev, ACPI_STATE_D0) : 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
+/**
|
|
|
+ * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI.
|
|
|
+ * @dev: Device to put into a low-power state.
|
|
|
+ *
|
|
|
+ * Put the given device into a runtime 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)
|
|
|
+{
|
|
|
+ struct acpi_device *adev = acpi_dev_pm_get_node(dev);
|
|
|
+ bool remote_wakeup;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!adev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
|
|
|
+ PM_QOS_FLAGS_NONE;
|
|
|
+ error = __acpi_device_run_wake(adev, 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);
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_runtime_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.
|
|
|
+ */
|
|
|
+int acpi_dev_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct acpi_device *adev = acpi_dev_pm_get_node(dev);
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!adev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ error = acpi_dev_pm_full_power(adev);
|
|
|
+ __acpi_device_run_wake(adev, false);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_subsys_runtime_suspend - Suspend device using ACPI.
|
|
|
+ * @dev: Device to suspend.
|
|
|
+ *
|
|
|
+ * Carry out the generic runtime suspend procedure for @dev and use ACPI to put
|
|
|
+ * it into a runtime low-power state.
|
|
|
+ */
|
|
|
+int acpi_subsys_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ int ret = pm_generic_runtime_suspend(dev);
|
|
|
+ return ret ? ret : acpi_dev_runtime_suspend(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_subsys_runtime_resume - Resume device using ACPI.
|
|
|
+ * @dev: Device to Resume.
|
|
|
+ *
|
|
|
+ * Use ACPI to put the given device into the full-power state and carry out the
|
|
|
+ * generic runtime resume procedure for it.
|
|
|
+ */
|
|
|
+int acpi_subsys_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ int ret = acpi_dev_runtime_resume(dev);
|
|
|
+ return ret ? ret : pm_generic_runtime_resume(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume);
|
|
|
+#endif /* CONFIG_PM_RUNTIME */
|
|
|
+
|
|
|
+#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)
|
|
|
+{
|
|
|
+ struct acpi_device *adev = acpi_dev_pm_get_node(dev);
|
|
|
+ u32 target_state;
|
|
|
+ bool wakeup;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!adev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ target_state = acpi_target_system_state();
|
|
|
+ wakeup = device_may_wakeup(dev);
|
|
|
+ error = __acpi_device_sleep_wake(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);
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_suspend_late);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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_dev_pm_get_node(dev);
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (!adev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ error = acpi_dev_pm_full_power(adev);
|
|
|
+ __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false);
|
|
|
+ return error;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_subsys_prepare - Prepare device for system transition to a sleep state.
|
|
|
+ * @dev: Device to prepare.
|
|
|
+ */
|
|
|
+int acpi_subsys_prepare(struct device *dev)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Follow PCI and resume devices suspended at run time before running
|
|
|
+ * their system suspend callbacks.
|
|
|
+ */
|
|
|
+ pm_runtime_resume(dev);
|
|
|
+ return pm_generic_prepare(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_subsys_suspend_late - Suspend device using ACPI.
|
|
|
+ * @dev: Device to suspend.
|
|
|
+ *
|
|
|
+ * Carry out the generic late suspend procedure for @dev and use ACPI to put
|
|
|
+ * it into a low-power state during system transition into a sleep state.
|
|
|
+ */
|
|
|
+int acpi_subsys_suspend_late(struct device *dev)
|
|
|
+{
|
|
|
+ int ret = pm_generic_suspend_late(dev);
|
|
|
+ return ret ? ret : acpi_dev_suspend_late(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_subsys_resume_early - Resume device using ACPI.
|
|
|
+ * @dev: Device to Resume.
|
|
|
+ *
|
|
|
+ * Use ACPI to put the given device into the full-power state and carry out the
|
|
|
+ * generic early resume procedure for it during system transition into the
|
|
|
+ * working state.
|
|
|
+ */
|
|
|
+int acpi_subsys_resume_early(struct device *dev)
|
|
|
+{
|
|
|
+ int ret = acpi_dev_resume_early(dev);
|
|
|
+ return ret ? ret : pm_generic_resume_early(dev);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_subsys_resume_early);
|
|
|
+#endif /* CONFIG_PM_SLEEP */
|
|
|
+
|
|
|
+static struct dev_pm_domain acpi_general_pm_domain = {
|
|
|
+ .ops = {
|
|
|
+#ifdef CONFIG_PM_RUNTIME
|
|
|
+ .runtime_suspend = acpi_subsys_runtime_suspend,
|
|
|
+ .runtime_resume = acpi_subsys_runtime_resume,
|
|
|
+ .runtime_idle = pm_generic_runtime_idle,
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
|
+ .prepare = acpi_subsys_prepare,
|
|
|
+ .suspend_late = acpi_subsys_suspend_late,
|
|
|
+ .resume_early = acpi_subsys_resume_early,
|
|
|
+ .poweroff_late = acpi_subsys_suspend_late,
|
|
|
+ .restore_early = acpi_subsys_resume_early,
|
|
|
+#endif
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_pm_attach - Prepare device for ACPI power management.
|
|
|
+ * @dev: Device to prepare.
|
|
|
+ *
|
|
|
+ * If @dev has a valid ACPI handle that has a valid struct acpi_device object
|
|
|
+ * attached to it, install a wakeup notification handler for the device and
|
|
|
+ * add it to the general ACPI PM domain.
|
|
|
+ *
|
|
|
+ * This assumes that the @dev's bus type uses generic power management callbacks
|
|
|
+ * (or doesn't use any power management callbacks at all).
|
|
|
+ *
|
|
|
+ * Callers must ensure proper synchronization of this function with power
|
|
|
+ * management callbacks.
|
|
|
+ */
|
|
|
+int acpi_dev_pm_attach(struct device *dev)
|
|
|
+{
|
|
|
+ struct acpi_device *adev = acpi_dev_pm_get_node(dev);
|
|
|
+
|
|
|
+ if (!adev)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ if (dev->pm_domain)
|
|
|
+ return -EEXIST;
|
|
|
+
|
|
|
+ acpi_add_pm_notifier(adev, acpi_wakeup_device, dev);
|
|
|
+ dev->pm_domain = &acpi_general_pm_domain;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_dev_pm_detach - Remove ACPI power management from the device.
|
|
|
+ * @dev: Device to take care of.
|
|
|
+ *
|
|
|
+ * Remove the device from the general ACPI PM domain and remove its wakeup
|
|
|
+ * notifier.
|
|
|
+ *
|
|
|
+ * Callers must ensure proper synchronization of this function with power
|
|
|
+ * management callbacks.
|
|
|
+ */
|
|
|
+void acpi_dev_pm_detach(struct device *dev)
|
|
|
+{
|
|
|
+ struct acpi_device *adev = acpi_dev_pm_get_node(dev);
|
|
|
+
|
|
|
+ if (adev && dev->pm_domain == &acpi_general_pm_domain) {
|
|
|
+ dev->pm_domain = NULL;
|
|
|
+ acpi_remove_pm_notifier(adev, acpi_wakeup_device);
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
|