Commit 98247e0a authored by Patrick Mochel's avatar Patrick Mochel

[power] Add PM usage counting

- Add ->pm_users and ->pm_parent fields to struct dev_pm_info. 
- Add function device_pm_set_parent()

The default power parent for a device is the device's physical parent, but
a driver may change it to represent a tranversal power dependency. 

Though it's not incorporated into the suspend/resume sequences yet, the 
core will respect the power tree, rather than the physical/electrical one.

Also added is a power usage count for devices, which indicates how many 
devices are dependent on that one for power (how many children it has in 
the power tree). The core will use this count to determine whether or not
a device can be put into a low power state or not. 
parent a40b9b0b
......@@ -22,7 +22,7 @@
#define DEBUG
#include <linux/device.h>
#include "power.h"
LIST_HEAD(dpm_active);
LIST_HEAD(dpm_suspended);
......@@ -39,6 +39,46 @@ static struct attribute_group pm_attr_group = {
.attrs = power_attrs,
};
/*
* PM Reference Counting.
*/
static inline void device_pm_hold(struct device * dev)
{
atomic_inc(&dev->power.pm_users);
}
static inline void device_pm_release(struct device * dev)
{
atomic_inc(&dev->power.pm_users);
}
/**
* device_pm_set_parent - Specify power dependency.
* @dev: Device who needs power.
* @parent: Device that supplies power.
*
* This function is used to manually describe a power-dependency
* relationship. It may be used to specify a transversal relationship
* (where the power supplier is not the physical (or electrical)
* ancestor of a specific device.
* The effect of this is that the supplier will not be powered down
* before the power dependent.
*/
void device_pm_set_parent(struct device * dev, struct device * parent)
{
struct device * old_parent = dev->power.pm_parent;
if (old_parent)
device_pm_release(old_parent);
dev->power.pm_parent = parent;
if (parent)
device_pm_hold(parent);
}
EXPORT_SYMBOL(device_pm_set_parent);
int device_pm_add(struct device * dev)
{
int error;
......@@ -47,6 +87,7 @@ int device_pm_add(struct device * dev)
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem);
list_add_tail(&dev->power.entry,&dpm_active);
device_pm_set_parent(dev,dev->parent);
error = sysfs_create_group(&dev->kobj,&pm_attr_group);
up(&dpm_sem);
return error;
......@@ -61,3 +102,5 @@ void device_pm_remove(struct device * dev)
list_del(&dev->power.entry);
up(&dpm_sem);
}
......@@ -25,6 +25,7 @@
#include <linux/config.h>
#include <linux/list.h>
#include <asm/atomic.h>
/*
* Power management requests
......@@ -188,15 +189,20 @@ static inline void pm_dev_idle(struct pm_dev *dev) {}
extern void (*pm_idle)(void);
extern void (*pm_power_off)(void);
struct device;
struct dev_pm_info {
#ifdef CONFIG_PM
u32 power_state;
u8 * saved_state;
atomic_t pm_users;
struct device * pm_parent;
struct list_head entry;
#endif
};
extern void device_pm_set_parent(struct device * dev, struct device * parent);
extern int device_pm_suspend(u32 state);
extern int device_pm_power_down(u32 state);
extern void device_pm_power_up(void);
......
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