diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 2479 |
1 files changed, 2479 insertions, 0 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c new file mode 100644 index 000000000..4cf120ebe --- /dev/null +++ b/drivers/pci/setup-bus.c @@ -0,0 +1,2479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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) + * + * 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> + * Converted to allocation in 3 passes, which gives + * tighter packing. Prefetchable range support. + */ + +#include <linux/align.h> +#include <linux/bitops.h> +#include <linux/bug.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/pci.h> +#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 <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; + struct resource *res; + struct pci_dev *dev; + resource_size_t start; + resource_size_t end; + resource_size_t add_size; + resource_size_t min_align; + unsigned long flags; +}; + +static void pci_dev_res_free_list(struct list_head *head) +{ + struct pci_dev_resource *dev_res, *tmp; + + list_for_each_entry_safe(dev_res, tmp, head, list) { + list_del(&dev_res->list); + kfree(dev_res); + } +} + +/** + * pci_dev_res_add_to_list() - Add a new resource tracker to the list + * @head: Head of the list + * @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 + */ +int pci_dev_res_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_obj(*tmp); + if (!tmp) + return -ENOMEM; + + tmp->res = res; + tmp->dev = dev; + tmp->start = res->start; + tmp->end = res->end; + tmp->flags = res->flags; + tmp->add_size = add_size; + tmp->min_align = min_align; + + list_add(&tmp->list, head); + + return 0; +} + +static void pci_dev_res_remove_from_list(struct list_head *head, + struct resource *res) +{ + struct pci_dev_resource *dev_res, *tmp; + + list_for_each_entry_safe(dev_res, tmp, head, list) { + if (dev_res->res == res) { + list_del(&dev_res->list); + kfree(dev_res); + break; + } + } +} + +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; + + dev_res = res_to_dev_res(head, res); + return dev_res ? dev_res->add_size : 0; +} + +static void pci_dev_res_restore(struct pci_dev_resource *dev_res) +{ + struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; + int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); + + if (WARN_ON_ONCE(resource_assigned(res))) + return; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + + pci_dbg(dev, "%s %pR: resource restored\n", res_name, res); +} + +/* + * 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 (!resource_assigned(r)) + return r; + if (!r_assigned) + r_assigned = r; + } + return r_assigned; +} + +/** + * 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: + win = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_IO_WINDOW); + if (win && (win->flags & IORESOURCE_IO)) + return win; + return NULL; + + 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 (mmio && !(mmio->flags & IORESOURCE_MEM)) + mmio = NULL; + if (mmio_pref && !(mmio_pref->flags & IORESOURCE_MEM)) + mmio_pref = NULL; + + if (!(type & IORESOURCE_PREFETCH) || !mmio_pref) + 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 (resource_assigned(res)) + 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 (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED) + return false; + + return true; +} + +static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res) +{ + if (resource_assigned(res)) + 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; + + 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; + + if (!pdev_resource_should_fit(dev, r)) + continue; + + r_align = pci_resource_alignment(dev, r); + if (!r_align) { + pci_warn(dev, "%s %pR: alignment must not be zero\n", + r_name, r); + continue; + } + + tmp = kzalloc_obj(*tmp); + if (!tmp) + 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 */ + n = head; + list_for_each_entry(dev_res, head, list) { + resource_size_t align; + + align = pci_resource_alignment(dev_res->dev, + dev_res->res); + + if (r_align > align) { + n = &dev_res->list; + break; + } + } + /* Insert it just before n */ + list_add_tail(&tmp->list, n); + } +} + +bool pci_resource_is_optional(const struct pci_dev *dev, int resno) +{ + const struct resource *res = pci_resource_n(dev, resno); + + if (pci_resource_is_iov(resno)) + return true; + if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE)) + return true; + if (pci_resource_is_bridge_win(resno) && !resource_size(res)) + return true; + + return false; +} + +static void reset_resource(struct pci_dev *dev, struct resource *res) +{ + int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); + + if (pci_resource_is_bridge_win(idx)) { + res->flags |= IORESOURCE_UNSET; + return; + } + + pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res); + + res->start = 0; + res->end = 0; + res->flags = 0; +} + +/** + * 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 + * + * 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 pci_dev_resource *add_res, *tmp; + 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) { + res = add_res->res; + dev = add_res->dev; + idx = pci_resource_num(dev, res); + + /* Skip this resource if not found in head list */ + if (!res_to_dev_res(head, res)) + continue; + + /* + * Skip resource that failed the earlier assignment and is + * not optional as it would just fail again. + */ + if (!resource_assigned(res) && resource_size(res) && + !pci_resource_is_optional(dev, idx)) + goto out; + + res_name = pci_resource_name(dev, idx); + add_size = add_res->add_size; + align = add_res->min_align; + if (!resource_assigned(res)) { + 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 || !IS_ALIGNED(res->start, align)) { + res->flags |= add_res->flags & + (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); + 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); + kfree(add_res); + } +} + +/** + * 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 + * @optional: Assign also optional resources + * + * 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, + bool optional) +{ + 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; + 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) { + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); + } + } + } +} + +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) +{ + /* + * 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, *tmp_res, *dev_res2, *addsize_res; + struct resource *res; + struct pci_dev *dev; + unsigned long fail_type; + resource_size_t align; + + if (!realloc_head) + realloc_head = &dummy_head; + + /* Check if optional add_size is there */ + if (list_empty(realloc_head)) + goto assign; + + /* Save original start, end, flags etc at first */ + list_for_each_entry(dev_res, head, list) { + if (pci_dev_res_add_to_list(&save_head, dev_res->dev, + dev_res->res, 0, 0)) { + pci_dev_res_free_list(&save_head); + goto assign; + } + } + + /* Update res in head list with add_size in realloc_head list */ + list_for_each_entry_safe(dev_res, tmp_res, head, list) { + res = dev_res->res; + + addsize_res = res_to_dev_res(realloc_head, res); + if (!addsize_res) + continue; + + res->end += addsize_res->add_size; + /* + * 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; + + if (addsize_res->min_align <= res->start) + continue; + /* + * 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. + */ + resource_set_range(res, addsize_res->min_align, + resource_size(res)); + + list_for_each_entry(dev_res2, head, list) { + align = pci_resource_alignment(dev_res2->dev, + dev_res2->res); + if (addsize_res->min_align > align) { + list_move_tail(&dev_res->list, &dev_res2->list); + break; + } + } + + } + +assign: + assign_requested_resources_sorted(head, &local_fail_head, true); + + /* 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) + pci_dev_res_remove_from_list(realloc_head, + dev_res->res); + pci_dev_res_free_list(&save_head); + 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 (resource_assigned(res)) + continue; + + pci_dev_res_restore(save_res); + } + pci_dev_res_free_list(&local_fail_head); + pci_dev_res_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 (resource_assigned(res) && + !pci_need_to_release(fail_type, res)) { + /* Remove it from realloc_head list */ + pci_dev_res_remove_from_list(realloc_head, res); + pci_dev_res_remove_from_list(&save_head, res); + list_del(&dev_res->list); + kfree(dev_res); + } + } + + pci_dev_res_free_list(&local_fail_head); + /* Release assigned resource */ + list_for_each_entry(dev_res, head, list) { + res = dev_res->res; + dev = dev_res->dev; + + pci_release_resource(dev, pci_resource_num(dev, res)); + pci_dev_res_restore(dev_res); + } + /* Restore start/end/flags from saved list */ + list_for_each_entry(save_res, &save_head, list) + pci_dev_res_restore(save_res); + pci_dev_res_free_list(&save_head); + + /* Satisfy the must-have resource requests */ + assign_requested_resources_sorted(head, NULL, false); + + /* 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 (resource_assigned(res)) + continue; + + if (fail_head) { + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); + } + + reset_resource(dev, res); + } + + pci_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) +{ + LIST_HEAD(head); + + pdev_sort_resources(dev, &head); + __assign_resources_sorted(&head, add_head, fail_head); + +} + +static void pbus_assign_resources_sorted(const struct pci_bus *bus, + struct list_head *realloc_head, + struct list_head *fail_head) +{ + struct pci_dev *dev; + LIST_HEAD(head); + + list_for_each_entry(dev, &bus->devices, bus_list) + pdev_sort_resources(dev, &head); + + __assign_resources_sorted(&head, realloc_head, fail_head); +} + +/* + * 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 resource *res; + const char *res_name; + struct pci_bus_region region; + unsigned long io_mask; + u8 io_base_lo, io_limit_lo; + 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 = &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 (resource_assigned(res) && 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 = ((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); + pci_info(bridge, " %s %pR\n", res_name, res); + } else { + /* 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 */ + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); + /* 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_dev *bridge) +{ + 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 = &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 (resource_assigned(res) && res->flags & IORESOURCE_MEM) { + l = (region.start >> 16) & 0xfff0; + l |= region.end & 0xfff00000; + 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_dev *bridge) +{ + 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. + */ + pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); + + /* Set up PREF base/limit */ + bu = lu = 0; + 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 (resource_assigned(res) && 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); + } + 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 */ + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); + pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); +} + +static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) +{ + struct pci_dev *bridge = bus->self; + + pci_info(bridge, "PCI bridge to %pR\n", &bus->busn_res); + + if (type & IORESOURCE_IO) + pci_setup_bridge_io(bridge); + + if (type & IORESOURCE_MEM) + pci_setup_bridge_mmio(bridge); + + if (type & IORESOURCE_PREFETCH) + pci_setup_bridge_mmio_pref(bridge); + + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); +} + +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); +} + + +int pci_claim_bridge_resource(struct pci_dev *bridge, int i) +{ + int ret = -EINVAL; + + if (!pci_resource_is_bridge_win(i)) + 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) +{ + struct pci_dev *bridge = bus->self; + struct resource *b_res; + + b_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; + b_res->flags |= IORESOURCE_MEM; + + if (bridge->io_window) { + b_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; + b_res->flags |= IORESOURCE_IO; + } + + 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; + } + } +} + +static resource_size_t calculate_iosize(resource_size_t size, + 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) + old_size = 0; + /* + * 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 = 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 children_add_size, + resource_size_t align) +{ + size = max(size, min_size) + children_add_size; + return ALIGN(size, align); +} + +resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, + unsigned long type) +{ + return 1; +} + +#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 + +resource_size_t pci_min_window_alignment(struct pci_bus *bus, unsigned long type) +{ + resource_size_t align = 1, arch_align; + + if (type & IORESOURCE_MEM) + 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. + */ + if (bus->self && bus->self->io_window_1k) + align = PCI_P2P_DEFAULT_IO_ALIGN_1K; + else + align = PCI_P2P_DEFAULT_IO_ALIGN; + } + + arch_align = pcibios_window_alignment(bus, type); + return max(align, arch_align); +} + +/** + * pbus_size_io() - Size the I/O window of a given bus + * + * @bus: The bus + * @add_size: Additional I/O window + * @realloc_head: Track the additional I/O window on this list + * + * 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 add_size, + struct list_head *realloc_head) +{ + struct pci_dev *dev; + 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, align; + + if (!b_res) + return; + + /* If resource is already assigned, nothing more to do */ + if (resource_assigned(b_res)) + return; + + min_align = pci_min_window_alignment(bus, IORESOURCE_IO); + list_for_each_entry(dev, &bus->devices, bus_list) { + struct resource *r; + + pci_dev_for_each_resource(dev, r) { + unsigned long r_size; + + if (resource_assigned(r) || !(r->flags & IORESOURCE_IO)) + continue; + + 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 + size1 += r_size; + + align = pci_resource_alignment(dev, r); + if (align > min_align) + min_align = align; + + if (realloc_head) + children_add_size += get_res_add_size(realloc_head, r); + } + } + + size0 = calculate_iosize(size, realloc_head ? 0 : add_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, 0, size1, add_size, + children_add_size, resource_size(b_res), + min_align); + } + + if (!size0 && !size1) { + 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; + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + size1 - size0, min_align); + pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", + b_res, &bus->busn_res, + (unsigned long long) size1 - size0); + } +} + +static inline resource_size_t calculate_mem_align(resource_size_t *aligns, + int max_order) +{ + resource_size_t align = 0; + resource_size_t min_align = 0; + int order; + + for (order = 0; order <= max_order; order++) { + resource_size_t align1 = 1; + + align1 <<= order + __ffs(SZ_1M); + + if (!align) + min_align = align1; + else if (ALIGN(align + min_align, min_align) < align1) + min_align = align1 >> 1; + align += aligns[order]; + } + + return min_align; +} + +/* + * Calculate bridge window head alignment that leaves no gaps in between + * resources. + */ +static resource_size_t calculate_head_align(resource_size_t *aligns, + int max_order) +{ + resource_size_t head_align = 1; + resource_size_t remainder = 0; + int order; + + /* Take the largest alignment as the starting point. */ + head_align <<= max_order + __ffs(SZ_1M); + + for (order = max_order - 1; order >= 0; order--) { + resource_size_t align1 = 1; + + align1 <<= order + __ffs(SZ_1M); + + /* + * Account smaller resources with alignment < max_order that + * could be used to fill head room if alignment less than + * max_order is used. + */ + remainder += aligns[order]; + + /* + * Test if head fill is enough to satisfy the alignment of + * the larger resources after reducing the alignment. + */ + while ((head_align > align1) && (remainder >= head_align / 2)) { + head_align /= 2; + remainder -= head_align; + } + } + + return head_align; +} + +/* + * pbus_size_mem_optional - Account optional resources in bridge window + * + * Account an optional resource or the optional part of the resource in bridge + * window size. + * + * Return: %true if the resource is entirely optional. + */ +static bool pbus_size_mem_optional(struct pci_dev *dev, int resno, + resource_size_t align, + struct list_head *realloc_head, + resource_size_t *add_align, + resource_size_t *children_add_size) +{ + struct resource *res = pci_resource_n(dev, resno); + bool optional = pci_resource_is_optional(dev, resno); + resource_size_t r_size = resource_size(res); + struct pci_dev_resource *dev_res = NULL; + + if (!realloc_head) + return false; + + /* + * Only bridges have optional sizes in realloc_head at this + * point. As res_to_dev_res() walks the entire realloc_head + * list, skip calling it when known unnecessary. + */ + if (pci_resource_is_bridge_win(resno)) { + dev_res = res_to_dev_res(realloc_head, res); + if (dev_res) { + *children_add_size += dev_res->add_size; + *add_align = max(*add_align, dev_res->min_align); + } + } + + if (!optional) + return false; + + /* + * Put requested res to the optional list if not there yet (SR-IOV, + * disabled ROM). Bridge windows with an optional part are already + * on the list. + */ + if (!dev_res) + pci_dev_res_add_to_list(realloc_head, dev, res, 0, align); + *children_add_size += r_size; + *add_align = max(align, *add_align); + + return true; +} + +/** + * pbus_size_mem() - Size the memory window of a given bus + * + * @bus: The bus + * @b_res: The bridge window resource + * @add_size: Additional memory window + * @realloc_head: Track the additional memory window on this list + * + * Calculate the size of the bridge window @b_res 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, struct resource *b_res, + resource_size_t add_size, + struct list_head *realloc_head) +{ + struct pci_dev *dev; + 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; + resource_size_t children_add_size = 0; + resource_size_t add_align = 0; + + if (!b_res) + return; + + /* If resource is already assigned, nothing more to do */ + if (resource_assigned(b_res)) + return; + + max_order = 0; + size = 0; + + list_for_each_entry(dev, &bus->devices, bus_list) { + struct resource *r; + int i; + + pci_dev_for_each_resource(dev, r, i) { + const char *r_name = pci_resource_name(dev, i); + resource_size_t r_size; + + if (!pdev_resources_assignable(dev) || + !pdev_resource_should_fit(dev, r)) + continue; + if (b_res != pbus_select_window(bus, r)) + continue; + + align = pci_resource_alignment(dev, r); + /* + * aligns[0] is for 1MB (since bridge memory + * windows are always at least 1MB aligned), so + * keep "order" from being negative for smaller + * resources. + */ + order = max_t(int, __ffs(align) - __ffs(SZ_1M), 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; + } + + if (pbus_size_mem_optional(dev, i, align, + realloc_head, &add_align, + &children_add_size)) + continue; + + r_size = resource_size(r); + size += max(r_size, align); + + /* + * If resource's size is larger than its alignment, + * some configurations result in an unwanted gap in + * the head space that the larger resource cannot + * fill. + */ + if (r_size <= align) + aligns[order] += align; + if (order > max_order) + max_order = order; + } + } + + win_align = pci_min_window_alignment(bus, b_res->flags); + min_align = calculate_head_align(aligns, max_order); + min_align = max(min_align, win_align); + size0 = calculate_memsize(size, realloc_head ? 0 : add_size, + 0, win_align); + + if (size0) { + resource_set_range(b_res, min_align, size0); + b_res->flags &= ~IORESOURCE_DISABLED; + } + + if (realloc_head && (add_size > 0 || children_add_size > 0)) { + add_align = max(min_align, add_align); + size1 = calculate_memsize(size, add_size, children_add_size, + win_align); + } + + if (!size0 && !size1) { + 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 && realloc_head && (size1 > size0 || add_align > min_align)) { + b_res->flags &= ~IORESOURCE_DISABLED; + add_size = size1 > size0 ? size1 - size0 : 0; + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + add_size, 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) add_size, + (unsigned long long) add_align); + } +} + +void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) +{ + struct pci_dev *dev; + resource_size_t additional_io_size = 0, additional_mmio_size = 0, + additional_mmio_pref_size = 0; + struct resource *b_res; + 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->hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: + if (pci_bus_size_cardbus_bridge(b, realloc_head)) + continue; + break; + + case PCI_HEADER_TYPE_BRIDGE: + default: + __pci_bus_size_bridges(b, realloc_head); + break; + } + } + + /* The root bus? */ + if (pci_is_root_bus(bus)) { + host = to_pci_host_bridge(bus->bridge); + if (!host->size_windows) + return; + hdr_type = -1; /* Intentionally invalid - not a PCI device. */ + } else { + hdr_type = bus->self->hdr_type; + } + + switch (hdr_type) { + case PCI_HEADER_TYPE_CARDBUS: + /* Don't size CardBuses yet */ + break; + + case PCI_HEADER_TYPE_BRIDGE: + pci_bridge_check_ranges(bus); + if (bus->self->is_hotplug_bridge) { + additional_io_size = pci_hotplug_io_size; + additional_mmio_size = pci_hotplug_mmio_size; + additional_mmio_pref_size = pci_hotplug_mmio_pref_size; + } + fallthrough; + default: + pbus_size_io(bus, additional_io_size, realloc_head); + + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM | + IORESOURCE_PREFETCH | + IORESOURCE_MEM_64); + if (b_res && (b_res->flags & IORESOURCE_PREFETCH)) { + pbus_size_mem(bus, b_res, additional_mmio_pref_size, + realloc_head); + } + + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM); + if (b_res) { + pbus_size_mem(bus, b_res, additional_mmio_size, + realloc_head); + } + break; + } +} + +void pci_bus_size_bridges(struct pci_bus *bus) +{ + __pci_bus_size_bridges(bus, NULL); +} +EXPORT_SYMBOL(pci_bus_size_bridges); + +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 (resource_assigned(r) || + !(r->flags & IORESOURCE_PCI_FIXED) || + !(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + continue; + + b = dev->bus; + while (b && !resource_assigned(r)) { + 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; + + 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->hdr_type) { + case PCI_HEADER_TYPE_BRIDGE: + if (!pci_is_enabled(dev)) + pci_setup_bridge(b); + break; + + case PCI_HEADER_TYPE_CARDBUS: + pci_setup_cardbus_bridge(b); + break; + + default: + pci_info(dev, "not setting up bridge for bus %04x:%02x\n", + pci_domain_nr(b), b->number); + break; + } + } +} + +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 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 || resource_assigned(r)) + 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 || resource_assigned(r)) + continue; + if (r->flags & IORESOURCE_DISABLED) + 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; + + pdev_assign_resources_sorted((struct pci_dev *)bridge, + add_head, fail_head); + + b = bridge->subordinate; + if (!b) + return; + + __pci_bus_assign_resources(b, add_head, fail_head); + + switch (bridge->class >> 8) { + case PCI_CLASS_BRIDGE_PCI: + pci_setup_bridge(b); + break; + + case PCI_CLASS_BRIDGE_CARDBUS: + pci_setup_cardbus_bridge(b); + break; + + default: + 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, + struct resource *b_win) +{ + struct pci_dev *dev = bus->self; + int idx, ret; + + if (!resource_assigned(b_win)) + return; + + 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 from leaf bridge, so we can allocate + * a larger window later. + */ +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; + + is_leaf_bridge = false; + + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + continue; + + 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)) + return; + + if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI) + return; + + if ((rel_type == whole_subtree) || is_leaf_bridge) + pci_bridge_release_resources(bus, b_win); +} + +static void pci_bus_dump_res(struct pci_bus *bus) +{ + struct resource *res; + int i; + + pci_bus_for_each_resource(bus, res, i) { + if (!res || !res->end || !res->flags) + continue; + + dev_info(&bus->dev, "resource %d %pR\n", i, res); + } +} + +static void pci_bus_dump_resources(struct pci_bus *bus) +{ + struct pci_bus *b; + struct pci_dev *dev; + + + pci_bus_dump_res(bus); + + list_for_each_entry(dev, &bus->devices, bus_list) { + b = dev->subordinate; + if (!b) + continue; + + pci_bus_dump_resources(b); + } +} + +static int pci_bus_get_depth(struct pci_bus *bus) +{ + int depth = 0; + struct pci_bus *child_bus; + + list_for_each_entry(child_bus, &bus->children, node) { + int ret; + + ret = pci_bus_get_depth(child_bus); + if (ret + 1 > depth) + depth = ret + 1; + } + + return depth; +} + +/* + * -1: undefined, will auto detect later + * 0: disabled by user + * 1: disabled by auto detect + * 2: enabled by user + * 3: enabled by auto detect + */ +enum enable_type { + undefined = -1, + user_disabled, + auto_disabled, + user_enabled, + auto_enabled, +}; + +static enum enable_type pci_realloc_enable = undefined; +void __init pci_realloc_get_opt(char *str) +{ + if (!strncmp(str, "off", 3)) + pci_realloc_enable = user_disabled; + else if (!strncmp(str, "on", 2)) + pci_realloc_enable = user_enabled; +} +static bool pci_realloc_enabled(enum enable_type enable) +{ + return enable >= user_enabled; +} + +#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO) +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() */ + } + } + + 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); + struct pci_dev_resource *dev_res; + + if (resource_assigned(res)) + return; + + if (!new_size) + return; + + 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) { + int idx = pci_resource_num(bridge, res); + + /* + * hpio/mmio/mmioprefsize hasn't been included at all? See the + * add_size param at the callsites of calculate_memsize(). + */ + if (!add_list) + return; + + /* Only shrink if the hotplug extra relates to window size. */ + switch (idx) { + case PCI_BRIDGE_IO_WINDOW: + if (size > pci_hotplug_io_size) + return; + break; + case PCI_BRIDGE_MEM_WINDOW: + if (size > pci_hotplug_mmio_size) + return; + break; + case PCI_BRIDGE_PREF_MEM_WINDOW: + if (size > pci_hotplug_mmio_pref_size) + return; + break; + default: + break; + } + + dev_res = res_to_dev_res(add_list, res); + add_size = size - new_size; + if (add_size < dev_res->add_size) { + dev_res->add_size -= add_size; + pci_dbg(bridge, "bridge window %pR optional size shrunken by %pa\n", + res, &add_size); + } else { + pci_dbg(bridge, "bridge window %pR optional size removed\n", + res); + pci_dev_res_remove_from_list(add_list, res); + } + return; + + } else { + return; + } + + resource_set_size(res, new_size); + + /* If the resource is part of the add_list, remove it now */ + if (add_list) + pci_dev_res_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 (!resource_assigned(res) && 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); + } +} + +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) + pci_dev_res_restore(fail_res); + + pci_dev_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 depth if can not find good one. + */ +void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) +{ + 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); + int pci_try_num = 1; + enum enable_type enable_local; + + /* 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; + dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n", + max_depth, pci_try_num); + } + + 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); + + pci_root_bus_distribute_available_resources(bus, add_list); + + /* 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))) + pci_dev_res_free_list(add_list); + tried_times++; + + /* Any device complain? */ + if (list_empty(&fail_head)) + break; + + 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"); + } + pci_dev_res_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; + + pci_prepare_next_assign_round(&fail_head, tried_times, rel_type); + } + + pci_bus_dump_resources(bus); +} + +void pci_assign_unassigned_resources(void) +{ + struct pci_bus *root_bus; + + list_for_each_entry(root_bus, &pci_root_buses, node) { + pci_assign_unassigned_root_bus_resources(root_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 of resources that want additional resources */ + LIST_HEAD(add_list); + int tried_times = 0; + LIST_HEAD(fail_head); + int ret; + + while (1) { + __pci_bus_size_bridges(parent, &add_list); + + /* + * 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))) + pci_dev_res_free_list(&add_list); + tried_times++; + + if (list_empty(&fail_head)) + break; + + if (tried_times >= 2) { + /* Still fail, don't need to try more */ + pci_dev_res_free_list(&fail_head); + break; + } + + pci_prepare_next_assign_round(&fail_head, tried_times, + whole_subtree); + } + + 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); + +/* + * 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 = pci_dev_res_add_to_list(saved, bridge, res, 0, 0); + if (ret) + return ret; + + 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; + } + + 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))) + pci_dev_res_free_list(&added); + + if (!list_empty(&failed)) { + if (pci_required_resource_failed(&failed, type)) + ret = -ENOSPC; + pci_dev_res_free_list(&failed); + if (ret) + return ret; + + /* Only resources with unrelated types failed (again) */ + } + + list_for_each_entry(dev_res, saved, list) { + struct pci_dev *dev = dev_res->dev; + + /* 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 old, ret; + + b_win = pbus_select_window(bus, res); + if (!b_win) + return -EINVAL; + + old = pci_rebar_get_current_size(pdev, resno); + if (old < 0) + return old; + + ret = pci_rebar_set_size(pdev, resno, size); + if (ret) + return ret; + + 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 = pci_dev_res_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); + pci_dev_res_free_list(&saved); + return ret; + +restore: + /* + * Revert to the old configuration. + * + * BAR Size must be restored first because it affects the read-only + * bits in BAR (the old address might not be restorable otherwise + * due to low address bits). + */ + pci_rebar_set_size(pdev, resno, old); + + 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 (resource_assigned(res)) { + release_child_resources(res); + pci_release_resource(dev, i); + } + + pci_dev_res_restore(dev_res); + + if (pci_claim_resource(dev, i)) + 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; +} + +void pci_assign_unassigned_bus_resources(struct pci_bus *bus) +{ + struct pci_dev *dev; + /* List of resources that want additional resources */ + LIST_HEAD(add_list); + + down_read(&pci_bus_sem); + 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); + if (WARN_ON_ONCE(!list_empty(&add_list))) + pci_dev_res_free_list(&add_list); +} +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); |
