]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/pci/setup-bus.c
Merge branches 'dma-api', 'pci/virtualization', 'pci/msi', 'pci/misc' and 'pci/resour...
[mirror_ubuntu-zesty-kernel.git] / drivers / pci / setup-bus.c
index 138bdd6393be87b2ebc63ba35b1b4edaa228b8ad..d9fdcea4412a927382cff551b448928e5691a89d 100644 (file)
@@ -713,12 +713,11 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
    bus resource of a given type. Note: we intentionally skip
    the bus resources which have already been assigned (that is,
    have non-NULL parent resource). */
-static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned long type)
+static struct resource *find_free_bus_resource(struct pci_bus *bus,
+                        unsigned long type_mask, unsigned long type)
 {
        int i;
        struct resource *r;
-       unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
-                                 IORESOURCE_PREFETCH;
 
        pci_bus_for_each_resource(bus, r, i) {
                if (r == &ioport_resource || r == &iomem_resource)
@@ -815,7 +814,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
                resource_size_t add_size, struct list_head *realloc_head)
 {
        struct pci_dev *dev;
-       struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
+       struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO,
+                                                       IORESOURCE_IO);
        resource_size_t size = 0, size0 = 0, size1 = 0;
        resource_size_t children_add_size = 0;
        resource_size_t min_align, align;
@@ -907,36 +907,40 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
  * @bus : the bus
  * @mask: mask the resource flag, then compare it with type
  * @type: the type of free resource from bridge
+ * @type2: second match type
+ * @type3: third match type
  * @min_size : the minimum memory window that must to be allocated
  * @add_size : additional optional memory window
  * @realloc_head : track the additional memory window on this list
  *
  * Calculate the size of the bus and minimal alignment which
  * guarantees that all child resources fit in this size.
+ *
+ * Returns -ENOSPC if there's no available bus resource of the desired type.
+ * Otherwise, sets the bus resource start/end to indicate the required
+ * size, adds things to realloc_head (if supplied), and returns 0.
  */
 static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
-                        unsigned long type, resource_size_t min_size,
-                       resource_size_t add_size,
-                       struct list_head *realloc_head)
+                        unsigned long type, unsigned long type2,
+                        unsigned long type3,
+                        resource_size_t min_size, resource_size_t add_size,
+                        struct list_head *realloc_head)
 {
        struct pci_dev *dev;
        resource_size_t min_align, align, size, size0, size1;
-       resource_size_t aligns[12];     /* Alignments from 1Mb to 2Gb */
+       resource_size_t aligns[14];     /* Alignments from 1Mb to 8Gb */
        int order, max_order;
-       struct resource *b_res = find_free_bus_resource(bus, type);
-       unsigned int mem64_mask = 0;
+       struct resource *b_res = find_free_bus_resource(bus,
+                                       mask | IORESOURCE_PREFETCH, type);
        resource_size_t children_add_size = 0;
 
        if (!b_res)
-               return 0;
+               return -ENOSPC;
 
        memset(aligns, 0, sizeof(aligns));
        max_order = 0;
        size = 0;
 
-       mem64_mask = b_res->flags & IORESOURCE_MEM_64;
-       b_res->flags &= ~IORESOURCE_MEM_64;
-
        list_for_each_entry(dev, &bus->devices, bus_list) {
                int i;
 
@@ -944,7 +948,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                        struct resource *r = &dev->resource[i];
                        resource_size_t r_size;
 
-                       if (r->parent || (r->flags & mask) != type)
+                       if (r->parent || ((r->flags & mask) != type &&
+                                         (r->flags & mask) != type2 &&
+                                         (r->flags & mask) != type3))
                                continue;
                        r_size = resource_size(r);
 #ifdef CONFIG_PCI_IOV
@@ -957,10 +963,17 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                                continue;
                        }
 #endif
-                       /* For bridges size != alignment */
+                       /*
+                        * aligns[0] is for 1MB (since bridge memory
+                        * windows are always at least 1MB aligned), so
+                        * keep "order" from being negative for smaller
+                        * resources.
+                        */
                        align = pci_resource_alignment(dev, r);
                        order = __ffs(align) - 20;
-                       if (order > 11) {
+                       if (order < 0)
+                               order = 0;
+                       if (order >= ARRAY_SIZE(aligns)) {
                                dev_warn(&dev->dev, "disabling BAR %d: %pR "
                                         "(bad alignment %#llx)\n", i, r,
                                         (unsigned long long) align);
@@ -968,15 +981,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                                continue;
                        }
                        size += r_size;
-                       if (order < 0)
-                               order = 0;
                        /* Exclude ranges with size > align from
                           calculation of the alignment. */
                        if (r_size == align)
                                aligns[order] += align;
                        if (order > max_order)
                                max_order = order;
-                       mem64_mask &= r->flags & IORESOURCE_MEM_64;
 
                        if (realloc_head)
                                children_add_size += get_res_add_size(realloc_head, r);
@@ -997,18 +1007,18 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
                                 "%pR to %pR (unused)\n", b_res,
                                 &bus->busn_res);
                b_res->flags = 0;
-               return 1;
+               return 0;
        }
        b_res->start = min_align;
        b_res->end = size0 + min_align - 1;
-       b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
+       b_res->flags |= IORESOURCE_STARTALIGN;
        if (size1 > size0 && realloc_head) {
                add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
                dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
                                 "%pR to %pR add_size %llx\n", b_res,
                                 &bus->busn_res, (unsigned long long)size1-size0);
        }
-       return 1;
+       return 0;
 }
 
 unsigned long pci_cardbus_resource_alignment(struct resource *res)
@@ -1113,12 +1123,13 @@ handle_done:
        ;
 }
 
-void __ref __pci_bus_size_bridges(struct pci_bus *bus,
-                       struct list_head *realloc_head)
+void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
 {
        struct pci_dev *dev;
-       unsigned long mask, prefmask;
+       unsigned long mask, prefmask, type2 = 0, type3 = 0;
        resource_size_t additional_mem_size = 0, additional_io_size = 0;
+       struct resource *b_res;
+       int ret;
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                struct pci_bus *b = dev->subordinate;
@@ -1152,41 +1163,93 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
                        additional_io_size  = pci_hotplug_io_size;
                        additional_mem_size = pci_hotplug_mem_size;
                }
-               /*
-                * Follow thru
-                */
+               /* Fall through */
        default:
                pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
                             additional_io_size, realloc_head);
-               /* If the bridge supports prefetchable range, size it
-                  separately. If it doesn't, or its prefetchable window
-                  has already been allocated by arch code, try
-                  non-prefetchable range for both types of PCI memory
-                  resources. */
+
+               /*
+                * If there's a 64-bit prefetchable MMIO window, compute
+                * the size required to put all 64-bit prefetchable
+                * resources in it.
+                */
+               b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES];
                mask = IORESOURCE_MEM;
                prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
-               if (pbus_size_mem(bus, prefmask, prefmask,
+               if (b_res[2].flags & IORESOURCE_MEM_64) {
+                       prefmask |= IORESOURCE_MEM_64;
+                       ret = pbus_size_mem(bus, prefmask, prefmask,
+                                 prefmask, prefmask,
                                  realloc_head ? 0 : additional_mem_size,
-                                 additional_mem_size, realloc_head))
-                       mask = prefmask; /* Success, size non-prefetch only. */
-               else
-                       additional_mem_size += additional_mem_size;
-               pbus_size_mem(bus, mask, IORESOURCE_MEM,
+                                 additional_mem_size, realloc_head);
+
+                       /*
+                        * If successful, all non-prefetchable resources
+                        * and any 32-bit prefetchable resources will go in
+                        * the non-prefetchable window.
+                        */
+                       if (ret == 0) {
+                               mask = prefmask;
+                               type2 = prefmask & ~IORESOURCE_MEM_64;
+                               type3 = prefmask & ~IORESOURCE_PREFETCH;
+                       }
+               }
+
+               /*
+                * If there is no 64-bit prefetchable window, compute the
+                * size required to put all prefetchable resources in the
+                * 32-bit prefetchable window (if there is one).
+                */
+               if (!type2) {
+                       prefmask &= ~IORESOURCE_MEM_64;
+                       ret = pbus_size_mem(bus, prefmask, prefmask,
+                                        prefmask, prefmask,
+                                        realloc_head ? 0 : additional_mem_size,
+                                        additional_mem_size, realloc_head);
+
+                       /*
+                        * If successful, only non-prefetchable resources
+                        * will go in the non-prefetchable window.
+                        */
+                       if (ret == 0)
+                               mask = prefmask;
+                       else
+                               additional_mem_size += additional_mem_size;
+
+                       type2 = type3 = IORESOURCE_MEM;
+               }
+
+               /*
+                * Compute the size required to put everything else in the
+                * non-prefetchable window.  This includes:
+                *
+                *   - all non-prefetchable resources
+                *   - 32-bit prefetchable resources if there's a 64-bit
+                *     prefetchable window or no prefetchable window at all
+                *   - 64-bit prefetchable resources if there's no
+                *     prefetchable window at all
+                *
+                * Note that the strategy in __pci_assign_resource() must
+                * match that used here.  Specifically, we cannot put a
+                * 32-bit prefetchable resource in a 64-bit prefetchable
+                * window.
+                */
+               pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3,
                                realloc_head ? 0 : additional_mem_size,
                                additional_mem_size, realloc_head);
                break;
        }
 }
 
-void __ref pci_bus_size_bridges(struct pci_bus *bus)
+void pci_bus_size_bridges(struct pci_bus *bus)
 {
        __pci_bus_size_bridges(bus, NULL);
 }
 EXPORT_SYMBOL(pci_bus_size_bridges);
 
-void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
-                                     struct list_head *realloc_head,
-                                     struct list_head *fail_head)
+void __pci_bus_assign_resources(const struct pci_bus *bus,
+                               struct list_head *realloc_head,
+                               struct list_head *fail_head)
 {
        struct pci_bus *b;
        struct pci_dev *dev;
@@ -1218,15 +1281,15 @@ void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
        }
 }
 
-void __ref pci_bus_assign_resources(const struct pci_bus *bus)
+void pci_bus_assign_resources(const struct pci_bus *bus)
 {
        __pci_bus_assign_resources(bus, NULL, NULL);
 }
 EXPORT_SYMBOL(pci_bus_assign_resources);
 
-static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge,
-                                        struct list_head *add_head,
-                                        struct list_head *fail_head)
+static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
+                                         struct list_head *add_head,
+                                         struct list_head *fail_head)
 {
        struct pci_bus *b;
 
@@ -1257,42 +1320,66 @@ static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge,
 static void pci_bridge_release_resources(struct pci_bus *bus,
                                          unsigned long type)
 {
-       int idx;
-       bool changed = false;
-       struct pci_dev *dev;
+       struct pci_dev *dev = bus->self;
        struct resource *r;
        unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
-                                 IORESOURCE_PREFETCH;
+                                 IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
+       unsigned old_flags = 0;
+       struct resource *b_res;
+       int idx = 1;
 
-       dev = bus->self;
-       for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END;
-            idx++) {
-               r = &dev->resource[idx];
-               if ((r->flags & type_mask) != type)
-                       continue;
-               if (!r->parent)
-                       continue;
-               /*
-                * if there are children under that, we should release them
-                *  all
-                */
-               release_child_resources(r);
-               if (!release_resource(r)) {
-                       dev_printk(KERN_DEBUG, &dev->dev,
-                                "resource %d %pR released\n", idx, r);
-                       /* keep the old size */
-                       r->end = resource_size(r) - 1;
-                       r->start = 0;
-                       r->flags = 0;
-                       changed = true;
-               }
-       }
+       b_res = &dev->resource[PCI_BRIDGE_RESOURCES];
+
+       /*
+        *     1. if there is io port assign fail, will release bridge
+        *        io port.
+        *     2. if there is non pref mmio assign fail, release bridge
+        *        nonpref mmio.
+        *     3. if there is 64bit pref mmio assign fail, and bridge pref
+        *        is 64bit, release bridge pref mmio.
+        *     4. if there is pref mmio assign fail, and bridge pref is
+        *        32bit mmio, release bridge pref mmio
+        *     5. if there is pref mmio assign fail, and bridge pref is not
+        *        assigned, release bridge nonpref mmio.
+        */
+       if (type & IORESOURCE_IO)
+               idx = 0;
+       else if (!(type & IORESOURCE_PREFETCH))
+               idx = 1;
+       else if ((type & IORESOURCE_MEM_64) &&
+                (b_res[2].flags & IORESOURCE_MEM_64))
+               idx = 2;
+       else if (!(b_res[2].flags & IORESOURCE_MEM_64) &&
+                (b_res[2].flags & IORESOURCE_PREFETCH))
+               idx = 2;
+       else
+               idx = 1;
+
+       r = &b_res[idx];
+
+       if (!r->parent)
+               return;
+
+       /*
+        * if there are children under that, we should release them
+        *  all
+        */
+       release_child_resources(r);
+       if (!release_resource(r)) {
+               type = old_flags = r->flags & type_mask;
+               dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
+                                       PCI_BRIDGE_RESOURCES + idx, r);
+               /* keep the old size */
+               r->end = resource_size(r) - 1;
+               r->start = 0;
+               r->flags = 0;
 
-       if (changed) {
                /* avoiding touch the one without PREF */
                if (type & IORESOURCE_PREFETCH)
                        type = IORESOURCE_PREFETCH;
                __pci_setup_bridge(bus, type);
+               /* for next child res under same bridge */
+               r->flags = old_flags;
        }
 }
 
@@ -1304,9 +1391,9 @@ enum release_type {
  * try to release pci bridge resources that is from leaf bridge,
  * so we can allocate big new one later
  */
-static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
-                                                  unsigned long type,
-                                                  enum release_type rel_type)
+static void pci_bus_release_bridge_resources(struct pci_bus *bus,
+                                            unsigned long type,
+                                            enum release_type rel_type)
 {
        struct pci_dev *dev;
        bool is_leaf_bridge = true;
@@ -1471,7 +1558,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
        LIST_HEAD(fail_head);
        struct pci_dev_resource *fail_res;
        unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
-                                 IORESOURCE_PREFETCH;
+                                 IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
        int pci_try_num = 1;
        enum enable_type enable_local;