Merge branch 'pm-core'

* 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
This commit is contained in:
Rafael J. Wysocki 2017-11-13 01:41:26 +01:00
commit 1efef68262
17 changed files with 371 additions and 103 deletions

View File

@ -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
@ -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

View File

@ -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

View File

@ -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));
}
@ -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,

View File

@ -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)
@ -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);
}
@ -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);
@ -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.
@ -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 = {
@ -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
},

View File

@ -464,6 +464,7 @@ pinctrl_bind_failed:
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:
@ -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);

View File

@ -528,7 +528,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
/*------------------------- Resume routines -------------------------*/
/**
* device_resume_noirq - Execute an "early resume" callback for given device.
* device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously.
@ -848,16 +848,10 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
goto Driver;
}
if (dev->class) {
if (dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Driver;
} else if (dev->class->resume) {
info = "legacy class ";
callback = dev->class->resume;
goto End;
}
if (dev->class && dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Driver;
}
if (dev->bus) {
@ -1083,7 +1077,7 @@ static pm_message_t resume_event(pm_message_t sleep_state)
}
/**
* device_suspend_noirq - Execute a "late suspend" callback for given device.
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
@ -1243,7 +1237,7 @@ int dpm_suspend_noirq(pm_message_t state)
}
/**
* device_suspend_late - Execute a "late suspend" callback for given device.
* __device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
@ -1445,7 +1439,7 @@ static void dpm_clear_suppliers_direct_complete(struct device *dev)
}
/**
* device_suspend - Execute "suspend" callbacks for given device.
* __device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously.
@ -1508,17 +1502,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Run;
}
if (dev->class) {
if (dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Run;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend,
"legacy class ");
goto End;
}
if (dev->class && dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Run;
}
if (dev->bus) {
@ -1665,6 +1652,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (dev->power.syscore)
return 0;
WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
!pm_runtime_enabled(dev));
/*
* If a device's parent goes into runtime suspend at the wrong time,
* it won't be possible to resume the device. To prevent this we
@ -1713,7 +1703,9 @@ unlock:
* applies to suspend transitions, however.
*/
spin_lock_irq(&dev->power.lock);
dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND;
dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
pm_runtime_suspended(dev) && ret > 0 &&
!dev_pm_test_driver_flags(dev, DPM_FLAG_NEVER_SKIP);
spin_unlock_irq(&dev->power.lock);
return 0;
}
@ -1862,11 +1854,16 @@ void device_pm_check_callbacks(struct device *dev)
dev->power.no_pm_callbacks =
(!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
!dev->bus->suspend && !dev->bus->resume)) &&
(!dev->class || (pm_ops_is_empty(dev->class->pm) &&
!dev->class->suspend && !dev->class->resume)) &&
(!dev->class || pm_ops_is_empty(dev->class->pm)) &&
(!dev->type || pm_ops_is_empty(dev->type->pm)) &&
(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
(!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
!dev->driver->suspend && !dev->driver->resume));
spin_unlock_irq(&dev->power.lock);
}
bool dev_pm_smart_suspend_and_suspended(struct device *dev)
{
return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
pm_runtime_status_suspended(dev);
}

View File

@ -894,9 +894,9 @@ static void pm_runtime_work(struct work_struct *work)
*
* Check if the time is right and queue a suspend request.
*/
static void pm_suspend_timer_fn(unsigned long data)
static void pm_suspend_timer_fn(struct timer_list *t)
{
struct device *dev = (struct device *)data;
struct device *dev = from_timer(dev, t, power.suspend_timer);
unsigned long flags;
unsigned long expires;
@ -1499,8 +1499,7 @@ void pm_runtime_init(struct device *dev)
INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0;
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
(unsigned long)dev);
timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0);
init_waitqueue_head(&dev->power.wait_queue);
}

View File

@ -54,7 +54,7 @@ static unsigned int saved_count;
static DEFINE_SPINLOCK(events_lock);
static void pm_wakeup_timer_fn(unsigned long data);
static void pm_wakeup_timer_fn(struct timer_list *t);
static LIST_HEAD(wakeup_sources);
@ -176,7 +176,7 @@ void wakeup_source_add(struct wakeup_source *ws)
return;
spin_lock_init(&ws->lock);
setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
timer_setup(&ws->timer, pm_wakeup_timer_fn, 0);
ws->active = false;
ws->last_time = ktime_get();
@ -481,8 +481,7 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws)
* Use timer struct to check if the given source is initialized
* by wakeup_source_add.
*/
return ws->timer.function != pm_wakeup_timer_fn ||
ws->timer.data != (unsigned long)ws;
return ws->timer.function != (TIMER_FUNC_TYPE)pm_wakeup_timer_fn;
}
/*
@ -724,9 +723,9 @@ EXPORT_SYMBOL_GPL(pm_relax);
* in @data if it is currently active and its timer has not been canceled and
* the expiration time of the timer is not in future.
*/
static void pm_wakeup_timer_fn(unsigned long data)
static void pm_wakeup_timer_fn(struct timer_list *t)
{
struct wakeup_source *ws = (struct wakeup_source *)data;
struct wakeup_source *ws = from_timer(ws, t, timer);
unsigned long flags;
spin_lock_irqsave(&ws->lock, flags);

View File

@ -1304,7 +1304,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
* becaue the HDA driver may require us to enable the audio power
* domain during system suspend.
*/
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
ret = i915_driver_init_early(dev_priv, ent);
if (ret < 0)

View File

@ -225,7 +225,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* MEI requires to resume from runtime suspend mode
* in order to perform link reset flow upon system suspend.
*/
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
/*
* ME maps runtime suspend/resume to D0i states,

View File

@ -141,7 +141,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* MEI requires to resume from runtime suspend mode
* in order to perform link reset flow upon system suspend.
*/
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
/*
* TXE maps runtime suspend/resume to own power gating states,

View File

@ -682,8 +682,11 @@ static int pci_pm_prepare(struct device *dev)
if (drv && drv->pm && drv->pm->prepare) {
int error = drv->pm->prepare(dev);
if (error)
if (error < 0)
return error;
if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
return 0;
}
return pci_dev_keep_suspended(to_pci_dev(dev));
}
@ -724,18 +727,25 @@ static int pci_pm_suspend(struct device *dev)
if (!pm) {
pci_pm_default_suspend(pci_dev);
goto Fixup;
return 0;
}
/*
* PCI devices suspended at run time need to be resumed at this point,
* because in general it is necessary to reconfigure them for system
* suspend. Namely, if the device is supposed to wake up the system
* from the sleep state, we may need to reconfigure it for this purpose.
* In turn, if the device is not supposed to wake up the system from the
* sleep state, we'll have to prevent it from signaling wake-up.
* PCI devices suspended at run time may need to be resumed at this
* point, because in general it may be necessary to reconfigure them for
* system suspend. Namely, if the device is expected to wake up the
* system from the sleep state, it may have to be reconfigured for this
* purpose, or if the device is not expected to wake up the system from
* the sleep state, it should be prevented from signaling wakeup events
* going forward.
*
* Also if the driver of the device does not indicate that its system
* suspend callbacks can cope with runtime-suspended devices, it is
* better to resume the device from runtime suspend here.
*/
pm_runtime_resume(dev);
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
!pci_dev_keep_suspended(pci_dev))
pm_runtime_resume(dev);
pci_dev->state_saved = false;
if (pm->suspend) {
@ -755,17 +765,27 @@ static int pci_pm_suspend(struct device *dev)
}
}
Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);
return 0;
}
static int pci_pm_suspend_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
return pm_generic_suspend_late(dev);
}
static int pci_pm_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
@ -827,6 +847,14 @@ static int pci_pm_resume_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
/*
* 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 are going to be put into D0 shortly.
*/
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
pci_pm_default_resume_early(pci_dev);
if (pci_has_legacy_pm_support(pci_dev))
@ -869,6 +897,7 @@ static int pci_pm_resume(struct device *dev)
#else /* !CONFIG_SUSPEND */
#define pci_pm_suspend NULL
#define pci_pm_suspend_late NULL
#define pci_pm_suspend_noirq NULL
#define pci_pm_resume NULL
#define pci_pm_resume_noirq NULL
@ -903,7 +932,8 @@ static int pci_pm_freeze(struct device *dev)
* devices should not be touched during freeze/thaw transitions,
* however.
*/
pm_runtime_resume(dev);
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
pm_runtime_resume(dev);
pci_dev->state_saved = false;
if (pm->freeze) {
@ -915,17 +945,25 @@ static int pci_pm_freeze(struct device *dev)
return error;
}
if (pcibios_pm_ops.freeze)
return pcibios_pm_ops.freeze(dev);
return 0;
}
static int pci_pm_freeze_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
return pm_generic_freeze_late(dev);;
}
static int pci_pm_freeze_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_FREEZE);
@ -955,6 +993,16 @@ static int pci_pm_thaw_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
/*
* If the device is in runtime suspend, the code below may not work
* correctly with it, so skip that code 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;
}
if (pcibios_pm_ops.thaw_noirq) {
error = pcibios_pm_ops.thaw_noirq(dev);
if (error)
@ -979,12 +1027,6 @@ static int pci_pm_thaw(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;
if (pcibios_pm_ops.thaw) {
error = pcibios_pm_ops.thaw(dev);
if (error)
return error;
}
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev);
@ -1010,11 +1052,13 @@ static int pci_pm_poweroff(struct device *dev)
if (!pm) {
pci_pm_default_suspend(pci_dev);
goto Fixup;
return 0;
}
/* The reason to do that is the same as in pci_pm_suspend(). */
pm_runtime_resume(dev);
if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
!pci_dev_keep_suspended(pci_dev))
pm_runtime_resume(dev);
pci_dev->state_saved = false;
if (pm->poweroff) {
@ -1026,20 +1070,27 @@ static int pci_pm_poweroff(struct device *dev)
return error;
}
Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);
if (pcibios_pm_ops.poweroff)
return pcibios_pm_ops.poweroff(dev);
return 0;
}
static int pci_pm_poweroff_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
return pm_generic_poweroff_late(dev);
}
static int pci_pm_poweroff_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(to_pci_dev(dev)))
return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
@ -1081,6 +1132,10 @@ static int pci_pm_restore_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
/* This is analogous to the pci_pm_resume_noirq() case. */
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
if (pcibios_pm_ops.restore_noirq) {
error = pcibios_pm_ops.restore_noirq(dev);
if (error)
@ -1104,12 +1159,6 @@ static int pci_pm_restore(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;
if (pcibios_pm_ops.restore) {
error = pcibios_pm_ops.restore(dev);
if (error)
return error;
}
/*
* This is necessary for the hibernation error path in which restore is
* called without restoring the standard config registers of the device.
@ -1135,10 +1184,12 @@ static int pci_pm_restore(struct device *dev)
#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL
#define pci_pm_freeze_late NULL
#define pci_pm_freeze_noirq NULL
#define pci_pm_thaw NULL
#define pci_pm_thaw_noirq NULL
#define pci_pm_poweroff NULL
#define pci_pm_poweroff_late NULL
#define pci_pm_poweroff_noirq NULL
#define pci_pm_restore NULL
#define pci_pm_restore_noirq NULL
@ -1254,10 +1305,13 @@ static const struct dev_pm_ops pci_dev_pm_ops = {
.prepare = pci_pm_prepare,
.complete = pci_pm_complete,
.suspend = pci_pm_suspend,
.suspend_late = pci_pm_suspend_late,
.resume = pci_pm_resume,
.freeze = pci_pm_freeze,
.freeze_late = pci_pm_freeze_late,
.thaw = pci_pm_thaw,
.poweroff = pci_pm_poweroff,
.poweroff_late = pci_pm_poweroff_late,
.restore = pci_pm_restore,
.suspend_noirq = pci_pm_suspend_noirq,
.resume_noirq = pci_pm_resume_noirq,

View File

@ -2166,8 +2166,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
if (!pm_runtime_suspended(dev)
|| pci_target_state(pci_dev, wakeup) != pci_dev->current_state
|| platform_pci_need_resume(pci_dev)
|| (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME))
|| platform_pci_need_resume(pci_dev))
return false;
/*

View File

@ -885,17 +885,27 @@ int acpi_dev_suspend_late(struct device *dev);
int acpi_subsys_prepare(struct device *dev);
void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev);
int acpi_subsys_suspend_noirq(struct device *dev);
int acpi_subsys_resume_noirq(struct device *dev);
int acpi_subsys_resume_early(struct device *dev);
int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev);
int acpi_subsys_freeze_late(struct device *dev);
int acpi_subsys_freeze_noirq(struct device *dev);
int acpi_subsys_thaw_noirq(struct device *dev);
#else
static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; }
#endif
#ifdef CONFIG_ACPI

View File

@ -370,9 +370,6 @@ int subsys_virtual_register(struct bus_type *subsys,
* @devnode: Callback to provide the devtmpfs.
* @class_release: Called to release this class.
* @dev_release: Called to release the device.
* @suspend: Used to put the device to sleep mode, usually to a low power
* state.
* @resume: Used to bring the device from the sleep mode.
* @shutdown_pre: Called at shut-down time before driver shutdown.
* @ns_type: Callbacks so sysfs can detemine namespaces.
* @namespace: Namespace of the device belongs to this class.
@ -400,8 +397,6 @@ struct class {
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*shutdown_pre)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
@ -1075,6 +1070,16 @@ static inline void dev_pm_syscore_device(struct device *dev, bool val)
#endif
}
static inline void dev_pm_set_driver_flags(struct device *dev, u32 flags)
{
dev->power.driver_flags = flags;
}
static inline bool dev_pm_test_driver_flags(struct device *dev, u32 flags)
{
return !!(dev->power.driver_flags & flags);
}
static inline void device_lock(struct device *dev)
{
mutex_lock(&dev->mutex);

View File

@ -206,13 +206,8 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = (__force pci_dev_flags_t) (1 << 9),
/* Do not use FLR even if device advertises PCI_AF_CAP */
PCI_DEV_FLAGS_NO_FLR_RESET = (__force pci_dev_flags_t) (1 << 10),
/*
* Resume before calling the driver's system suspend hooks, disabling
* the direct_complete optimization.
*/
PCI_DEV_FLAGS_NEEDS_RESUME = (__force pci_dev_flags_t) (1 << 11),
/* Don't use Relaxed Ordering for TLPs directed at this device */
PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 12),
PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 11),
};
enum pci_irq_reroute_variant {

View File

@ -550,6 +550,33 @@ struct pm_subsys_data {
#endif
};
/*
* Driver flags to control system suspend/resume behavior.
*
* These flags can be set by device drivers at the probe time. They need not be
* cleared by the drivers as the driver core will take care of that.
*
* NEVER_SKIP: Do not skip system suspend/resume callbacks for the device.
* SMART_PREPARE: Check the return value of the driver's ->prepare callback.
* SMART_SUSPEND: No need to resume the device from runtime suspend.
*
* Setting SMART_PREPARE instructs bus types and PM domains which may want
* system suspend/resume callbacks to be skipped for the device to return 0 from
* their ->prepare callbacks if the driver's ->prepare callback returns 0 (in
* other words, the system suspend/resume callbacks can only be skipped for the
* device if its driver doesn't object against that). This flag has no effect
* if NEVER_SKIP is set.
*
* Setting SMART_SUSPEND instructs bus types and PM domains which may want to
* runtime resume the device upfront during system suspend that doing so is not
* necessary from the driver's perspective. It also may cause them to skip
* invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
* the driver if they decide to leave the device in runtime suspend.
*/
#define DPM_FLAG_NEVER_SKIP BIT(0)
#define DPM_FLAG_SMART_PREPARE BIT(1)
#define DPM_FLAG_SMART_SUSPEND BIT(2)
struct dev_pm_info {
pm_message_t power_state;
unsigned int can_wakeup:1;
@ -561,6 +588,7 @@ struct dev_pm_info {
bool is_late_suspended:1;
bool early_init:1; /* Owned by the PM core */
bool direct_complete:1; /* Owned by the PM core */
u32 driver_flags;
spinlock_t lock;
#ifdef CONFIG_PM_SLEEP
struct list_head entry;
@ -737,6 +765,8 @@ extern int pm_generic_poweroff_late(struct device *dev);
extern int pm_generic_poweroff(struct device *dev);
extern void pm_generic_complete(struct device *dev);
extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
#else /* !CONFIG_PM_SLEEP */
#define device_pm_lock() do {} while (0)