diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 2464 |
1 files changed, 1742 insertions, 722 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d254e2379533..6e90f46f52af 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1,15 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * drivers/pci/setup-bus.c + * Support routines for initializing a PCI subsystem * * Extruded from code written by * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) * David Miller (davem@redhat.com) * - * Support routines for initializing a PCI subsystem. - */ - -/* * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> * PCI-PCI bridges cleanup, sorted resource allocation. * Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru> @@ -17,6 +14,8 @@ * tighter packing. Prefetchable range support. */ +#include <linux/bitops.h> +#include <linux/bug.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -24,11 +23,18 @@ #include <linux/errno.h> #include <linux/ioport.h> #include <linux/cache.h> +#include <linux/limits.h> +#include <linux/sizes.h> #include <linux/slab.h> -#include <asm-generic/pci-bridge.h> +#include <linux/acpi.h> #include "pci.h" +#define PCI_RES_TYPE_MASK \ + (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ + IORESOURCE_MEM_64) + unsigned int pci_flags; +EXPORT_SYMBOL_GPL(pci_flags); struct pci_dev_resource { struct list_head list; @@ -52,25 +58,22 @@ static void free_list(struct list_head *head) } /** - * add_to_list() - add a new resource tracker to the list + * add_to_list() - Add a new resource tracker to the list * @head: Head of the list - * @dev: device corresponding to which the resource - * belongs - * @res: The resource to be tracked - * @add_size: additional size to be optionally added - * to the resource + * @dev: Device to which the resource belongs + * @res: Resource to be tracked + * @add_size: Additional size to be optionally added to the resource + * @min_align: Minimum memory window alignment */ -static int add_to_list(struct list_head *head, - struct pci_dev *dev, struct resource *res, - resource_size_t add_size, resource_size_t min_align) +static int add_to_list(struct list_head *head, struct pci_dev *dev, + struct resource *res, resource_size_t add_size, + resource_size_t min_align) { struct pci_dev_resource *tmp; tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) { - pr_warning("add_to_list: kmalloc() failed!\n"); + if (!tmp) return -ENOMEM; - } tmp->res = res; tmp->dev = dev; @@ -85,8 +88,7 @@ static int add_to_list(struct list_head *head, return 0; } -static void remove_from_list(struct list_head *head, - struct resource *res) +static void remove_from_list(struct list_head *head, struct resource *res) { struct pci_dev_resource *dev_res, *tmp; @@ -99,61 +101,260 @@ static void remove_from_list(struct list_head *head, } } +static struct pci_dev_resource *res_to_dev_res(struct list_head *head, + struct resource *res) +{ + struct pci_dev_resource *dev_res; + + list_for_each_entry(dev_res, head, list) { + if (dev_res->res == res) + return dev_res; + } + + return NULL; +} + static resource_size_t get_res_add_size(struct list_head *head, struct resource *res) { struct pci_dev_resource *dev_res; - list_for_each_entry(dev_res, head, list) { - if (dev_res->res == res) { - int idx = res - &dev_res->dev->resource[0]; + dev_res = res_to_dev_res(head, res); + return dev_res ? dev_res->add_size : 0; +} - dev_printk(KERN_DEBUG, &dev_res->dev->dev, - "res[%d]=%pR get_res_add_size add_size %llx\n", - idx, dev_res->res, - (unsigned long long)dev_res->add_size); +static resource_size_t get_res_add_align(struct list_head *head, + struct resource *res) +{ + struct pci_dev_resource *dev_res; - return dev_res->add_size; - } + dev_res = res_to_dev_res(head, res); + return dev_res ? dev_res->min_align : 0; +} + +static void restore_dev_resource(struct pci_dev_resource *dev_res) +{ + struct resource *res = dev_res->res; + + if (WARN_ON_ONCE(res->parent)) + return; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; +} + +/* + * Helper function for sizing routines. Assigned resources have non-NULL + * parent resource. + * + * Return first unassigned resource of the correct type. If there is none, + * return first assigned resource of the correct type. If none of the + * above, return NULL. + * + * Returning an assigned resource of the correct type allows the caller to + * distinguish between already assigned and no resource of the correct type. + */ +static struct resource *find_bus_resource_of_type(struct pci_bus *bus, + unsigned long type_mask, + unsigned long type) +{ + struct resource *r, *r_assigned = NULL; + + pci_bus_for_each_resource(bus, r) { + if (!r || r == &ioport_resource || r == &iomem_resource) + continue; + + if ((r->flags & type_mask) != type) + continue; + + if (!r->parent) + return r; + if (!r_assigned) + r_assigned = r; } + return r_assigned; +} - return 0; +/** + * pbus_select_window_for_type - Select bridge window for a resource type + * @bus: PCI bus + * @type: Resource type (resource flags can be passed as is) + * + * Select the bridge window based on a resource @type. + * + * For memory resources, the selection is done as follows: + * + * Any non-prefetchable resource is put into the non-prefetchable window. + * + * If there is no prefetchable MMIO window, put all memory resources into the + * non-prefetchable window. + * + * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable + * resources into it and place 32-bit prefetchable memory into the + * non-prefetchable window. + * + * Otherwise, put all prefetchable resources into the prefetchable window. + * + * Return: the bridge window resource or NULL if no bridge window is found. + */ +static struct resource *pbus_select_window_for_type(struct pci_bus *bus, + unsigned long type) +{ + int iores_type = type & IORESOURCE_TYPE_BITS; /* w/o 64bit & pref */ + struct resource *mmio, *mmio_pref, *win; + + type &= PCI_RES_TYPE_MASK; /* with 64bit & pref */ + + if ((iores_type != IORESOURCE_IO) && (iores_type != IORESOURCE_MEM)) + return NULL; + + if (pci_is_root_bus(bus)) { + win = find_bus_resource_of_type(bus, type, type); + if (win) + return win; + + type &= ~IORESOURCE_MEM_64; + win = find_bus_resource_of_type(bus, type, type); + if (win) + return win; + + type &= ~IORESOURCE_PREFETCH; + return find_bus_resource_of_type(bus, type, type); + } + + switch (iores_type) { + case IORESOURCE_IO: + return pci_bus_resource_n(bus, PCI_BUS_BRIDGE_IO_WINDOW); + + case IORESOURCE_MEM: + mmio = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_MEM_WINDOW); + mmio_pref = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_PREF_MEM_WINDOW); + + if (!(type & IORESOURCE_PREFETCH) || + !(mmio_pref->flags & IORESOURCE_MEM)) + return mmio; + + if ((type & IORESOURCE_MEM_64) || + !(mmio_pref->flags & IORESOURCE_MEM_64)) + return mmio_pref; + + return mmio; + default: + return NULL; + } +} + +/** + * pbus_select_window - Select bridge window for a resource + * @bus: PCI bus + * @res: Resource + * + * Select the bridge window for @res. If the resource is already assigned, + * return the current bridge window. + * + * For memory resources, the selection is done as follows: + * + * Any non-prefetchable resource is put into the non-prefetchable window. + * + * If there is no prefetchable MMIO window, put all memory resources into the + * non-prefetchable window. + * + * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable + * resources into it and place 32-bit prefetchable memory into the + * non-prefetchable window. + * + * Otherwise, put all prefetchable resources into the prefetchable window. + * + * Return: the bridge window resource or NULL if no bridge window is found. + */ +struct resource *pbus_select_window(struct pci_bus *bus, + const struct resource *res) +{ + if (res->parent) + return res->parent; + + return pbus_select_window_for_type(bus, res->flags); +} + +static bool pdev_resources_assignable(struct pci_dev *dev) +{ + u16 class = dev->class >> 8, command; + + /* Don't touch classless devices or host bridges or IOAPICs */ + if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) + return false; + + /* Don't touch IOAPIC devices already enabled by firmware */ + if (class == PCI_CLASS_SYSTEM_PIC) { + pci_read_config_word(dev, PCI_COMMAND, &command); + if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) + return false; + } + + return true; +} + +static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) +{ + int idx = pci_resource_num(dev, res); + + if (!res->flags) + return false; + + if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END && + res->flags & IORESOURCE_DISABLED) + return false; + + return true; +} + +static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res) +{ + if (res->parent) + return false; + + if (res->flags & IORESOURCE_PCI_FIXED) + return false; + + return pdev_resource_assignable(dev, res); } /* Sort resources by alignment */ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) { + struct resource *r; int i; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r; + if (!pdev_resources_assignable(dev)) + return; + + pci_dev_for_each_resource(dev, r, i) { + const char *r_name = pci_resource_name(dev, i); struct pci_dev_resource *dev_res, *tmp; resource_size_t r_align; struct list_head *n; - r = &dev->resource[i]; - - if (r->flags & IORESOURCE_PCI_FIXED) - continue; - - if (!(r->flags) || r->parent) + if (!pdev_resource_should_fit(dev, r)) continue; r_align = pci_resource_alignment(dev, r); if (!r_align) { - dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", - i, r); + pci_warn(dev, "%s %pR: alignment must not be zero\n", + r_name, r); continue; } tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) - panic("pdev_sort_resources(): " - "kmalloc() failed!\n"); + panic("%s: kzalloc() failed!\n", __func__); tmp->res = r; tmp->dev = dev; + tmp->start = r->start; + tmp->end = r->end; + tmp->flags = r->flags; - /* fallback is smallest one or list is empty*/ + /* Fallback is smallest one or list is empty */ n = head; list_for_each_entry(dev_res, head, list) { resource_size_t align; @@ -166,94 +367,102 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) break; } } - /* Insert it just before n*/ + /* Insert it just before n */ list_add_tail(&tmp->list, n); } } -static void __dev_sort_resources(struct pci_dev *dev, - struct list_head *head) +bool pci_resource_is_optional(const struct pci_dev *dev, int resno) { - u16 class = dev->class >> 8; + const struct resource *res = pci_resource_n(dev, resno); - /* Don't touch classless devices or host bridges or ioapics. */ - if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) - return; + if (pci_resource_is_iov(resno)) + return true; + if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE)) + return true; - /* 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; - } - - pdev_sort_resources(dev, head); + return false; } -static inline void reset_resource(struct resource *res) +static inline void reset_resource(struct pci_dev *dev, struct resource *res) { + int idx = pci_resource_num(dev, res); + + if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) { + res->flags |= IORESOURCE_UNSET; + return; + } + res->start = 0; res->end = 0; res->flags = 0; } /** - * reassign_resources_sorted() - satisfy any additional resource requests + * reassign_resources_sorted() - Satisfy any additional resource requests * - * @realloc_head : head of the list tracking requests requiring additional - * resources - * @head : head of the list tracking requests with allocated - * resources + * @realloc_head: Head of the list tracking requests requiring + * additional resources + * @head: Head of the list tracking requests with allocated + * resources * - * Walk through each element of the realloc_head and try to procure - * additional resources for the element, provided the element - * is in the head list. + * Walk through each element of the realloc_head and try to procure additional + * resources for the element, provided the element is in the head list. */ static void reassign_resources_sorted(struct list_head *realloc_head, - struct list_head *head) + struct list_head *head) { - struct resource *res; struct pci_dev_resource *add_res, *tmp; struct pci_dev_resource *dev_res; - resource_size_t add_size; + struct pci_dev *dev; + struct resource *res; + const char *res_name; + resource_size_t add_size, align; int idx; list_for_each_entry_safe(add_res, tmp, realloc_head, list) { bool found_match = false; res = add_res->res; - /* skip resource that has been reset */ - if (!res->flags) + dev = add_res->dev; + idx = pci_resource_num(dev, res); + + /* + * Skip resource that failed the earlier assignment and is + * not optional as it would just fail again. + */ + if (!res->parent && resource_size(res) && + !pci_resource_is_optional(dev, idx)) goto out; - /* skip this resource if not found in head list */ + /* Skip this resource if not found in head list */ list_for_each_entry(dev_res, head, list) { if (dev_res->res == res) { found_match = true; break; } } - if (!found_match)/* just skip */ + if (!found_match) /* Just skip */ continue; - idx = res - &add_res->dev->resource[0]; + res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; - if (!resource_size(res)) { - res->start = add_res->start; - res->end = res->start + add_size - 1; - if (pci_assign_resource(add_res->dev, idx)) - reset_resource(res); - } else { - resource_size_t align = add_res->min_align; + align = add_res->min_align; + if (!res->parent) { + resource_set_range(res, align, + resource_size(res) + add_size); + if (pci_assign_resource(dev, idx)) { + pci_dbg(dev, + "%s %pR: ignoring failure in optional allocation\n", + res_name, res); + } + } else if (add_size > 0) { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); - if (pci_reassign_resource(add_res->dev, idx, - add_size, align)) - dev_printk(KERN_DEBUG, &add_res->dev->dev, - "failed to add %llx res[%d]=%pR\n", - (unsigned long long)add_size, - idx, res); + if (pci_reassign_resource(dev, idx, add_size, align)) + pci_info(dev, "%s %pR: failed to add optional %llx\n", + res_name, res, + (unsigned long long) add_size); } out: list_del(&add_res->list); @@ -262,125 +471,292 @@ out: } /** - * assign_requested_resources_sorted() - satisfy resource requests + * assign_requested_resources_sorted() - Satisfy resource requests * - * @head : head of the list tracking requests for resources - * @fail_head : head of the list tracking requests that could - * not be allocated + * @head: Head of the list tracking requests for resources + * @fail_head: Head of the list tracking requests that could not be + * allocated + * @optional: Assign also optional resources * - * Satisfy resource requests of each element in the list. Add - * requests that could not satisfied to the failed_list. + * Satisfy resource requests of each element in the list. Add requests that + * could not be satisfied to the failed_list. */ static void assign_requested_resources_sorted(struct list_head *head, - struct list_head *fail_head) + struct list_head *fail_head, + bool optional) { - struct resource *res; struct pci_dev_resource *dev_res; + struct resource *res; + struct pci_dev *dev; + bool optional_res; int idx; list_for_each_entry(dev_res, head, list) { res = dev_res->res; - idx = res - &dev_res->dev->resource[0]; - if (resource_size(res) && - pci_assign_resource(dev_res->dev, idx)) { + dev = dev_res->dev; + idx = pci_resource_num(dev, res); + optional_res = pci_resource_is_optional(dev, idx); + + if (!resource_size(res)) + continue; + + if (!optional && optional_res) + continue; + + if (pci_assign_resource(dev, idx)) { if (fail_head) { - /* - * 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_list(fail_head, - dev_res->dev, res, - 0 /* dont care */, - 0 /* dont care */); + add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } - reset_resource(res); } } } +static unsigned long pci_fail_res_type_mask(struct list_head *fail_head) +{ + struct pci_dev_resource *fail_res; + unsigned long mask = 0; + + /* Check failed type */ + list_for_each_entry(fail_res, fail_head, list) + mask |= fail_res->flags; + + /* + * One pref failed resource will set IORESOURCE_MEM, as we can + * allocate pref in non-pref range. Will release all assigned + * non-pref sibling resources according to that bit. + */ + return mask & (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH); +} + +static bool pci_need_to_release(unsigned long mask, struct resource *res) +{ + if (res->flags & IORESOURCE_IO) + return !!(mask & IORESOURCE_IO); + + /* Check pref at first */ + if (res->flags & IORESOURCE_PREFETCH) { + if (mask & IORESOURCE_PREFETCH) + return true; + /* Count pref if its parent is non-pref */ + else if ((mask & IORESOURCE_MEM) && + !(res->parent->flags & IORESOURCE_PREFETCH)) + return true; + else + return false; + } + + if (res->flags & IORESOURCE_MEM) + return !!(mask & IORESOURCE_MEM); + + return false; /* Should not get here */ +} + +/* Return: @true if assignment of a required resource failed. */ +static bool pci_required_resource_failed(struct list_head *fail_head, + unsigned long type) +{ + struct pci_dev_resource *fail_res; + + type &= PCI_RES_TYPE_MASK; + + list_for_each_entry(fail_res, fail_head, list) { + int idx = pci_resource_num(fail_res->dev, fail_res->res); + + if (type && (fail_res->flags & PCI_RES_TYPE_MASK) != type) + continue; + + if (!pci_resource_is_optional(fail_res->dev, idx)) + return true; + } + return false; +} + static void __assign_resources_sorted(struct list_head *head, - struct list_head *realloc_head, - struct list_head *fail_head) + struct list_head *realloc_head, + struct list_head *fail_head) { /* - * Should not assign requested resources at first. - * they could be adjacent, so later reassign can not reallocate - * them one by one in parent resource window. - * Try to assign requested + add_size at beginning - * if could do that, could get out early. - * if could not do that, we still try to assign requested at first, - * then try to reassign add_size for some resources. + * Should not assign requested resources at first. They could be + * adjacent, so later reassign can not reallocate them one by one in + * parent resource window. + * + * Try to assign required and any optional resources at beginning + * (add_size included). If all required resources were successfully + * assigned, get out early. If could not do that, we still try to + * assign required at first, then try to reassign some optional + * resources. + * + * Separate three resource type checking if we need to release + * assigned resource after requested + add_size try. + * + * 1. If IO port assignment fails, will release assigned IO + * port. + * 2. If pref MMIO assignment fails, release assigned pref + * MMIO. If assigned pref MMIO's parent is non-pref MMIO + * and non-pref MMIO assignment fails, will release that + * assigned pref MMIO. + * 3. If non-pref MMIO assignment fails or pref MMIO + * assignment fails, will release assigned non-pref MMIO. */ LIST_HEAD(save_head); LIST_HEAD(local_fail_head); + LIST_HEAD(dummy_head); struct pci_dev_resource *save_res; - struct pci_dev_resource *dev_res; + struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; + struct resource *res; + struct pci_dev *dev; + unsigned long fail_type; + resource_size_t add_align, align; + + if (!realloc_head) + realloc_head = &dummy_head; /* Check if optional add_size is there */ - if (!realloc_head || list_empty(realloc_head)) - goto requested_and_reassign; + if (list_empty(realloc_head)) + goto assign; /* Save original start, end, flags etc at first */ list_for_each_entry(dev_res, head, list) { if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { free_list(&save_head); - goto requested_and_reassign; + goto assign; } } /* Update res in head list with add_size in realloc_head list */ - list_for_each_entry(dev_res, head, list) - dev_res->res->end += get_res_add_size(realloc_head, - dev_res->res); + list_for_each_entry_safe(dev_res, tmp_res, head, list) { + res = dev_res->res; + + res->end += get_res_add_size(realloc_head, res); + + /* + * There are two kinds of additional resources in the list: + * 1. bridge resource -- IORESOURCE_STARTALIGN + * 2. SR-IOV resource -- IORESOURCE_SIZEALIGN + * Here just fix the additional alignment for bridge + */ + if (!(res->flags & IORESOURCE_STARTALIGN)) + continue; + + add_align = get_res_add_align(realloc_head, res); + + /* + * The "head" list is sorted by alignment so resources with + * bigger alignment will be assigned first. After we + * change the alignment of a dev_res in "head" list, we + * need to reorder the list by alignment to make it + * consistent. + */ + if (add_align > res->start) { + resource_set_range(res, add_align, resource_size(res)); + + list_for_each_entry(dev_res2, head, list) { + align = pci_resource_alignment(dev_res2->dev, + dev_res2->res); + if (add_align > align) { + list_move_tail(&dev_res->list, + &dev_res2->list); + break; + } + } + } + + } - /* Try updated head list with add_size added */ - assign_requested_resources_sorted(head, &local_fail_head); +assign: + assign_requested_resources_sorted(head, &local_fail_head, true); - /* all assigned with add_size ? */ + /* All non-optional resources assigned? */ if (list_empty(&local_fail_head)) { /* Remove head list from realloc_head list */ list_for_each_entry(dev_res, head, list) remove_from_list(realloc_head, dev_res->res); free_list(&save_head); - free_list(head); - return; + goto out; + } + + /* Without realloc_head and only optional fails, nothing more to do. */ + if (!pci_required_resource_failed(&local_fail_head, 0) && + list_empty(realloc_head)) { + list_for_each_entry(save_res, &save_head, list) { + struct resource *res = save_res->res; + + if (res->parent) + continue; + + restore_dev_resource(save_res); + } + free_list(&local_fail_head); + free_list(&save_head); + goto out; + } + + /* Check failed type */ + fail_type = pci_fail_res_type_mask(&local_fail_head); + /* Remove not need to be released assigned res from head list etc */ + list_for_each_entry_safe(dev_res, tmp_res, head, list) { + res = dev_res->res; + + if (res->parent && !pci_need_to_release(fail_type, res)) { + /* Remove it from realloc_head list */ + remove_from_list(realloc_head, res); + remove_from_list(&save_head, res); + list_del(&dev_res->list); + kfree(dev_res); + } } free_list(&local_fail_head); /* Release assigned resource */ - list_for_each_entry(dev_res, head, list) - if (dev_res->res->parent) - release_resource(dev_res->res); - /* Restore start/end/flags from saved list */ - list_for_each_entry(save_res, &save_head, list) { - struct resource *res = save_res->res; + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + dev = dev_res->dev; - res->start = save_res->start; - res->end = save_res->end; - res->flags = save_res->flags; + pci_release_resource(dev, pci_resource_num(dev, res)); + restore_dev_resource(dev_res); } + /* Restore start/end/flags from saved list */ + list_for_each_entry(save_res, &save_head, list) + restore_dev_resource(save_res); free_list(&save_head); -requested_and_reassign: /* Satisfy the must-have resource requests */ - assign_requested_resources_sorted(head, fail_head); + assign_requested_resources_sorted(head, NULL, false); - /* Try to satisfy any additional optional resource - requests */ - if (realloc_head) + /* Try to satisfy any additional optional resource requests */ + if (!list_empty(realloc_head)) reassign_resources_sorted(realloc_head, head); + +out: + /* Reset any failed resource, cannot use fail_head as it can be NULL. */ + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + dev = dev_res->dev; + + if (res->parent) + continue; + + if (fail_head) { + add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); + } + + reset_resource(dev, res); + } + free_list(head); } static void pdev_assign_resources_sorted(struct pci_dev *dev, - struct list_head *add_head, - struct list_head *fail_head) + struct list_head *add_head, + struct list_head *fail_head) { LIST_HEAD(head); - __dev_sort_resources(dev, &head); + pdev_sort_resources(dev, &head); __assign_resources_sorted(&head, add_head, fail_head); } @@ -393,7 +769,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, LIST_HEAD(head); list_for_each_entry(dev, &bus->devices, bus_list) - __dev_sort_resources(dev, &head); + pdev_sort_resources(dev, &head); __assign_resources_sorted(&head, realloc_head, fail_head); } @@ -404,17 +780,17 @@ void pci_setup_cardbus(struct pci_bus *bus) struct resource *res; struct pci_bus_region region; - dev_info(&bridge->dev, "CardBus bridge to %pR\n", + pci_info(bridge, "CardBus bridge to %pR\n", &bus->busn_res); res = bus->resource[0]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_IO) { + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_IO) { /* * The IO resource is allocated a range twice as large as it * would normally need. This allows us to set both IO regs. */ - dev_info(&bridge->dev, " bridge window %pR\n", res); + pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, region.start); pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, @@ -422,9 +798,9 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[1]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_IO) { - dev_info(&bridge->dev, " bridge window %pR\n", res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_IO) { + pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, region.start); pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, @@ -432,9 +808,9 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[2]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_MEM) { - dev_info(&bridge->dev, " bridge window %pR\n", res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, region.start); pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, @@ -442,9 +818,9 @@ void pci_setup_cardbus(struct pci_bus *bus) } res = bus->resource[3]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_MEM) { - dev_info(&bridge->dev, " bridge window %pR\n", res); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, region.start); pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, @@ -453,105 +829,112 @@ void pci_setup_cardbus(struct pci_bus *bus) } EXPORT_SYMBOL(pci_setup_cardbus); -/* Initialize bridges with base/limit values we have collected. - PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998) - requires that if there is no I/O ports or memory behind the - bridge, corresponding range must be turned off by writing base - value greater than limit to the bridge's base/limit registers. - - Note: care must be taken when updating I/O base/limit registers - of bridges which support 32-bit I/O. This update requires two - config space writes, so it's quite possible that an I/O window of - the bridge will have some undesirable address (e.g. 0) after the - first write. Ditto 64-bit prefetchable MMIO. */ -static void pci_setup_bridge_io(struct pci_bus *bus) +/* + * Initialize bridges with base/limit values we have collected. PCI-to-PCI + * Bridge Architecture Specification rev. 1.1 (1998) requires that if there + * are no I/O ports or memory behind the bridge, the corresponding range + * must be turned off by writing base value greater than limit to the + * bridge's base/limit registers. + * + * Note: care must be taken when updating I/O base/limit registers of + * bridges which support 32-bit I/O. This update requires two config space + * writes, so it's quite possible that an I/O window of the bridge will + * have some undesirable address (e.g. 0) after the first write. Ditto + * 64-bit prefetchable MMIO. + */ +static void pci_setup_bridge_io(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; + const char *res_name; struct pci_bus_region region; unsigned long io_mask; u8 io_base_lo, io_limit_lo; - u32 l, io_upper16; + u16 l; + u32 io_upper16; io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) io_mask = PCI_IO_1K_RANGE_MASK; - /* Set up the top and bottom of the PCI I/O segment for this bus. */ - res = bus->resource[0]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_IO) { - pci_read_config_dword(bridge, PCI_IO_BASE, &l); - l &= 0xffff0000; + /* Set up the top and bottom of the PCI I/O segment for this bus */ + res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_IO) { + pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; - l |= ((u32) io_limit_lo << 8) | io_base_lo; - /* Set up upper 16 bits of I/O base/limit. */ + l = ((u16) io_limit_lo << 8) | io_base_lo; + /* Set up upper 16 bits of I/O base/limit */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); - dev_info(&bridge->dev, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { - /* Clear upper 16 bits of I/O base/limit. */ + /* Clear upper 16 bits of I/O base/limit */ io_upper16 = 0; l = 0x00f0; } - /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ + /* Temporarily disable the I/O range before updating PCI_IO_BASE */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); - /* Update lower 16 bits of I/O base/limit. */ - pci_write_config_dword(bridge, PCI_IO_BASE, l); - /* Update upper 16 bits of I/O base/limit. */ + /* Update lower 16 bits of I/O base/limit */ + pci_write_config_word(bridge, PCI_IO_BASE, l); + /* Update upper 16 bits of I/O base/limit */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); } -static void pci_setup_bridge_mmio(struct pci_bus *bus) +static void pci_setup_bridge_mmio(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; + const char *res_name; struct pci_bus_region region; u32 l; - /* Set up the top and bottom of the PCI Memory segment for this bus. */ - res = bus->resource[1]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_MEM) { + /* Set up the top and bottom of the PCI Memory segment for this bus */ + res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; - dev_info(&bridge->dev, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { l = 0x0000fff0; } pci_write_config_dword(bridge, PCI_MEMORY_BASE, l); } -static void pci_setup_bridge_mmio_pref(struct pci_bus *bus) +static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; + const char *res_name; struct pci_bus_region region; u32 l, bu, lu; - /* Clear out the upper 32 bits of PREF limit. - If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily - disables PREF range, which is ok. */ + /* + * Clear out the upper 32 bits of PREF limit. If + * PCI_PREF_BASE_UPPER32 was non-zero, this temporarily disables + * PREF range, which is ok. + */ pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); - /* Set up PREF base/limit. */ + /* Set up PREF base/limit */ bu = lu = 0; - res = bus->resource[2]; - pcibios_resource_to_bus(bridge, ®ion, res); - if (res->flags & IORESOURCE_PREFETCH) { + res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW); + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (res->parent && res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; if (res->flags & IORESOURCE_MEM_64) { bu = upper_32_bits(region.start); lu = upper_32_bits(region.end); } - dev_info(&bridge->dev, " bridge window %pR\n", res); + pci_info(bridge, " %s %pR\n", res_name, res); } else { l = 0x0000fff0; } pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); - /* Set the upper 32 bits of PREF base & limit. */ + /* Set the upper 32 bits of PREF base & limit */ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); } @@ -560,141 +943,143 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) { struct pci_dev *bridge = bus->self; - dev_info(&bridge->dev, "PCI bridge to %pR\n", - &bus->busn_res); + pci_info(bridge, "PCI bridge to %pR\n", &bus->busn_res); if (type & IORESOURCE_IO) - pci_setup_bridge_io(bus); + pci_setup_bridge_io(bridge); if (type & IORESOURCE_MEM) - pci_setup_bridge_mmio(bus); + pci_setup_bridge_mmio(bridge); if (type & IORESOURCE_PREFETCH) - pci_setup_bridge_mmio_pref(bus); + pci_setup_bridge_mmio_pref(bridge); pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } -void pci_setup_bridge(struct pci_bus *bus) +static void pci_setup_one_bridge_window(struct pci_dev *bridge, int resno) +{ + switch (resno) { + case PCI_BRIDGE_IO_WINDOW: + pci_setup_bridge_io(bridge); + break; + case PCI_BRIDGE_MEM_WINDOW: + pci_setup_bridge_mmio(bridge); + break; + case PCI_BRIDGE_PREF_MEM_WINDOW: + pci_setup_bridge_mmio_pref(bridge); + break; + default: + return; + } +} + +void __weak pcibios_setup_bridge(struct pci_bus *bus, unsigned long type) +{ +} + +static void pci_setup_bridge(struct pci_bus *bus) { unsigned long type = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; + pcibios_setup_bridge(bus, type); __pci_setup_bridge(bus, type); } -/* Check whether the bridge supports optional I/O and - prefetchable memory ranges. If not, the respective - base/limit registers must be read-only and read as 0. */ + +int pci_claim_bridge_resource(struct pci_dev *bridge, int i) +{ + int ret = -EINVAL; + + if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) + return 0; + + if (pci_claim_resource(bridge, i) == 0) + return 0; /* Claimed the window */ + + if ((bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) + return 0; + + if (i > PCI_BRIDGE_PREF_MEM_WINDOW) + return -EINVAL; + + /* Try to clip the resource and claim the smaller window */ + if (pci_bus_clip_resource(bridge, i)) + ret = pci_claim_resource(bridge, i); + + pci_setup_one_bridge_window(bridge, i); + + return ret; +} + +/* + * Check whether the bridge supports optional I/O and prefetchable memory + * ranges. If not, the respective base/limit registers must be read-only + * and read as 0. + */ static void pci_bridge_check_ranges(struct pci_bus *bus) { - u16 io; - u32 pmem; struct pci_dev *bridge = bus->self; struct resource *b_res; - b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; - b_res[1].flags |= IORESOURCE_MEM; - - pci_read_config_word(bridge, PCI_IO_BASE, &io); - if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); - pci_read_config_word(bridge, PCI_IO_BASE, &io); - pci_write_config_word(bridge, PCI_IO_BASE, 0x0); - } - if (io) - b_res[0].flags |= IORESOURCE_IO; - /* DECchip 21050 pass 2 errata: the bridge may miss an address - disconnect boundary by one PCI data phase. - Workaround: do not use prefetching on this device. */ - if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) - return; - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - if (!pmem) { - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xfff0fff0); - pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); - pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); - } - if (pmem) { - b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - 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; - } - } + b_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; + b_res->flags |= IORESOURCE_MEM; - /* double check if bridge does support 64 bit pref */ - if (b_res[2].flags & IORESOURCE_MEM_64) { - u32 mem_base_hi, tmp; - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, - &mem_base_hi); - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - 0xffffffff); - pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); - if (!tmp) - b_res[2].flags &= ~IORESOURCE_MEM_64; - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, - mem_base_hi); + if (bridge->io_window) { + b_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; + b_res->flags |= IORESOURCE_IO; } -} - -/* Helper function for sizing routines: find first available - 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) -{ - 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) - continue; - if (r && (r->flags & type_mask) == type && !r->parent) - return r; + if (bridge->pref_window) { + b_res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + if (bridge->pref_64_window) { + b_res->flags |= IORESOURCE_MEM_64 | + PCI_PREF_RANGE_TYPE_64; + } } - return NULL; } static resource_size_t calculate_iosize(resource_size_t size, - resource_size_t min_size, - resource_size_t size1, - resource_size_t old_size, - resource_size_t align) + resource_size_t min_size, + resource_size_t size1, + resource_size_t add_size, + resource_size_t children_add_size, + resource_size_t old_size, + resource_size_t align) { if (size < min_size) size = min_size; - if (old_size == 1 ) + if (old_size == 1) old_size = 0; - /* To be fixed in 2.5: we should have sort of HAVE_ISA - flag in the struct pci_bus. */ + /* + * To be fixed in 2.5: we should have sort of HAVE_ISA flag in the + * struct pci_bus. + */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA) size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif - size = ALIGN(size + size1, align); - if (size < old_size) - size = old_size; - return size; + size = size + size1; + + size = max(size, add_size) + children_add_size; + return ALIGN(max(size, old_size), align); } static resource_size_t calculate_memsize(resource_size_t size, - resource_size_t min_size, - resource_size_t size1, - resource_size_t old_size, - resource_size_t align) + resource_size_t min_size, + resource_size_t add_size, + resource_size_t children_add_size, + resource_size_t old_size, + resource_size_t align) { if (size < min_size) size = min_size; - if (old_size == 1 ) + if (old_size == 1) old_size = 0; - if (size < old_size) - size = old_size; - size = ALIGN(size + size1, align); - return size; + + size = max(size, add_size) + children_add_size; + return ALIGN(max(size, old_size), align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, @@ -703,12 +1088,11 @@ resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, return 1; } -#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ -#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ -#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ +#define PCI_P2P_DEFAULT_MEM_ALIGN SZ_1M +#define PCI_P2P_DEFAULT_IO_ALIGN SZ_4K +#define PCI_P2P_DEFAULT_IO_ALIGN_1K SZ_1K -static resource_size_t window_alignment(struct pci_bus *bus, - unsigned long type) +static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) { resource_size_t align = 1, arch_align; @@ -716,10 +1100,10 @@ static resource_size_t window_alignment(struct pci_bus *bus, align = PCI_P2P_DEFAULT_MEM_ALIGN; else if (type & IORESOURCE_IO) { /* - * Per spec, I/O windows are 4K-aligned, but some - * bridges have an extension to support 1K alignment. + * Per spec, I/O windows are 4K-aligned, but some bridges have + * an extension to support 1K alignment. */ - if (bus->self->io_window_1k) + if (bus->self && bus->self->io_window_1k) align = PCI_P2P_DEFAULT_IO_ALIGN_1K; else align = PCI_P2P_DEFAULT_IO_ALIGN; @@ -730,43 +1114,50 @@ static resource_size_t window_alignment(struct pci_bus *bus, } /** - * pbus_size_io() - size the io window of a given bus + * pbus_size_io() - Size the I/O window of a given bus * - * @bus : the bus - * @min_size : the minimum io window that must to be allocated - * @add_size : additional optional io window - * @realloc_head : track the additional io window on this list + * @bus: The bus + * @min_size: The minimum I/O window that must be allocated + * @add_size: Additional optional I/O window + * @realloc_head: Track the additional I/O window on this list * - * Sizing the IO windows of the PCI-PCI bridge is trivial, - * since these windows have 1K or 4K granularity and the IO ranges - * of non-bridge PCI devices are limited to 256 bytes. - * We must be careful with the ISA aliasing though. + * Sizing the I/O windows of the PCI-PCI bridge is trivial, since these + * windows have 1K or 4K granularity and the I/O ranges of non-bridge PCI + * devices are limited to 256 bytes. We must be careful with the ISA + * aliasing though. */ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, - resource_size_t add_size, struct list_head *realloc_head) + 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); - unsigned long size = 0, size0 = 0, size1 = 0; + struct resource *b_res = pbus_select_window_for_type(bus, IORESOURCE_IO); + resource_size_t size = 0, size0 = 0, size1 = 0; resource_size_t children_add_size = 0; - resource_size_t min_align, io_align, align; + resource_size_t min_align, align; if (!b_res) - return; + return; + + /* If resource is already assigned, nothing more to do */ + if (b_res->parent) + return; - io_align = min_align = window_alignment(bus, IORESOURCE_IO); + min_align = window_alignment(bus, IORESOURCE_IO); list_for_each_entry(dev, &bus->devices, bus_list) { - int i; + struct resource *r; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; + pci_dev_for_each_resource(dev, r) { unsigned long r_size; if (r->parent || !(r->flags & IORESOURCE_IO)) continue; - r_size = resource_size(r); - if (r_size < 0x400) + if (!pdev_resource_assignable(dev, r)) + continue; + + r_size = resource_size(r); + if (r_size < SZ_1K) /* Might be re-aligned for ISA */ size += r_size; else @@ -781,34 +1172,36 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, } } - if (min_align > io_align) - min_align = io_align; - - size0 = calculate_iosize(size, min_size, size1, - resource_size(b_res), min_align); - if (children_add_size > add_size) - add_size = children_add_size; - size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_iosize(size, min_size, add_size + size1, + size0 = calculate_iosize(size, min_size, size1, 0, 0, resource_size(b_res), min_align); + + if (size0) + b_res->flags &= ~IORESOURCE_DISABLED; + + size1 = size0; + if (realloc_head && (add_size > 0 || children_add_size > 0)) { + size1 = calculate_iosize(size, min_size, size1, add_size, + children_add_size, resource_size(b_res), + min_align); + } + if (!size0 && !size1) { - if (b_res->start || b_res->end) - dev_info(&bus->self->dev, "disabling bridge window " - "%pR to %pR (unused)\n", b_res, - &bus->busn_res); - b_res->flags = 0; + if (bus->self && (b_res->start || b_res->end)) + pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", + b_res, &bus->busn_res); + b_res->flags |= IORESOURCE_DISABLED; return; } - b_res->start = min_align; - b_res->end = b_res->start + size0 - 1; + resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; - if (size1 > size0 && realloc_head) { + if (bus->self && size1 > size0 && realloc_head) { + b_res->flags &= ~IORESOURCE_DISABLED; 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 %lx\n", b_res, - &bus->busn_res, size1-size0); + pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", + b_res, &bus->busn_res, + (unsigned long long) size1 - size0); } } @@ -822,7 +1215,7 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, for (order = 0; order <= max_order; order++) { resource_size_t align1 = 1; - align1 <<= (order + 20); + align1 <<= order + __ffs(SZ_1M); if (!align) min_align = align1; @@ -835,111 +1228,228 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, } /** - * pbus_size_mem() - size the memory window of a given bus + * pbus_upstream_space_available - Check no upstream resource limits allocation + * @bus: The bus + * @res: The resource to help select the correct bridge window + * @size: The size required from the bridge window + * @align: Required alignment for the resource * - * @bus : the bus - * @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 + * Check that @size can fit inside the upstream bridge resources that are + * already assigned. Select the upstream bridge window based on the type of + * @res. * - * Calculate the size of the bus and minimal alignment which - * guarantees that all child resources fit in this size. + * Return: %true if enough space is available on all assigned upstream + * resources. */ -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) +static bool pbus_upstream_space_available(struct pci_bus *bus, + struct resource *res, + resource_size_t size, + resource_size_t align) +{ + struct resource_constraint constraint = { + .max = RESOURCE_SIZE_MAX, + .align = align, + }; + struct pci_bus *downstream = bus; + + while ((bus = bus->parent)) { + if (pci_is_root_bus(bus)) + break; + + res = pbus_select_window(bus, res); + if (!res) + return false; + if (!res->parent) + continue; + + if (resource_size(res) >= size) { + struct resource gap = {}; + + if (find_resource_space(res, &gap, size, &constraint) == 0) { + gap.flags = res->flags; + pci_dbg(bus->self, + "Assigned bridge window %pR to %pR free space at %pR\n", + res, &bus->busn_res, &gap); + return true; + } + } + + if (bus->self) { + pci_info(bus->self, + "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", + res, &bus->busn_res, + (unsigned long long)size, + pci_name(downstream->self), + &downstream->busn_res); + } + + return false; + } + + return true; +} + +/** + * pbus_size_mem() - Size the memory window of a given bus + * + * @bus: The bus + * @type: The type of bridge resource + * @min_size: The minimum memory window that must be allocated + * @add_size: Additional optional memory window + * @realloc_head: Track the additional memory window on this list + * + * Calculate the size of the bus resource for @type and minimal alignment + * which guarantees that all child resources fit in this size. + * + * Set the bus resource start/end to indicate the required size if there an + * available unassigned bus resource of the desired @type. + * + * Add optional resource requests to the @realloc_head list if it is + * supplied. + */ +static void pbus_size_mem(struct pci_bus *bus, unsigned long type, + 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 min_align, win_align, align, size, size0, size1 = 0; + resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */ int order, max_order; - struct resource *b_res = find_free_bus_resource(bus, type); - unsigned int mem64_mask = 0; + struct resource *b_res = pbus_select_window_for_type(bus, type); resource_size_t children_add_size = 0; + resource_size_t children_add_align = 0; + resource_size_t add_align = 0; + resource_size_t relaxed_align; + resource_size_t old_size; if (!b_res) - return 0; + return; + + /* If resource is already assigned, nothing more to do */ + if (b_res->parent) + return; 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) { + struct resource *r; int i; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; + pci_dev_for_each_resource(dev, r, i) { + const char *r_name = pci_resource_name(dev, i); resource_size_t r_size; - if (r->parent || (r->flags & mask) != type) + if (!pdev_resources_assignable(dev) || + !pdev_resource_should_fit(dev, r)) + continue; + if (b_res != pbus_select_window(bus, r)) continue; + r_size = resource_size(r); -#ifdef CONFIG_PCI_IOV - /* put SRIOV requested res to the optional list */ - if (realloc_head && i >= PCI_IOV_RESOURCES && - i <= PCI_IOV_RESOURCE_END) { - r->end = r->start - 1; - add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */); + + /* Put SRIOV requested res to the optional list */ + if (realloc_head && pci_resource_is_optional(dev, i)) { + add_align = max(pci_resource_alignment(dev, r), add_align); + add_to_list(realloc_head, dev, r, 0, 0 /* Don't care */); children_add_size += r_size; 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) { - dev_warn(&dev->dev, "disabling BAR %d: %pR " - "(bad alignment %#llx)\n", i, r, - (unsigned long long) align); + order = __ffs(align) - __ffs(SZ_1M); + if (order < 0) + order = 0; + if (order >= ARRAY_SIZE(aligns)) { + pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n", + r_name, r, (unsigned long long) align); r->flags = 0; continue; } - size += r_size; - if (order < 0) - order = 0; - /* Exclude ranges with size > align from - calculation of the alignment. */ - if (r_size == align) + size += max(r_size, align); + /* + * 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) + if (realloc_head) { children_add_size += get_res_add_size(realloc_head, r); + children_add_align = get_res_add_align(realloc_head, r); + add_align = max(add_align, children_add_align); + } } } + old_size = resource_size(b_res); + win_align = window_alignment(bus, b_res->flags); min_align = calculate_mem_align(aligns, max_order); - min_align = max(min_align, window_alignment(bus, b_res->flags & mask)); - size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); - if (children_add_size > add_size) - add_size = children_add_size; - size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 : - calculate_memsize(size, min_size, add_size, - resource_size(b_res), min_align); + min_align = max(min_align, win_align); + size0 = calculate_memsize(size, min_size, 0, 0, old_size, min_align); + + if (size0) { + resource_set_range(b_res, min_align, size0); + b_res->flags &= ~IORESOURCE_DISABLED; + } + + if (bus->self && size0 && + !pbus_upstream_space_available(bus, b_res, size0, min_align)) { + relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); + relaxed_align = max(relaxed_align, win_align); + min_align = min(min_align, relaxed_align); + size0 = calculate_memsize(size, min_size, 0, 0, old_size, win_align); + resource_set_range(b_res, min_align, size0); + pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", + b_res, &bus->busn_res); + } + + if (realloc_head && (add_size > 0 || children_add_size > 0)) { + add_align = max(min_align, add_align); + size1 = calculate_memsize(size, min_size, add_size, children_add_size, + old_size, add_align); + + if (bus->self && size1 && + !pbus_upstream_space_available(bus, b_res, size1, add_align)) { + relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); + relaxed_align = max(relaxed_align, win_align); + min_align = min(min_align, relaxed_align); + size1 = calculate_memsize(size, min_size, add_size, children_add_size, + old_size, win_align); + pci_info(bus->self, + "bridge window %pR to %pR requires relaxed alignment rules\n", + b_res, &bus->busn_res); + } + } + if (!size0 && !size1) { - if (b_res->start || b_res->end) - dev_info(&bus->self->dev, "disabling bridge window " - "%pR to %pR (unused)\n", b_res, - &bus->busn_res); - b_res->flags = 0; - return 1; - } - b_res->start = min_align; - b_res->end = size0 + min_align - 1; - b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask; - 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); + if (bus->self && (b_res->start || b_res->end)) + pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", + b_res, &bus->busn_res); + b_res->flags |= IORESOURCE_DISABLED; + return; + } + + resource_set_range(b_res, min_align, size0); + b_res->flags |= IORESOURCE_STARTALIGN; + if (bus->self && size1 > size0 && realloc_head) { + b_res->flags &= ~IORESOURCE_DISABLED; + add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); + pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", + b_res, &bus->busn_res, + (unsigned long long) (size1 - size0), + (unsigned long long) add_align); } - return 1; } unsigned long pci_cardbus_resource_alignment(struct resource *res) @@ -952,42 +1462,42 @@ unsigned long pci_cardbus_resource_alignment(struct resource *res) } static void pci_bus_size_cardbus(struct pci_bus *bus, - struct list_head *realloc_head) + struct list_head *realloc_head) { struct pci_dev *bridge = bus->self; - struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; + struct resource *b_res; resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; u16 ctrl; - if (b_res[0].parent) + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; + if (b_res->parent) goto handle_b_res_1; /* - * Reserve some resources for CardBus. We reserve - * a fixed amount of bus space for CardBus bridges. + * Reserve some resources for CardBus. We reserve a fixed amount + * of bus space for CardBus bridges. */ - b_res[0].start = pci_cardbus_io_size; - b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1; - b_res[0].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; if (realloc_head) { - b_res[0].end -= pci_cardbus_io_size; + b_res->end -= pci_cardbus_io_size; add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); + pci_cardbus_io_size); } handle_b_res_1: - if (b_res[1].parent) + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; + if (b_res->parent) goto handle_b_res_2; - b_res[1].start = pci_cardbus_io_size; - b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1; - b_res[1].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; if (realloc_head) { - b_res[1].end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, - pci_cardbus_io_size); + b_res->end -= pci_cardbus_io_size; + add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, + pci_cardbus_io_size); } handle_b_res_2: - /* MEM1 must not be pref mmio */ + /* MEM1 must not be pref MMIO */ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; @@ -995,10 +1505,7 @@ handle_b_res_2: pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); } - /* - * Check whether prefetchable memory is supported - * by this bridge. - */ + /* Check whether prefetchable memory is supported by this bridge. */ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; @@ -1006,62 +1513,64 @@ handle_b_res_2: pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); } - if (b_res[2].parent) + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; + if (b_res->parent) goto handle_b_res_3; /* - * If we have prefetchable memory support, allocate - * two regions. Otherwise, allocate one region of - * twice the size. + * If we have prefetchable memory support, allocate two regions. + * Otherwise, allocate one region of twice the size. */ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - b_res[2].start = pci_cardbus_mem_size; - b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1; - b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | - IORESOURCE_STARTALIGN; + resource_set_range(b_res, pci_cardbus_mem_size, + pci_cardbus_mem_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | + IORESOURCE_STARTALIGN; if (realloc_head) { - b_res[2].end -= pci_cardbus_mem_size; - add_to_list(realloc_head, bridge, b_res+2, - pci_cardbus_mem_size, pci_cardbus_mem_size); + b_res->end -= pci_cardbus_mem_size; + add_to_list(realloc_head, bridge, b_res, + pci_cardbus_mem_size, pci_cardbus_mem_size); } - /* reduce that to half */ + /* Reduce that to half */ b_res_3_size = pci_cardbus_mem_size; } handle_b_res_3: - if (b_res[3].parent) + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; + if (b_res->parent) goto handle_done; - b_res[3].start = pci_cardbus_mem_size; - b_res[3].end = b_res[3].start + b_res_3_size - 1; - b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; + resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; if (realloc_head) { - b_res[3].end -= b_res_3_size; - add_to_list(realloc_head, bridge, b_res+3, b_res_3_size, - pci_cardbus_mem_size); + b_res->end -= b_res_3_size; + add_to_list(realloc_head, bridge, b_res, b_res_3_size, + pci_cardbus_mem_size); } 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; - resource_size_t additional_mem_size = 0, additional_io_size = 0; + resource_size_t additional_io_size = 0, additional_mmio_size = 0, + additional_mmio_pref_size = 0; + struct resource *pref; + struct pci_host_bridge *host; + int hdr_type; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; if (!b) continue; - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: pci_bus_size_cardbus(b, realloc_head); break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: default: __pci_bus_size_bridges(b, realloc_head); break; @@ -1069,55 +1578,99 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, } /* The root bus? */ - if (!bus->self) - return; + if (pci_is_root_bus(bus)) { + host = to_pci_host_bridge(bus->bridge); + if (!host->size_windows) + return; + pci_bus_for_each_resource(bus, pref) + if (pref && (pref->flags & IORESOURCE_PREFETCH)) + break; + hdr_type = -1; /* Intentionally invalid - not a PCI device. */ + } else { + pref = &bus->self->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + hdr_type = bus->self->hdr_type; + } - switch (bus->self->class >> 8) { - case PCI_CLASS_BRIDGE_CARDBUS: - /* don't size cardbuses yet. */ + switch (hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: + /* Don't size CardBuses yet */ break; - case PCI_CLASS_BRIDGE_PCI: + case PCI_HEADER_TYPE_BRIDGE: pci_bridge_check_ranges(bus); if (bus->self->is_hotplug_bridge) { additional_io_size = pci_hotplug_io_size; - additional_mem_size = pci_hotplug_mem_size; + additional_mmio_size = pci_hotplug_mmio_size; + additional_mmio_pref_size = pci_hotplug_mmio_pref_size; } - /* - * Follow thru - */ + fallthrough; 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. */ - mask = IORESOURCE_MEM; - prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, 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, - realloc_head ? 0 : additional_mem_size, - additional_mem_size, realloc_head); + + if (pref && (pref->flags & IORESOURCE_PREFETCH)) { + pbus_size_mem(bus, + IORESOURCE_MEM | IORESOURCE_PREFETCH | + (pref->flags & IORESOURCE_MEM_64), + realloc_head ? 0 : additional_mmio_pref_size, + additional_mmio_pref_size, realloc_head); + } + + pbus_size_mem(bus, IORESOURCE_MEM, + realloc_head ? 0 : additional_mmio_size, + additional_mmio_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) +static void assign_fixed_resource_on_bus(struct pci_bus *b, struct resource *r) +{ + struct resource *parent_r; + unsigned long mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + + pci_bus_for_each_resource(b, parent_r) { + if (!parent_r) + continue; + + if ((r->flags & mask) == (parent_r->flags & mask) && + resource_contains(parent_r, r)) + request_resource(parent_r, r); + } +} + +/* + * Try to assign any resources marked as IORESOURCE_PCI_FIXED, as they are + * skipped by pbus_assign_resources_sorted(). + */ +static void pdev_assign_fixed_resources(struct pci_dev *dev) +{ + struct resource *r; + + pci_dev_for_each_resource(dev, r) { + struct pci_bus *b; + + if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) || + !(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + continue; + + b = dev->bus; + while (b && !r->parent) { + assign_fixed_resource_on_bus(b, r); + b = b->parent; + } + } +} + +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; @@ -1125,39 +1678,108 @@ void __ref __pci_bus_assign_resources(const struct pci_bus *bus, pbus_assign_resources_sorted(bus, realloc_head, fail_head); list_for_each_entry(dev, &bus->devices, bus_list) { + pdev_assign_fixed_resources(dev); + b = dev->subordinate; if (!b) continue; __pci_bus_assign_resources(b, realloc_head, fail_head); - switch (dev->class >> 8) { - case PCI_CLASS_BRIDGE_PCI: + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_BRIDGE: if (!pci_is_enabled(dev)) pci_setup_bridge(b); break; - case PCI_CLASS_BRIDGE_CARDBUS: + case PCI_HEADER_TYPE_CARDBUS: pci_setup_cardbus(b); break; default: - dev_info(&dev->dev, "not setting up bridge for bus " - "%04x:%02x\n", pci_domain_nr(b), b->number); + pci_info(dev, "not setting up bridge for bus %04x:%02x\n", + pci_domain_nr(b), b->number); break; } } } -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_claim_device_resources(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (!r->flags || r->parent) + continue; + + pci_claim_resource(dev, i); + } +} + +static void pci_claim_bridge_resources(struct pci_dev *dev) +{ + int i; + + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + struct resource *r = &dev->resource[i]; + + if (!r->flags || r->parent) + continue; + + pci_claim_bridge_resource(dev, i); + } +} + +static void pci_bus_allocate_dev_resources(struct pci_bus *b) +{ + struct pci_dev *dev; + struct pci_bus *child; + + list_for_each_entry(dev, &b->devices, bus_list) { + pci_claim_device_resources(dev); + + child = dev->subordinate; + if (child) + pci_bus_allocate_dev_resources(child); + } +} + +static void pci_bus_allocate_resources(struct pci_bus *b) +{ + struct pci_bus *child; + + /* + * Carry out a depth-first search on the PCI bus tree to allocate + * bridge apertures. Read the programmed bridge bases and + * recursively claim the respective bridge resources. + */ + if (b->self) { + pci_read_bridge_bases(b); + pci_claim_bridge_resources(b->self); + } + + list_for_each_entry(child, &b->children, node) + pci_bus_allocate_resources(child); +} + +void pci_bus_claim_resources(struct pci_bus *b) +{ + pci_bus_allocate_resources(b); + pci_bus_allocate_dev_resources(b); +} +EXPORT_SYMBOL(pci_bus_claim_resources); + +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; @@ -1180,70 +1802,53 @@ static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, break; default: - dev_info(&bridge->dev, "not setting up bridge for bus " - "%04x:%02x\n", pci_domain_nr(b), b->number); + pci_info(bridge, "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) + struct resource *b_win) { - int idx; - bool changed = false; - struct pci_dev *dev; - struct resource *r; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; + struct pci_dev *dev = bus->self; + int idx, ret; - 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; - } - } + if (!b_win->parent) + return; - if (changed) { - /* avoiding touch the one without PREF */ - if (type & IORESOURCE_PREFETCH) - type = IORESOURCE_PREFETCH; - __pci_setup_bridge(bus, type); - } + idx = pci_resource_num(dev, b_win); + + /* If there are children, release them all */ + release_child_resources(b_win); + + ret = pci_release_resource(dev, idx); + if (ret) + return; + + pci_setup_one_bridge_window(dev, idx); } enum release_type { leaf_only, whole_subtree, }; + /* - * try to release pci bridge resources that is from leaf bridge, - * so we can allocate big new one later + * Try to release PCI bridge resources from leaf bridge, so we can allocate + * a larger window 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, + struct resource *b_win, + enum release_type rel_type) { struct pci_dev *dev; bool is_leaf_bridge = true; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; + struct resource *res; + if (!b) continue; @@ -1252,9 +1857,15 @@ static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus, if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) continue; - if (rel_type == whole_subtree) - pci_bus_release_bridge_resources(b, type, - whole_subtree); + if (rel_type != whole_subtree) + continue; + + pci_bus_for_each_resource(b, res) { + if (res->parent != b_win) + continue; + + pci_bus_release_bridge_resources(b, res, rel_type); + } } if (pci_is_root_bus(bus)) @@ -1264,7 +1875,7 @@ static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus, return; if ((rel_type == whole_subtree) || is_leaf_bridge) - pci_bridge_release_resources(bus, type); + pci_bridge_release_resources(bus, b_win); } static void pci_bus_dump_res(struct pci_bus *bus) @@ -1274,10 +1885,10 @@ static void pci_bus_dump_res(struct pci_bus *bus) pci_bus_for_each_resource(bus, res, i) { if (!res || !res->end || !res->flags) - continue; + continue; - dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res); - } + dev_info(&bus->dev, "resource %d %pR\n", i, res); + } } static void pci_bus_dump_resources(struct pci_bus *bus) @@ -1297,39 +1908,21 @@ static void pci_bus_dump_resources(struct pci_bus *bus) } } -static int __init pci_bus_get_depth(struct pci_bus *bus) +static int pci_bus_get_depth(struct pci_bus *bus) { int depth = 0; - struct pci_dev *dev; + struct pci_bus *child_bus; - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each_entry(child_bus, &bus->children, node) { int ret; - struct pci_bus *b = dev->subordinate; - if (!b) - continue; - ret = pci_bus_get_depth(b); + ret = pci_bus_get_depth(child_bus); if (ret + 1 > depth) depth = ret + 1; } return depth; } -static int __init pci_get_max_depth(void) -{ - int depth = 0; - struct pci_bus *bus; - - list_for_each_entry(bus, &pci_root_buses, node) { - int ret; - - ret = pci_bus_get_depth(bus); - if (ret > depth) - depth = ret; - } - - return depth; -} /* * -1: undefined, will auto detect later @@ -1346,7 +1939,7 @@ enum enable_type { auto_enabled, }; -static enum enable_type pci_realloc_enable __initdata = undefined; +static enum enable_type pci_realloc_enable = undefined; void __init pci_realloc_get_opt(char *str) { if (!strncmp(str, "off", 3)) @@ -1354,218 +1947,645 @@ void __init pci_realloc_get_opt(char *str) else if (!strncmp(str, "on", 2)) pci_realloc_enable = user_enabled; } -static bool __init pci_realloc_enabled(void) +static bool pci_realloc_enabled(enum enable_type enable) { - return pci_realloc_enable >= user_enabled; + return enable >= user_enabled; } -static void __init pci_realloc_detect(void) -{ #if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO) - struct pci_dev *dev = NULL; +static int iov_resources_unassigned(struct pci_dev *dev, void *data) +{ + int i; + bool *unassigned = data; + + for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { + int idx = pci_resource_num_from_vf_bar(i); + struct resource *r = &dev->resource[idx]; + struct pci_bus_region region; + + /* Not assigned or rejected by kernel? */ + if (!r->flags) + continue; + + pcibios_resource_to_bus(dev->bus, ®ion, r); + if (!region.start) { + *unassigned = true; + return 1; /* Return early from pci_walk_bus() */ + } + } - if (pci_realloc_enable != undefined) + return 0; +} + +static enum enable_type pci_realloc_detect(struct pci_bus *bus, + enum enable_type enable_local) +{ + bool unassigned = false; + struct pci_host_bridge *host; + + if (enable_local != undefined) + return enable_local; + + host = pci_find_host_bridge(bus); + if (host->preserve_config) + return auto_disabled; + + pci_walk_bus(bus, iov_resources_unassigned, &unassigned); + if (unassigned) + return auto_enabled; + + return enable_local; +} +#else +static enum enable_type pci_realloc_detect(struct pci_bus *bus, + enum enable_type enable_local) +{ + return enable_local; +} +#endif + +static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, + struct list_head *add_list, + resource_size_t new_size) +{ + resource_size_t add_size, size = resource_size(res); + + if (res->parent) return; - for_each_pci_dev(dev) { - int i; + if (!new_size) + return; - for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { - struct resource *r = &dev->resource[i]; + if (new_size > size) { + add_size = new_size - size; + pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, + &add_size); + } else if (new_size < size) { + add_size = size - new_size; + pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res, + &add_size); + } else { + return; + } - /* Not assigned, or rejected by kernel ? */ - if (r->flags && !r->start) { - pci_realloc_enable = auto_enabled; + resource_set_size(res, new_size); - return; - } + /* If the resource is part of the add_list, remove it now */ + if (add_list) + remove_from_list(add_list, res); +} + +static void remove_dev_resource(struct resource *avail, struct pci_dev *dev, + struct resource *res) +{ + resource_size_t size, align, tmp; + + size = resource_size(res); + if (!size) + return; + + align = pci_resource_alignment(dev, res); + align = align ? ALIGN(avail->start, align) - avail->start : 0; + tmp = align + size; + avail->start = min(avail->start + tmp, avail->end + 1); +} + +static void remove_dev_resources(struct pci_dev *dev, + struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM]) +{ + struct resource *res, *b_win; + int idx; + + pci_dev_for_each_resource(dev, res) { + b_win = pbus_select_window(dev->bus, res); + if (!b_win) + continue; + + idx = pci_resource_num(dev->bus->self, b_win); + idx -= PCI_BRIDGE_RESOURCES; + + remove_dev_resource(&available[idx], dev, res); + } +} + +#define ALIGN_DOWN_IF_NONZERO(addr, align) \ + ((align) ? ALIGN_DOWN((addr), (align)) : (addr)) + +/* + * io, mmio and mmio_pref contain the total amount of bridge window space + * available. This includes the minimal space needed to cover all the + * existing devices on the bus and the possible extra space that can be + * shared with the bridges. + */ +static void pci_bus_distribute_available_resources(struct pci_bus *bus, + struct list_head *add_list, + struct resource available_in[PCI_P2P_BRIDGE_RESOURCE_NUM]) +{ + struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM]; + unsigned int normal_bridges = 0, hotplug_bridges = 0; + struct pci_dev *dev, *bridge = bus->self; + resource_size_t per_bridge[PCI_P2P_BRIDGE_RESOURCE_NUM]; + resource_size_t align; + int i; + + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + struct resource *res = + pci_resource_n(bridge, PCI_BRIDGE_RESOURCES + i); + + available[i] = available_in[i]; + + /* + * The alignment of this bridge is yet to be considered, + * hence it must be done now before extending its bridge + * window. + */ + align = pci_resource_alignment(bridge, res); + if (!res->parent && align) + available[i].start = min(ALIGN(available[i].start, align), + available[i].end + 1); + + /* + * Now that we have adjusted for alignment, update the + * bridge window resources to fill as much remaining + * resource space as possible. + */ + adjust_bridge_window(bridge, res, add_list, + resource_size(&available[i])); + } + + /* + * Calculate how many hotplug bridges and normal bridges there + * are on this bus. We will distribute the additional available + * resources between hotplug bridges. + */ + for_each_pci_bridge(dev, bus) { + if (dev->is_hotplug_bridge) + hotplug_bridges++; + else + normal_bridges++; + } + + if (!(hotplug_bridges + normal_bridges)) + return; + + /* + * Calculate the amount of space we can forward from "bus" to any + * downstream buses, i.e., the space left over after assigning the + * BARs and windows on "bus". + */ + list_for_each_entry(dev, &bus->devices, bus_list) { + if (!dev->is_virtfn) + remove_dev_resources(dev, available); + } + + /* + * If there is at least one hotplug bridge on this bus it gets all + * the extra resource space that was left after the reductions + * above. + * + * If there are no hotplug bridges the extra resource space is + * split between non-hotplug bridges. This is to allow possible + * hotplug bridges below them to get the extra space as well. + */ + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + per_bridge[i] = div64_ul(resource_size(&available[i]), + hotplug_bridges ?: normal_bridges); + } + + for_each_pci_bridge(dev, bus) { + struct resource *res; + struct pci_bus *b; + + b = dev->subordinate; + if (!b) + continue; + if (hotplug_bridges && !dev->is_hotplug_bridge) + continue; + + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + res = pci_resource_n(dev, PCI_BRIDGE_RESOURCES + i); + + /* + * Make sure the split resource space is properly + * aligned for bridge windows (align it down to + * avoid going above what is available). + */ + align = pci_resource_alignment(dev, res); + resource_set_size(&available[i], + ALIGN_DOWN_IF_NONZERO(per_bridge[i], + align)); + + /* + * The per_bridge holds the extra resource space + * that can be added for each bridge but there is + * the minimal already reserved as well so adjust + * x.start down accordingly to cover the whole + * space. + */ + available[i].start -= resource_size(res); } + + pci_bus_distribute_available_resources(b, add_list, available); + + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) + available[i].start += available[i].end + 1; + } +} + +static void pci_bridge_distribute_available_resources(struct pci_dev *bridge, + struct list_head *add_list) +{ + struct resource *res, available[PCI_P2P_BRIDGE_RESOURCE_NUM]; + unsigned int i; + + if (!bridge->is_hotplug_bridge) + return; + + pci_dbg(bridge, "distributing available resources\n"); + + /* Take the initial extra resources from the hotplug port */ + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + res = pci_resource_n(bridge, PCI_BRIDGE_RESOURCES + i); + available[i] = *res; + } + + pci_bus_distribute_available_resources(bridge->subordinate, + add_list, available); +} + +static bool pci_bridge_resources_not_assigned(struct pci_dev *dev) +{ + const struct resource *r; + + /* + * If the child device's resources are not yet assigned it means we + * are configuring them (not the boot firmware), so we should be + * able to extend the upstream bridge resources in the same way we + * do with the normal hotplug case. + */ + r = &dev->resource[PCI_BRIDGE_IO_WINDOW]; + if (r->flags && !(r->flags & IORESOURCE_STARTALIGN)) + return false; + r = &dev->resource[PCI_BRIDGE_MEM_WINDOW]; + if (r->flags && !(r->flags & IORESOURCE_STARTALIGN)) + return false; + r = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + if (r->flags && !(r->flags & IORESOURCE_STARTALIGN)) + return false; + + return true; +} + +static void +pci_root_bus_distribute_available_resources(struct pci_bus *bus, + struct list_head *add_list) +{ + struct pci_dev *dev, *bridge = bus->self; + + for_each_pci_bridge(dev, bus) { + struct pci_bus *b; + + b = dev->subordinate; + if (!b) + continue; + + /* + * Need to check "bridge" here too because it is NULL + * in case of root bus. + */ + if (bridge && pci_bridge_resources_not_assigned(dev)) + pci_bridge_distribute_available_resources(dev, add_list); + else + pci_root_bus_distribute_available_resources(b, add_list); } -#endif +} + +static void pci_prepare_next_assign_round(struct list_head *fail_head, + int tried_times, + enum release_type rel_type) +{ + struct pci_dev_resource *fail_res; + + pr_info("PCI: No. %d try to assign unassigned res\n", tried_times + 1); + + /* + * Try to release leaf bridge's resources that aren't big + * enough to contain child device resources. + */ + list_for_each_entry(fail_res, fail_head, list) { + struct pci_bus *bus = fail_res->dev->bus; + struct resource *b_win; + + b_win = pbus_select_window_for_type(bus, fail_res->flags); + if (!b_win) + continue; + pci_bus_release_bridge_resources(bus, b_win, rel_type); + } + + /* Restore size and flags */ + list_for_each_entry(fail_res, fail_head, list) + restore_dev_resource(fail_res); + + free_list(fail_head); } /* - * first try will not touch pci bridge res - * second and later try will clear small leaf bridge res - * will stop till to the max deepth if can not find good one + * First try will not touch PCI bridge res. + * Second and later try will clear small leaf bridge res. + * Will stop till to the max depth if can not find good one. */ -void __init -pci_assign_unassigned_resources(void) +void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) { - struct pci_bus *bus; - LIST_HEAD(realloc_head); /* list of resources that - want additional resources */ + LIST_HEAD(realloc_head); + /* List of resources that want additional resources */ struct list_head *add_list = NULL; int tried_times = 0; enum release_type rel_type = leaf_only; LIST_HEAD(fail_head); - struct pci_dev_resource *fail_res; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; int pci_try_num = 1; + enum enable_type enable_local; - /* don't realloc if asked to do so */ - pci_realloc_detect(); - if (pci_realloc_enabled()) { - int max_depth = pci_get_max_depth(); + /* Don't realloc if asked to do so */ + enable_local = pci_realloc_detect(bus, pci_realloc_enable); + if (pci_realloc_enabled(enable_local)) { + int max_depth = pci_bus_get_depth(bus); pci_try_num = max_depth + 1; - printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", + dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n", max_depth, pci_try_num); } -again: - /* - * last try will use add_list, otherwise will try good to have as - * must have, so can realloc parent bridge resource - */ - if (tried_times + 1 == pci_try_num) - add_list = &realloc_head; - /* Depth first, calculate sizes and alignments of all - subordinate buses. */ - list_for_each_entry(bus, &pci_root_buses, node) + while (1) { + /* + * Last try will use add_list, otherwise will try good to + * have as must have, so can realloc parent bridge resource + */ + if (tried_times + 1 == pci_try_num) + add_list = &realloc_head; + /* + * Depth first, calculate sizes and alignments of all + * subordinate buses. + */ __pci_bus_size_bridges(bus, add_list); - /* Depth last, allocate resources and update the hardware. */ - list_for_each_entry(bus, &pci_root_buses, node) - __pci_bus_assign_resources(bus, add_list, &fail_head); - if (add_list) - BUG_ON(!list_empty(add_list)); - tried_times++; - - /* any device complain? */ - if (list_empty(&fail_head)) - goto enable_and_dump; + pci_root_bus_distribute_available_resources(bus, add_list); - if (tried_times >= pci_try_num) { - if (pci_realloc_enable == undefined) - printk(KERN_INFO "Some PCI device resources are unassigned, try booting with pci=realloc\n"); - else if (pci_realloc_enable == auto_enabled) - printk(KERN_INFO "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); + /* Depth last, allocate resources and update the hardware. */ + __pci_bus_assign_resources(bus, add_list, &fail_head); + if (WARN_ON_ONCE(add_list && !list_empty(add_list))) + free_list(add_list); + tried_times++; - free_list(&fail_head); - goto enable_and_dump; - } + /* Any device complain? */ + if (list_empty(&fail_head)) + break; - printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", - tried_times + 1); + if (tried_times >= pci_try_num) { + if (enable_local == undefined) { + dev_info(&bus->dev, + "Some PCI device resources are unassigned, try booting with pci=realloc\n"); + } else if (enable_local == auto_enabled) { + dev_info(&bus->dev, + "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); + } + free_list(&fail_head); + break; + } - /* third times and later will not check if it is leaf */ - if ((tried_times + 1) > 2) - rel_type = whole_subtree; + /* Third times and later will not check if it is leaf */ + if (tried_times + 1 > 2) + rel_type = whole_subtree; - /* - * Try to release leaf bridge's resources that doesn't fit resource of - * child device under that bridge - */ - list_for_each_entry(fail_res, &fail_head, list) { - bus = fail_res->dev->bus; - pci_bus_release_bridge_resources(bus, - fail_res->flags & type_mask, - rel_type); + pci_prepare_next_assign_round(&fail_head, tried_times, rel_type); } - /* restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; - } - free_list(&fail_head); + pci_bus_dump_resources(bus); +} - goto again; +void pci_assign_unassigned_resources(void) +{ + struct pci_bus *root_bus; -enable_and_dump: - /* Depth last, update the hardware. */ - list_for_each_entry(bus, &pci_root_buses, node) - pci_enable_bridges(bus); + list_for_each_entry(root_bus, &pci_root_buses, node) { + pci_assign_unassigned_root_bus_resources(root_bus); - /* dump the resource on buses */ - list_for_each_entry(bus, &pci_root_buses, node) - pci_bus_dump_resources(bus); + /* Make sure the root bridge has a companion ACPI device */ + if (ACPI_HANDLE(root_bus->bridge)) + acpi_ioapic_add(ACPI_HANDLE(root_bus->bridge)); + } } void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) { struct pci_bus *parent = bridge->subordinate; - LIST_HEAD(add_list); /* list of resources that - want additional resources */ + /* List of resources that want additional resources */ + LIST_HEAD(add_list); int tried_times = 0; LIST_HEAD(fail_head); - struct pci_dev_resource *fail_res; - int retval; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; + int ret; -again: - __pci_bus_size_bridges(parent, &add_list); - __pci_bridge_assign_resources(bridge, &add_list, &fail_head); - BUG_ON(!list_empty(&add_list)); - tried_times++; + while (1) { + __pci_bus_size_bridges(parent, &add_list); - if (list_empty(&fail_head)) - goto enable_all; + /* + * Distribute remaining resources (if any) equally between + * hotplug bridges below. This makes it possible to extend + * the hierarchy later without running out of resources. + */ + pci_bridge_distribute_available_resources(bridge, &add_list); + + __pci_bridge_assign_resources(bridge, &add_list, &fail_head); + if (WARN_ON_ONCE(!list_empty(&add_list))) + free_list(&add_list); + tried_times++; + + if (list_empty(&fail_head)) + break; - if (tried_times >= 2) { - /* still fail, don't need to try more */ - free_list(&fail_head); - goto enable_all; + if (tried_times >= 2) { + /* Still fail, don't need to try more */ + free_list(&fail_head); + break; + } + + pci_prepare_next_assign_round(&fail_head, tried_times, + whole_subtree); } - printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", - tried_times + 1); + ret = pci_reenable_device(bridge); + if (ret) + pci_err(bridge, "Error reenabling bridge (%d)\n", ret); + pci_set_master(bridge); +} +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); - /* - * Try to release leaf bridge's resources that doesn't fit resource of - * child device under that bridge - */ - list_for_each_entry(fail_res, &fail_head, list) { - struct pci_bus *bus = fail_res->dev->bus; - unsigned long flags = fail_res->flags; +/* + * Walk to the root bus, find the bridge window relevant for @res and + * release it when possible. If the bridge window contains assigned + * resources, it cannot be released. + */ +static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res, + struct list_head *saved) +{ + unsigned long type = res->flags; + struct pci_dev_resource *dev_res; + struct pci_dev *bridge = NULL; + LIST_HEAD(added); + LIST_HEAD(failed); + unsigned int i; + int ret = 0; + + while (!pci_is_root_bus(bus)) { + bridge = bus->self; + res = pbus_select_window(bus, res); + if (!res) + break; + + i = pci_resource_num(bridge, res); + + /* Ignore BARs which are still in use */ + if (!res->child) { + ret = add_to_list(saved, bridge, res, 0, 0); + if (ret) + return ret; - pci_bus_release_bridge_resources(bus, flags & type_mask, - whole_subtree); + pci_release_resource(bridge, i); + } else { + const char *res_name = pci_resource_name(bridge, i); + + pci_warn(bridge, + "%s %pR: was not released (still contains assigned resources)\n", + res_name, res); + } + + bus = bus->parent; } - /* restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - if (fail_res->dev->subordinate) - res->flags = 0; + if (!bridge) + return -ENOENT; + + __pci_bus_size_bridges(bridge->subordinate, &added); + __pci_bridge_assign_resources(bridge, &added, &failed); + if (WARN_ON_ONCE(!list_empty(&added))) + free_list(&added); + + if (!list_empty(&failed)) { + if (pci_required_resource_failed(&failed, type)) + ret = -ENOSPC; + free_list(&failed); + if (ret) + return ret; + + /* Only resources with unrelated types failed (again) */ } - free_list(&fail_head); - goto again; + list_for_each_entry(dev_res, saved, list) { + struct pci_dev *dev = dev_res->dev; -enable_all: - retval = pci_reenable_device(bridge); - if (retval) - dev_err(&bridge->dev, "Error reenabling bridge (%d)\n", retval); - pci_set_master(bridge); - pci_enable_bridges(parent); + /* Skip the bridge we just assigned resources for */ + if (bridge == dev) + continue; + + if (!dev->subordinate) + continue; + + pci_setup_bridge(dev->subordinate); + } + + return 0; +} + +int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size, + int exclude_bars) +{ + struct resource *res = pci_resource_n(pdev, resno); + struct pci_dev_resource *dev_res; + struct pci_bus *bus = pdev->bus; + struct resource *b_win, *r; + LIST_HEAD(saved); + unsigned int i; + int ret = 0; + + b_win = pbus_select_window(bus, res); + if (!b_win) + return -EINVAL; + + pci_dev_for_each_resource(pdev, r, i) { + if (i >= PCI_BRIDGE_RESOURCES) + break; + + if (exclude_bars & BIT(i)) + continue; + + if (b_win != pbus_select_window(bus, r)) + continue; + + ret = add_to_list(&saved, pdev, r, 0, 0); + if (ret) + goto restore; + pci_release_resource(pdev, i); + } + + pci_resize_resource_set_size(pdev, resno, size); + + if (!bus->self) + goto out; + + down_read(&pci_bus_sem); + ret = pbus_reassign_bridge_resources(bus, res, &saved); + if (ret) + goto restore; + +out: + up_read(&pci_bus_sem); + free_list(&saved); + return ret; + +restore: + /* Revert to the old configuration */ + list_for_each_entry(dev_res, &saved, list) { + struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; + + i = pci_resource_num(dev, res); + + if (res->parent) { + release_child_resources(res); + pci_release_resource(dev, i); + } + + restore_dev_resource(dev_res); + + ret = pci_claim_resource(dev, i); + if (ret) + continue; + + if (i < PCI_BRIDGE_RESOURCES) { + const char *res_name = pci_resource_name(dev, i); + + pci_update_resource(dev, i); + pci_info(dev, "%s %pR: old value restored\n", + res_name, res); + } + if (dev->subordinate) + pci_setup_bridge(dev->subordinate); + } + goto out; } -EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); void pci_assign_unassigned_bus_resources(struct pci_bus *bus) { struct pci_dev *dev; - LIST_HEAD(add_list); /* list of resources that - want additional resources */ + /* List of resources that want additional resources */ + LIST_HEAD(add_list); down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - if (dev->subordinate) - __pci_bus_size_bridges(dev->subordinate, - &add_list); + for_each_pci_bridge(dev, bus) + if (pci_has_subordinate(dev)) + __pci_bus_size_bridges(dev->subordinate, &add_list); up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); - BUG_ON(!list_empty(&add_list)); + if (WARN_ON_ONCE(!list_empty(&add_list))) + free_list(&add_list); } +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); |
