summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/Makefile2
-rw-r--r--drivers/base/power/power.h22
-rw-r--r--drivers/base/power/resume.c4
-rw-r--r--drivers/base/power/runtime.c89
-rw-r--r--drivers/base/power/suspend.c49
5 files changed, 140 insertions, 26 deletions
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 26d423c916ba..98d0fb3be20d 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,2 +1,2 @@
obj-y := shutdown.o
-obj-$(CONFIG_PM) += main.o suspend.o resume.o
+obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 728a58e4e99e..6221c21d6eb1 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -25,10 +25,32 @@ static inline struct device * to_device(struct list_head * entry)
/*
+ * 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 *);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index e95a61c7fa1c..544104c6bbd1 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -21,7 +21,7 @@ extern int sysdev_restore(void);
*
*/
-static int resume_device(struct device * dev)
+int resume_device(struct device * dev)
{
struct device_driver * drv = dev->driver;
@@ -70,7 +70,7 @@ void device_pm_resume(void)
* @dev: Device.
*/
-static void power_up_device(struct device * dev)
+void power_up_device(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv && drv->resume)
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/suspend.c b/drivers/base/power/suspend.c
index cb43a62438a9..0747e409d0ec 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -36,13 +36,19 @@ extern int sysdev_suspend(u32 state);
* @state: Power state device is entering.
*/
-static int suspend_device(struct device * dev, u32 state)
+int suspend_device(struct device * dev, u32 state)
{
struct device_driver * drv = dev->driver;
+ int error = 0;
- if (drv && drv->suspend)
- return drv->suspend(dev,state,SUSPEND_SAVE_STATE);
- return 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;
}
@@ -74,15 +80,8 @@ int device_pm_suspend(u32 state)
while(!list_empty(&dpm_active)) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
- list_del_init(entry);
- error = suspend_device(dev,state);
-
- if (!error)
- list_add(entry,&dpm_suspended);
- else {
- list_add_tail(entry,&dpm_active);
+ if ((error = suspend_device(dev,state)))
goto Error;
- }
}
if ((error = sysdev_save(state)))
@@ -102,12 +101,18 @@ int device_pm_suspend(u32 state)
* @state: Power state to enter.
*/
-static int power_down_device(struct device * dev, u32 state)
+int power_down_device(struct device * dev, u32 state)
{
struct device_driver * drv = dev->driver;
+ int error = 0;
+
if (drv && drv->suspend)
- return drv->suspend(dev,state,SUSPEND_POWER_DOWN);
- return 0;
+ error = drv->suspend(dev,state,SUSPEND_POWER_DOWN);
+ if (!error) {
+ list_del(&dev->power.entry);
+ list_add(&dev->power.entry,&dpm_off);
+ }
+ return error;
}
@@ -128,15 +133,13 @@ static int dpm_power_down(u32 state)
while(!list_empty(&dpm_suspended)) {
struct list_head * entry = dpm_suspended.prev;
int error;
-
- list_del_init(entry);
error = power_down_device(to_device(entry),state);
- if (!error)
- list_add(entry,&dpm_off);
- else if (error == -EAGAIN)
- list_add(entry,&dpm_off_irq);
- else {
- list_add_tail(entry,&dpm_suspended);
+ if (error) {
+ if (error == -EAGAIN) {
+ list_del(entry);
+ list_add(entry,&dpm_off_irq);
+ continue;
+ }
return error;
}
}