Merge branch 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

* 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  PM / Suspend: Fix bug in suspend statistics update
  PM / Hibernate: Fix the early termination of test modes
  PM / shmobile: Fix build of sh7372_pm_init() for CONFIG_PM unset
  PM Sleep: Do not extend wakeup paths to devices with ignore_children set
  PM / driver core: disable device's runtime PM during shutdown
  PM / devfreq: correct Kconfig dependency
  PM / devfreq: fix use after free in devfreq_remove_device
  PM / shmobile: Avoid restoring the INTCS state during initialization
  PM / devfreq: Remove compiler error after irq.h update
  PM / QoS: Properly use the WARN() macro in dev_pm_qos_add_request()
  PM / Clocks: Only disable enabled clocks in pm_clk_suspend()
  ARM: mach-shmobile: sh7372 A3SP no_suspend_console fix
  PM / shmobile: Don't skip debugging output in pd_power_up()
This commit is contained in:
Linus Torvalds 2011-11-20 14:33:02 -08:00
commit 2d360fcbd8
12 changed files with 89 additions and 61 deletions

View File

@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/bitrev.h>
#include <linux/console.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/tlbflush.h>
@ -106,9 +107,8 @@ static int pd_power_down(struct generic_pm_domain *genpd)
return 0;
}
static int pd_power_up(struct generic_pm_domain *genpd)
static int __pd_power_up(struct sh7372_pm_domain *sh7372_pd, bool do_resume)
{
struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
unsigned int mask = 1 << sh7372_pd->bit_shift;
unsigned int retry_count;
int ret = 0;
@ -123,13 +123,13 @@ static int pd_power_up(struct generic_pm_domain *genpd)
for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
if (!(__raw_readl(SWUCR) & mask))
goto out;
break;
if (retry_count > PSTR_RETRIES)
udelay(PSTR_DELAY_US);
else
cpu_relax();
}
if (__raw_readl(SWUCR) & mask)
if (!retry_count)
ret = -EIO;
if (!sh7372_pd->no_debug)
@ -137,12 +137,17 @@ static int pd_power_up(struct generic_pm_domain *genpd)
mask, __raw_readl(PSTR));
out:
if (ret == 0 && sh7372_pd->resume)
if (ret == 0 && sh7372_pd->resume && do_resume)
sh7372_pd->resume();
return ret;
}
static int pd_power_up(struct generic_pm_domain *genpd)
{
return __pd_power_up(to_sh7372_pd(genpd), true);
}
static void sh7372_a4r_suspend(void)
{
sh7372_intcs_suspend();
@ -174,7 +179,7 @@ void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
genpd->active_wakeup = pd_active_wakeup;
genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up;
genpd->power_on(&sh7372_pd->genpd);
__pd_power_up(sh7372_pd, false);
}
void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
@ -227,11 +232,23 @@ struct sh7372_pm_domain sh7372_a3sp = {
.no_debug = true,
};
static void sh7372_a3sp_init(void)
{
/* serial consoles make use of SCIF hardware located in A3SP,
* keep such power domain on if "no_console_suspend" is set.
*/
sh7372_a3sp.stay_on = !console_suspend_enabled;
}
struct sh7372_pm_domain sh7372_a3sg = {
.bit_shift = 13,
};
#endif /* CONFIG_PM */
#else /* !CONFIG_PM */
static inline void sh7372_a3sp_init(void) {}
#endif /* !CONFIG_PM */
#if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
static int sh7372_do_idle_core_standby(unsigned long unused)
@ -465,6 +482,8 @@ void __init sh7372_pm_init(void)
/* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */
__raw_writel(0, PDNSEL);
sh7372_a3sp_init();
sh7372_suspend_init();
sh7372_cpuidle_init();
}

View File

@ -22,6 +22,7 @@
#include <linux/kallsyms.h>
#include <linux/mutex.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include "base.h"
#include "power/power.h"
@ -1742,6 +1743,8 @@ void device_shutdown(void)
*/
list_del_init(&dev->kobj.entry);
spin_unlock(&devices_kset->list_lock);
/* Disable all device's runtime power management */
pm_runtime_disable(dev);
if (dev->bus && dev->bus->shutdown) {
dev_dbg(dev, "shutdown\n");

View File

@ -229,7 +229,8 @@ int pm_clk_suspend(struct device *dev)
list_for_each_entry_reverse(ce, &psd->clock_list, node) {
if (ce->status < PCE_STATUS_ERROR) {
clk_disable(ce->clk);
if (ce->status == PCE_STATUS_ENABLED)
clk_disable(ce->clk);
ce->status = PCE_STATUS_ACQUIRED;
}
}

View File

@ -920,7 +920,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
End:
if (!error) {
dev->power.is_suspended = true;
if (dev->power.wakeup_path && dev->parent)
if (dev->power.wakeup_path
&& dev->parent && !dev->parent->power.ignore_children)
dev->parent->power.wakeup_path = true;
}

View File

@ -212,11 +212,9 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
if (!dev || !req) /*guard against callers passing in null */
return -EINVAL;
if (dev_pm_qos_request_active(req)) {
WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already "
"added request\n");
if (WARN(dev_pm_qos_request_active(req),
"%s() called for already added request\n", __func__))
return -EINVAL;
}
req->dev = dev;
@ -271,11 +269,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
if (!req) /*guard against callers passing in null */
return -EINVAL;
if (!dev_pm_qos_request_active(req)) {
WARN(1, KERN_ERR "dev_pm_qos_update_request() called for "
"unknown object\n");
if (WARN(!dev_pm_qos_request_active(req),
"%s() called for unknown object\n", __func__))
return -EINVAL;
}
mutex_lock(&dev_pm_qos_mtx);
@ -312,11 +308,9 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
if (!req) /*guard against callers passing in null */
return -EINVAL;
if (!dev_pm_qos_request_active(req)) {
WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for "
"unknown object\n");
if (WARN(!dev_pm_qos_request_active(req),
"%s() called for unknown object\n", __func__))
return -EINVAL;
}
mutex_lock(&dev_pm_qos_mtx);

View File

@ -1,36 +1,29 @@
config ARCH_HAS_DEVFREQ
bool
depends on ARCH_HAS_OPP
help
Denotes that the architecture supports DEVFREQ. If the architecture
supports multiple OPP entries per device and the frequency of the
devices with OPPs may be altered dynamically, the architecture
supports DEVFREQ.
menuconfig PM_DEVFREQ
bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
depends on PM_OPP && ARCH_HAS_DEVFREQ
help
With OPP support, a device may have a list of frequencies and
voltages available. DEVFREQ, a generic DVFS framework can be
registered for a device with OPP support in order to let the
governor provided to DEVFREQ choose an operating frequency
based on the OPP's list and the policy given with DEVFREQ.
A device may have a list of frequencies and voltages available.
devfreq, a generic DVFS framework can be registered for a device
in order to let the governor provided to devfreq choose an
operating frequency based on the device driver's policy.
Each device may have its own governor and policy. DEVFREQ can
Each device may have its own governor and policy. Devfreq can
reevaluate the device state periodically and/or based on the
OPP list changes (each frequency/voltage pair in OPP may be
disabled or enabled).
notification to "nb", a notifier block, of devfreq.
Like some CPUs with CPUFREQ, a device may have multiple clocks.
Like some CPUs with CPUfreq, a device may have multiple clocks.
However, because the clock frequencies of a single device are
determined by the single device's state, an instance of DEVFREQ
determined by the single device's state, an instance of devfreq
is attached to a single device and returns a "representative"
clock frequency from the OPP of the device, which is also attached
to a device by 1-to-1. The device registering DEVFREQ takes the
responsiblity to "interpret" the frequency listed in OPP and
clock frequency of the device, which is also attached
to a device by 1-to-1. The device registering devfreq takes the
responsiblity to "interpret" the representative frequency and
to set its every clock accordingly with the "target" callback
given to DEVFREQ.
given to devfreq.
When OPP is used with the devfreq device, it is recommended to
register devfreq's nb to the OPP's notifier head. If OPP is
used with the devfreq device, you may use OPP helper
functions defined in devfreq.h.
if PM_DEVFREQ

View File

@ -15,7 +15,9 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/opp.h>
#include <linux/devfreq.h>
#include <linux/workqueue.h>
@ -416,10 +418,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
*/
int devfreq_remove_device(struct devfreq *devfreq)
{
bool central_polling;
if (!devfreq)
return -EINVAL;
if (!devfreq->governor->no_central_polling) {
central_polling = !devfreq->governor->no_central_polling;
if (central_polling) {
mutex_lock(&devfreq_list_lock);
while (wait_remove_device == devfreq) {
mutex_unlock(&devfreq_list_lock);
@ -431,7 +437,7 @@ int devfreq_remove_device(struct devfreq *devfreq)
mutex_lock(&devfreq->lock);
_remove_devfreq(devfreq, false); /* it unlocks devfreq->lock */
if (!devfreq->governor->no_central_polling)
if (central_polling)
mutex_unlock(&devfreq_list_lock);
return 0;

View File

@ -682,6 +682,11 @@ static inline bool device_async_suspend_enabled(struct device *dev)
return !!dev->power.async_suspend;
}
static inline void pm_suspend_ignore_children(struct device *dev, bool enable)
{
dev->power.ignore_children = enable;
}
static inline void device_lock(struct device *dev)
{
mutex_lock(&dev->mutex);

View File

@ -447,6 +447,7 @@ struct dev_pm_info {
unsigned int async_suspend:1;
bool is_prepared:1; /* Owned by the PM core */
bool is_suspended:1; /* Ditto */
bool ignore_children:1;
spinlock_t lock;
#ifdef CONFIG_PM_SLEEP
struct list_head entry;
@ -464,7 +465,6 @@ struct dev_pm_info {
atomic_t usage_count;
atomic_t child_count;
unsigned int disable_depth:3;
unsigned int ignore_children:1;
unsigned int idle_notification:1;
unsigned int request_pending:1;
unsigned int deferred_resume:1;

View File

@ -52,11 +52,6 @@ static inline bool pm_children_suspended(struct device *dev)
|| !atomic_read(&dev->power.child_count);
}
static inline void pm_suspend_ignore_children(struct device *dev, bool enable)
{
dev->power.ignore_children = enable;
}
static inline void pm_runtime_get_noresume(struct device *dev)
{
atomic_inc(&dev->power.usage_count);
@ -130,7 +125,6 @@ static inline void pm_runtime_allow(struct device *dev) {}
static inline void pm_runtime_forbid(struct device *dev) {}
static inline bool pm_children_suspended(struct device *dev) { return false; }
static inline void pm_suspend_ignore_children(struct device *dev, bool en) {}
static inline void pm_runtime_get_noresume(struct device *dev) {}
static inline void pm_runtime_put_noidle(struct device *dev) {}
static inline bool device_run_wake(struct device *dev) { return false; }

View File

@ -55,6 +55,8 @@ enum {
static int hibernation_mode = HIBERNATION_SHUTDOWN;
static bool freezer_test_done;
static const struct platform_hibernation_ops *hibernation_ops;
/**
@ -347,6 +349,17 @@ int hibernation_snapshot(int platform_mode)
if (error)
goto Close;
if (hibernation_test(TEST_FREEZER) ||
hibernation_testmode(HIBERNATION_TESTPROC)) {
/*
* Indicate to the caller that we are returning due to a
* successful freezer test.
*/
freezer_test_done = true;
goto Close;
}
error = dpm_prepare(PMSG_FREEZE);
if (error)
goto Complete_devices;
@ -641,15 +654,13 @@ int hibernate(void)
if (error)
goto Finish;
if (hibernation_test(TEST_FREEZER))
goto Thaw;
if (hibernation_testmode(HIBERNATION_TESTPROC))
goto Thaw;
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error)
goto Thaw;
if (freezer_test_done) {
freezer_test_done = false;
goto Thaw;
}
if (in_suspend) {
unsigned int flags = 0;

View File

@ -290,13 +290,14 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX && *s)
if (state < PM_SUSPEND_MAX && *s) {
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else
suspend_stats.success++;
}
#endif
Exit: