Commit 7ae033cc authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-runtime' into for-linus

* pm-runtime:
  OMAP: PM: disable idle on suspend for GPIO and UART
  OMAP: PM: omap_device: add API to disable idle on suspend
  OMAP: PM: omap_device: add system PM methods for PM domain handling
  OMAP: PM: omap_device: conditionally use PM domain runtime helpers
  PM / Runtime: Add new helper function: pm_runtime_status_suspended()
  PM / Runtime: Consistent utilization of deferred_resume
  PM / Runtime: Prevent runtime_resume from racing with probe
  PM / Runtime: Replace "run-time" with "runtime" in documentation
  PM / Runtime: Improve documentation of enable, disable and barrier
  PM: Limit race conditions between runtime PM and system sleep (v2)
  PCI / PM: Detect early wakeup in pci_pm_prepare()
  PM / Runtime: Return special error code if runtime PM is disabled
  PM / Runtime: Update documentation of interactions with system sleep
parents ba1389d7 c8c9fda5
......@@ -604,7 +604,7 @@ state temporarily, for example so that its system wakeup capability can be
disabled. This all depends on the hardware and the design of the subsystem and
device driver in question.
During system-wide resume from a sleep state it's best to put devices into the
full-power state, as explained in Documentation/power/runtime_pm.txt. Refer to
that document for more information regarding this particular issue as well as
During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in Documentation/power/runtime_pm.txt. Refer
to that document for more information regarding this particular issue as well as
for information on the device runtime power management framework in general.
This diff is collapsed.
......@@ -87,6 +87,8 @@ static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
return PTR_ERR(od);
}
omap_device_disable_idle_on_suspend(od);
gpio_bank_count++;
return 0;
}
......
......@@ -805,6 +805,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata)
WARN(IS_ERR(od), "Could not build omap_device for %s: %s.\n",
name, oh->name);
omap_device_disable_idle_on_suspend(od);
oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
uart->irq = oh->mpu_irqs[0].irq;
......
......@@ -44,6 +44,10 @@ extern struct device omap_device_parent;
#define OMAP_DEVICE_STATE_IDLE 2
#define OMAP_DEVICE_STATE_SHUTDOWN 3
/* omap_device.flags values */
#define OMAP_DEVICE_SUSPENDED BIT(0)
#define OMAP_DEVICE_NO_IDLE_ON_SUSPEND BIT(1)
/**
* struct omap_device - omap_device wrapper for platform_devices
* @pdev: platform_device
......@@ -73,6 +77,7 @@ struct omap_device {
s8 pm_lat_level;
u8 hwmods_cnt;
u8 _state;
u8 flags;
};
/* Device driver interface (call via platform_data fn ptrs) */
......@@ -117,6 +122,10 @@ int omap_device_enable_hwmods(struct omap_device *od);
int omap_device_disable_clocks(struct omap_device *od);
int omap_device_enable_clocks(struct omap_device *od);
static inline void omap_device_disable_idle_on_suspend(struct omap_device *od)
{
od->flags |= OMAP_DEVICE_NO_IDLE_ON_SUSPEND;
}
/*
* Entries should be kept in latency order ascending
......
......@@ -537,6 +537,7 @@ int omap_early_device_register(struct omap_device *od)
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int _od_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
......@@ -563,13 +564,55 @@ static int _od_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
#endif
#ifdef CONFIG_SUSPEND
static int _od_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct omap_device *od = to_omap_device(pdev);
int ret;
if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
return pm_generic_suspend_noirq(dev);
ret = pm_generic_suspend_noirq(dev);
if (!ret && !pm_runtime_status_suspended(dev)) {
if (pm_generic_runtime_suspend(dev) == 0) {
omap_device_idle(pdev);
od->flags |= OMAP_DEVICE_SUSPENDED;
}
}
return ret;
}
static int _od_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct omap_device *od = to_omap_device(pdev);
if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
return pm_generic_resume_noirq(dev);
if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
!pm_runtime_status_suspended(dev)) {
od->flags &= ~OMAP_DEVICE_SUSPENDED;
omap_device_enable(pdev);
pm_generic_runtime_resume(dev);
}
return pm_generic_resume_noirq(dev);
}
#endif
static struct dev_pm_domain omap_device_pm_domain = {
.ops = {
.runtime_suspend = _od_runtime_suspend,
.runtime_idle = _od_runtime_idle,
.runtime_resume = _od_runtime_resume,
SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
_od_runtime_idle)
USE_PLATFORM_PM_SLEEP_OPS
SET_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, _od_resume_noirq)
}
};
......
......@@ -505,6 +505,7 @@ static int legacy_resume(struct device *dev, int (*cb)(struct device *dev))
static int device_resume(struct device *dev, pm_message_t state, bool async)
{
int error = 0;
bool put = false;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
......@@ -521,6 +522,9 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
if (!dev->power.is_suspended)
goto Unlock;
pm_runtime_enable(dev);
put = true;
if (dev->pm_domain) {
pm_dev_dbg(dev, state, "power domain ");
error = pm_op(dev, &dev->pm_domain->ops, state);
......@@ -563,6 +567,10 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
complete_all(&dev->power.completion);
TRACE_RESUME(error);
if (put)
pm_runtime_put_sync(dev);
return error;
}
......@@ -843,16 +851,22 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
int error = 0;
dpm_wait_for_children(dev, async);
device_lock(dev);
if (async_error)
goto Unlock;
return 0;
pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
pm_wakeup_event(dev, 0);
if (pm_wakeup_pending()) {
pm_runtime_put_sync(dev);
async_error = -EBUSY;
goto Unlock;
return 0;
}
device_lock(dev);
if (dev->pm_domain) {
pm_dev_dbg(dev, state, "power domain ");
error = pm_op(dev, &dev->pm_domain->ops, state);
......@@ -890,12 +904,15 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
End:
dev->power.is_suspended = !error;
Unlock:
device_unlock(dev);
complete_all(&dev->power.completion);
if (error)
if (error) {
pm_runtime_put_sync(dev);
async_error = error;
} else if (dev->power.is_suspended) {
__pm_runtime_disable(dev, false);
}
return error;
}
......@@ -1035,13 +1052,7 @@ int dpm_prepare(pm_message_t state)
get_device(dev);
mutex_unlock(&dpm_list_mtx);
pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
pm_wakeup_event(dev, 0);
pm_runtime_put_sync(dev);
error = pm_wakeup_pending() ?
-EBUSY : device_prepare(dev, state);
error = device_prepare(dev, state);
mutex_lock(&dpm_list_mtx);
if (error) {
......
This diff is collapsed.
......@@ -116,12 +116,14 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
cp = memchr(buf, '\n', n);
if (cp)
len = cp - buf;
device_lock(dev);
if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
pm_runtime_allow(dev);
else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
pm_runtime_forbid(dev);
else
return -EINVAL;
n = -EINVAL;
device_unlock(dev);
return n;
}
......@@ -205,7 +207,9 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay)
return -EINVAL;
device_lock(dev);
pm_runtime_set_autosuspend_delay(dev, delay);
device_unlock(dev);
return n;
}
......
......@@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/cpu.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include "pci.h"
struct pci_dynid {
......@@ -615,6 +616,21 @@ static int pci_pm_prepare(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;
/*
* If a PCI device configured to wake up the system from sleep states
* has been suspended at run time and there's a resume request pending
* for it, this is equivalent to the device signaling wakeup, so the
* system suspend operation should be aborted.
*/
pm_runtime_get_noresume(dev);
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
pm_wakeup_event(dev, 0);
if (pm_wakeup_pending()) {
pm_runtime_put_sync(dev);
return -EBUSY;
}
/*
* PCI devices suspended at run time need to be resumed at this
* point, because in general it is necessary to reconfigure them for
......@@ -624,7 +640,7 @@ static int pci_pm_prepare(struct device *dev)
* system from the sleep state, we'll have to prevent it from signaling
* wake-up.
*/
pm_runtime_get_sync(dev);
pm_runtime_resume(dev);
if (drv && drv->pm && drv->pm->prepare)
error = drv->pm->prepare(dev);
......
......@@ -144,9 +144,9 @@ int scsi_autopm_get_device(struct scsi_device *sdev)
int err;
err = pm_runtime_get_sync(&sdev->sdev_gendev);
if (err < 0)
if (err < 0 && err !=-EACCES)
pm_runtime_put_sync(&sdev->sdev_gendev);
else if (err > 0)
else
err = 0;
return err;
}
......@@ -173,9 +173,9 @@ int scsi_autopm_get_host(struct Scsi_Host *shost)
int err;
err = pm_runtime_get_sync(&shost->shost_gendev);
if (err < 0)
if (err < 0 && err !=-EACCES)
pm_runtime_put_sync(&shost->shost_gendev);
else if (err > 0)
else
err = 0;
return err;
}
......
......@@ -82,6 +82,11 @@ static inline bool pm_runtime_suspended(struct device *dev)
&& !dev->power.disable_depth;
}
static inline bool pm_runtime_status_suspended(struct device *dev)
{
return dev->power.runtime_status == RPM_SUSPENDED;
}
static inline bool pm_runtime_enabled(struct device *dev)
{
return !dev->power.disable_depth;
......@@ -130,6 +135,7 @@ static inline void pm_runtime_put_noidle(struct device *dev) {}
static inline bool device_run_wake(struct device *dev) { return false; }
static inline void device_set_run_wake(struct device *dev, bool enable) {}
static inline bool pm_runtime_suspended(struct device *dev) { return false; }
static inline bool pm_runtime_status_suspended(struct device *dev) { return false; }
static inline bool pm_runtime_enabled(struct device *dev) { return false; }
static inline int pm_generic_runtime_idle(struct device *dev) { return 0; }
......
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