diff options
| -rw-r--r-- | drivers/base/power/Makefile | 2 | ||||
| -rw-r--r-- | drivers/base/power/power.h | 22 | ||||
| -rw-r--r-- | drivers/base/power/resume.c | 4 | ||||
| -rw-r--r-- | drivers/base/power/runtime.c | 89 | ||||
| -rw-r--r-- | drivers/base/power/suspend.c | 49 |
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; } } |
