Merge branch 'for-2.6.34' of git://git.kernel.dk/linux-2.6-block
[pandora-kernel.git] / drivers / pci / setup-bus.c
index f76f6e9..4fe36d2 100644 (file)
@@ -71,40 +71,47 @@ static void free_failed_list(struct resource_list_x *head)
        head->next = NULL;
 }
 
-static void pbus_assign_resources_sorted(const struct pci_bus *bus,
-                                        struct resource_list_x *fail_head)
+static void __dev_sort_resources(struct pci_dev *dev,
+                                struct resource_list *head)
 {
-       struct pci_dev *dev;
-       struct resource *res;
-       struct resource_list head, *list, *tmp;
-       int idx;
+       u16 class = dev->class >> 8;
 
-       head.next = NULL;
-       list_for_each_entry(dev, &bus->devices, bus_list) {
-               u16 class = dev->class >> 8;
+       /* Don't touch classless devices or host bridges or ioapics.  */
+       if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST)
+               return;
 
-               /* Don't touch classless devices or host bridges or ioapics.  */
-               if (class == PCI_CLASS_NOT_DEFINED ||
-                   class == PCI_CLASS_BRIDGE_HOST)
-                       continue;
+       /* Don't touch ioapic devices already enabled by firmware */
+       if (class == PCI_CLASS_SYSTEM_PIC) {
+               u16 command;
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
+                       return;
+       }
 
-               /* Don't touch ioapic devices already enabled by firmware */
-               if (class == PCI_CLASS_SYSTEM_PIC) {
-                       u16 command;
-                       pci_read_config_word(dev, PCI_COMMAND, &command);
-                       if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
-                               continue;
-               }
+       pdev_sort_resources(dev, head);
+}
 
-               pdev_sort_resources(dev, &head);
-       }
+static void __assign_resources_sorted(struct resource_list *head,
+                                struct resource_list_x *fail_head)
+{
+       struct resource *res;
+       struct resource_list *list, *tmp;
+       int idx;
 
-       for (list = head.next; list;) {
+       for (list = head->next; list;) {
                res = list->res;
                idx = res - &list->dev->resource[0];
+
                if (pci_assign_resource(list->dev, idx)) {
-                       if (fail_head && !pci_is_root_bus(list->dev->bus))
-                               add_to_failed_list(fail_head, list->dev, res);
+                       if (fail_head && !pci_is_root_bus(list->dev->bus)) {
+                               /*
+                                * if the failed res is for ROM BAR, and it will
+                                * be enabled later, don't add it to the list
+                                */
+                               if (!((idx == PCI_ROM_RESOURCE) &&
+                                     (!(res->flags & IORESOURCE_ROM_ENABLE))))
+                                       add_to_failed_list(fail_head, list->dev, res);
+                       }
                        res->start = 0;
                        res->end = 0;
                        res->flags = 0;
@@ -115,6 +122,30 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus,
        }
 }
 
+static void pdev_assign_resources_sorted(struct pci_dev *dev,
+                                struct resource_list_x *fail_head)
+{
+       struct resource_list head;
+
+       head.next = NULL;
+       __dev_sort_resources(dev, &head);
+       __assign_resources_sorted(&head, fail_head);
+
+}
+
+static void pbus_assign_resources_sorted(const struct pci_bus *bus,
+                                        struct resource_list_x *fail_head)
+{
+       struct pci_dev *dev;
+       struct resource_list head;
+
+       head.next = NULL;
+       list_for_each_entry(dev, &bus->devices, bus_list)
+               __dev_sort_resources(dev, &head);
+
+       __assign_resources_sorted(&head, fail_head);
+}
+
 void pci_setup_cardbus(struct pci_bus *bus)
 {
        struct pci_dev *bridge = bus->self;
@@ -273,9 +304,6 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type)
 {
        struct pci_dev *bridge = bus->self;
 
-       if (pci_is_enabled(bridge))
-               return;
-
        dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n",
                 bus->secondary, bus->subordinate);
 
@@ -334,8 +362,11 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
        }
        if (pmem) {
                b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
-               if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
+               if ((pmem & PCI_PREF_RANGE_TYPE_MASK) ==
+                   PCI_PREF_RANGE_TYPE_64) {
                        b_res[2].flags |= IORESOURCE_MEM_64;
+                       b_res[2].flags |= PCI_PREF_RANGE_TYPE_64;
+               }
        }
 
        /* double check if bridge does support 64 bit pref */
@@ -364,8 +395,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon
        unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
                                  IORESOURCE_PREFETCH;
 
-       for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
-               r = bus->resource[i];
+       pci_bus_for_each_resource(bus, r, i) {
                if (r == &ioport_resource || r == &iomem_resource)
                        continue;
                if (r && (r->flags & type_mask) == type && !r->parent)
@@ -646,7 +676,8 @@ static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
 
                switch (dev->class >> 8) {
                case PCI_CLASS_BRIDGE_PCI:
-                       pci_setup_bridge(b);
+                       if (!pci_is_enabled(dev))
+                               pci_setup_bridge(b);
                        break;
 
                case PCI_CLASS_BRIDGE_CARDBUS:
@@ -667,6 +698,34 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus)
 }
 EXPORT_SYMBOL(pci_bus_assign_resources);
 
+static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge,
+                                        struct resource_list_x *fail_head)
+{
+       struct pci_bus *b;
+
+       pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head);
+
+       b = bridge->subordinate;
+       if (!b)
+               return;
+
+       __pci_bus_assign_resources(b, fail_head);
+
+       switch (bridge->class >> 8) {
+       case PCI_CLASS_BRIDGE_PCI:
+               pci_setup_bridge(b);
+               break;
+
+       case PCI_CLASS_BRIDGE_CARDBUS:
+               pci_setup_cardbus(b);
+               break;
+
+       default:
+               dev_info(&bridge->dev, "not setting up bridge for bus "
+                        "%04x:%02x\n", pci_domain_nr(b), b->number);
+               break;
+       }
+}
 static void pci_bridge_release_resources(struct pci_bus *bus,
                                          unsigned long type)
 {
@@ -751,11 +810,10 @@ static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
 
 static void pci_bus_dump_res(struct pci_bus *bus)
 {
-        int i;
-
-        for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
-                struct resource *res = bus->resource[i];
+       struct resource *res;
+       int i;
 
+       pci_bus_for_each_resource(bus, res, i) {
                if (!res || !res->end || !res->flags)
                         continue;
 
@@ -911,3 +969,65 @@ enable_and_dump:
                pci_bus_dump_resources(bus);
        }
 }
+
+void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
+{
+       struct pci_bus *parent = bridge->subordinate;
+       int tried_times = 0;
+       struct resource_list_x head, *list;
+       int retval;
+       unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+                                 IORESOURCE_PREFETCH;
+
+       head.next = NULL;
+
+again:
+       pci_bus_size_bridges(parent);
+       __pci_bridge_assign_resources(bridge, &head);
+       retval = pci_reenable_device(bridge);
+       pci_set_master(bridge);
+       pci_enable_bridges(parent);
+
+       tried_times++;
+
+       if (!head.next)
+               return;
+
+       if (tried_times >= 2) {
+               /* still fail, don't need to try more */
+               free_failed_list(&head);
+               return;
+       }
+
+       printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n",
+                        tried_times + 1);
+
+       /*
+        * Try to release leaf bridge's resources that doesn't fit resource of
+        * child device under that bridge
+        */
+       for (list = head.next; list;) {
+               struct pci_bus *bus = list->dev->bus;
+               unsigned long flags = list->flags;
+
+               pci_bus_release_bridge_resources(bus, flags & type_mask,
+                                                whole_subtree);
+               list = list->next;
+       }
+       /* restore size and flags */
+       for (list = head.next; list;) {
+               struct resource *res = list->res;
+
+               res->start = list->start;
+               res->end = list->end;
+               res->flags = list->flags;
+               if (list->dev->subordinate)
+                       res->flags = 0;
+
+               list = list->next;
+       }
+       free_failed_list(&head);
+
+       goto again;
+}
+EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);