summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2003-08-14 00:40:55 -0700
committerPatrick Mochel <mochel@osdl.org>2003-08-14 00:40:55 -0700
commitbee60089075ef59a0e997148e43069aa3df8e227 (patch)
treebca1e0067bc9e66a48c1d0f51f4f02f030490a16
parent310b411f7f1ac37de5c8b9db5edd8b436b4b5eba (diff)
parent0b081c819fa9419fb6e027772f08dccac1f8ea8a (diff)
Hand Merge
-rw-r--r--drivers/acpi/Kconfig2
-rw-r--r--drivers/acpi/sleep/main.c8
-rw-r--r--drivers/base/Makefile3
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/bus.c26
-rw-r--r--drivers/base/class.c8
-rw-r--r--drivers/base/core.c5
-rw-r--r--drivers/base/interface.c88
-rw-r--r--drivers/base/power.c140
-rw-r--r--drivers/base/power/Makefile2
-rw-r--r--drivers/base/power/main.c98
-rw-r--r--drivers/base/power/power.h104
-rw-r--r--drivers/base/power/resume.c153
-rw-r--r--drivers/base/power/runtime.c89
-rw-r--r--drivers/base/power/shutdown.c68
-rw-r--r--drivers/base/power/suspend.c235
-rw-r--r--drivers/base/power/sysfs.c68
-rw-r--r--fs/sysfs/Makefile3
-rw-r--r--fs/sysfs/dir.c78
-rw-r--r--fs/sysfs/file.c39
-rw-r--r--fs/sysfs/group.c81
-rw-r--r--fs/sysfs/sysfs.h5
-rw-r--r--include/linux/device.h25
-rw-r--r--include/linux/pm.h35
-rw-r--r--include/linux/reboot.h2
-rw-r--r--include/linux/suspend.h10
-rw-r--r--include/linux/sysfs.h17
-rw-r--r--init/do_mounts.c4
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/power/Makefile4
-rw-r--r--kernel/power/console.c14
-rw-r--r--kernel/power/pm.c (renamed from kernel/pm.c)37
-rw-r--r--kernel/power/poweroff.c44
-rw-r--r--kernel/power/process.c1
-rw-r--r--kernel/power/swsusp.c155
35 files changed, 1219 insertions, 436 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 7217b4ec616e..2077ffebdbc1 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -68,7 +68,7 @@ config ACPI_BOOT
config ACPI_SLEEP
bool "Sleep States (EXPERIMENTAL)"
depends on X86 && ACPI
- depends on EXPERIMENTAL
+ depends on EXPERIMENTAL && PM
default y
---help---
This option adds support for ACPI suspend states.
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 296dd3fd99e4..ba7294275354 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -22,6 +22,7 @@ ACPI_MODULE_NAME ("sleep")
u8 sleep_states[ACPI_S_STATE_COUNT];
extern void do_suspend_lowlevel_s4bios(int);
+extern void do_suspend_lowlevel(int);
/**
* acpi_system_restore_state - OS-specific restoration of state
@@ -71,10 +72,6 @@ acpi_system_restore_state (
* First, we call to the device driver layer to save device state.
* Once we have that, we save whatevery processor and kernel state we
* need to memory.
- * If we're entering S4, we then write the memory image to disk.
- *
- * Only then is it safe for us to power down devices, since we may need
- * the disks and upstream buses to write to.
*/
acpi_status
acpi_system_save_state(
@@ -185,12 +182,11 @@ acpi_system_suspend(
status = acpi_enter_sleep_state(state);
break;
-#ifdef CONFIG_SOFTWARE_SUSPEND
case ACPI_STATE_S2:
case ACPI_STATE_S3:
do_suspend_lowlevel(0);
break;
-#endif
+
case ACPI_STATE_S4:
do_suspend_lowlevel_s4bios(0);
break;
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 334fcc6c6ee6..5203e01c4547 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,7 +1,8 @@
# Makefile for the Linux device tree
-obj-y := core.o sys.o interface.o power.o bus.o \
+obj-y := core.o sys.o interface.o bus.o \
driver.o class.o platform.o \
cpu.o firmware.o init.o map.o
+obj-y += power/
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o memblk.o
diff --git a/drivers/base/base.h b/drivers/base/base.h
index cc92a7359569..da16cc5c9e30 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -13,3 +13,5 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
{
return container_of(_attr,struct class_device_attribute,attr);
}
+
+
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 6b19c58feb98..a9ad9caa1cab 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/string.h>
#include "base.h"
+#include "power/power.h"
#define to_dev(node) container_of(node,struct device,bus_list)
#define to_drv(node) container_of(node,struct device_driver,kobj.entry)
@@ -287,6 +288,7 @@ static int device_attach(struct device * dev)
{
struct bus_type * bus = dev->bus;
struct list_head * entry;
+ int error;
if (dev->driver) {
device_bind_driver(dev);
@@ -296,8 +298,15 @@ static int device_attach(struct device * dev)
if (bus->match) {
list_for_each(entry,&bus->drivers.list) {
struct device_driver * drv = to_drv(entry);
- if (!bus_match(dev,drv))
- return 1;
+ error = bus_match(dev,drv);
+ if (!error )
+ /* success, driver matched */
+ return 1;
+ if (error != -ENODEV)
+ /* driver matched but the probe failed */
+ printk(KERN_WARNING
+ "%s: probe of %s failed with error %d\n",
+ drv->name, dev->bus_id, error);
}
}
@@ -314,13 +323,14 @@ static int device_attach(struct device * dev)
* If bus_match() returns 0 and the @dev->driver is set, we've found
* a compatible pair.
*
- * Note that we ignore the error from bus_match(), since it's perfectly
- * valid for a driver not to bind to any devices.
+ * Note that we ignore the -ENODEV error from bus_match(), since it's
+ * perfectly valid for a driver not to bind to any devices.
*/
void driver_attach(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
struct list_head * entry;
+ int error;
if (!bus->match)
return;
@@ -328,7 +338,12 @@ void driver_attach(struct device_driver * drv)
list_for_each(entry,&bus->devices.list) {
struct device * dev = container_of(entry,struct device,bus_list);
if (!dev->driver) {
- bus_match(dev,drv);
+ error = bus_match(dev,drv);
+ if (error && (error != -ENODEV))
+ /* driver matched but the probe failed */
+ printk(KERN_WARNING
+ "%s: probe of %s failed with error %d\n",
+ drv->name, dev->bus_id, error);
}
}
}
@@ -350,6 +365,7 @@ void device_release_driver(struct device * dev)
if (drv) {
sysfs_remove_link(&drv->kobj,dev->kobj.name);
list_del_init(&dev->driver_list);
+ device_detach_shutdown(dev);
if (drv->remove)
drv->remove(dev);
dev->driver = NULL;
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 2a9c349bd7c9..ef7aff1b89ea 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -59,7 +59,7 @@ static struct kobj_type ktype_class = {
static decl_subsys(class,&ktype_class,NULL);
-int class_create_file(struct class * cls, struct class_attribute * attr)
+int class_create_file(struct class * cls, const struct class_attribute * attr)
{
int error;
if (cls) {
@@ -69,7 +69,7 @@ int class_create_file(struct class * cls, struct class_attribute * attr)
return error;
}
-void class_remove_file(struct class * cls, struct class_attribute * attr)
+void class_remove_file(struct class * cls, const struct class_attribute * attr)
{
if (cls)
sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr);
@@ -110,7 +110,7 @@ void class_unregister(struct class * cls)
/* Class Device Stuff */
int class_device_create_file(struct class_device * class_dev,
- struct class_device_attribute * attr)
+ const struct class_device_attribute * attr)
{
int error = -EINVAL;
if (class_dev)
@@ -119,7 +119,7 @@ int class_device_create_file(struct class_device * class_dev,
}
void class_device_remove_file(struct class_device * class_dev,
- struct class_device_attribute * attr)
+ const struct class_device_attribute * attr)
{
if (class_dev)
sysfs_remove_file(&class_dev->kobj, &attr->attr);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 3a36e17d4890..eaa842f8add6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -20,6 +20,7 @@
#include <asm/semaphore.h>
#include "base.h"
+#include "power/power.h"
int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
@@ -230,6 +231,8 @@ int device_add(struct device *dev)
bus_add_device(dev);
+ device_pm_add(dev);
+
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
@@ -304,6 +307,8 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
+ device_pm_remove(dev);
+
down_write(&devices_subsys.rwsem);
if (parent)
list_del_init(&dev->node);
diff --git a/drivers/base/interface.c b/drivers/base/interface.c
index 1622db6010d2..f5da0418284d 100644
--- a/drivers/base/interface.c
+++ b/drivers/base/interface.c
@@ -21,77 +21,39 @@ static ssize_t device_read_name(struct device * dev, char * buf)
static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL);
-static ssize_t
-device_read_power(struct device * dev, char * page)
+/**
+ * detach_state - control the default power state for the device.
+ *
+ * This is the state the device enters when it's driver module is
+ * unloaded. The value is an unsigned integer, in the range of 0-4.
+ * '0' indicates 'On', so no action will be taken when the driver is
+ * unloaded. This is the default behavior.
+ * '4' indicates 'Off', meaning the driver core will call the driver's
+ * shutdown method to quiesce the device.
+ * 1-3 indicate a low-power state for the device to enter via the
+ * driver's suspend method.
+ */
+
+static ssize_t detach_show(struct device * dev, char * buf)
{
- return sprintf(page,"%d\n",dev->power_state);
+ return sprintf(buf,"%u\n",dev->detach_state);
}
-static ssize_t
-device_write_power(struct device * dev, const char * buf, size_t count)
+static ssize_t detach_store(struct device * dev, const char * buf, size_t n)
{
- char str_command[20];
- char str_level[20];
- int num_args;
- u32 state;
- u32 int_level;
- int error = 0;
-
- if (!dev->driver)
- goto done;
-
- num_args = sscanf(buf,"%10s %10s %u",str_command,str_level,&state);
-
- error = -EINVAL;
-
- if (!num_args)
- goto done;
-
- if (!strnicmp(str_command,"suspend",7)) {
- if (num_args != 3)
- goto done;
- if (!strnicmp(str_level,"notify",6))
- int_level = SUSPEND_NOTIFY;
- else if (!strnicmp(str_level,"save",4))
- int_level = SUSPEND_SAVE_STATE;
- else if (!strnicmp(str_level,"disable",7))
- int_level = SUSPEND_DISABLE;
- else if (!strnicmp(str_level,"powerdown",8))
- int_level = SUSPEND_POWER_DOWN;
- else
- goto done;
-
- if (dev->driver->suspend)
- error = dev->driver->suspend(dev,state,int_level);
- else
- error = 0;
- } else if (!strnicmp(str_command,"resume",6)) {
- if (num_args != 2)
- goto done;
-
- if (!strnicmp(str_level,"poweron",7))
- int_level = RESUME_POWER_ON;
- else if (!strnicmp(str_level,"restore",7))
- int_level = RESUME_RESTORE_STATE;
- else if (!strnicmp(str_level,"enable",6))
- int_level = RESUME_ENABLE;
- else
- goto done;
-
- if (dev->driver->resume)
- error = dev->driver->resume(dev,int_level);
- else
- error = 0;
- }
- done:
- return error < 0 ? error : count;
+ u32 state;
+ state = simple_strtoul(buf,NULL,10);
+ if (state > 4)
+ return -EINVAL;
+ dev->detach_state = state;
+ return n;
}
-static DEVICE_ATTR(power,S_IWUSR | S_IRUGO,
- device_read_power,device_write_power);
+static DEVICE_ATTR(detach_state,0644,detach_show,detach_store);
+
struct attribute * dev_default_attrs[] = {
&dev_attr_name.attr,
- &dev_attr_power.attr,
+ &dev_attr_detach_state.attr,
NULL,
};
diff --git a/drivers/base/power.c b/drivers/base/power.c
deleted file mode 100644
index ba83ee21f758..000000000000
--- a/drivers/base/power.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * power.c - power management functions for the device tree.
- *
- * Copyright (c) 2002-3 Patrick Mochel
- * 2002-3 Open Source Development Lab
- *
- * This file is released under the GPLv2
- *
- * Kai Germaschewski contributed to the list walking routines.
- *
- */
-
-#undef DEBUG
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <asm/semaphore.h>
-#include "base.h"
-
-#define to_dev(node) container_of(node,struct device,kobj.entry)
-
-extern struct subsystem devices_subsys;
-
-/**
- * We handle system devices differently - we suspend and shut them
- * down first and resume them first. That way, we do anything stupid like
- * shutting down the interrupt controller before any devices..
- *
- * Note that there are not different stages for power management calls -
- * they only get one called once when interrupts are disabled.
- */
-
-extern int sysdev_shutdown(void);
-extern int sysdev_save(u32 state);
-extern int sysdev_suspend(u32 state);
-extern int sysdev_resume(void);
-extern int sysdev_restore(void);
-
-/**
- * device_suspend - suspend/remove all devices on the device ree
- * @state: state we're entering
- * @level: what stage of the suspend process we're at
- * (emb: it seems that these two arguments are described backwards of what
- * they actually mean .. is this correct?)
- *
- * The entries in the global device list are inserted such that they're in a
- * depth-first ordering. So, simply interate over the list, and call the
- * driver's suspend or remove callback for each device.
- */
-int device_suspend(u32 state, u32 level)
-{
- struct device * dev;
- int error = 0;
-
- down_write(&devices_subsys.rwsem);
- list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
- if (dev->driver && dev->driver->suspend) {
- pr_debug("suspending device %s\n",dev->name);
- error = dev->driver->suspend(dev,state,level);
- if (error)
- printk(KERN_ERR "%s: suspend returned %d\n",
- dev->name,error);
- }
- }
- up_write(&devices_subsys.rwsem);
-
- /*
- * Make sure system devices are suspended.
- */
- switch(level) {
- case SUSPEND_SAVE_STATE:
- sysdev_save(state);
- break;
- case SUSPEND_POWER_DOWN:
- sysdev_suspend(state);
- break;
- default:
- break;
- }
-
- return error;
-}
-
-/**
- * device_resume - resume all the devices in the system
- * @level: stage of resume process we're at
- *
- * Similar to device_suspend above, though we want to do a breadth-first
- * walk of the tree to make sure we wake up parents before children.
- * So, we iterate over the list backward.
- */
-void device_resume(u32 level)
-{
- struct device * dev;
-
- switch (level) {
- case RESUME_POWER_ON:
- sysdev_resume();
- break;
- case RESUME_RESTORE_STATE:
- sysdev_restore();
- break;
- default:
- break;
- }
-
- down_write(&devices_subsys.rwsem);
- list_for_each_entry(dev,&devices_subsys.kset.list,kobj.entry) {
- if (dev->driver && dev->driver->resume) {
- pr_debug("resuming device %s\n",dev->name);
- dev->driver->resume(dev,level);
- }
- }
- up_write(&devices_subsys.rwsem);
-}
-
-/**
- * device_shutdown - call ->remove() on each device to shutdown.
- */
-void device_shutdown(void)
-{
- struct device * dev;
-
- down_write(&devices_subsys.rwsem);
- list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
- pr_debug("shutting down %s: ",dev->name);
- if (dev->driver && dev->driver->shutdown) {
- pr_debug("Ok\n");
- dev->driver->shutdown(dev);
- } else
- pr_debug("Ignored.\n");
- }
- up_write(&devices_subsys.rwsem);
-
- sysdev_shutdown();
-}
-
-EXPORT_SYMBOL(device_suspend);
-EXPORT_SYMBOL(device_resume);
-EXPORT_SYMBOL(device_shutdown);
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
new file mode 100644
index 000000000000..99f811cd26cf
--- /dev/null
+++ b/drivers/base/power/Makefile
@@ -0,0 +1,2 @@
+obj-y := shutdown.o
+obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
new file mode 100644
index 000000000000..6f53840c6b39
--- /dev/null
+++ b/drivers/base/power/main.c
@@ -0,0 +1,98 @@
+/*
+ * drivers/base/power/main.c - Where the driver meets power management.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ *
+ * The driver model core calls device_pm_add() when a device is registered.
+ * This will intialize the embedded device_pm_info object in the device
+ * and add it to the list of power-controlled devices. sysfs entries for
+ * controlling device power management will also be added.
+ *
+ * A different set of lists than the global subsystem list are used to
+ * keep track of power info because we use different lists to hold
+ * devices based on what stage of the power management process they
+ * are in. The power domain dependencies may also differ from the
+ * ancestral dependencies that the subsystem list maintains.
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#include "power.h"
+
+LIST_HEAD(dpm_active);
+LIST_HEAD(dpm_suspended);
+LIST_HEAD(dpm_off);
+LIST_HEAD(dpm_off_irq);
+
+DECLARE_MUTEX(dpm_sem);
+
+/*
+ * PM Reference Counting.
+ */
+
+static inline void device_pm_hold(struct device * dev)
+{
+ atomic_inc(&dev->power.pm_users);
+}
+
+static inline void device_pm_release(struct device * dev)
+{
+ atomic_inc(&dev->power.pm_users);
+}
+
+
+/**
+ * device_pm_set_parent - Specify power dependency.
+ * @dev: Device who needs power.
+ * @parent: Device that supplies power.
+ *
+ * This function is used to manually describe a power-dependency
+ * relationship. It may be used to specify a transversal relationship
+ * (where the power supplier is not the physical (or electrical)
+ * ancestor of a specific device.
+ * The effect of this is that the supplier will not be powered down
+ * before the power dependent.
+ */
+
+void device_pm_set_parent(struct device * dev, struct device * parent)
+{
+ struct device * old_parent = dev->power.pm_parent;
+ if (old_parent)
+ device_pm_release(old_parent);
+ dev->power.pm_parent = parent;
+ if (parent)
+ device_pm_hold(parent);
+}
+EXPORT_SYMBOL(device_pm_set_parent);
+
+int device_pm_add(struct device * dev)
+{
+ int error;
+
+ pr_debug("PM: Adding info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
+ down(&dpm_sem);
+ list_add_tail(&dev->power.entry,&dpm_active);
+ device_pm_set_parent(dev,dev->parent);
+ if ((error = dpm_sysfs_add(dev)))
+ list_del(&dev->power.entry);
+ up(&dpm_sem);
+ return error;
+}
+
+void device_pm_remove(struct device * dev)
+{
+ pr_debug("PM: Removing info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
+ down(&dpm_sem);
+ dpm_sysfs_remove(dev);
+ list_del(&dev->power.entry);
+ up(&dpm_sem);
+}
+
+
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
new file mode 100644
index 000000000000..8130b04ffe5f
--- /dev/null
+++ b/drivers/base/power/power.h
@@ -0,0 +1,104 @@
+
+
+enum {
+ DEVICE_PM_ON,
+ DEVICE_PM1,
+ DEVICE_PM2,
+ DEVICE_PM3,
+ DEVICE_PM_OFF,
+};
+
+/*
+ * shutdown.c
+ */
+
+extern int device_detach_shutdown(struct device *);
+extern void device_shutdown(void);
+
+
+#ifdef CONFIG_PM
+
+/*
+ * main.c
+ */
+
+/*
+ * Used to synchronize global power management operations.
+ */
+extern struct semaphore dpm_sem;
+
+/*
+ * The PM lists.
+ */
+extern struct list_head dpm_active;
+extern struct list_head dpm_suspended;
+extern struct list_head dpm_off;
+extern struct list_head dpm_off_irq;
+
+
+static inline struct dev_pm_info * to_pm_info(struct list_head * entry)
+{
+ return container_of(entry,struct dev_pm_info,entry);
+}
+
+static inline struct device * to_device(struct list_head * entry)
+{
+ return container_of(to_pm_info(entry),struct device,power);
+}
+
+extern int device_pm_add(struct device *);
+extern void device_pm_remove(struct device *);
+
+/*
+ * sysfs.c
+ */
+
+extern int dpm_sysfs_add(struct device *);
+extern void dpm_sysfs_remove(struct device *);
+
+/*
+ * resume.c
+ */
+extern int dpm_resume(void);
+extern void dpm_power_up(void);
+extern void dpm_power_up_irq(void);
+extern void power_up_device(struct device *);
+extern int resume_device(struct device *);
+
+/*
+ * suspend.c
+ */
+extern int suspend_device(struct device *, u32);
+extern int power_down_device(struct device *, u32);
+
+
+/*
+ * runtime.c
+ */
+
+extern int dpm_runtime_suspend(struct device *, u32);
+extern void dpm_runtime_resume(struct device *);
+
+#else /* CONFIG_PM */
+
+
+static inline int device_pm_add(struct device * dev)
+{
+ return 0;
+}
+static inline void device_pm_remove(struct device * dev)
+{
+
+}
+
+static inline int dpm_runtime_suspend(struct device * dev, u32 state)
+{
+ return 0;
+}
+
+static inline void dpm_runtime_resume(struct device * dev)
+{
+
+}
+
+#endif
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
new file mode 100644
index 000000000000..544104c6bbd1
--- /dev/null
+++ b/drivers/base/power/resume.c
@@ -0,0 +1,153 @@
+/*
+ * resume.c - Functions for waking devices up.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+extern int sysdev_resume(void);
+extern int sysdev_restore(void);
+
+
+/**
+ * resume_device - Restore state for one device.
+ * @dev: Device.
+ *
+ */
+
+int resume_device(struct device * dev)
+{
+ struct device_driver * drv = dev->driver;
+
+ if (drv && drv->resume)
+ return drv->resume(dev,RESUME_RESTORE_STATE);
+ return 0;
+}
+
+/**
+ * dpm_resume - Restore all device state.
+ *
+ * Walk the dpm_suspended list and restore each device. As they are
+ * resumed, move the devices to the dpm_active list.
+ */
+
+int dpm_resume(void)
+{
+ while(!list_empty(&dpm_suspended)) {
+ struct list_head * entry = dpm_suspended.next;
+ struct device * dev = to_device(entry);
+ list_del_init(entry);
+ resume_device(dev);
+ list_add_tail(entry,&dpm_active);
+ }
+ return 0;
+}
+
+
+/**
+ * device_pm_resume - Restore state of each device in system.
+ *
+ * Restore system device state, then common device state. Finally,
+ * release dpm_sem, as we're done with device PM.
+ */
+
+void device_pm_resume(void)
+{
+ sysdev_restore();
+ dpm_resume();
+ up(&dpm_sem);
+}
+
+
+/**
+ * power_up_device - Power one device on.
+ * @dev: Device.
+ */
+
+void power_up_device(struct device * dev)
+{
+ struct device_driver * drv = dev->driver;
+ if (drv && drv->resume)
+ drv->resume(dev,RESUME_POWER_ON);
+}
+
+
+/**
+ * device_power_up_irq - Power on some devices.
+ *
+ * Walk the dpm_off_irq list and power each device up. This
+ * is used for devices that required they be powered down with
+ * interrupts disabled. As devices are powered on, they are moved to
+ * the dpm_suspended list.
+ *
+ * Interrupts must be disabled when calling this.
+ */
+
+void dpm_power_up_irq(void)
+{
+ while(!list_empty(&dpm_off_irq)) {
+ struct list_head * entry = dpm_off_irq.next;
+ list_del_init(entry);
+ power_up_device(to_device(entry));
+ list_add_tail(entry,&dpm_suspended);
+ }
+}
+
+
+/**
+ * dpm_power_up - Power on most devices.
+ *
+ * Walk the dpm_off list and power each device up. This is used
+ * to power on devices that were able to power down with interrupts
+ * enabled.
+ */
+
+void dpm_power_up(void)
+{
+ while (!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.next;
+ list_del_init(entry);
+ power_up_device(to_device(entry));
+ list_add_tail(entry,&dpm_suspended);
+ }
+}
+
+
+/**
+ * device_pm_power_up - Turn on all devices.
+ *
+ * First, power on system devices, which must happen with interrupts
+ * disbled. Then, power on devices that also require interrupts disabled.
+ * Turn interrupts back on, and finally power up the rest of the normal
+ * devices.
+ */
+
+void device_pm_power_up(void)
+{
+ sysdev_resume();
+ dpm_power_up_irq();
+ local_irq_enable();
+ dpm_power_up();
+}
+
+/**
+ * device_resume - resume all the devices in the system
+ * @level: stage of resume process we're at
+ *
+ * This function is deprecated, and should be replaced with appropriate
+ * calls to device_pm_power_up() and device_pm_resume() above.
+ */
+
+void device_resume(u32 level)
+{
+
+ printk("%s is deprecated. Called from:\n",__FUNCTION__);
+ dump_stack();
+}
+
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
new file mode 100644
index 000000000000..4a4ac9f7764d
--- /dev/null
+++ b/drivers/base/power/runtime.c
@@ -0,0 +1,89 @@
+/*
+ * drivers/base/power/runtime.c - Handling dynamic device power management.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+
+static void runtime_resume(struct device * dev)
+{
+ if (!dev->power.power_state)
+ return;
+
+ power_up_device(dev);
+ resume_device(dev);
+}
+
+
+/**
+ * dpm_runtime_resume - Power one device back on.
+ * @dev: Device.
+ *
+ * Bring one device back to the on state by first powering it
+ * on, then restoring state. We only operate on devices that aren't
+ * already on.
+ * FIXME: We need to handle devices that are in an unknown state.
+ */
+
+void dpm_runtime_resume(struct device * dev)
+{
+ down(&dpm_sem);
+ runtime_resume(dev);
+ up(&dpm_sem);
+}
+
+
+/**
+ * dpm_runtime_suspend - Put one device in low-power state.
+ * @dev: Device.
+ * @state: State to enter.
+ */
+
+int dpm_runtime_suspend(struct device * dev, u32 state)
+{
+ int error = 0;
+
+ down(&dpm_sem);
+ if (dev->power.power_state == state)
+ goto Done;
+
+ if (dev->power.power_state)
+ dpm_runtime_resume(dev);
+
+ error = suspend_device(dev,state);
+ if (!error) {
+ error = power_down_device(dev,state);
+ if (error)
+ goto ErrResume;
+ dev->power.power_state = state;
+ }
+ Done:
+ up(&dpm_sem);
+ return error;
+ ErrResume:
+ resume_device(dev);
+ goto Done;
+}
+
+
+/**
+ * dpm_set_power_state - Update power_state field.
+ * @dev: Device.
+ * @state: Power state device is in.
+ *
+ * This is an update mechanism for drivers to notify the core
+ * what power state a device is in. Device probing code may not
+ * always be able to tell, but we need accurate information to
+ * work reliably.
+ */
+void dpm_set_power_state(struct device * dev, u32 state)
+{
+ down(&dpm_sem);
+ dev->power.power_state = state;
+ up(&dpm_sem);
+}
diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c
new file mode 100644
index 000000000000..a48b97681329
--- /dev/null
+++ b/drivers/base/power/shutdown.c
@@ -0,0 +1,68 @@
+/*
+ * shutdown.c - power management functions for the device tree.
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * 2002-3 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/device.h>
+#include <asm/semaphore.h>
+
+#include "power.h"
+
+#define to_dev(node) container_of(node,struct device,kobj.entry)
+
+extern struct subsystem devices_subsys;
+
+
+int device_detach_shutdown(struct device * dev)
+{
+ if (!dev->detach_state)
+ return 0;
+
+ if (dev->detach_state == DEVICE_PM_OFF) {
+ if (dev->driver && dev->driver->shutdown)
+ dev->driver->shutdown(dev);
+ return 0;
+ }
+ return dpm_runtime_suspend(dev,dev->detach_state);
+}
+
+
+/**
+ * We handle system devices differently - we suspend and shut them
+ * down first and resume them first. That way, we do anything stupid like
+ * shutting down the interrupt controller before any devices..
+ *
+ * Note that there are not different stages for power management calls -
+ * they only get one called once when interrupts are disabled.
+ */
+
+extern int sysdev_shutdown(void);
+
+/**
+ * device_shutdown - call ->remove() on each device to shutdown.
+ */
+void device_shutdown(void)
+{
+ struct device * dev;
+
+ down_write(&devices_subsys.rwsem);
+ list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
+ pr_debug("shutting down %s: ",dev->name);
+ if (dev->driver && dev->driver->shutdown) {
+ pr_debug("Ok\n");
+ dev->driver->shutdown(dev);
+ } else
+ pr_debug("Ignored.\n");
+ }
+ up_write(&devices_subsys.rwsem);
+
+ sysdev_shutdown();
+}
+
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
new file mode 100644
index 000000000000..0747e409d0ec
--- /dev/null
+++ b/drivers/base/power/suspend.c
@@ -0,0 +1,235 @@
+/*
+ * suspend.c - Functions for putting devices to sleep.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+extern int sysdev_save(u32 state);
+extern int sysdev_suspend(u32 state);
+
+/*
+ * The entries in the dpm_active list are in a depth first order, simply
+ * because children are guaranteed to be discovered after parents, and
+ * are inserted at the back of the list on discovery.
+ *
+ * All list on the suspend path are done in reverse order, so we operate
+ * on the leaves of the device tree (or forests, depending on how you want
+ * to look at it ;) first. As nodes are removed from the back of the list,
+ * they are inserted into the front of their destintation lists.
+ *
+ * Things are the reverse on the resume path - iterations are done in
+ * forward order, and nodes are inserted at the back of their destination
+ * lists. This way, the ancestors will be accessed before their descendents.
+ */
+
+
+/**
+ * suspend_device - Save state of one device.
+ * @dev: Device.
+ * @state: Power state device is entering.
+ */
+
+int suspend_device(struct device * dev, u32 state)
+{
+ struct device_driver * drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->suspend)
+ error = drv->suspend(dev,state,SUSPEND_SAVE_STATE);
+
+ if (!error) {
+ list_del(&dev->power.entry);
+ list_add(&dev->power.entry,&dpm_suspended);
+ }
+ return error;
+}
+
+
+/**
+ * device_pm_suspend - Save state and stop all devices in system.
+ * @state: Power state to put each device in.
+ *
+ * Walk the dpm_active list, call ->suspend() for each device, and move
+ * it to dpm_suspended. If we hit a failure with any of the devices, call
+ * dpm_resume() above to bring the suspended devices back to life.
+ *
+ * Have system devices save state last.
+ *
+ * Note this function leaves dpm_sem held to
+ * a) block other devices from registering.
+ * b) prevent other PM operations from happening after we've begun.
+ * c) make sure we're exclusive when we disable interrupts.
+ *
+ * device_pm_resume() will release dpm_sem after restoring state to
+ * all devices (as will this on error). You must call it once you've
+ * called device_pm_suspend().
+ */
+
+int device_pm_suspend(u32 state)
+{
+ int error = 0;
+
+ down(&dpm_sem);
+ while(!list_empty(&dpm_active)) {
+ struct list_head * entry = dpm_active.prev;
+ struct device * dev = to_device(entry);
+ if ((error = suspend_device(dev,state)))
+ goto Error;
+ }
+
+ if ((error = sysdev_save(state)))
+ goto Error;
+ Done:
+ return error;
+ Error:
+ dpm_resume();
+ up(&dpm_sem);
+ goto Done;
+}
+
+
+/**
+ * power_down_device - Put one device in low power state.
+ * @dev: Device.
+ * @state: Power state to enter.
+ */
+
+int power_down_device(struct device * dev, u32 state)
+{
+ struct device_driver * drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->suspend)
+ error = drv->suspend(dev,state,SUSPEND_POWER_DOWN);
+ if (!error) {
+ list_del(&dev->power.entry);
+ list_add(&dev->power.entry,&dpm_off);
+ }
+ return error;
+}
+
+
+/**
+ * dpm_power_down - Put all devices in low power state.
+ * @state: Power state to enter.
+ *
+ * Walk the dpm_suspended list (with interrupts enabled) and try
+ * to power down each each. If any fail with -EAGAIN, they require
+ * the call to be done with interrupts disabled. So, we move them to
+ * the dpm_off_irq list.
+ *
+ * If the call succeeds, we move each device to the dpm_off list.
+ */
+
+static int dpm_power_down(u32 state)
+{
+ while(!list_empty(&dpm_suspended)) {
+ struct list_head * entry = dpm_suspended.prev;
+ int error;
+ error = power_down_device(to_device(entry),state);
+ if (error) {
+ if (error == -EAGAIN) {
+ list_del(entry);
+ list_add(entry,&dpm_off_irq);
+ continue;
+ }
+ return error;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * dpm_power_down_irq - Power down devices without interrupts.
+ * @state: State to enter.
+ *
+ * Walk the dpm_off_irq list (built by dpm_power_down) and power
+ * down each device that requires the call to be made with interrupts
+ * disabled.
+ */
+
+static int dpm_power_down_irq(u32 state)
+{
+ struct device * dev;
+ int error = 0;
+
+ list_for_each_entry_reverse(dev,&dpm_off_irq,power.entry) {
+ if ((error = power_down_device(dev,state)))
+ break;
+ }
+ return error;
+}
+
+
+/**
+ * device_pm_power_down - Put all devices in low power state.
+ * @state: Power state to enter.
+ *
+ * Walk the dpm_suspended list, calling ->power_down() for each device.
+ * Check the return value for each. If it returns 0, then we move the
+ * the device to the dpm_off list. If it returns -EAGAIN, we move it to
+ * the dpm_off_irq list. If we get a different error, try and back out.
+ *
+ * dpm_irq_off is for devices that require interrupts to be disabled to
+ * either to power down the device or power it back on.
+ *
+ * When we're done, we disable interrrupts (!!) and walk the dpm_off_irq
+ * list to shut down the devices that need interrupts disabled.
+ *
+ * This function leaves interrupts disabled on exit, since powering down
+ * devices should be the very last thing before the system is put into a
+ * low-power state.
+ *
+ * device_pm_power_on() should be called to re-enable interrupts and power
+ * the devices back on.
+ */
+
+int device_pm_power_down(u32 state)
+{
+ int error = 0;
+
+ if ((error = dpm_power_down(state)))
+ goto ErrorIRQOn;
+ local_irq_disable();
+ if ((error = dpm_power_down_irq(state)))
+ goto ErrorIRQOff;
+
+ sysdev_suspend(state);
+ Done:
+ return error;
+
+ ErrorIRQOff:
+ dpm_power_up_irq();
+ local_irq_enable();
+ ErrorIRQOn:
+ dpm_power_up();
+ goto Done;
+}
+
+
+/**
+ * device_suspend - suspend all devices on the device ree
+ * @state: state we're entering
+ * @level: Stage of suspend sequence we're in.
+ *
+ *
+ * This function is deprecated. Calls should be replaced with
+ * appropriate calls to device_pm_suspend() and device_pm_power_down().
+ */
+
+int device_suspend(u32 state, u32 level)
+{
+
+ printk("%s Called from:\n",__FUNCTION__);
+ dump_stack();
+ return -EFAULT;
+}
+
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
new file mode 100644
index 000000000000..0fddee58893f
--- /dev/null
+++ b/drivers/base/power/sysfs.c
@@ -0,0 +1,68 @@
+/*
+ * drivers/base/power/sysfs.c - sysfs entries for device PM
+ */
+
+#include <linux/device.h>
+#include "power.h"
+
+
+/**
+ * state - Control current power state of device
+ *
+ * show() returns the current power state of the device. '0' indicates
+ * the device is on. Other values (1-3) indicate the device is in a low
+ * power state.
+ *
+ * store() sets the current power state, which is an integer value
+ * between 0-3. If the device is on ('0'), and the value written is
+ * greater than 0, then the device is placed directly into the low-power
+ * state (via its driver's ->suspend() method).
+ * If the device is currently in a low-power state, and the value is 0,
+ * the device is powered back on (via the ->resume() method).
+ * If the device is in a low-power state, and a different low-power state
+ * is requested, the device is first resumed, then suspended into the new
+ * low-power state.
+ */
+
+static ssize_t state_show(struct device * dev, char * buf)
+{
+ return sprintf(buf,"%u\n",dev->power.power_state);
+}
+
+static ssize_t state_store(struct device * dev, const char * buf, size_t n)
+{
+ u32 state;
+ char * rest;
+ int error = 0;
+
+ state = simple_strtoul(buf,&rest,10);
+ if (rest)
+ return -EINVAL;
+ if (state)
+ error = dpm_runtime_suspend(dev,state);
+ else
+ dpm_runtime_resume(dev);
+ return error ? error : n;
+}
+
+static DEVICE_ATTR(state,0644,state_show,state_store);
+
+
+static struct attribute * power_attrs[] = {
+ &dev_attr_state.attr,
+ NULL,
+};
+static struct attribute_group pm_attr_group = {
+ .name = "power",
+ .attrs = power_attrs,
+};
+
+int dpm_sysfs_add(struct device * dev)
+{
+ return sysfs_create_group(&dev->kobj,&pm_attr_group);
+}
+
+void dpm_sysfs_remove(struct device * dev)
+{
+ sysfs_remove_group(&dev->kobj,&pm_attr_group);
+}
diff --git a/fs/sysfs/Makefile b/fs/sysfs/Makefile
index 32fa9dfbfb0b..7a1ceb946b80 100644
--- a/fs/sysfs/Makefile
+++ b/fs/sysfs/Makefile
@@ -2,4 +2,5 @@
# Makefile for the sysfs virtual filesystem
#
-obj-y := inode.o file.o dir.o symlink.o mount.o bin.o
+obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \
+ group.o
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 0b1588ab9259..8288f9f1b6a2 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -20,6 +20,38 @@ static int init_dir(struct inode * inode)
return 0;
}
+
+static struct dentry *
+create_dir(struct kobject * k, struct dentry * p, const char * n)
+{
+ struct dentry * dentry;
+
+ down(&p->d_inode->i_sem);
+ dentry = sysfs_get_dentry(p,n);
+ if (!IS_ERR(dentry)) {
+ int error;
+
+ dentry->d_fsdata = (void *)k;
+ error = sysfs_create(dentry,
+ (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
+ init_dir);
+ if (!error)
+ p->d_inode->i_nlink++;
+ else {
+ dput(dentry);
+ dentry = ERR_PTR(error);
+ }
+ }
+ up(&p->d_inode->i_sem);
+ return dentry;
+}
+
+
+struct dentry * sysfs_create_subdir(struct kobject * k, const char * n)
+{
+ return create_dir(k,k->dentry,n);
+}
+
/**
* sysfs_create_dir - create a directory for an object.
* @parent: parent parent object.
@@ -42,23 +74,34 @@ int sysfs_create_dir(struct kobject * kobj)
else
return -EFAULT;
- down(&parent->d_inode->i_sem);
- dentry = sysfs_get_dentry(parent,kobj->name);
- if (!IS_ERR(dentry)) {
- dentry->d_fsdata = (void *)kobj;
+ dentry = create_dir(kobj,parent,kobj->name);
+ if (!IS_ERR(dentry))
kobj->dentry = dentry;
- error = sysfs_create(dentry,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
- init_dir);
- if (!error)
- parent->d_inode->i_nlink++;
- } else
+ else
error = PTR_ERR(dentry);
- up(&parent->d_inode->i_sem);
-
return error;
}
+static void remove_dir(struct dentry * d)
+{
+ struct dentry * parent = dget(d->d_parent);
+ down(&parent->d_inode->i_sem);
+ d_delete(d);
+ simple_rmdir(parent->d_inode,d);
+
+ pr_debug(" o %s removing done (%d)\n",d->d_name.name,
+ atomic_read(&d->d_count));
+
+ up(&parent->d_inode->i_sem);
+ dput(parent);
+}
+
+void sysfs_remove_subdir(struct dentry * d)
+{
+ remove_dir(d);
+}
+
/**
* sysfs_remove_dir - remove an object's directory.
@@ -73,14 +116,11 @@ void sysfs_remove_dir(struct kobject * kobj)
{
struct list_head * node;
struct dentry * dentry = dget(kobj->dentry);
- struct dentry * parent;
if (!dentry)
return;
pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
- parent = dget(dentry->d_parent);
- down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem);
spin_lock(&dcache_lock);
@@ -107,21 +147,15 @@ void sysfs_remove_dir(struct kobject * kobj)
}
spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem);
- d_delete(dentry);
- simple_rmdir(parent->d_inode,dentry);
-
- pr_debug(" o %s removing done (%d)\n",dentry->d_name.name,
- atomic_read(&dentry->d_count));
+ remove_dir(dentry);
/**
* Drop reference from dget() on entrance.
*/
dput(dentry);
- up(&parent->d_inode->i_sem);
- dput(parent);
}
-void sysfs_rename_dir(struct kobject * kobj, char *new_name)
+void sysfs_rename_dir(struct kobject * kobj, const char *new_name)
{
struct dentry * new_dentry, * parent;
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 2cedefe8c4a0..11d70d8ca85b 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -344,36 +344,39 @@ static struct file_operations sysfs_file_operations = {
.release = sysfs_release,
};
-/**
- * sysfs_create_file - create an attribute file for an object.
- * @kobj: object we're creating for.
- * @attr: atrribute descriptor.
- */
-int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
+int sysfs_add_file(struct dentry * dir, const struct attribute * attr)
{
struct dentry * dentry;
- struct dentry * parent;
- int error = 0;
-
- if (!kobj || !attr)
- return -EINVAL;
-
- parent = kobj->dentry;
+ int error;
- down(&parent->d_inode->i_sem);
- dentry = sysfs_get_dentry(parent,attr->name);
+ down(&dir->d_inode->i_sem);
+ dentry = sysfs_get_dentry(dir,attr->name);
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)attr;
error = sysfs_create(dentry,(attr->mode & S_IALLUGO) | S_IFREG,init_file);
} else
error = PTR_ERR(dentry);
- up(&parent->d_inode->i_sem);
+ up(&dir->d_inode->i_sem);
return error;
}
/**
+ * sysfs_create_file - create an attribute file for an object.
+ * @kobj: object we're creating for.
+ * @attr: atrribute descriptor.
+ */
+
+int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
+{
+ if (kobj && attr)
+ return sysfs_add_file(kobj->dentry,attr);
+ return -EINVAL;
+}
+
+
+/**
* sysfs_update_file - update the modified timestamp on an object attribute.
* @kobj: object we're acting for.
* @attr: attribute descriptor.
@@ -381,7 +384,7 @@ int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
* Also call dnotify for the dentry, which lots of userspace programs
* use.
*/
-int sysfs_update_file(struct kobject * kobj, struct attribute * attr)
+int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
{
struct dentry * dir = kobj->dentry;
struct dentry * victim;
@@ -422,7 +425,7 @@ int sysfs_update_file(struct kobject * kobj, struct attribute * attr)
* Hash the attribute name and kill the victim.
*/
-void sysfs_remove_file(struct kobject * kobj, struct attribute * attr)
+void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
{
sysfs_hash_and_remove(kobj->dentry,attr->name);
}
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
new file mode 100644
index 000000000000..cfd113284e9f
--- /dev/null
+++ b/fs/sysfs/group.c
@@ -0,0 +1,81 @@
+/*
+ * fs/sysfs/group.c - Operations for adding/removing multiple files at once.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released undert the GPL v2.
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/err.h>
+#include "sysfs.h"
+
+
+static void remove_files(struct dentry * dir,
+ const struct attribute_group * grp)
+{
+ struct attribute *const* attr;
+
+ for (attr = grp->attrs; *attr; attr++)
+ sysfs_hash_and_remove(dir,(*attr)->name);
+}
+
+static int create_files(struct dentry * dir,
+ const struct attribute_group * grp)
+{
+ struct attribute *const* attr;
+ int error = 0;
+
+ for (attr = grp->attrs; *attr && !error; attr++) {
+ error = sysfs_add_file(dir,*attr);
+ }
+ if (error)
+ remove_files(dir,grp);
+ return error;
+}
+
+
+int sysfs_create_group(struct kobject * kobj,
+ const struct attribute_group * grp)
+{
+ struct dentry * dir;
+ int error;
+
+ if (grp->name) {
+ dir = sysfs_create_subdir(kobj,grp->name);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+ } else
+ dir = kobj->dentry;
+ dir = dget(dir);
+ if ((error = create_files(dir,grp))) {
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+ dput(dir);
+ }
+ return error;
+}
+
+void sysfs_remove_group(struct kobject * kobj,
+ const struct attribute_group * grp)
+{
+ struct dentry * dir;
+
+ if (grp->name)
+ dir = sysfs_get_dentry(kobj->dentry,grp->name);
+ else
+ dir = kobj->dentry;
+
+ remove_files(dir,grp);
+ dput(dir);
+ if (grp->name)
+ sysfs_remove_subdir(dir);
+}
+
+
+EXPORT_SYMBOL(sysfs_create_group);
+EXPORT_SYMBOL(sysfs_remove_group);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 7abcccdf15a3..cc6f05798564 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -4,7 +4,10 @@ extern struct vfsmount * sysfs_mount;
extern struct inode * sysfs_new_inode(mode_t mode);
extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *));
-extern struct dentry * sysfs_get_dentry(struct dentry *, char *);
+extern struct dentry * sysfs_get_dentry(struct dentry *, const char *);
+extern int sysfs_add_file(struct dentry * dir, const struct attribute * attr);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
+extern struct dentry * sysfs_create_subdir(struct kobject *, const char *);
+extern void sysfs_remove_subdir(struct dentry *);
diff --git a/include/linux/device.h b/include/linux/device.h
index 2795b85ac6f1..59808676d3ef 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/module.h>
+#include <linux/pm.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -41,13 +42,6 @@ enum {
RESUME_ENABLE,
};
-enum device_state {
- DEVICE_UNINITIALIZED = 0,
- DEVICE_INITIALIZED = 1,
- DEVICE_REGISTERED = 2,
- DEVICE_GONE = 3,
-};
-
struct device;
struct device_driver;
struct class;
@@ -64,8 +58,8 @@ struct bus_type {
struct device * (*add) (struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
-};
+};
extern int bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type * bus);
@@ -182,8 +176,8 @@ struct class_attribute class_attr_##_name = { \
.store = _store, \
};
-extern int class_create_file(struct class *, struct class_attribute *);
-extern void class_remove_file(struct class *, struct class_attribute *);
+extern int class_create_file(struct class *, const struct class_attribute *);
+extern void class_remove_file(struct class *, const struct class_attribute *);
struct class_device {
@@ -234,8 +228,10 @@ struct class_device_attribute class_device_attr_##_name = { \
.store = _store, \
};
-extern int class_device_create_file(struct class_device *, struct class_device_attribute *);
-extern void class_device_remove_file(struct class_device *, struct class_device_attribute *);
+extern int class_device_create_file(struct class_device *,
+ const struct class_device_attribute *);
+extern void class_device_remove_file(struct class_device *,
+ const struct class_device_attribute *);
struct class_interface {
@@ -267,13 +263,16 @@ struct device {
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
-
+ struct dev_pm_info power;
u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0
being fully functional, and D3
being off. */
unsigned char *saved_state; /* saved device state */
+ u32 detach_state; /* State to enter when device is
+ detached from its driver. */
+
u64 *dma_mask; /* dma mask (if dma'able device) */
void (*release)(struct device * dev);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index ab9305b0953a..6942ad5d5cb3 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -25,6 +25,7 @@
#include <linux/config.h>
#include <linux/list.h>
+#include <asm/atomic.h>
/*
* Power management requests
@@ -118,29 +119,29 @@ extern int pm_active;
/*
* Register a device with power management
*/
-struct pm_dev *pm_register(pm_dev_t type,
- unsigned long id,
- pm_callback callback);
+struct pm_dev __deprecated *pm_register(pm_dev_t type,
+ unsigned long id,
+ pm_callback callback);
/*
* Unregister a device with power management
*/
-void pm_unregister(struct pm_dev *dev);
+void __deprecated pm_unregister(struct pm_dev *dev);
/*
* Unregister all devices with matching callback
*/
-void pm_unregister_all(pm_callback callback);
+void __deprecated pm_unregister_all(pm_callback callback);
/*
* Send a request to a single device
*/
-int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);
+int __deprecated pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);
/*
* Send a request to all devices
*/
-int pm_send_all(pm_request_t rqst, void *data);
+int __deprecated pm_send_all(pm_request_t rqst, void *data);
/*
* Find a device
@@ -188,6 +189,26 @@ static inline void pm_dev_idle(struct pm_dev *dev) {}
extern void (*pm_idle)(void);
extern void (*pm_power_off)(void);
+struct device;
+
+struct dev_pm_info {
+#ifdef CONFIG_PM
+ u32 power_state;
+ u8 * saved_state;
+ atomic_t pm_users;
+ struct device * pm_parent;
+ struct list_head entry;
+#endif
+};
+
+extern void device_pm_set_parent(struct device * dev, struct device * parent);
+
+extern int device_pm_suspend(u32 state);
+extern int device_pm_power_down(u32 state);
+extern void device_pm_power_up(void);
+extern void device_pm_resume(void);
+
+
#endif /* __KERNEL__ */
#endif /* _LINUX_PM_H */
diff --git a/include/linux/reboot.h b/include/linux/reboot.h
index 68b0cb81d986..b73b84d514d6 100644
--- a/include/linux/reboot.h
+++ b/include/linux/reboot.h
@@ -21,7 +21,7 @@
* CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
* POWER_OFF Stop OS and remove all power from system, if possible.
* RESTART2 Restart system using given command string.
- * SW_SUSPEND Suspend system using Software Suspend if compiled in
+ * SW_SUSPEND Suspend system using software suspend if compiled in.
*/
#define LINUX_REBOOT_CMD_RESTART 0x01234567
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index af0143331c94..28788d8a65ff 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -11,9 +11,6 @@
extern unsigned char software_suspend_enabled;
-#define NORESUME 1
-#define RESUME_SPECIFIED 2
-
#ifdef CONFIG_SOFTWARE_SUSPEND
/* page backup entry */
typedef struct pbe {
@@ -50,8 +47,7 @@ extern int shrink_mem(void);
extern void drain_local_pages(void);
/* kernel/suspend.c */
-extern void software_suspend(void);
-extern void software_resume(void);
+extern int software_suspend(void);
extern int register_suspend_notifier(struct notifier_block *);
extern int unregister_suspend_notifier(struct notifier_block *);
@@ -72,10 +68,10 @@ extern void do_suspend_lowlevel(int resume);
extern void do_suspend_lowlevel_s4bios(int resume);
#else /* CONFIG_SOFTWARE_SUSPEND */
-static inline void software_suspend(void)
+static inline int software_suspend(void)
{
+ return -EPERM;
}
-#define software_resume() do { } while(0)
#define register_suspend_notifier(a) do { } while(0)
#define unregister_suspend_notifier(a) do { } while(0)
#endif /* CONFIG_SOFTWARE_SUSPEND */
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 441c0d91f583..b34de79dcf3b 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -40,16 +40,16 @@ extern void
sysfs_remove_dir(struct kobject *);
extern void
-sysfs_rename_dir(struct kobject *, char *new_name);
+sysfs_rename_dir(struct kobject *, const char *new_name);
extern int
-sysfs_create_file(struct kobject *, struct attribute *);
+sysfs_create_file(struct kobject *, const struct attribute *);
extern int
-sysfs_update_file(struct kobject *, struct attribute *);
+sysfs_update_file(struct kobject *, const struct attribute *);
extern void
-sysfs_remove_file(struct kobject *, struct attribute *);
+sysfs_remove_file(struct kobject *, const struct attribute *);
extern int
sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
@@ -57,4 +57,13 @@ sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
extern void
sysfs_remove_link(struct kobject *, char * name);
+
+struct attribute_group {
+ char * name;
+ struct attribute ** attrs;
+};
+
+int sysfs_create_group(struct kobject *, const struct attribute_group *);
+void sysfs_remove_group(struct kobject *, const struct attribute_group *);
+
#endif /* _SYSFS_H_ */
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 9d7e30252c46..7ede163e26ba 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -373,10 +373,6 @@ void __init prepare_namespace(void)
is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
- /* This has to be before mounting root, because even readonly mount of reiserfs would replay
- log corrupting stuff */
- software_resume();
-
if (initrd_load())
goto out;
diff --git a/kernel/Makefile b/kernel/Makefile
index 495214d708bd..72eb0287e843 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += cpu.o
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += ksyms.o module.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
-obj-$(CONFIG_PM) += pm.o power/
+obj-$(CONFIG_PM) += power/
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_COMPAT) += compat.o
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 2a1e6368f7e5..3a11202cc91e 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -1,2 +1,4 @@
-obj-y := process.o console.o
+obj-y := process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
+
+obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/console.c b/kernel/power/console.c
index bfaf3f14c114..c05d0e43675f 100644
--- a/kernel/power/console.c
+++ b/kernel/power/console.c
@@ -1,3 +1,9 @@
+/*
+ * drivers/power/process.c - Functions for saving/restoring console.
+ *
+ * Originally from swsusp.
+ */
+
#include <linux/vt_kern.h>
#include <linux/kbd_kern.h>
#include "power.h"
@@ -14,13 +20,13 @@ int pm_prepare_console(void)
#ifdef SUSPEND_CONSOLE
orig_fgconsole = fg_console;
- if(vc_allocate(SUSPEND_CONSOLE))
+ if (vc_allocate(SUSPEND_CONSOLE))
/* we can't have a free VC for now. Too bad,
* we don't want to mess the screen for now. */
return 1;
- set_console (SUSPEND_CONSOLE);
- if(vt_waitactive(SUSPEND_CONSOLE)) {
+ set_console(SUSPEND_CONSOLE);
+ if (vt_waitactive(SUSPEND_CONSOLE)) {
pr_debug("Suspend: Can't switch VCs.");
return 1;
}
@@ -34,7 +40,7 @@ void pm_restore_console(void)
{
console_loglevel = orig_loglevel;
#ifdef SUSPEND_CONSOLE
- set_console (orig_fgconsole);
+ set_console(orig_fgconsole);
#endif
return;
}
diff --git a/kernel/pm.c b/kernel/power/pm.c
index afffd046c2f6..09d55363cefc 100644
--- a/kernel/pm.c
+++ b/kernel/power/pm.c
@@ -24,7 +24,6 @@
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/interrupt.h>
-#include <linux/sysrq.h>
int pm_active;
@@ -295,39 +294,3 @@ EXPORT_SYMBOL(pm_find);
EXPORT_SYMBOL(pm_active);
-#ifdef CONFIG_MAGIC_SYSRQ
-
-/**
- * handle_poweroff - sysrq callback for power down
- * @key: key pressed (unused)
- * @pt_regs: register state (unused)
- * @kbd: keyboard state (unused)
- * @tty: tty involved (unused)
- *
- * When the user hits Sys-Rq o to power down the machine this is the
- * callback we use.
- */
-
-static void handle_poweroff (int key, struct pt_regs *pt_regs,
- struct tty_struct *tty)
-{
- if (pm_power_off)
- pm_power_off();
-}
-
-static struct sysrq_key_op sysrq_poweroff_op = {
- .handler = handle_poweroff,
- .help_msg = "powerOff",
- .action_msg = "Power Off\n"
-};
-
-#endif /* CONFIG_MAGIC_SYSRQ */
-
-
-static int pm_init(void)
-{
- register_sysrq_key('o', &sysrq_poweroff_op);
- return 0;
-}
-
-subsys_initcall(pm_init);
diff --git a/kernel/power/poweroff.c b/kernel/power/poweroff.c
new file mode 100644
index 000000000000..9abe4ac23f02
--- /dev/null
+++ b/kernel/power/poweroff.c
@@ -0,0 +1,44 @@
+/*
+ * poweroff.c - sysrq handler to gracefully power down machine.
+ *
+ * This file is released under the GPL v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/sysrq.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+
+
+/**
+ * handle_poweroff - sysrq callback for power down
+ * @key: key pressed (unused)
+ * @pt_regs: register state (unused)
+ * @kbd: keyboard state (unused)
+ * @tty: tty involved (unused)
+ *
+ * When the user hits Sys-Rq o to power down the machine this is the
+ * callback we use.
+ */
+
+static void handle_poweroff (int key, struct pt_regs *pt_regs,
+ struct tty_struct *tty)
+{
+ if (pm_power_off)
+ pm_power_off();
+}
+
+static struct sysrq_key_op sysrq_poweroff_op = {
+ .handler = handle_poweroff,
+ .help_msg = "powerOff",
+ .action_msg = "Power Off\n"
+};
+
+
+static int pm_sysrq_init(void)
+{
+ register_sysrq_key('o', &sysrq_poweroff_op);
+ return 0;
+}
+
+subsys_initcall(pm_sysrq_init);
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 671730612e0f..15c1b340c2ed 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -8,6 +8,7 @@
#undef DEBUG
+#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/module.h>
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index b9cd4bd18358..3da9c0142676 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -67,7 +67,7 @@
extern long sys_sync(void);
-unsigned char software_suspend_enabled = 0;
+unsigned char software_suspend_enabled = 1;
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT)
@@ -85,8 +85,7 @@ spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED;
static int pagedir_order_check;
static int nr_copy_pages_check;
-static int resume_status;
-static char resume_file[256] = ""; /* For resume= kernel option */
+static char resume_file[256]; /* For resume= kernel option */
static dev_t resume_device;
/* Local variables that should not be affected by save */
unsigned int nr_copy_pages __nosavedata = 0;
@@ -352,15 +351,10 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p)
int pfn;
struct page *page;
-#ifdef CONFIG_DISCONTIGMEM
- panic("Discontingmem not supported");
-#else
BUG_ON (max_pfn != num_physpages);
-#endif
+
for (pfn = 0; pfn < max_pfn; pfn++) {
page = pfn_to_page(pfn);
- if (PageHighMem(page))
- panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to <pavel@ucw.cz> and try again ;-).");
if (!PageReserved(page)) {
if (PageNosave(page))
@@ -479,19 +473,23 @@ static void drivers_unsuspend(void)
/* Called from process context */
static int drivers_suspend(void)
{
- device_suspend(4, SUSPEND_NOTIFY);
- device_suspend(4, SUSPEND_SAVE_STATE);
- device_suspend(4, SUSPEND_DISABLE);
- if(!pm_suspend_state) {
+ if (device_suspend(4, SUSPEND_NOTIFY))
+ return -EIO;
+ if (device_suspend(4, SUSPEND_SAVE_STATE)) {
+ device_resume(RESUME_RESTORE_STATE);
+ return -EIO;
+ }
+ if (!pm_suspend_state) {
if(pm_send_all(PM_SUSPEND,(void *)3)) {
printk(KERN_WARNING "Problem while sending suspend event\n");
- return(1);
+ return -EIO;
}
pm_suspend_state=1;
} else
printk(KERN_WARNING "PM suspend state already raised\n");
+ device_suspend(4, SUSPEND_DISABLE);
- return(0);
+ return 0;
}
#define RESUME_PHASE1 1 /* Called from interrupts disabled */
@@ -504,7 +502,7 @@ static void drivers_resume(int flags)
device_resume(RESUME_ENABLE);
}
if (flags & RESUME_PHASE2) {
- if(pm_suspend_state) {
+ if (pm_suspend_state) {
if(pm_send_all(PM_RESUME,(void *)0))
printk(KERN_WARNING "Problem while sending resume event\n");
pm_suspend_state=0;
@@ -696,7 +694,7 @@ void do_magic_suspend_2(void)
mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
}
-static void do_software_suspend(void)
+static int do_software_suspend(void)
{
arch_prepare_suspend();
if (pm_prepare_console())
@@ -715,7 +713,7 @@ static void do_software_suspend(void)
blk_run_queues();
/* Save state of all device drivers, and stop them. */
- if(drivers_suspend()==0)
+ if (drivers_suspend()==0)
/* If stopping device drivers worked, we proceed basically into
* suspend_save_image.
*
@@ -731,20 +729,35 @@ static void do_software_suspend(void)
software_suspend_enabled = 1;
MDELAY(1000);
pm_restore_console();
+ return 0;
}
-/*
- * This is main interface to the outside world. It needs to be
- * called from process context.
+
+/**
+ * software_suspend - initiate suspend-to-swap transition.
+ *
+ * This is main interface to the outside world. It needs to be
+ * called from process context.
*/
-void software_suspend(void)
+
+int software_suspend(void)
{
if(!software_suspend_enabled)
- return;
+ return -EINVAL;
+
+ if (num_online_cpus() > 1) {
+ printk(KERN_WARNING "swsusp does not support SMP.\n");
+ return -EPERM;
+ }
+
+#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
+ printk("swsusp is not supported with high- or discontig-mem.\n");
+ return -EPERM;
+#endif
software_suspend_enabled = 0;
might_sleep();
- do_software_suspend();
+ return do_software_suspend();
}
/* More restore stuff */
@@ -890,31 +903,9 @@ static int bdev_read_page(struct block_device *bdev, long pos, void *buf)
return 0;
}
-static int bdev_write_page(struct block_device *bdev, long pos, void *buf)
-{
-#if 0
- struct buffer_head *bh;
- BUG_ON (pos%PAGE_SIZE);
- bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
- if (!bh || (!bh->b_data)) {
- return -1;
- }
- memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */
- BUG_ON(!buffer_uptodate(bh));
- generic_make_request(WRITE, bh);
- if (!buffer_uptodate(bh))
- printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file);
- wait_on_buffer(bh);
- brelse(bh);
- return 0;
-#endif
- printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file);
- return 0;
-}
-
extern dev_t __init name_to_dev_t(const char *line);
-static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume)
+static int __read_suspend_image(struct block_device *bdev, union diskpage *cur)
{
swp_entry_t next;
int i, nr_pgdir_pages;
@@ -939,18 +930,9 @@ static int __read_suspend_image(struct block_device *bdev, union diskpage *cur,
else if (!memcmp("S2",cur->swh.magic.magic,2))
memcpy(cur->swh.magic.magic,"SWAPSPACE2",10);
else {
- if (noresume)
- return -EINVAL;
- panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n",
+ printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n",
name_resume, cur->swh.magic.magic);
- }
- if (noresume) {
- /* We don't do a sanity check here: we want to restore the swap
- whatever version of kernel made the suspend image;
- We need to write swap, but swap is *not* enabled so
- we must write the device directly */
- printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file);
- bdev_write_page(bdev, 0, cur);
+ return -EFAULT;
}
printk( "%sSignature found, resuming\n", name_resume );
@@ -1000,7 +982,7 @@ static int __read_suspend_image(struct block_device *bdev, union diskpage *cur,
return 0;
}
-static int read_suspend_image(const char * specialfile, int noresume)
+static int read_suspend_image(const char * specialfile)
{
union diskpage *cur;
unsigned long scratch_page = 0;
@@ -1019,7 +1001,7 @@ static int read_suspend_image(const char * specialfile, int noresume)
error = PTR_ERR(bdev);
} else {
set_blocksize(bdev, PAGE_SIZE);
- error = __read_suspend_image(bdev, cur, noresume);
+ error = __read_suspend_image(bdev, cur);
blkdev_put(bdev, BDEV_RAW);
}
} else error = -ENOMEM;
@@ -1048,64 +1030,47 @@ static int read_suspend_image(const char * specialfile, int noresume)
return error;
}
-/*
- * Called from init kernel_thread.
- * We check if we have an image and if so we try to resume
+/**
+ * software_resume - Check and load saved image from swap.
+ *
+ * Defined as a late_initcall, so it gets called after all devices
+ * have been probed and initialized, but before we've mounted anything.
*/
-void software_resume(void)
+static int software_resume(void)
{
- if (num_online_cpus() > 1) {
- printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n");
- return;
- }
- /* We enable the possibility of machine suspend */
- software_suspend_enabled = 1;
- if (!resume_status)
- return;
-
- printk( "%s", name_resume );
- if (resume_status == NORESUME) {
- if(resume_file[0])
- read_suspend_image(resume_file, 1);
- printk( "disabled\n" );
- return;
- }
- MDELAY(1000);
+ if (!strlen(resume_file))
+ return 0;
if (pm_prepare_console())
printk("swsusp: Can't allocate a console... proceeding\n");
- if (!resume_file[0] && resume_status == RESUME_SPECIFIED) {
- printk( "suspension device unspecified\n" );
- return;
- }
+ printk("swsusp: %s\n", name_resume );
+
+ MDELAY(1000);
- printk( "resuming from %s\n", resume_file);
- if (read_suspend_image(resume_file, 0))
+ printk("swsusp: resuming from %s\n", resume_file);
+ if (read_suspend_image(resume_file))
goto read_failure;
do_magic(1);
- panic("This never returns");
+ printk("swsusp: Resume failed. Continuing.\n");
read_failure:
pm_restore_console();
- return;
+ return -EFAULT;
}
+late_initcall(software_resume);
+
static int __init resume_setup(char *str)
{
- if (resume_status == NORESUME)
- return 1;
-
strncpy( resume_file, str, 255 );
- resume_status = RESUME_SPECIFIED;
-
return 1;
}
static int __init noresume_setup(char *str)
{
- resume_status = NORESUME;
+ resume_file[0] = '\0';
return 1;
}