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
arch/arm/mach-shmobile
drivers
include/linux
kernel/power

View File

@ -20,6 +20,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/bitrev.h> #include <linux/bitrev.h>
#include <linux/console.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
@ -106,9 +107,8 @@ static int pd_power_down(struct generic_pm_domain *genpd)
return 0; 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 mask = 1 << sh7372_pd->bit_shift;
unsigned int retry_count; unsigned int retry_count;
int ret = 0; 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--) { for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
if (!(__raw_readl(SWUCR) & mask)) if (!(__raw_readl(SWUCR) & mask))
goto out; break;
if (retry_count > PSTR_RETRIES) if (retry_count > PSTR_RETRIES)
udelay(PSTR_DELAY_US); udelay(PSTR_DELAY_US);
else else
cpu_relax(); cpu_relax();
} }
if (__raw_readl(SWUCR) & mask) if (!retry_count)
ret = -EIO; ret = -EIO;
if (!sh7372_pd->no_debug) if (!sh7372_pd->no_debug)
@ -137,12 +137,17 @@ static int pd_power_up(struct generic_pm_domain *genpd)
mask, __raw_readl(PSTR)); mask, __raw_readl(PSTR));
out: out:
if (ret == 0 && sh7372_pd->resume) if (ret == 0 && sh7372_pd->resume && do_resume)
sh7372_pd->resume(); sh7372_pd->resume();
return ret; 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) static void sh7372_a4r_suspend(void)
{ {
sh7372_intcs_suspend(); 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->active_wakeup = pd_active_wakeup;
genpd->power_off = pd_power_down; genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up; 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, 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, .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 = { struct sh7372_pm_domain sh7372_a3sg = {
.bit_shift = 13, .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) #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE)
static int sh7372_do_idle_core_standby(unsigned long unused) 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 */ /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */
__raw_writel(0, PDNSEL); __raw_writel(0, PDNSEL);
sh7372_a3sp_init();
sh7372_suspend_init(); sh7372_suspend_init();
sh7372_cpuidle_init(); sh7372_cpuidle_init();
} }

View File

@ -22,6 +22,7 @@
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/async.h> #include <linux/async.h>
#include <linux/pm_runtime.h>
#include "base.h" #include "base.h"
#include "power/power.h" #include "power/power.h"
@ -1742,6 +1743,8 @@ void device_shutdown(void)
*/ */
list_del_init(&dev->kobj.entry); list_del_init(&dev->kobj.entry);
spin_unlock(&devices_kset->list_lock); spin_unlock(&devices_kset->list_lock);
/* Disable all device's runtime power management */
pm_runtime_disable(dev);
if (dev->bus && dev->bus->shutdown) { if (dev->bus && dev->bus->shutdown) {
dev_dbg(dev, "shutdown\n"); 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) { list_for_each_entry_reverse(ce, &psd->clock_list, node) {
if (ce->status < PCE_STATUS_ERROR) { 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; 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: End:
if (!error) { if (!error) {
dev->power.is_suspended = true; 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; 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 */ if (!dev || !req) /*guard against callers passing in null */
return -EINVAL; return -EINVAL;
if (dev_pm_qos_request_active(req)) { if (WARN(dev_pm_qos_request_active(req),
WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already " "%s() called for already added request\n", __func__))
"added request\n");
return -EINVAL; return -EINVAL;
}
req->dev = dev; 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 */ if (!req) /*guard against callers passing in null */
return -EINVAL; return -EINVAL;
if (!dev_pm_qos_request_active(req)) { if (WARN(!dev_pm_qos_request_active(req),
WARN(1, KERN_ERR "dev_pm_qos_update_request() called for " "%s() called for unknown object\n", __func__))
"unknown object\n");
return -EINVAL; return -EINVAL;
}
mutex_lock(&dev_pm_qos_mtx); 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 */ if (!req) /*guard against callers passing in null */
return -EINVAL; return -EINVAL;
if (!dev_pm_qos_request_active(req)) { if (WARN(!dev_pm_qos_request_active(req),
WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for " "%s() called for unknown object\n", __func__))
"unknown object\n");
return -EINVAL; return -EINVAL;
}
mutex_lock(&dev_pm_qos_mtx); 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 menuconfig PM_DEVFREQ
bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support" bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
depends on PM_OPP && ARCH_HAS_DEVFREQ
help help
With OPP support, a device may have a list of frequencies and A device may have a list of frequencies and voltages available.
voltages available. DEVFREQ, a generic DVFS framework can be devfreq, a generic DVFS framework can be registered for a device
registered for a device with OPP support in order to let the in order to let the governor provided to devfreq choose an
governor provided to DEVFREQ choose an operating frequency operating frequency based on the device driver's policy.
based on the OPP's list and the policy given with DEVFREQ.
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 reevaluate the device state periodically and/or based on the
OPP list changes (each frequency/voltage pair in OPP may be notification to "nb", a notifier block, of devfreq.
disabled or enabled).
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 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" is attached to a single device and returns a "representative"
clock frequency from the OPP of the device, which is also attached clock frequency of the device, which is also attached
to a device by 1-to-1. The device registering DEVFREQ takes the to a device by 1-to-1. The device registering devfreq takes the
responsiblity to "interpret" the frequency listed in OPP and responsiblity to "interpret" the representative frequency and
to set its every clock accordingly with the "target" callback 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 if PM_DEVFREQ

View File

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

View File

@ -682,6 +682,11 @@ static inline bool device_async_suspend_enabled(struct device *dev)
return !!dev->power.async_suspend; 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) static inline void device_lock(struct device *dev)
{ {
mutex_lock(&dev->mutex); mutex_lock(&dev->mutex);

View File

@ -447,6 +447,7 @@ struct dev_pm_info {
unsigned int async_suspend:1; unsigned int async_suspend:1;
bool is_prepared:1; /* Owned by the PM core */ bool is_prepared:1; /* Owned by the PM core */
bool is_suspended:1; /* Ditto */ bool is_suspended:1; /* Ditto */
bool ignore_children:1;
spinlock_t lock; spinlock_t lock;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
struct list_head entry; struct list_head entry;
@ -464,7 +465,6 @@ struct dev_pm_info {
atomic_t usage_count; atomic_t usage_count;
atomic_t child_count; atomic_t child_count;
unsigned int disable_depth:3; unsigned int disable_depth:3;
unsigned int ignore_children:1;
unsigned int idle_notification:1; unsigned int idle_notification:1;
unsigned int request_pending:1; unsigned int request_pending:1;
unsigned int deferred_resume: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); || !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) static inline void pm_runtime_get_noresume(struct device *dev)
{ {
atomic_inc(&dev->power.usage_count); 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 void pm_runtime_forbid(struct device *dev) {}
static inline bool pm_children_suspended(struct device *dev) { return false; } 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_get_noresume(struct device *dev) {}
static inline void pm_runtime_put_noidle(struct device *dev) {} static inline void pm_runtime_put_noidle(struct device *dev) {}
static inline bool device_run_wake(struct device *dev) { return false; } 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 int hibernation_mode = HIBERNATION_SHUTDOWN;
static bool freezer_test_done;
static const struct platform_hibernation_ops *hibernation_ops; static const struct platform_hibernation_ops *hibernation_ops;
/** /**
@ -347,6 +349,17 @@ int hibernation_snapshot(int platform_mode)
if (error) if (error)
goto Close; 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); error = dpm_prepare(PMSG_FREEZE);
if (error) if (error)
goto Complete_devices; goto Complete_devices;
@ -641,15 +654,13 @@ int hibernate(void)
if (error) if (error)
goto Finish; goto Finish;
if (hibernation_test(TEST_FREEZER))
goto Thaw;
if (hibernation_testmode(HIBERNATION_TESTPROC))
goto Thaw;
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
if (error) if (error)
goto Thaw; goto Thaw;
if (freezer_test_done) {
freezer_test_done = false;
goto Thaw;
}
if (in_suspend) { if (in_suspend) {
unsigned int flags = 0; 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)) if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
break; break;
} }
if (state < PM_SUSPEND_MAX && *s) if (state < PM_SUSPEND_MAX && *s) {
error = enter_state(state); error = enter_state(state);
if (error) { if (error) {
suspend_stats.fail++; suspend_stats.fail++;
dpm_save_failed_errno(error); dpm_save_failed_errno(error);
} else } else
suspend_stats.success++; suspend_stats.success++;
}
#endif #endif
Exit: Exit: