1
0
Fork 0

Merge branches 'pm-sleep' and 'pm-domains'

* pm-sleep:
  PM / watchdog: iTCO: stop watchdog during system suspend
  PM / sleep: add pm-trace support for suspending phase
  PM / sleep: add configurable delay for pm_test

* pm-domains:
  PM / domains: avoid potential oops in pm_genpd_remove_device()
  PM / domains: factor out code to get the generic PM domain from a struct device
  PM / domains: quieten down generic pm domains
  PM / Domains: Sync runtime PM status with genpd after probe
  driver core / PM: Add PM domain callbacks for device setup/cleanup
  MAINTAINERS: add entry for Generic PM domains (genpd)
hifive-unleashed-5.1
Rafael J. Wysocki 2015-04-13 00:37:13 +02:00
commit 0fe0952b21
14 changed files with 189 additions and 46 deletions

View File

@ -3462,6 +3462,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
improve throughput, but will also increase the improve throughput, but will also increase the
amount of memory reserved for use by the client. amount of memory reserved for use by the client.
suspend.pm_test_delay=
[SUSPEND]
Sets the number of seconds to remain in a suspend test
mode before resuming the system (see
/sys/power/pm_test). Only available when CONFIG_PM_DEBUG
is set. Default value is 5.
swapaccount=[0|1] swapaccount=[0|1]
[KNL] Enable accounting of swap in memory resource [KNL] Enable accounting of swap in memory resource
controller if no parameter or 1 is given or disable controller if no parameter or 1 is given or disable

View File

@ -75,12 +75,14 @@ you should do the following:
# echo platform > /sys/power/disk # echo platform > /sys/power/disk
# echo disk > /sys/power/state # echo disk > /sys/power/state
Then, the kernel will try to freeze processes, suspend devices, wait 5 seconds, Then, the kernel will try to freeze processes, suspend devices, wait a few
resume devices and thaw processes. If "platform" is written to seconds (5 by default, but configurable by the suspend.pm_test_delay module
parameter), resume devices and thaw processes. If "platform" is written to
/sys/power/pm_test , then after suspending devices the kernel will additionally /sys/power/pm_test , then after suspending devices the kernel will additionally
invoke the global control methods (eg. ACPI global control methods) used to invoke the global control methods (eg. ACPI global control methods) used to
prepare the platform firmware for hibernation. Next, it will wait 5 seconds and prepare the platform firmware for hibernation. Next, it will wait a
invoke the platform (eg. ACPI) global methods used to cancel hibernation etc. configurable number of seconds and invoke the platform (eg. ACPI) global
methods used to cancel hibernation etc.
Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal
hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test

View File

@ -4312,6 +4312,15 @@ S: Supported
F: drivers/phy/ F: drivers/phy/
F: include/linux/phy/ F: include/linux/phy/
GENERIC PM DOMAINS
M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
M: Kevin Hilman <khilman@kernel.org>
M: Ulf Hansson <ulf.hansson@linaro.org>
L: linux-pm@vger.kernel.org
S: Supported
F: drivers/base/power/domain*.c
F: include/linux/pm_domain.h
GENERIC UIO DRIVER FOR PCI DEVICES GENERIC UIO DRIVER FOR PCI DEVICES
M: "Michael S. Tsirkin" <mst@redhat.com> M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org L: kvm@vger.kernel.org

View File

@ -1,5 +1,5 @@
#ifndef _ASM_X86_RESUME_TRACE_H #ifndef _ASM_X86_PM_TRACE_H
#define _ASM_X86_RESUME_TRACE_H #define _ASM_X86_PM_TRACE_H
#include <asm/asm.h> #include <asm/asm.h>
@ -14,8 +14,10 @@ do { \
".previous" \ ".previous" \
:"=r" (tracedata) \ :"=r" (tracedata) \
: "i" (__LINE__), "i" (__FILE__)); \ : "i" (__LINE__), "i" (__FILE__)); \
generate_resume_trace(tracedata, user); \ generate_pm_trace(tracedata, user); \
} \ } \
} while (0) } while (0)
#endif /* _ASM_X86_RESUME_TRACE_H */ #define TRACE_SUSPEND(user) TRACE_RESUME(user)
#endif /* _ASM_X86_PM_TRACE_H */

View File

@ -298,6 +298,12 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto probe_failed; goto probe_failed;
} }
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
if (dev->bus->probe) { if (dev->bus->probe) {
ret = dev->bus->probe(dev); ret = dev->bus->probe(dev);
if (ret) if (ret)
@ -308,6 +314,9 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto probe_failed; goto probe_failed;
} }
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
driver_bound(dev); driver_bound(dev);
ret = 1; ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n", pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
@ -319,6 +328,8 @@ probe_failed:
driver_sysfs_remove(dev); driver_sysfs_remove(dev);
dev->driver = NULL; dev->driver = NULL;
dev_set_drvdata(dev, NULL); dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
if (ret == -EPROBE_DEFER) { if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */ /* Driver requested deferred probing */
@ -525,6 +536,9 @@ static void __device_release_driver(struct device *dev)
devres_release_all(dev); devres_release_all(dev);
dev->driver = NULL; dev->driver = NULL;
dev_set_drvdata(dev, NULL); dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
klist_remove(&dev->p->knode_driver); klist_remove(&dev->p->knode_driver);
if (dev->bus) if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

View File

@ -68,7 +68,36 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
return genpd; return genpd;
} }
struct generic_pm_domain *dev_to_genpd(struct device *dev) /*
* Get the generic PM domain for a particular struct device.
* This validates the struct device pointer, the PM domain pointer,
* and checks that the PM domain pointer is a real generic PM domain.
* Any failure results in NULL being returned.
*/
struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
{
struct generic_pm_domain *genpd = NULL, *gpd;
if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain))
return NULL;
mutex_lock(&gpd_list_lock);
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (&gpd->domain == dev->pm_domain) {
genpd = gpd;
break;
}
}
mutex_unlock(&gpd_list_lock);
return genpd;
}
/*
* This should only be used where we are certain that the pm_domain
* attached to the device is a genpd domain.
*/
static struct generic_pm_domain *dev_to_genpd(struct device *dev)
{ {
if (IS_ERR_OR_NULL(dev->pm_domain)) if (IS_ERR_OR_NULL(dev->pm_domain))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
@ -173,8 +202,8 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
genpd->power_on_latency_ns = elapsed_ns; genpd->power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
genpd_recalc_cpu_exit_latency(genpd); genpd_recalc_cpu_exit_latency(genpd);
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "on", elapsed_ns); genpd->name, "on", elapsed_ns);
return ret; return ret;
} }
@ -199,8 +228,8 @@ static int genpd_power_off(struct generic_pm_domain *genpd)
genpd->power_off_latency_ns = elapsed_ns; genpd->power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns); genpd->name, "off", elapsed_ns);
return ret; return ret;
} }
@ -1513,9 +1542,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) if (!genpd || genpd != pm_genpd_lookup_dev(dev))
|| IS_ERR_OR_NULL(dev->pm_domain)
|| pd_to_genpd(dev->pm_domain) != genpd)
return -EINVAL; return -EINVAL;
/* The above validation also means we have existing domain_data. */ /* The above validation also means we have existing domain_data. */
@ -2093,21 +2120,10 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
*/ */
static void genpd_dev_pm_detach(struct device *dev, bool power_off) static void genpd_dev_pm_detach(struct device *dev, bool power_off)
{ {
struct generic_pm_domain *pd = NULL, *gpd; struct generic_pm_domain *pd;
int ret = 0; int ret = 0;
if (!dev->pm_domain) pd = pm_genpd_lookup_dev(dev);
return;
mutex_lock(&gpd_list_lock);
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (&gpd->domain == dev->pm_domain) {
pd = gpd;
break;
}
}
mutex_unlock(&gpd_list_lock);
if (!pd) if (!pd)
return; return;
@ -2130,6 +2146,17 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
genpd_queue_power_off_work(pd); genpd_queue_power_off_work(pd);
} }
static void genpd_dev_pm_sync(struct device *dev)
{
struct generic_pm_domain *pd;
pd = dev_to_genpd(dev);
if (IS_ERR(pd))
return;
genpd_queue_power_off_work(pd);
}
/** /**
* genpd_dev_pm_attach - Attach a device to its PM domain using DT. * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
* @dev: Device to attach. * @dev: Device to attach.
@ -2196,6 +2223,7 @@ int genpd_dev_pm_attach(struct device *dev)
} }
dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
pm_genpd_poweron(pd); pm_genpd_poweron(pd);
return 0; return 0;

View File

@ -23,7 +23,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/async.h> #include <linux/async.h>
@ -1017,6 +1017,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
if (async_error) if (async_error)
goto Complete; goto Complete;
@ -1057,6 +1060,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
Complete: Complete:
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1078,7 +1082,7 @@ static int device_suspend_noirq(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_noirq, dev); async_schedule(async_suspend_noirq, dev);
return 0; return 0;
@ -1157,6 +1161,9 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
__pm_runtime_disable(dev, false); __pm_runtime_disable(dev, false);
if (async_error) if (async_error)
@ -1198,6 +1205,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
async_error = error; async_error = error;
Complete: Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
return error; return error;
} }
@ -1219,7 +1227,7 @@ static int device_suspend_late(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_late, dev); async_schedule(async_suspend_late, dev);
return 0; return 0;
@ -1338,6 +1346,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
int error = 0; int error = 0;
DECLARE_DPM_WATCHDOG_ON_STACK(wd); DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
dpm_wait_for_children(dev, async); dpm_wait_for_children(dev, async);
if (async_error) if (async_error)
@ -1444,6 +1455,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (error) if (error)
async_error = error; async_error = error;
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1465,7 +1477,7 @@ static int device_suspend(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend, dev); async_schedule(async_suspend, dev);
return 0; return 0;

View File

@ -7,7 +7,7 @@
* devices may be working. * devices may be working.
*/ */
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@ -154,7 +154,7 @@ EXPORT_SYMBOL(set_trace_device);
* it's not any guarantee, but it's a high _likelihood_ that * it's not any guarantee, but it's a high _likelihood_ that
* the match is valid). * the match is valid).
*/ */
void generate_resume_trace(const void *tracedata, unsigned int user) void generate_pm_trace(const void *tracedata, unsigned int user)
{ {
unsigned short lineno = *(unsigned short *)tracedata; unsigned short lineno = *(unsigned short *)tracedata;
const char *file = *(const char **)(tracedata + 2); const char *file = *(const char **)(tracedata + 2);
@ -164,7 +164,7 @@ void generate_resume_trace(const void *tracedata, unsigned int user)
file_hash_value = hash_string(lineno, file, FILEHASH); file_hash_value = hash_string(lineno, file, FILEHASH);
set_magic_time(user_hash_value, file_hash_value, dev_hash_value); set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
} }
EXPORT_SYMBOL(generate_resume_trace); EXPORT_SYMBOL(generate_pm_trace);
extern char __tracedata_start, __tracedata_end; extern char __tracedata_start, __tracedata_end;
static int show_file_hash(unsigned int value) static int show_file_hash(unsigned int value)

View File

@ -51,6 +51,7 @@
#define DRV_VERSION "1.11" #define DRV_VERSION "1.11"
/* Includes */ /* Includes */
#include <linux/acpi.h> /* For ACPI support */
#include <linux/module.h> /* For module specific items */ #include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */ #include <linux/types.h> /* For standard types (like size_t) */
@ -103,6 +104,8 @@ static struct { /* this is private data for the iTCO_wdt device */
struct platform_device *dev; struct platform_device *dev;
/* the PCI-device */ /* the PCI-device */
struct pci_dev *pdev; struct pci_dev *pdev;
/* whether or not the watchdog has been suspended */
bool suspended;
} iTCO_wdt_private; } iTCO_wdt_private;
/* module parameters */ /* module parameters */
@ -571,12 +574,60 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)
iTCO_wdt_stop(NULL); iTCO_wdt_stop(NULL);
} }
#ifdef CONFIG_PM_SLEEP
/*
* Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
* the watchdog cannot be pinged while in that state. In ACPI sleep states the
* watchdog is stopped by the platform firmware.
*/
#ifdef CONFIG_ACPI
static inline bool need_suspend(void)
{
return acpi_target_system_state() == ACPI_STATE_S0;
}
#else
static inline bool need_suspend(void) { return true; }
#endif
static int iTCO_wdt_suspend_noirq(struct device *dev)
{
int ret = 0;
iTCO_wdt_private.suspended = false;
if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
if (!ret)
iTCO_wdt_private.suspended = true;
}
return ret;
}
static int iTCO_wdt_resume_noirq(struct device *dev)
{
if (iTCO_wdt_private.suspended)
iTCO_wdt_start(&iTCO_wdt_watchdog_dev);
return 0;
}
static struct dev_pm_ops iTCO_wdt_pm = {
.suspend_noirq = iTCO_wdt_suspend_noirq,
.resume_noirq = iTCO_wdt_resume_noirq,
};
#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
#else
#define ITCO_WDT_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver iTCO_wdt_driver = { static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe, .probe = iTCO_wdt_probe,
.remove = iTCO_wdt_remove, .remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown, .shutdown = iTCO_wdt_shutdown,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = ITCO_WDT_PM_OPS,
}, },
}; };

View File

@ -1,8 +1,8 @@
#ifndef RESUME_TRACE_H #ifndef PM_TRACE_H
#define RESUME_TRACE_H #define PM_TRACE_H
#ifdef CONFIG_PM_TRACE #ifdef CONFIG_PM_TRACE
#include <asm/resume-trace.h> #include <asm/pm-trace.h>
#include <linux/types.h> #include <linux/types.h>
extern int pm_trace_enabled; extern int pm_trace_enabled;
@ -14,7 +14,7 @@ static inline int pm_trace_is_enabled(void)
struct device; struct device;
extern void set_trace_device(struct device *); extern void set_trace_device(struct device *);
extern void generate_resume_trace(const void *tracedata, unsigned int user); extern void generate_pm_trace(const void *tracedata, unsigned int user);
extern int show_trace_dev_match(char *buf, size_t size); extern int show_trace_dev_match(char *buf, size_t size);
#define TRACE_DEVICE(dev) do { \ #define TRACE_DEVICE(dev) do { \
@ -28,6 +28,7 @@ static inline int pm_trace_is_enabled(void) { return 0; }
#define TRACE_DEVICE(dev) do { } while (0) #define TRACE_DEVICE(dev) do { } while (0)
#define TRACE_RESUME(dev) do { } while (0) #define TRACE_RESUME(dev) do { } while (0)
#define TRACE_SUSPEND(dev) do { } while (0)
#endif #endif

View File

@ -603,10 +603,18 @@ extern void dev_pm_put_subsys_data(struct device *dev);
* Power domains provide callbacks that are executed during system suspend, * Power domains provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions along with * hibernation, system resume and during runtime PM transitions along with
* subsystem-level and driver-level callbacks. * subsystem-level and driver-level callbacks.
*
* @detach: Called when removing a device from the domain.
* @activate: Called before executing probe routines for bus types and drivers.
* @sync: Called after successful driver probe.
* @dismiss: Called after unsuccessful driver probe and after driver removal.
*/ */
struct dev_pm_domain { struct dev_pm_domain {
struct dev_pm_ops ops; struct dev_pm_ops ops;
void (*detach)(struct device *dev, bool power_off); void (*detach)(struct device *dev, bool power_off);
int (*activate)(struct device *dev);
void (*sync)(struct device *dev);
void (*dismiss)(struct device *dev);
}; };
/* /*

View File

@ -127,7 +127,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
return to_gpd_data(dev->power.subsys_data->domain_data); return to_gpd_data(dev->power.subsys_data->domain_data);
} }
extern struct generic_pm_domain *dev_to_genpd(struct device *dev); extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev);
extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,
struct gpd_timing_data *td); struct gpd_timing_data *td);
@ -163,9 +163,9 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOSYS);
} }
static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
{ {
return ERR_PTR(-ENOSYS); return NULL;
} }
static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,

View File

@ -11,7 +11,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>

View File

@ -28,6 +28,7 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/moduleparam.h>
#include "power.h" #include "power.h"
@ -233,12 +234,20 @@ static bool platform_suspend_again(suspend_state_t state)
suspend_ops->suspend_again() : false; suspend_ops->suspend_again() : false;
} }
#ifdef CONFIG_PM_DEBUG
static unsigned int pm_test_delay = 5;
module_param(pm_test_delay, uint, 0644);
MODULE_PARM_DESC(pm_test_delay,
"Number of seconds to wait before resuming from suspend test");
#endif
static int suspend_test(int level) static int suspend_test(int level)
{ {
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
if (pm_test_level == level) { if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); printk(KERN_INFO "suspend debug: Waiting for %d second(s).\n",
mdelay(5000); pm_test_delay);
mdelay(pm_test_delay * 1000);
return 1; return 1;
} }
#endif /* !CONFIG_PM_DEBUG */ #endif /* !CONFIG_PM_DEBUG */