X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fbus.c;h=aadaa3c8096b73ecf50428b044b0151c6f10b50d;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=dbd33605cc107c058e028ab447ca39eb1c814001;hpb=6a77f38946aaee1cd85eeec6cf4229b204c15071;p=linux-2.6.git diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index dbd33605c..aadaa3c80 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -34,11 +34,11 @@ */ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, - unsigned long size, unsigned long align, unsigned long min, - unsigned int type_mask, - void (*alignf)(void *, struct resource *, - unsigned long, unsigned long), - void *alignf_data) + 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; @@ -60,7 +60,9 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, continue; /* Ok, try it out.. */ - ret = allocate_resource(r, res, size, min, -1, align, + ret = allocate_resource(r, res, size, + r->start ? : min, + -1, align, alignf, alignf_data); if (ret == 0) break; @@ -75,16 +77,20 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, * This adds a single pci device to the global * device list and adds sysfs and procfs entries */ -void __devinit pci_bus_add_device(struct pci_dev *dev) +int __devinit pci_bus_add_device(struct pci_dev *dev) { - device_add(&dev->dev); + int retval; + retval = device_add(&dev->dev); + if (retval) + return retval; - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_add_tail(&dev->global_list, &pci_devices); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); pci_proc_attach_device(dev); pci_create_sysfs_dev_files(dev); + return 0; } /** @@ -102,6 +108,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev) void __devinit pci_bus_add_devices(struct pci_bus *bus) { struct pci_dev *dev; + int retval; list_for_each_entry(dev, &bus->devices, bus_list) { /* @@ -110,7 +117,9 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) */ if (!list_empty(&dev->global_list)) continue; - pci_bus_add_device(dev); + 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) { @@ -121,13 +130,19 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) * If there is an unattached subordinate bus, attach * it and then scan for unattached PCI devices. */ - if (dev->subordinate && list_empty(&dev->subordinate->node)) { - spin_lock(&pci_bus_lock); - list_add_tail(&dev->subordinate->node, &dev->bus->children); - spin_unlock(&pci_bus_lock); + if (dev->subordinate) { + if (list_empty(&dev->subordinate->node)) { + down_write(&pci_bus_sem); + list_add_tail(&dev->subordinate->node, + &dev->bus->children); + up_write(&pci_bus_sem); + } pci_bus_add_devices(dev->subordinate); - - sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge"); + retval = sysfs_create_link(&dev->subordinate->class_dev.kobj, + &dev->dev.kobj, "bridge"); + if (retval) + dev_err(&dev->dev, "Error creating sysfs " + "bridge symlink, continuing...\n"); } } } @@ -135,16 +150,62 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) 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) { - pci_enable_device(dev); + 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_GPL(pci_walk_bus); + EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); EXPORT_SYMBOL(pci_bus_add_devices);