]> git.neil.brown.name Git - history.git/commitdiff
[power] Update device handling.
authorPatrick Mochel <mochel@osdl.org>
Tue, 19 Aug 2003 16:23:34 +0000 (09:23 -0700)
committerPatrick Mochel <mochel@osdl.org>
Tue, 19 Aug 2003 16:23:34 +0000 (09:23 -0700)
- From conversations with Ben Herrenschmidt.

Most devices should be able to handle powering down with interrupts enabled,
which I already assume. But since suspending will stop I/O transactions
before the call to power it off (making the device unusable anyway), there
is no need to separate the calls - we may as well make it simpler for
driver authors and require that driver authors do everything at the same
time.

There will always be devices that need to either power down or power up the
device with interrupts disabled. They will get called with interrupts
enabled, but may return -EAGAIN to be called again with interrupts disabled
to do what they need to do.

System devices are now always called only with interrupts disabled. Come
on - they're system devices. Of course we need interrupts disabled.

drivers/base/power/main.c
drivers/base/power/power.h
drivers/base/power/resume.c
drivers/base/power/runtime.c
drivers/base/power/suspend.c
drivers/base/sys.c
include/linux/device.h
include/linux/sysdev.h
kernel/power/main.c

index 5aabe5179fd15cc036d166ef3b0bf805bbfcf9e0..611a69accdd0095ad8fcada50d3aa7158fa1d515 100644 (file)
@@ -25,7 +25,6 @@
 #include "power.h"
 
 LIST_HEAD(dpm_active);
-LIST_HEAD(dpm_suspended);
 LIST_HEAD(dpm_off);
 LIST_HEAD(dpm_off_irq);
 
index 8130b04ffe5fd28c8c8d71d5c15831613e69142f..fde72b37f938b241f8cfc28b7e62f81da1242507 100644 (file)
@@ -31,7 +31,6 @@ 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;
 
@@ -61,15 +60,12 @@ extern void dpm_sysfs_remove(struct device *);
  */
 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);
 
 
 /*
index 544104c6bbd14286f7cb5e50ea2591dbce578dc1..a34f66a1b42c1b9298201eef41d18f5e326c310f 100644 (file)
@@ -12,7 +12,6 @@
 #include "power.h"
 
 extern int sysdev_resume(void);
-extern int sysdev_restore(void);
 
 
 /**
@@ -30,37 +29,22 @@ int resume_device(struct device * dev)
        return 0;
 }
 
+
 /**
- *     dpm_resume - Restore all device state.
+ *     device_pm_resume - Restore state of each device in system.
  *
- *     Walk the dpm_suspended list and restore each device. As they are 
- *     resumed, move the devices to the dpm_active list.
+ *     Restore normal device state and release the dpm_sem.
  */
 
-int dpm_resume(void)
+void device_pm_resume(void)
 {
-       while(!list_empty(&dpm_suspended)) {
-               struct list_head * entry = dpm_suspended.next;
+       while(!list_empty(&dpm_off)) {
+               struct list_head * entry = dpm_off.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);
 }
 
@@ -89,65 +73,27 @@ void power_up_device(struct device * dev)
  *     Interrupts must be disabled when calling this. 
  */
 
-void dpm_power_up_irq(void)
+void dpm_power_up(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);
+               list_add_tail(entry,&dpm_active);
        }
 }
 
 
 /**
- *     device_pm_power_up - Turn on all devices.
+ *     device_pm_power_up - Turn on all devices that need special attention.
  *
- *     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.
+ *     Power on system devices then devices that required we shut them down
+ *     with interrupts disabled.
+ *     Called with interrupts disabled.
  */
 
 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();
-}
-
index 4a4ac9f7764dd45a95e9f60b609f657fb4555e23..05ef979a37912a51cf7fb75cf5d88db3d12f7e80 100644 (file)
@@ -14,8 +14,6 @@ static void runtime_resume(struct device * dev)
 {
        if (!dev->power.power_state)
                return;
-
-       power_up_device(dev);
        resume_device(dev);
 }
 
@@ -55,19 +53,11 @@ int dpm_runtime_suspend(struct device * dev, u32 state)
        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;
+       if (!(error = suspend_device(dev,state)))
                dev->power.power_state = state;
-       }
  Done:
        up(&dpm_sem);
        return error;
- ErrResume:
-       resume_device(dev);
-       goto Done;
 }
 
 
index 0747e409d0ecda6d8fea7668d4b476cf4cad8163..8e5842521d4d2c9e44b5c164cb0e9def783c6ef0 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/device.h>
 #include "power.h"
  
-extern int sysdev_save(u32 state);
 extern int sysdev_suspend(u32 state);
 
 /*
@@ -46,7 +45,10 @@ int suspend_device(struct device * dev, u32 state)
 
        if (!error) {
                list_del(&dev->power.entry);
-               list_add(&dev->power.entry,&dpm_suspended);
+               list_add(&dev->power.entry,&dpm_off);
+       } else if (error == -EAGAIN) {
+               list_del(&dev->power.entry);
+               list_add(&dev->power.entry,&dpm_off_irq);
        }
        return error;
 }
@@ -57,10 +59,13 @@ int suspend_device(struct device * dev, u32 state)
  *     @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. 
+ *     it to dpm_off. 
+ *     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. 
  *
- *     Have system devices save state last. 
+ *     If we hit a failure with any of the devices, call device_pm_resume()
+ *     above to bring the suspended devices back to life. 
  *
  *     Note this function leaves dpm_sem held to
  *     a) block other devices from registering.
@@ -83,153 +88,56 @@ int device_pm_suspend(u32 state)
                if ((error = suspend_device(dev,state)))
                        goto Error;
        }
-
-       if ((error = sysdev_save(state)))
-               goto Error;
  Done:
        return error;
  Error:
-       dpm_resume();
-       up(&dpm_sem);
+       device_pm_resume();
        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.
+ *     dpm_power_down - Power down devices without interrupts.
  *     @state: State to enter.
  *
- *     Walk the dpm_off_irq list (built by dpm_power_down) and power
+ *     Walk the dpm_off_irq list (built by device_pm_suspend) and power
  *     down each device that requires the call to be made with interrupts
  *     disabled. 
  */
 
-static int dpm_power_down_irq(u32 state)
+static int dpm_power_down(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.
+ *     device_pm_power_down - Shut down special devices.
  *     @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. 
+ *     Walk the dpm_off_irq list, calling ->power_down() for each device that
+ *     couldn't power down the device with interrupts enabled. When we're 
+ *     done, power down system devices. 
  */
 
 int device_pm_power_down(u32 state)
 {
        int error = 0;
+       struct device * dev;
 
-       if ((error = dpm_power_down(state)))
-               goto ErrorIRQOn;
-       local_irq_disable();
-       if ((error = dpm_power_down_irq(state)))
-               goto ErrorIRQOff;
-
-       sysdev_suspend(state);
+       list_for_each_entry_reverse(dev,&dpm_off_irq,power.entry) {
+               if ((error = suspend_device(dev,state)))
+                       break;
+       } 
+       if (error)
+               goto Error;
+       if ((error = sysdev_suspend(state)))
+               goto Error;
  Done:
        return error;
-
- ErrorIRQOff:
-       dpm_power_up_irq();
-       local_irq_enable();
- ErrorIRQOn:
+ Error:
        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;
-}
-
index e306d2e26363d19862c0ebde472a96d2ca4dc63e..299b390e243bc25c910bbad0a99ef436744a6852 100644 (file)
@@ -282,62 +282,17 @@ void sysdev_shutdown(void)
 }
 
 
-/**
- *     sysdev_save - Save system device state
- *     @state: Power state we're entering.
- *
- *     This is called when the system is going to sleep, but before interrupts 
- *     have been disabled. This allows system device drivers to allocate and 
- *     save device state, including sleeping during the process..
- */
-
-int sysdev_save(u32 state)
-{
-       struct sysdev_class * cls;
-
-       pr_debug("Saving System Device State\n");
-
-       down_write(&system_subsys.rwsem);
-
-       list_for_each_entry_reverse(cls,&system_subsys.kset.list,
-                                   kset.kobj.entry) {
-               struct sys_device * sysdev;
-               pr_debug("Saving state for type '%s':\n",cls->kset.kobj.name);
-
-               list_for_each_entry(sysdev,&cls->kset.list,kobj.entry) {
-                       struct sysdev_driver * drv;
-
-                       pr_debug(" %s\n",sysdev->kobj.name);
-
-                       list_for_each_entry(drv,&global_drivers,entry) {
-                               if (drv->save)
-                                       drv->save(sysdev,state);
-                       }
-
-                       list_for_each_entry(drv,&cls->drivers,entry) {
-                               if (drv->save)
-                                       drv->save(sysdev,state);
-                       }
-
-                       if (cls->save)
-                               cls->save(sysdev,state);
-               }
-       }
-       up_write(&system_subsys.rwsem);
-       return 0;
-}
-
-
 /**
  *     sysdev_suspend - Suspend all system devices.
  *     @state:         Power state to enter.
  *
  *     We perform an almost identical operation as sys_device_shutdown()
- *     above, though calling ->suspend() instead.
+ *     above, though calling ->suspend() instead. Interrupts are disabled
+ *     when this called. Devices are responsible for both saving state and
+ *     quiescing or powering down the device. 
  *
- *     Note: Interrupts are disabled when called, so we can't sleep when
- *     trying to get the subsystem's rwsem. If that happens, print a nasty
- *     warning and return an error.
+ *     This is only called by the device PM core, so we let them handle
+ *     all synchronization.
  */
 
 int sysdev_suspend(u32 state)
@@ -346,11 +301,6 @@ int sysdev_suspend(u32 state)
 
        pr_debug("Suspending System Devices\n");
 
-       if (!down_write_trylock(&system_subsys.rwsem)) {
-               printk("%s: Cannot acquire semaphore; Failing\n",__FUNCTION__);
-               return -EFAULT;
-       }
-
        list_for_each_entry_reverse(cls,&system_subsys.kset.list,
                                    kset.kobj.entry) {
                struct sys_device * sysdev;
@@ -378,8 +328,6 @@ int sysdev_suspend(u32 state)
                                cls->suspend(sysdev,state);
                }
        }
-       up_write(&system_subsys.rwsem);
-
        return 0;
 }
 
@@ -390,7 +338,7 @@ int sysdev_suspend(u32 state)
  *     Similar to sys_device_suspend(), but we iterate the list forwards
  *     to guarantee that parent devices are resumed before their children.
  *
- *     Note: Interrupts are disabled when called.
+ *     Note: Interrupts are disabled when called. 
  */
 
 int sysdev_resume(void)
@@ -399,9 +347,6 @@ int sysdev_resume(void)
 
        pr_debug("Resuming System Devices\n");
 
-       if(!down_write_trylock(&system_subsys.rwsem))
-               return -EFAULT;
-
        list_for_each_entry(cls,&system_subsys.kset.list,kset.kobj.entry) {
                struct sys_device * sysdev;
 
@@ -429,50 +374,6 @@ int sysdev_resume(void)
 
                }
        }
-       up_write(&system_subsys.rwsem);
-       return 0;
-}
-
-
-/**
- *     sysdev_restore - Restore system device state
- *
- *     This is called during a suspend/resume cycle last, after interrupts 
- *     have been re-enabled. This is intended for auxillary drivers, etc, 
- *     that may sleep when restoring state.
- */
-
-int sysdev_restore(void)
-{
-       struct sysdev_class * cls;
-
-       down_write(&system_subsys.rwsem);
-       pr_debug("Restoring System Device State\n");
-
-       list_for_each_entry(cls,&system_subsys.kset.list,kset.kobj.entry) {
-               struct sys_device * sysdev;
-
-               pr_debug("Restoring state for type '%s':\n",cls->kset.kobj.name);
-               list_for_each_entry(sysdev,&cls->kset.list,kobj.entry) {
-                       struct sysdev_driver * drv;
-                       pr_debug(" %s\n",sysdev->kobj.name);
-
-                       if (cls->restore)
-                               cls->restore(sysdev);
-
-                       list_for_each_entry(drv,&cls->drivers,entry) {
-                               if (drv->restore)
-                                       drv->restore(sysdev);
-                       }
-
-                       list_for_each_entry(drv,&global_drivers,entry) {
-                               if (drv->restore)
-                                       drv->restore(sysdev);
-                       }
-               }
-       }
-
-       up_write(&system_subsys.rwsem);
        return 0;
 }
 
index 7b49400adf3198deebb623aa6fd05bcb0366e214..c99ad50c67848dd339dcaef46ae2cdbced75fc2e 100644 (file)
@@ -372,8 +372,6 @@ extern struct bus_type platform_bus_type;
 extern struct device legacy_bus;
 
 /* drivers/base/power.c */
-extern int device_suspend(u32 state, u32 level);
-extern void device_resume(u32 level);
 extern void device_shutdown(void);
 
 
index 4bc3e22b51046e37295902fe226541fb679a2c64..2a90db8d41def314dd0bf68c308bd9d6395933fa 100644 (file)
@@ -31,10 +31,8 @@ struct sysdev_class {
 
        /* Default operations for these types of devices */
        int     (*shutdown)(struct sys_device *);
-       int     (*save)(struct sys_device *, u32 state);
        int     (*suspend)(struct sys_device *, u32 state);
        int     (*resume)(struct sys_device *);
-       int     (*restore)(struct sys_device *);
        struct kset             kset;
 };
 
@@ -52,10 +50,8 @@ struct sysdev_driver {
        int     (*add)(struct sys_device *);
        int     (*remove)(struct sys_device *);
        int     (*shutdown)(struct sys_device *);
-       int     (*save)(struct sys_device *, u32 state);
        int     (*suspend)(struct sys_device *, u32 state);
        int     (*resume)(struct sys_device *);
-       int     (*restore)(struct sys_device *);
 };
 
 
index 42bf7233a3a7a7641a48f68895b026c1f8b8aa14..4d1476ba8156eed3ed465f8879f5ff306b5e52ce 100644 (file)
@@ -65,9 +65,9 @@ static int pm_suspend_standby(void)
        if (!pm_ops || !pm_ops->enter)
                return -EPERM;
 
+       local_irq_save(flags);
        if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
                goto Done;
-       local_irq_save(flags);
        error = pm_ops->enter(PM_SUSPEND_STANDBY);
        local_irq_restore(flags);
        device_pm_power_up();
@@ -91,9 +91,9 @@ static int pm_suspend_mem(void)
        if (!pm_ops || !pm_ops->enter)
                return -EPERM;
 
+       local_irq_save(flags);
        if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
                goto Done;
-       local_irq_save(flags);
        error = pm_ops->enter(PM_SUSPEND_STANDBY);
        local_irq_restore(flags);
        device_pm_power_up();
@@ -114,9 +114,19 @@ static int pm_suspend_mem(void)
 
 static int power_down(u32 mode)
 {
+       unsigned long flags;
+       int error = 0;
+
+       local_irq_save(flags);
+       device_pm_power_down();
        switch(mode) {
        case PM_DISK_PLATFORM:
-               return pm_ops->enter(PM_SUSPEND_DISK);
+               error = pm_ops->enter(PM_SUSPEND_DISK);
+               if (error) {
+                       device_pm_power_up();
+                       local_irq_restore(flags);
+                       return error;
+               }
        case PM_DISK_SHUTDOWN:
                machine_power_off();
                break;