Commit 456f5a1c authored by Patrick Mochel's avatar Patrick Mochel

Merge hera.kernel.org:/home/torvalds/BK/linux-2.5

into hera.kernel.org:/home/mochel/BK/linux-2.5
parents 94f5af8b 43f5feae
O_TARGET := base.o O_TARGET := base.o
obj-y := core.o interface.o fs.o obj-y := core.o sys.o interface.o fs.o power.o
export-objs := core.o interface.o fs.o export-objs := $(obj-y)
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
#undef DEBUG
#ifdef DEBUG
# define DBG(x...) printk(x)
#else
# define DBG(x...)
#endif
extern struct device device_root;
extern spinlock_t device_lock;
extern int device_make_dir(struct device * dev);
extern void device_remove_dir(struct device * dev);
...@@ -10,16 +10,9 @@ ...@@ -10,16 +10,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include "base.h"
#undef DEBUG struct device device_root = {
#ifdef DEBUG
# define DBG(x...) printk(x)
#else
# define DBG(x...)
#endif
static struct device device_root = {
bus_id: "root", bus_id: "root",
name: "System root", name: "System root",
}; };
...@@ -27,10 +20,7 @@ static struct device device_root = { ...@@ -27,10 +20,7 @@ static struct device device_root = {
int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL;
extern int device_make_dir(struct device * dev); spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
extern void device_remove_dir(struct device * dev);
static spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
/** /**
* device_register - register a device * device_register - register a device
...@@ -39,10 +29,14 @@ static spinlock_t device_lock = SPIN_LOCK_UNLOCKED; ...@@ -39,10 +29,14 @@ static spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
* First, make sure that the device has a parent, create * First, make sure that the device has a parent, create
* a directory for it, then add it to the parent's list of * a directory for it, then add it to the parent's list of
* children. * children.
*
* Maintains a global list of all devices, in depth-first ordering.
* The head for that list is device_root.g_list.
*/ */
int device_register(struct device *dev) int device_register(struct device *dev)
{ {
int error; int error;
struct device *prev_dev;
if (!dev || !strlen(dev->bus_id)) if (!dev || !strlen(dev->bus_id))
return -EINVAL; return -EINVAL;
...@@ -50,6 +44,7 @@ int device_register(struct device *dev) ...@@ -50,6 +44,7 @@ int device_register(struct device *dev)
spin_lock(&device_lock); spin_lock(&device_lock);
INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,2); atomic_set(&dev->refcount,2);
...@@ -57,6 +52,13 @@ int device_register(struct device *dev) ...@@ -57,6 +52,13 @@ int device_register(struct device *dev)
if (!dev->parent) if (!dev->parent)
dev->parent = &device_root; dev->parent = &device_root;
get_device(dev->parent); get_device(dev->parent);
if (list_empty(&dev->parent->children))
prev_dev = dev->parent;
else
prev_dev = list_entry(dev->parent->children.prev, struct device, node);
list_add(&dev->g_list, &prev_dev->g_list);
list_add_tail(&dev->node,&dev->parent->children); list_add_tail(&dev->node,&dev->parent->children);
} }
spin_unlock(&device_lock); spin_unlock(&device_lock);
...@@ -79,22 +81,15 @@ int device_register(struct device *dev) ...@@ -79,22 +81,15 @@ int device_register(struct device *dev)
} }
/** /**
* put_device - clean up device * put_device - decrement reference count, and clean up when it hits 0
* @dev: device in question * @dev: device in question
*
* Decrement reference count for device.
* If it hits 0, we need to clean it up.
* However, we may be here in interrupt context, and it may
* take some time to do proper clean up (removing files, calling
* back down to device to clean up everything it has).
* So, we remove it from its parent's list and add it to the list of
* devices to be cleaned up.
*/ */
void put_device(struct device * dev) void put_device(struct device * dev)
{ {
if (!atomic_dec_and_lock(&dev->refcount,&device_lock)) if (!atomic_dec_and_lock(&dev->refcount,&device_lock))
return; return;
list_del_init(&dev->node); list_del_init(&dev->node);
list_del_init(&dev->g_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n", DBG("DEV: Unregistering device. ID = '%s', name = '%s'\n",
......
/*
* power.c - power management functions for the device tree.
*
* Copyright (c) 2002 Patrick Mochel
* 2002 Open Source Development Lab
*
* Kai Germaschewski contributed to the list walking routines.
*
* FIXME: The suspend and shutdown walks are identical. The resume walk
* is simply walking the list backward. Anyway we can combine these (cleanly)?
*/
#include <linux/device.h>
#include <linux/module.h>
#include "base.h"
/**
* device_suspend - suspend all devices on the device tree
* @state: state we're entering
* @level: what stage of the suspend process we're at
*
* The entries in the global device list are inserted such that they're in a
* depth-first ordering. So, simply iterate over the list, and call the driver's
* suspend callback for each device.
*/
int device_suspend(u32 state, u32 level)
{
struct device * dev;
struct device * prev = &device_root;
int error = 0;
get_device(prev);
spin_lock(&device_lock);
dev = g_list_to_dev(prev->g_list.next);
while(dev != &device_root && !error) {
get_device(dev);
spin_unlock(&device_lock);
put_device(prev);
if (dev->driver && dev->driver->suspend)
error = dev->driver->suspend(dev,state,level);
spin_lock(&device_lock);
prev = dev;
dev = g_list_to_dev(prev->g_list.next);
}
spin_unlock(&device_root);
put_device(prev);
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;
struct device * prev = &device_root;
get_device(prev);
spin_lock(&device_lock);
dev = g_list_to_dev(prev->g_list.prev);
while(dev != &device_root) {
get_device(dev);
spin_unlock(&device_lock);
put_device(prev);
if (dev->driver && dev->driver->resume)
dev->driver->resume(dev,level);
spin_lock(&device_lock);
prev = dev;
dev = g_list_to_dev(prev->g_list.prev);
}
spin_unlock(&device_root);
put_device(prev);
}
/**
* device_shutdown - queisce all the devices before reboot/shutdown
*
* Do depth first iteration over device tree, calling ->remove() for each
* device. This should ensure the devices are put into a sane state before
* we reboot the system.
*
*/
void device_shutdown(void)
{
struct device * dev;
struct device * prev = &device_root;
get_device(prev);
spin_lock(&device_lock);
dev = g_list_to_dev(prev->g_list.next);
while(dev != &device_root) {
get_device(dev);
spin_unlock(&device_lock);
put_device(prev);
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
spin_lock(&device_lock);
prev = dev;
dev = g_list_to_dev(prev->g_list.next);
}
spin_unlock(&device_root);
put_device(prev);
}
EXPORT_SYMBOL(device_suspend);
EXPORT_SYMBOL(device_resume);
EXPORT_SYMBOL(device_shutdown);
/*
* sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc)
*
* Copyright (c) 2002 Patrick Mochel
* 2002 Open Source Development Lab
*
* This exports a 'system' bus type.
* By default, a 'sys' bus gets added to the root of the system. There will
* always be core system devices. Devices can use register_sys_device() to
* add themselves as children of the system bus.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
static struct device system_bus = {
name: "System Bus",
bus_id: "sys",
};
int register_sys_device(struct device * dev)
{
int error = -EINVAL;
if (dev) {
if (!dev->parent)
dev->parent = &system_bus;
error = device_register(dev);
}
return error;
}
void unregister_sys_device(struct device * dev)
{
if (dev)
put_device(dev);
}
static int sys_bus_init(void)
{
return device_register(&system_bus);
}
subsys_initcall(sys_bus_init);
EXPORT_SYMBOL(register_sys_device);
EXPORT_SYMBOL(unregister_sys_device);
...@@ -64,6 +64,7 @@ struct device_driver { ...@@ -64,6 +64,7 @@ struct device_driver {
}; };
struct device { struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */ struct list_head node; /* node in sibling list */
struct list_head children; struct list_head children;
struct device * parent; struct device * parent;
...@@ -99,6 +100,12 @@ list_to_dev(struct list_head *node) ...@@ -99,6 +100,12 @@ list_to_dev(struct list_head *node)
return list_entry(node, struct device, node); return list_entry(node, struct device, node);
} }
static inline struct device *
g_list_to_dev(struct list_head *g_list)
{
return list_entry(g_list, struct device, g_list);
}
/* /*
* High level routines for use by the bus drivers * High level routines for use by the bus drivers
*/ */
...@@ -143,4 +150,13 @@ static inline void get_device(struct device * dev) ...@@ -143,4 +150,13 @@ static inline void get_device(struct device * dev)
extern void put_device(struct device * dev); extern void put_device(struct device * dev);
/* drivers/base/sys.c */
extern int register_sys_device(struct device * dev);
extern void unregister_sys_device(struct device * dev);
/* drivers/base/power.c */
extern int device_suspend(u32 state, u32 level);
extern void device_resume(u32 level);
extern void device_shutdown(void);
#endif /* _DEVICE_H_ */ #endif /* _DEVICE_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