Commit 86ccb795 authored by Patrick Mochel's avatar Patrick Mochel

[power] Add hooks for runtime device power control.

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.
parent 98247e0a
obj-y := shutdown.o
obj-$(CONFIG_PM) += main.o suspend.o resume.o
obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o
......@@ -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 *);
......@@ -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)
......
/*
* 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);
}
......@@ -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;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment