diff options
Diffstat (limited to 'libdde-linux26/contrib/drivers/pci/bus.c')
-rw-r--r-- | libdde-linux26/contrib/drivers/pci/bus.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/libdde-linux26/contrib/drivers/pci/bus.c b/libdde-linux26/contrib/drivers/pci/bus.c new file mode 100644 index 00000000..52b54f05 --- /dev/null +++ b/libdde-linux26/contrib/drivers/pci/bus.c @@ -0,0 +1,241 @@ +/* + * drivers/pci/bus.c + * + * From setup-res.c, by: + * Dave Rusling (david.rusling@reo.mts.dec.com) + * David Mosberger (davidm@cs.arizona.edu) + * David Miller (davem@redhat.com) + * Ivan Kokshaysky (ink@jurassic.park.msu.ru) + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/proc_fs.h> +#include <linux/init.h> + +#include "pci.h" + +/** + * pci_bus_alloc_resource - allocate a resource from a parent bus + * @bus: PCI bus + * @res: resource to allocate + * @size: size of resource to allocate + * @align: alignment of resource to allocate + * @min: minimum /proc/iomem address to allocate + * @type_mask: IORESOURCE_* type flags + * @alignf: resource alignment function + * @alignf_data: data argument for resource alignment function + * + * Given the PCI bus a device resides on, the size, minimum address, + * alignment and type, try to find an acceptable resource allocation + * for a specific device resource. + */ +int +pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, + resource_size_t size, resource_size_t align, + resource_size_t min, unsigned int type_mask, + void (*alignf)(void *, struct resource *, resource_size_t, + resource_size_t), + void *alignf_data) +{ + int i, ret = -ENOMEM; + + type_mask |= IORESOURCE_IO | IORESOURCE_MEM; + + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + struct resource *r = bus->resource[i]; + if (!r) + continue; + + /* type_mask must match */ + if ((res->flags ^ r->flags) & type_mask) + continue; + + /* We cannot allocate a non-prefetching resource + from a pre-fetching area */ + if ((r->flags & IORESOURCE_PREFETCH) && + !(res->flags & IORESOURCE_PREFETCH)) + continue; + + /* Ok, try it out.. */ + ret = allocate_resource(r, res, size, + r->start ? : min, + -1, align, + alignf, alignf_data); + if (ret == 0) + break; + } + return ret; +} + +/** + * pci_bus_add_device - add a single device + * @dev: device to add + * + * This adds a single pci device to the global + * device list and adds sysfs and procfs entries + */ +int pci_bus_add_device(struct pci_dev *dev) +{ + int retval; + retval = device_add(&dev->dev); + if (retval) + return retval; + + dev->is_added = 1; + pci_proc_attach_device(dev); + pci_create_sysfs_dev_files(dev); + return 0; +} + +/** + * pci_bus_add_child - add a child bus + * @bus: bus to add + * + * This adds sysfs entries for a single bus + */ +int pci_bus_add_child(struct pci_bus *bus) +{ + int retval; + + if (bus->bridge) + bus->dev.parent = bus->bridge; + + retval = device_register(&bus->dev); + if (retval) + return retval; + + bus->is_added = 1; + + retval = device_create_file(&bus->dev, &dev_attr_cpuaffinity); + if (retval) + return retval; + + retval = device_create_file(&bus->dev, &dev_attr_cpulistaffinity); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(bus); + + return retval; +} + +/** + * pci_bus_add_devices - insert newly discovered PCI devices + * @bus: bus to check for new devices + * + * Add newly discovered PCI devices (which are on the bus->devices + * list) to the global PCI device list, add the sysfs and procfs + * entries. Where a bridge is found, add the discovered bus to + * the parents list of child buses, and recurse (breadth-first + * to be compatible with 2.4) + * + * Call hotplug for each new devices. + */ +void pci_bus_add_devices(struct pci_bus *bus) +{ + struct pci_dev *dev; + struct pci_bus *child; + int retval; + + list_for_each_entry(dev, &bus->devices, bus_list) { + /* Skip already-added devices */ + if (dev->is_added) + continue; + retval = pci_bus_add_device(dev); + if (retval) + dev_err(&dev->dev, "Error adding device, continuing\n"); + } + + list_for_each_entry(dev, &bus->devices, bus_list) { + BUG_ON(!dev->is_added); + + child = dev->subordinate; + /* + * If there is an unattached subordinate bus, attach + * it and then scan for unattached PCI devices. + */ + if (!child) + continue; + if (list_empty(&child->node)) { + down_write(&pci_bus_sem); + list_add_tail(&child->node, &dev->bus->children); + up_write(&pci_bus_sem); + } + pci_bus_add_devices(child); + + /* + * register the bus with sysfs as the parent is now + * properly registered. + */ + if (child->is_added) + continue; + retval = pci_bus_add_child(child); + if (retval) + dev_err(&dev->dev, "Error adding bus, continuing\n"); + } +} + +void pci_enable_bridges(struct pci_bus *bus) +{ + struct pci_dev *dev; + int retval; + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->subordinate) { + retval = pci_enable_device(dev); + pci_set_master(dev); + pci_enable_bridges(dev->subordinate); + } + } +} + +/** pci_walk_bus - walk devices on/under bus, calling callback. + * @top bus whose devices should be walked + * @cb callback to be called for each device found + * @userdata arbitrary pointer to be passed to callback. + * + * Walk the given bus, including any bridged devices + * on buses under this bus. Call the provided callback + * on each device found. + */ +void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), + void *userdata) +{ + struct pci_dev *dev; + struct pci_bus *bus; + struct list_head *next; + + bus = top; + down_read(&pci_bus_sem); + next = top->devices.next; + for (;;) { + if (next == &bus->devices) { + /* end of this bus, go up or finish */ + if (bus == top) + break; + next = bus->self->bus_list.next; + bus = bus->self->bus; + continue; + } + dev = list_entry(next, struct pci_dev, bus_list); + if (dev->subordinate) { + /* this is a pci-pci bridge, do its devices next */ + next = dev->subordinate->devices.next; + bus = dev->subordinate; + } else + next = dev->bus_list.next; + + /* Run device routines with the device locked */ + down(&dev->dev.sem); + cb(dev, userdata); + up(&dev->dev.sem); + } + up_read(&pci_bus_sem); +} + +EXPORT_SYMBOL(pci_bus_alloc_resource); +EXPORT_SYMBOL_GPL(pci_bus_add_device); +EXPORT_SYMBOL(pci_bus_add_devices); +EXPORT_SYMBOL(pci_enable_bridges); |