Skip to content

Commit

Permalink
Merge branch 'pm-core'
Browse files Browse the repository at this point in the history
* pm-core:
  ACPI / PM: Take SMART_SUSPEND driver flag into account
  PCI / PM: Take SMART_SUSPEND driver flag into account
  PCI / PM: Drop unnecessary invocations of pcibios_pm_ops callbacks
  PM / core: Add SMART_SUSPEND driver flag
  PCI / PM: Use the NEVER_SKIP driver flag
  PM / core: Add NEVER_SKIP and SMART_PREPARE driver flags
  PM / core: Convert timers to use timer_setup()
  PM / core: Fix kerneldoc comments of four functions
  PM / core: Drop legacy class suspend/resume operations
  • Loading branch information
Rafael J. Wysocki committed Nov 13, 2017
2 parents 05d658b + 0508736 commit 1efef68
Show file tree
Hide file tree
Showing 17 changed files with 370 additions and 102 deletions.
34 changes: 34 additions & 0 deletions Documentation/driver-api/pm/devices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,20 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
is because all such devices are initially set to runtime-suspended with
runtime PM disabled.

This feature also can be controlled by device drivers by using the
``DPM_FLAG_NEVER_SKIP`` and ``DPM_FLAG_SMART_PREPARE`` driver power
management flags. [Typically, they are set at the time the driver is
probed against the device in question by passing them to the
:c:func:`dev_pm_set_driver_flags` helper function.] If the first of
these flags is set, the PM core will not apply the direct-complete
procedure described above to the given device and, consequenty, to any
of its ancestors. The second flag, when set, informs the middle layer
code (bus types, device types, PM domains, classes) that it should take
the return value of the ``->prepare`` callback provided by the driver
into account and it may only return a positive value from its own
``->prepare`` callback if the driver's one also has returned a positive
value.

2. The ``->suspend`` methods should quiesce the device to stop it from
performing I/O. They also may save the device registers and put it into
the appropriate low-power state, depending on the bus type the device is
Expand Down Expand Up @@ -752,6 +766,26 @@ the state of devices (possibly except for resuming them from runtime suspend)
from their ``->prepare`` and ``->suspend`` callbacks (or equivalent) *before*
invoking device drivers' ``->suspend`` callbacks (or equivalent).

Some bus types and PM domains have a policy to resume all devices from runtime
suspend upfront in their ``->suspend`` callbacks, but that may not be really
necessary if the driver of the device can cope with runtime-suspended devices.
The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in
:c:member:`power.driver_flags` at the probe time, by passing it to the
:c:func:`dev_pm_set_driver_flags` helper. That also may cause middle-layer code
(bus types, PM domains etc.) to skip the ``->suspend_late`` and
``->suspend_noirq`` callbacks provided by the driver if the device remains in
runtime suspend at the beginning of the ``suspend_late`` phase of system-wide
suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM
has been disabled for it, under the assumption that its state should not change
after that point until the system-wide transition is over. If that happens, the
driver's system-wide resume callbacks, if present, may still be invoked during
the subsequent system-wide resume transition and the device's runtime power
management status may be set to "active" before enabling runtime PM for it,
so the driver must be prepared to cope with the invocation of its system-wide
resume callbacks back-to-back with its ``->runtime_suspend`` one (without the
intervening ``->runtime_resume`` and so on) and the final state of the device
must reflect the "active" status for runtime PM in that case.

During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
Refer to that document for more information regarding this particular issue as
Expand Down
33 changes: 33 additions & 0 deletions Documentation/power/pci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,39 @@ dev_pm_ops to indicate that one suspend routine is to be pointed to by the
.suspend(), .freeze(), and .poweroff() members and one resume routine is to
be pointed to by the .resume(), .thaw(), and .restore() members.

3.1.19. Driver Flags for Power Management

The PM core allows device drivers to set flags that influence the handling of
power management for the devices by the core itself and by middle layer code
including the PCI bus type. The flags should be set once at the driver probe
time with the help of the dev_pm_set_driver_flags() function and they should not
be updated directly afterwards.

The DPM_FLAG_NEVER_SKIP flag prevents the PM core from using the direct-complete
mechanism allowing device suspend/resume callbacks to be skipped if the device
is in runtime suspend when the system suspend starts. That also affects all of
the ancestors of the device, so this flag should only be used if absolutely
necessary.

The DPM_FLAG_SMART_PREPARE flag instructs the PCI bus type to only return a
positive value from pci_pm_prepare() if the ->prepare callback provided by the
driver of the device returns a positive value. That allows the driver to opt
out from using the direct-complete mechanism dynamically.

The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's
perspective the device can be safely left in runtime suspend during system
suspend. That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff()
to skip resuming the device from runtime suspend unless there are PCI-specific
reasons for doing that. Also, it causes pci_pm_suspend_late/noirq(),
pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq() to return early
if the device remains in runtime suspend in the beginning of the "late" phase
of the system-wide transition under way. Moreover, if the device is in
runtime suspend in pci_pm_resume_noirq() or pci_pm_restore_noirq(), its runtime
power management status will be changed to "active" (as it is going to be put
into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
the function will set the power.direct_complete flag for it (to make the PM core
skip the subsequent "thaw" callbacks for it) and return.

3.2. Device Runtime Power Management
------------------------------------
In addition to providing device power management callbacks PCI device drivers
Expand Down
13 changes: 12 additions & 1 deletion drivers/acpi/acpi_lpss.c
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,12 @@ static int acpi_lpss_resume(struct device *dev)
#ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev)
{
int ret = pm_generic_suspend_late(dev);
int ret;

if (dev_pm_smart_suspend_and_suspended(dev))
return 0;

ret = pm_generic_suspend_late(dev);
return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
}

Expand Down Expand Up @@ -889,10 +893,17 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
.complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend,
.suspend_late = acpi_lpss_suspend_late,
.suspend_noirq = acpi_subsys_suspend_noirq,
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late,
.freeze_noirq = acpi_subsys_freeze_noirq,
.thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_lpss_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
.restore_noirq = acpi_subsys_resume_noirq,
.restore_early = acpi_lpss_resume_early,
#endif
.runtime_suspend = acpi_lpss_runtime_suspend,
Expand Down
124 changes: 112 additions & 12 deletions drivers/acpi/device_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,8 @@ static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
u32 sys_target = acpi_target_system_state();
int ret, state;

if (device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
if (!pm_runtime_suspended(dev) || !adev ||
device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
return true;

if (sys_target == ACPI_STATE_S0)
Expand All @@ -962,14 +963,16 @@ static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
int acpi_subsys_prepare(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
int ret;

ret = pm_generic_prepare(dev);
if (ret < 0)
return ret;
if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
int ret = dev->driver->pm->prepare(dev);

if (!adev || !pm_runtime_suspended(dev))
return 0;
if (ret < 0)
return ret;

if (!ret && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
return 0;
}

return !acpi_dev_needs_resume(dev, adev);
}
Expand All @@ -996,12 +999,17 @@ EXPORT_SYMBOL_GPL(acpi_subsys_complete);
* acpi_subsys_suspend - Run the device driver's suspend callback.
* @dev: Device to handle.
*
* Follow PCI and resume devices suspended at run time before running their
* system suspend callbacks.
* Follow PCI and resume devices from runtime suspend before running their
* system suspend callbacks, unless the driver can cope with runtime-suspended
* devices during system suspend and there are no ACPI-specific reasons for
* resuming them.
*/
int acpi_subsys_suspend(struct device *dev)
{
pm_runtime_resume(dev);
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
pm_runtime_resume(dev);

return pm_generic_suspend(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
Expand All @@ -1015,11 +1023,47 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
*/
int acpi_subsys_suspend_late(struct device *dev)
{
int ret = pm_generic_suspend_late(dev);
int ret;

if (dev_pm_smart_suspend_and_suspended(dev))
return 0;

ret = pm_generic_suspend_late(dev);
return ret ? ret : acpi_dev_suspend(dev, device_may_wakeup(dev));
}
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);

/**
* acpi_subsys_suspend_noirq - Run the device driver's "noirq" suspend callback.
* @dev: Device to suspend.
*/
int acpi_subsys_suspend_noirq(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;

return pm_generic_suspend_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);

/**
* acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback.
* @dev: Device to handle.
*/
int acpi_subsys_resume_noirq(struct device *dev)
{
/*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active"
* as they will be put into D0 going forward.
*/
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);

return pm_generic_resume_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq);

/**
* acpi_subsys_resume_early - Resume device using ACPI.
* @dev: Device to Resume.
Expand Down Expand Up @@ -1047,11 +1091,60 @@ int acpi_subsys_freeze(struct device *dev)
* runtime-suspended devices should not be touched during freeze/thaw
* transitions.
*/
pm_runtime_resume(dev);
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
pm_runtime_resume(dev);

return pm_generic_freeze(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_freeze);

/**
* acpi_subsys_freeze_late - Run the device driver's "late" freeze callback.
* @dev: Device to handle.
*/
int acpi_subsys_freeze_late(struct device *dev)
{

if (dev_pm_smart_suspend_and_suspended(dev))
return 0;

return pm_generic_freeze_late(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late);

/**
* acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback.
* @dev: Device to handle.
*/
int acpi_subsys_freeze_noirq(struct device *dev)
{

if (dev_pm_smart_suspend_and_suspended(dev))
return 0;

return pm_generic_freeze_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq);

/**
* acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback.
* @dev: Device to handle.
*/
int acpi_subsys_thaw_noirq(struct device *dev)
{
/*
* If the device is in runtime suspend, the "thaw" code may not work
* correctly with it, so skip the driver callback and make the PM core
* skip all of the subsequent "thaw" callbacks for the device.
*/
if (dev_pm_smart_suspend_and_suspended(dev)) {
dev->power.direct_complete = true;
return 0;
}

return pm_generic_thaw_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq);
#endif /* CONFIG_PM_SLEEP */

static struct dev_pm_domain acpi_general_pm_domain = {
Expand All @@ -1063,10 +1156,17 @@ static struct dev_pm_domain acpi_general_pm_domain = {
.complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend,
.suspend_late = acpi_subsys_suspend_late,
.suspend_noirq = acpi_subsys_suspend_noirq,
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late,
.freeze_noirq = acpi_subsys_freeze_noirq,
.thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_subsys_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
.restore_noirq = acpi_subsys_resume_noirq,
.restore_early = acpi_subsys_resume_early,
#endif
},
Expand Down
2 changes: 2 additions & 0 deletions drivers/base/dd.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);

switch (ret) {
case -EPROBE_DEFER:
Expand Down Expand Up @@ -869,6 +870,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);

klist_remove(&dev->p->knode_driver);
device_pm_check_callbacks(dev);
Expand Down
Loading

0 comments on commit 1efef68

Please sign in to comment.