r8169: Fix receive buffer length when MTU is between 1515 and 1536
[safe/jmp/linux-2.6] / drivers / pci / bus.c
index fb9a112..cef28a7 100644 (file)
  */
 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;
+       resource_size_t max = -1;
 
        type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
 
+       /* don't allocate too high if the pref mem doesn't support 64bit*/
+       if (!(res->flags & IORESOURCE_MEM_64))
+               max = PCIBIOS_MAX_MEM_32;
+
        for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
                struct resource *r = bus->resource[i];
                if (!r)
@@ -62,7 +67,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
                /* Ok, try it out.. */
                ret = allocate_resource(r, res, size,
                                        r->start ? : min,
-                                       -1, align,
+                                       max, align,
                                        alignf, alignf_data);
                if (ret == 0)
                        break;
@@ -71,22 +76,54 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 }
 
 /**
- * add a single device
+ * 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
  */
-void __devinit pci_bus_add_device(struct pci_dev *dev)
+int pci_bus_add_device(struct pci_dev *dev)
 {
-       device_add(&dev->dev);
-
-       spin_lock(&pci_bus_lock);
-       list_add_tail(&dev->global_list, &pci_devices);
-       spin_unlock(&pci_bus_lock);
+       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;
 }
 
 /**
@@ -101,55 +138,117 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
  *
  * Call hotplug for each new devices.
  */
-void __devinit pci_bus_add_devices(struct pci_bus *bus)
+void pci_bus_add_devices(const 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-present devices (which are on the
-                * global device list.)
-                */
-               if (!list_empty(&dev->global_list))
+               /* Skip already-added devices */
+               if (dev->is_added)
                        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) {
+               BUG_ON(!dev->is_added);
 
-               BUG_ON(list_empty(&dev->global_list));
-
+               child = dev->subordinate;
                /*
                 * If there is an unattached subordinate bus, attach
                 * it and then scan for unattached PCI devices.
                 */
-               if (dev->subordinate) {
-                      if (list_empty(&dev->subordinate->node)) {
-                              spin_lock(&pci_bus_lock);
-                              list_add_tail(&dev->subordinate->node,
-                                              &dev->bus->children);
-                              spin_unlock(&pci_bus_lock);
-                      }
-                       pci_bus_add_devices(dev->subordinate);
-
-                       sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
+               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) {
-                       pci_enable_device(dev);
-                       pci_set_master(dev);
+                       if (!pci_is_enabled(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.
+ *
+ *  We check the return of @cb each time. If it returns anything
+ *  other than 0, we break out.
+ *
+ */
+void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
+                 void *userdata)
+{
+       struct pci_dev *dev;
+       struct pci_bus *bus;
+       struct list_head *next;
+       int retval;
+
+       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);
+               retval = cb(dev, userdata);
+               up(&dev->dev.sem);
+               if (retval)
+                       break;
+       }
+       up_read(&pci_bus_sem);
+}
+
 EXPORT_SYMBOL(pci_bus_alloc_resource);
 EXPORT_SYMBOL_GPL(pci_bus_add_device);
 EXPORT_SYMBOL(pci_bus_add_devices);