Skip to content

Commit

Permalink
Merge branch 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…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()
  • Loading branch information
torvalds committed Nov 20, 2011
2 parents a767835 + 501a708 commit 2d360fc
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 61 deletions.
33 changes: 26 additions & 7 deletions arch/arm/mach-shmobile/pm-sh7372.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand All @@ -123,26 +123,31 @@ 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)
pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
}
3 changes: 3 additions & 0 deletions drivers/base/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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");
Expand Down
3 changes: 2 additions & 1 deletion drivers/base/power/clock_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
3 changes: 2 additions & 1 deletion drivers/base/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
18 changes: 6 additions & 12 deletions drivers/base/power/qos.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
41 changes: 17 additions & 24 deletions drivers/devfreq/Kconfig
Original file line number Diff line number Diff line change
@@ -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

Expand Down
10 changes: 8 additions & 2 deletions drivers/devfreq/devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions include/linux/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
6 changes: 0 additions & 6 deletions include/linux/pm_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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; }
Expand Down
23 changes: 17 additions & 6 deletions kernel/power/hibernate.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ enum {

static int hibernation_mode = HIBERNATION_SHUTDOWN;

static bool freezer_test_done;

static const struct platform_hibernation_ops *hibernation_ops;

/**
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion kernel/power/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 2d360fc

Please sign in to comment.