]> git.hungrycats.org Git - linux/commitdiff
[power] Add hooks for runtime device power control.
authorPatrick Mochel <mochel@osdl.org>
Tue, 12 Aug 2003 02:25:20 +0000 (19:25 -0700)
committerPatrick Mochel <mochel@osdl.org>
Tue, 12 Aug 2003 02:25:20 +0000 (19:25 -0700)
dpm_runtime_{suspend,resume} control the power state of a single device
while the system is running.

dpm_runtime_suspend() will save state of the device, then attempt to power
it down. This happens with interrupts enabled, so if the device does not
support that, the device's state is restored, and we continue on our merry
way.

dpm_runtime_resume() powers the device back on, then restores state of the
device.

dpm_set_power_state() simply notifies the core of the power state the
device is in. Drivers can use this, since they are the only ones that can
really tell.

drivers/base/power/Makefile
drivers/base/power/power.h
drivers/base/power/resume.c
drivers/base/power/runtime.c [new file with mode: 0644]
drivers/base/power/suspend.c

index 26d423c916ba927fb52310a94b63f39f73d788fd..98d0fb3be20ddcea5864099a9d4444ffae73099b 100644 (file)
@@ -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 
index 728a58e4e99ea617b5d4bcd4785089dc0d153e19..6221c21d6eb178105ea40a97d4ac3eaf0de934b2 100644 (file)
@@ -24,11 +24,33 @@ 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 *);
 
index e95a61c7fa1cfbfc3d3ea808f2ce1f8f98aacbce..544104c6bbd14286f7cb5e50ea2591dbce578dc1 100644 (file)
@@ -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 (file)
index 0000000..4a4ac9f
--- /dev/null
@@ -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);
+}
index cb43a62438a93e8209e09bb73e3f6eb9887cdbbf..0747e409d0ecda6d8fea7668d4b476cf4cad8163 100644 (file)
@@ -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;
                }
        }