Commit 78156c16 authored by Patrick Mochel's avatar Patrick Mochel

Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-virgin

into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-power
parents 59e53847 5f5773c9
...@@ -22,6 +22,7 @@ ACPI_MODULE_NAME ("sleep") ...@@ -22,6 +22,7 @@ ACPI_MODULE_NAME ("sleep")
u8 sleep_states[ACPI_S_STATE_COUNT]; u8 sleep_states[ACPI_S_STATE_COUNT];
extern void do_suspend_lowlevel_s4bios(int); extern void do_suspend_lowlevel_s4bios(int);
extern void do_suspend_lowlevel(int);
/** /**
* acpi_system_restore_state - OS-specific restoration of state * acpi_system_restore_state - OS-specific restoration of state
...@@ -71,10 +72,6 @@ acpi_system_restore_state ( ...@@ -71,10 +72,6 @@ acpi_system_restore_state (
* First, we call to the device driver layer to save device state. * First, we call to the device driver layer to save device state.
* Once we have that, we save whatevery processor and kernel state we * Once we have that, we save whatevery processor and kernel state we
* need to memory. * need to memory.
* If we're entering S4, we then write the memory image to disk.
*
* Only then is it safe for us to power down devices, since we may need
* the disks and upstream buses to write to.
*/ */
acpi_status acpi_status
acpi_system_save_state( acpi_system_save_state(
...@@ -185,12 +182,11 @@ acpi_system_suspend( ...@@ -185,12 +182,11 @@ acpi_system_suspend(
status = acpi_enter_sleep_state(state); status = acpi_enter_sleep_state(state);
break; break;
#ifdef CONFIG_SOFTWARE_SUSPEND
case ACPI_STATE_S2: case ACPI_STATE_S2:
case ACPI_STATE_S3: case ACPI_STATE_S3:
do_suspend_lowlevel(0); do_suspend_lowlevel(0);
break; break;
#endif
case ACPI_STATE_S4: case ACPI_STATE_S4:
do_suspend_lowlevel_s4bios(0); do_suspend_lowlevel_s4bios(0);
break; break;
......
# Makefile for the Linux device tree # Makefile for the Linux device tree
obj-y := core.o sys.o interface.o power.o bus.o \ obj-y := core.o sys.o interface.o bus.o \
driver.o class.o platform.o \ driver.o class.o platform.o \
cpu.o firmware.o init.o map.o cpu.o firmware.o init.o map.o
obj-y += power/
obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o memblk.o obj-$(CONFIG_NUMA) += node.o memblk.o
...@@ -13,3 +13,21 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr) ...@@ -13,3 +13,21 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
{ {
return container_of(_attr,struct class_device_attribute,attr); return container_of(_attr,struct class_device_attribute,attr);
} }
#ifdef CONFIG_PM
extern int device_pm_add(struct device *);
extern void device_pm_remove(struct device *);
#else
static inline int device_pm_add(struct device * dev)
{
return 0;
}
static inline void device_pm_remove(struct device * dev)
{
}
#endif
...@@ -230,6 +230,8 @@ int device_add(struct device *dev) ...@@ -230,6 +230,8 @@ int device_add(struct device *dev)
bus_add_device(dev); bus_add_device(dev);
device_pm_add(dev);
/* notify platform of device entry */ /* notify platform of device entry */
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
...@@ -304,6 +306,8 @@ void device_del(struct device * dev) ...@@ -304,6 +306,8 @@ void device_del(struct device * dev)
{ {
struct device * parent = dev->parent; struct device * parent = dev->parent;
device_pm_remove(dev);
down_write(&devices_subsys.rwsem); down_write(&devices_subsys.rwsem);
if (parent) if (parent)
list_del_init(&dev->node); list_del_init(&dev->node);
......
obj-y := shutdown.o
obj-$(CONFIG_PM) += main.o suspend.o resume.o
/*
* drivers/base/power/main.c - Where the driver meets power management.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
*
* This file is released under the GPLv2
*
*
* The driver model core calls device_pm_add() when a device is registered.
* This will intialize the embedded device_pm_info object in the device
* and add it to the list of power-controlled devices. sysfs entries for
* controlling device power management will also be added.
*
* A different set of lists than the global subsystem list are used to
* keep track of power info because we use different lists to hold
* devices based on what stage of the power management process they
* are in. The power domain dependencies may also differ from the
* ancestral dependencies that the subsystem list maintains.
*/
#define DEBUG
#include <linux/device.h>
LIST_HEAD(dpm_active);
LIST_HEAD(dpm_suspended);
LIST_HEAD(dpm_off);
LIST_HEAD(dpm_off_irq);
spinlock_t dpm_lock = SPIN_LOCK_UNLOCKED;
DECLARE_MUTEX(dpm_sem);
static struct attribute power_attrs[] = {
{ .name = NULL },
};
static struct attribute_group pm_attr_group = {
.name = "pm",
.attrs = power_attrs,
};
int device_pm_add(struct device * dev)
{
int error;
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem);
spin_lock(&dpm_lock);
list_add_tail(&dev->power.entry,&dpm_active);
spin_unlock(&dpm_lock);
error = sysfs_create_group(&dev->kobj,&pm_attr_group);
up(&dpm_sem);
return error;
}
void device_pm_remove(struct device * dev)
{
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem);
sysfs_remove_group(&dev->kobj,&pm_attr_group);
spin_lock(&dpm_lock);
list_del(&dev->power.entry);
spin_unlock(&dpm_lock);
up(&dpm_sem);
}
/*
* Used to synchronize global power management operations.
*/
extern struct semaphore dpm_sem;
/*
* Used to protect PM lists.
*/
extern spinlock_t dpm_lock;
/*
* 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;
static inline struct dev_pm_info * to_pm_info(struct list_head * entry)
{
return container_of(entry,struct dev_pm_info,entry);
}
static inline struct device * to_device(struct list_head * entry)
{
return container_of(to_pm_info(entry),struct device,power);
}
/*
* resume.c
*/
extern int dpm_resume(void);
extern void dpm_power_up(void);
extern void dpm_power_up_irq(void);
/*
* resume.c - Functions for waking devices up.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Labs
*
* This file is released under the GPLv2
*
*/
#include <linux/device.h>
#include "power.h"
extern int sysdev_resume(void);
extern int sysdev_restore(void);
/**
* resume_device - Restore state for one device.
* @dev: Device.
*
*/
static int resume_device(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv && drv->resume)
return drv->resume(dev,RESUME_RESTORE_STATE);
return 0;
}
/**
* dpm_resume - Restore all device state.
*
* Walk the dpm_suspended list and restore each device. As they are
* resumed, move the devices to the dpm_active list.
*/
int dpm_resume(void)
{
spin_lock(&dpm_lock);
while(!list_empty(&dpm_suspended)) {
struct list_head * entry = dpm_suspended.next;
struct device * dev = to_device(entry);
list_del_init(entry);
spin_unlock(&dpm_lock);
resume_device(dev);
spin_lock(&dpm_lock);
list_add_tail(entry,&dpm_active);
}
spin_unlock(&dpm_lock);
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);
}
/**
* power_up_device - Power one device on.
* @dev: Device.
*/
static void power_up_device(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv && drv->resume)
drv->resume(dev,RESUME_POWER_ON);
}
/**
* device_power_up_irq - Power on some devices.
*
* Walk the dpm_off_irq list and power each device up. This
* is used for devices that required they be powered down with
* interrupts disabled. As devices are powered on, they are moved to
* the dpm_suspended list.
*
* Interrupts must be disabled when calling this.
*/
void dpm_power_up_irq(void)
{
spin_lock_irq(&dpm_lock);
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);
}
spin_unlock_irq(&dpm_lock);
}
/**
* 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)
{
spin_lock(&dpm_lock);
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);
}
spin_unlock(&dpm_lock);
}
/**
* device_pm_power_up - Turn on all devices.
*
* 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.
*/
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();
}
/* /*
* power.c - power management functions for the device tree. * shutdown.c - power management functions for the device tree.
* *
* Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Patrick Mochel
* 2002-3 Open Source Development Lab * 2002-3 Open Source Development Lab
* *
* This file is released under the GPLv2 * This file is released under the GPLv2
* *
* Kai Germaschewski contributed to the list walking routines.
*
*/ */
#undef DEBUG #undef DEBUG
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include "base.h"
#define to_dev(node) container_of(node,struct device,kobj.entry) #define to_dev(node) container_of(node,struct device,kobj.entry)
...@@ -31,88 +27,6 @@ extern struct subsystem devices_subsys; ...@@ -31,88 +27,6 @@ extern struct subsystem devices_subsys;
*/ */
extern int sysdev_shutdown(void); extern int sysdev_shutdown(void);
extern int sysdev_save(u32 state);
extern int sysdev_suspend(u32 state);
extern int sysdev_resume(void);
extern int sysdev_restore(void);
/**
* device_suspend - suspend/remove all devices on the device ree
* @state: state we're entering
* @level: what stage of the suspend process we're at
* (emb: it seems that these two arguments are described backwards of what
* they actually mean .. is this correct?)
*
* The entries in the global device list are inserted such that they're in a
* depth-first ordering. So, simply interate over the list, and call the
* driver's suspend or remove callback for each device.
*/
int device_suspend(u32 state, u32 level)
{
struct device * dev;
int error = 0;
down_write(&devices_subsys.rwsem);
list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name);
error = dev->driver->suspend(dev,state,level);
if (error)
printk(KERN_ERR "%s: suspend returned %d\n",
dev->name,error);
}
}
up_write(&devices_subsys.rwsem);
/*
* Make sure system devices are suspended.
*/
switch(level) {
case SUSPEND_SAVE_STATE:
sysdev_save(state);
break;
case SUSPEND_POWER_DOWN:
sysdev_suspend(state);
break;
default:
break;
}
return error;
}
/**
* device_resume - resume all the devices in the system
* @level: stage of resume process we're at
*
* Similar to device_suspend above, though we want to do a breadth-first
* walk of the tree to make sure we wake up parents before children.
* So, we iterate over the list backward.
*/
void device_resume(u32 level)
{
struct device * dev;
switch (level) {
case RESUME_POWER_ON:
sysdev_resume();
break;
case RESUME_RESTORE_STATE:
sysdev_restore();
break;
default:
break;
}
down_write(&devices_subsys.rwsem);
list_for_each_entry(dev,&devices_subsys.kset.list,kobj.entry) {
if (dev->driver && dev->driver->resume) {
pr_debug("resuming device %s\n",dev->name);
dev->driver->resume(dev,level);
}
}
up_write(&devices_subsys.rwsem);
}
/** /**
* device_shutdown - call ->remove() on each device to shutdown. * device_shutdown - call ->remove() on each device to shutdown.
...@@ -135,6 +49,3 @@ void device_shutdown(void) ...@@ -135,6 +49,3 @@ void device_shutdown(void)
sysdev_shutdown(); sysdev_shutdown();
} }
EXPORT_SYMBOL(device_suspend);
EXPORT_SYMBOL(device_resume);
EXPORT_SYMBOL(device_shutdown);
/*
* suspend.c - Functions for putting devices to sleep.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Labs
*
* This file is released under the GPLv2
*
*/
#include <linux/device.h>
#include "power.h"
extern int sysdev_save(u32 state);
extern int sysdev_suspend(u32 state);
/*
* The entries in the dpm_active list are in a depth first order, simply
* because children are guaranteed to be discovered after parents, and
* are inserted at the back of the list on discovery.
*
* All list on the suspend path are done in reverse order, so we operate
* on the leaves of the device tree (or forests, depending on how you want
* to look at it ;) first. As nodes are removed from the back of the list,
* they are inserted into the front of their destintation lists.
*
* Things are the reverse on the resume path - iterations are done in
* forward order, and nodes are inserted at the back of their destination
* lists. This way, the ancestors will be accessed before their descendents.
*/
/**
* suspend_device - Save state of one device.
* @dev: Device.
* @state: Power state device is entering.
*/
static int suspend_device(struct device * dev, u32 state)
{
struct device_driver * drv = dev->driver;
if (drv && drv->suspend)
return drv->suspend(dev,state,SUSPEND_SAVE_STATE);
return 0;
}
/**
* device_pm_suspend - Save state and stop all devices in system.
* @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.
*
* Have system devices save state last.
*
* Note this function leaves dpm_sem held to
* a) block other devices from registering.
* b) prevent other PM operations from happening after we've begun.
* c) make sure we're exclusive when we disable interrupts.
*
* device_pm_resume() will release dpm_sem after restoring state to
* all devices (as will this on error). You must call it once you've
* called device_pm_suspend().
*/
int device_pm_suspend(u32 state)
{
int error = 0;
down(&dpm_sem);
spin_lock(&dpm_lock);
while(!list_empty(&dpm_active)) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
list_del_init(entry);
spin_unlock(&dpm_lock);
error = suspend_device(dev,state);
spin_lock(&dpm_lock);
if (!error)
list_add(entry,&dpm_suspended);
else {
list_add_tail(entry,&dpm_active);
goto Error;
}
}
spin_unlock(&dpm_lock);
if ((error = sysdev_save(state)))
goto Error;
Done:
return error;
Error:
dpm_resume();
up(&dpm_sem);
goto Done;
}
/**
* power_down_device - Put one device in low power state.
* @dev: Device.
* @state: Power state to enter.
*/
static int power_down_device(struct device * dev, u32 state)
{
struct device_driver * drv = dev->driver;
if (drv && drv->suspend)
return drv->suspend(dev,state,SUSPEND_POWER_DOWN);
return 0;
}
/**
* 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)
{
spin_lock(&dpm_lock);
while(!list_empty(&dpm_suspended)) {
struct list_head * entry = dpm_suspended.prev;
int error;
list_del_init(entry);
spin_unlock(&dpm_lock);
error = power_down_device(to_device(entry),state);
spin_lock(&dpm_lock);
if (!error)
list_add(entry,&dpm_off);
else if (error == -EAGAIN)
list_add(entry,&dpm_off_irq);
else {
list_add_tail(entry,&dpm_suspended);
return error;
}
}
spin_unlock(&dpm_lock);
return 0;
}
/**
* dpm_power_down_irq - Power down devices without interrupts.
* @state: State to enter.
*
* Walk the dpm_off_irq list (built by dpm_power_down) and power
* down each device that requires the call to be made with interrupts
* disabled.
*/
static int dpm_power_down_irq(u32 state)
{
struct device * dev;
int error = 0;
spin_lock_irq(&dpm_lock);
list_for_each_entry_reverse(dev,&dpm_off_irq,power.entry) {
if ((error = power_down_device(dev,state)))
break;
}
spin_unlock_irq(&dpm_lock);
return error;
}
/**
* device_pm_power_down - Put all devices in low power state.
* @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.
*/
int device_pm_power_down(u32 state)
{
int error = 0;
if ((error = dpm_power_down(state)))
goto ErrorIRQOn;
local_irq_disable();
if ((error = dpm_power_down_irq(state)))
goto ErrorIRQOff;
sysdev_suspend(state);
Done:
return error;
ErrorIRQOff:
dpm_power_up_irq();
local_irq_enable();
ErrorIRQOn:
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;
}
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
# Makefile for the sysfs virtual filesystem # Makefile for the sysfs virtual filesystem
# #
obj-y := inode.o file.o dir.o symlink.o mount.o bin.o obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \
group.o
...@@ -20,6 +20,38 @@ static int init_dir(struct inode * inode) ...@@ -20,6 +20,38 @@ static int init_dir(struct inode * inode)
return 0; return 0;
} }
static struct dentry *
create_dir(struct kobject * k, struct dentry * p, char * n)
{
struct dentry * dentry;
down(&p->d_inode->i_sem);
dentry = sysfs_get_dentry(p,n);
if (!IS_ERR(dentry)) {
int error;
dentry->d_fsdata = (void *)k;
error = sysfs_create(dentry,
(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
init_dir);
if (!error)
p->d_inode->i_nlink++;
else {
dput(dentry);
dentry = ERR_PTR(error);
}
}
up(&p->d_inode->i_sem);
return dentry;
}
struct dentry * sysfs_create_subdir(struct kobject * k, char * n)
{
return create_dir(k,k->dentry,n);
}
/** /**
* sysfs_create_dir - create a directory for an object. * sysfs_create_dir - create a directory for an object.
* @parent: parent parent object. * @parent: parent parent object.
...@@ -42,23 +74,34 @@ int sysfs_create_dir(struct kobject * kobj) ...@@ -42,23 +74,34 @@ int sysfs_create_dir(struct kobject * kobj)
else else
return -EFAULT; return -EFAULT;
down(&parent->d_inode->i_sem); dentry = create_dir(kobj,parent,kobj->name);
dentry = sysfs_get_dentry(parent,kobj->name); if (!IS_ERR(dentry))
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)kobj;
kobj->dentry = dentry; kobj->dentry = dentry;
error = sysfs_create(dentry,(S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO), else
init_dir);
if (!error)
parent->d_inode->i_nlink++;
} else
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem);
return error; return error;
} }
static void remove_dir(struct dentry * d)
{
struct dentry * parent = dget(d->d_parent);
down(&parent->d_inode->i_sem);
d_delete(d);
simple_rmdir(parent->d_inode,d);
pr_debug(" o %s removing done (%d)\n",d->d_name.name,
atomic_read(&d->d_count));
up(&parent->d_inode->i_sem);
dput(parent);
}
void sysfs_remove_subdir(struct dentry * d)
{
remove_dir(d);
}
/** /**
* sysfs_remove_dir - remove an object's directory. * sysfs_remove_dir - remove an object's directory.
...@@ -73,14 +116,11 @@ void sysfs_remove_dir(struct kobject * kobj) ...@@ -73,14 +116,11 @@ void sysfs_remove_dir(struct kobject * kobj)
{ {
struct list_head * node; struct list_head * node;
struct dentry * dentry = dget(kobj->dentry); struct dentry * dentry = dget(kobj->dentry);
struct dentry * parent;
if (!dentry) if (!dentry)
return; return;
pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); pr_debug("sysfs %s: removing dir\n",dentry->d_name.name);
parent = dget(dentry->d_parent);
down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem); down(&dentry->d_inode->i_sem);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
...@@ -107,18 +147,12 @@ void sysfs_remove_dir(struct kobject * kobj) ...@@ -107,18 +147,12 @@ void sysfs_remove_dir(struct kobject * kobj)
} }
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
up(&dentry->d_inode->i_sem); up(&dentry->d_inode->i_sem);
d_delete(dentry);
simple_rmdir(parent->d_inode,dentry);
pr_debug(" o %s removing done (%d)\n",dentry->d_name.name,
atomic_read(&dentry->d_count));
remove_dir(dentry);
/** /**
* Drop reference from dget() on entrance. * Drop reference from dget() on entrance.
*/ */
dput(dentry); dput(dentry);
up(&parent->d_inode->i_sem);
dput(parent);
} }
void sysfs_rename_dir(struct kobject * kobj, char *new_name) void sysfs_rename_dir(struct kobject * kobj, char *new_name)
......
...@@ -344,35 +344,38 @@ static struct file_operations sysfs_file_operations = { ...@@ -344,35 +344,38 @@ static struct file_operations sysfs_file_operations = {
.release = sysfs_release, .release = sysfs_release,
}; };
/**
* sysfs_create_file - create an attribute file for an object.
* @kobj: object we're creating for.
* @attr: atrribute descriptor.
*/
int sysfs_create_file(struct kobject * kobj, struct attribute * attr) int sysfs_add_file(struct dentry * dir, struct attribute * attr)
{ {
struct dentry * dentry; struct dentry * dentry;
struct dentry * parent; int error;
int error = 0;
if (!kobj || !attr)
return -EINVAL;
parent = kobj->dentry;
down(&parent->d_inode->i_sem); down(&dir->d_inode->i_sem);
dentry = sysfs_get_dentry(parent,attr->name); dentry = sysfs_get_dentry(dir,attr->name);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)attr; dentry->d_fsdata = (void *)attr;
error = sysfs_create(dentry,(attr->mode & S_IALLUGO) | S_IFREG,init_file); error = sysfs_create(dentry,(attr->mode & S_IALLUGO) | S_IFREG,init_file);
} else } else
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
up(&parent->d_inode->i_sem); up(&dir->d_inode->i_sem);
return error; return error;
} }
/**
* sysfs_create_file - create an attribute file for an object.
* @kobj: object we're creating for.
* @attr: atrribute descriptor.
*/
int sysfs_create_file(struct kobject * kobj, struct attribute * attr)
{
if (kobj && attr)
return sysfs_add_file(kobj->dentry,attr);
return -EINVAL;
}
/** /**
* sysfs_update_file - update the modified timestamp on an object attribute. * sysfs_update_file - update the modified timestamp on an object attribute.
* @kobj: object we're acting for. * @kobj: object we're acting for.
......
/*
* fs/sysfs/group.c - Operations for adding/removing multiple files at once.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
*
* This file is released undert the GPL v2.
*
*/
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include "sysfs.h"
static void remove_files(struct dentry * dir, struct attribute_group * grp)
{
struct attribute * attr;
for (attr = grp->attrs; attr->name; attr++)
sysfs_hash_and_remove(dir,attr->name);
}
static int create_files(struct kobject * kobj, struct dentry * dir,
struct attribute_group * grp)
{
struct attribute * attr;
int error = 0;
for (attr = grp->attrs; attr->name && !error; attr++) {
error = sysfs_add_file(dir,attr);
}
if (error)
remove_files(dir,grp);
return error;
}
int sysfs_create_group(struct kobject * kobj, struct attribute_group * grp)
{
struct dentry * dir;
int error;
if (grp->name) {
dir = sysfs_create_subdir(kobj,grp->name);
if (IS_ERR(dir))
return PTR_ERR(dir);
} else
dir = kobj->dentry;
dir = dget(dir);
if ((error = create_files(kobj,dir,grp))) {
if (grp->name)
sysfs_remove_subdir(dir);
dput(dir);
}
return error;
}
void sysfs_remove_group(struct kobject * kobj, struct attribute_group * grp)
{
struct dentry * dir;
if (grp->name)
dir = sysfs_get_dentry(kobj->dentry,grp->name);
else
dir = kobj->dentry;
remove_files(dir,grp);
dput(dir);
if (grp->name)
sysfs_remove_subdir(dir);
}
EXPORT_SYMBOL(sysfs_create_group);
EXPORT_SYMBOL(sysfs_remove_group);
...@@ -6,5 +6,8 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); ...@@ -6,5 +6,8 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *));
extern struct dentry * sysfs_get_dentry(struct dentry *, char *); extern struct dentry * sysfs_get_dentry(struct dentry *, char *);
extern int sysfs_add_file(struct dentry * dir, struct attribute * attr);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern struct dentry * sysfs_create_subdir(struct kobject *, char *);
extern void sysfs_remove_subdir(struct dentry *);
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/atomic.h> #include <asm/atomic.h>
...@@ -41,13 +42,6 @@ enum { ...@@ -41,13 +42,6 @@ enum {
RESUME_ENABLE, RESUME_ENABLE,
}; };
enum device_state {
DEVICE_UNINITIALIZED = 0,
DEVICE_INITIALIZED = 1,
DEVICE_REGISTERED = 2,
DEVICE_GONE = 3,
};
struct device; struct device;
struct device_driver; struct device_driver;
struct class; struct class;
...@@ -64,8 +58,8 @@ struct bus_type { ...@@ -64,8 +58,8 @@ struct bus_type {
struct device * (*add) (struct device * parent, char * bus_id); struct device * (*add) (struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp, int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size); int num_envp, char *buffer, int buffer_size);
};
};
extern int bus_register(struct bus_type * bus); extern int bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type * bus); extern void bus_unregister(struct bus_type * bus);
...@@ -267,7 +261,7 @@ struct device { ...@@ -267,7 +261,7 @@ struct device {
void *driver_data; /* data private to the driver */ void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data (e.g. ACPI, void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */ BIOS data relevant to device) */
struct dev_pm_info power;
u32 power_state; /* Current operating state. In u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0 ACPI-speak, this is D0-D3, D0
being fully functional, and D3 being fully functional, and D3
......
...@@ -118,29 +118,29 @@ extern int pm_active; ...@@ -118,29 +118,29 @@ extern int pm_active;
/* /*
* Register a device with power management * Register a device with power management
*/ */
struct pm_dev *pm_register(pm_dev_t type, struct pm_dev __deprecated *pm_register(pm_dev_t type,
unsigned long id, unsigned long id,
pm_callback callback); pm_callback callback);
/* /*
* Unregister a device with power management * Unregister a device with power management
*/ */
void pm_unregister(struct pm_dev *dev); void __deprecated pm_unregister(struct pm_dev *dev);
/* /*
* Unregister all devices with matching callback * Unregister all devices with matching callback
*/ */
void pm_unregister_all(pm_callback callback); void __deprecated pm_unregister_all(pm_callback callback);
/* /*
* Send a request to a single device * Send a request to a single device
*/ */
int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data); int __deprecated pm_send(struct pm_dev *dev, pm_request_t rqst, void *data);
/* /*
* Send a request to all devices * Send a request to all devices
*/ */
int pm_send_all(pm_request_t rqst, void *data); int __deprecated pm_send_all(pm_request_t rqst, void *data);
/* /*
* Find a device * Find a device
...@@ -188,6 +188,21 @@ static inline void pm_dev_idle(struct pm_dev *dev) {} ...@@ -188,6 +188,21 @@ static inline void pm_dev_idle(struct pm_dev *dev) {}
extern void (*pm_idle)(void); extern void (*pm_idle)(void);
extern void (*pm_power_off)(void); extern void (*pm_power_off)(void);
struct dev_pm_info {
#ifdef CONFIG_PM
u32 power_state;
u8 * saved_state;
struct list_head entry;
#endif
};
extern int device_pm_suspend(u32 state);
extern int device_pm_power_down(u32 state);
extern void device_pm_power_up(void);
extern void device_pm_resume(void);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_PM_H */ #endif /* _LINUX_PM_H */
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task. * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
* POWER_OFF Stop OS and remove all power from system, if possible. * POWER_OFF Stop OS and remove all power from system, if possible.
* RESTART2 Restart system using given command string. * RESTART2 Restart system using given command string.
* SW_SUSPEND Suspend system using Software Suspend if compiled in * SW_SUSPEND Suspend system using software suspend if compiled in.
*/ */
#define LINUX_REBOOT_CMD_RESTART 0x01234567 #define LINUX_REBOOT_CMD_RESTART 0x01234567
......
...@@ -57,4 +57,13 @@ sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name); ...@@ -57,4 +57,13 @@ sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name);
extern void extern void
sysfs_remove_link(struct kobject *, char * name); sysfs_remove_link(struct kobject *, char * name);
struct attribute_group {
char * name;
struct attribute * attrs;
};
int sysfs_create_group(struct kobject *, struct attribute_group *);
void sysfs_remove_group(struct kobject *, struct attribute_group *);
#endif /* _SYSFS_H_ */ #endif /* _SYSFS_H_ */
...@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += cpu.o ...@@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += cpu.o
obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += ksyms.o module.o obj-$(CONFIG_MODULES) += ksyms.o module.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += pm.o power/ obj-$(CONFIG_PM) += power/
obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_COMPAT) += compat.o obj-$(CONFIG_COMPAT) += compat.o
......
obj-y := process.o console.o obj-y := process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sysrq.h>
int pm_active; int pm_active;
...@@ -295,39 +294,3 @@ EXPORT_SYMBOL(pm_find); ...@@ -295,39 +294,3 @@ EXPORT_SYMBOL(pm_find);
EXPORT_SYMBOL(pm_active); EXPORT_SYMBOL(pm_active);
#ifdef CONFIG_MAGIC_SYSRQ
/**
* handle_poweroff - sysrq callback for power down
* @key: key pressed (unused)
* @pt_regs: register state (unused)
* @kbd: keyboard state (unused)
* @tty: tty involved (unused)
*
* When the user hits Sys-Rq o to power down the machine this is the
* callback we use.
*/
static void handle_poweroff (int key, struct pt_regs *pt_regs,
struct tty_struct *tty)
{
if (pm_power_off)
pm_power_off();
}
static struct sysrq_key_op sysrq_poweroff_op = {
.handler = handle_poweroff,
.help_msg = "powerOff",
.action_msg = "Power Off\n"
};
#endif /* CONFIG_MAGIC_SYSRQ */
static int pm_init(void)
{
register_sysrq_key('o', &sysrq_poweroff_op);
return 0;
}
subsys_initcall(pm_init);
/*
* poweroff.c - sysrq handler to gracefully power down machine.
*
* This file is released under the GPL v2
*/
#include <linux/kernel.h>
#include <linux/sysrq.h>
#include <linux/init.h>
#include <linux/pm.h>
/**
* handle_poweroff - sysrq callback for power down
* @key: key pressed (unused)
* @pt_regs: register state (unused)
* @kbd: keyboard state (unused)
* @tty: tty involved (unused)
*
* When the user hits Sys-Rq o to power down the machine this is the
* callback we use.
*/
static void handle_poweroff (int key, struct pt_regs *pt_regs,
struct tty_struct *tty)
{
if (pm_power_off)
pm_power_off();
}
static struct sysrq_key_op sysrq_poweroff_op = {
.handler = handle_poweroff,
.help_msg = "powerOff",
.action_msg = "Power Off\n"
};
static int pm_sysrq_init(void)
{
register_sysrq_key('o', &sysrq_poweroff_op);
return 0;
}
subsys_initcall(pm_sysrq_init);
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#undef DEBUG #undef DEBUG
#include <linux/smp_lock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/module.h> #include <linux/module.h>
......
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