From 8dea78da5cee153b8af9c07a2745f6c55057fe12 Mon Sep 17 00:00:00 2001 From: Jonathan Herman Date: Thu, 17 Jan 2013 16:15:55 -0500 Subject: Patched in Tegra support. --- drivers/pci/setup-bus.c | 752 +++++++++++++++--------------------------------- 1 file changed, 226 insertions(+), 526 deletions(-) (limited to 'drivers/pci/setup-bus.c') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 6d3591d57ea..784da9d3602 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -25,13 +25,10 @@ #include #include #include -#include #include "pci.h" -unsigned int pci_flags; - -struct pci_dev_resource { - struct list_head list; +struct resource_list_x { + struct resource_list_x *next; struct resource *res; struct pci_dev *dev; resource_size_t start; @@ -41,14 +38,21 @@ struct pci_dev_resource { unsigned long flags; }; -static void free_list(struct list_head *head) +#define free_list(type, head) do { \ + struct type *list, *tmp; \ + for (list = (head)->next; list;) { \ + tmp = list; \ + list = list->next; \ + kfree(tmp); \ + } \ + (head)->next = NULL; \ +} while (0) + +int pci_realloc_enable = 0; +#define pci_realloc_enabled() pci_realloc_enable +void pci_realloc(void) { - 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_realloc_enable = 1; } /** @@ -60,18 +64,21 @@ static void free_list(struct list_head *head) * @add_size: additional size to be optionally added * to the resource */ -static int add_to_list(struct list_head *head, +static void add_to_list(struct resource_list_x *head, struct pci_dev *dev, struct resource *res, resource_size_t add_size, resource_size_t min_align) { - struct pci_dev_resource *tmp; + struct resource_list_x *list = head; + struct resource_list_x *ln = list->next; + struct resource_list_x *tmp; - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) { pr_warning("add_to_list: kmalloc() failed!\n"); - return -ENOMEM; + return; } + tmp->next = ln; tmp->res = res; tmp->dev = dev; tmp->start = res->start; @@ -79,100 +86,19 @@ static int add_to_list(struct list_head *head, tmp->flags = res->flags; tmp->add_size = add_size; tmp->min_align = min_align; - - list_add(&tmp->list, head); - - return 0; + list->next = tmp; } -static void remove_from_list(struct list_head *head, - struct resource *res) +static void add_to_failed_list(struct resource_list_x *head, + struct pci_dev *dev, 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 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_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); - - return dev_res->add_size; - } - } - - return 0; -} - -/* Sort resources by alignment */ -static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) -{ - int i; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r; - 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) - continue; - - r_align = pci_resource_alignment(dev, r); - if (!r_align) { - dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", - i, r); - continue; - } - - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - panic("pdev_sort_resources(): " - "kmalloc() failed!\n"); - tmp->res = r; - tmp->dev = dev; - - /* 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); - } + add_to_list(head, dev, res, + 0 /* dont care */, + 0 /* dont care */); } static void __dev_sort_resources(struct pci_dev *dev, - struct list_head *head) + struct resource_list *head) { u16 class = dev->class >> 8; @@ -210,54 +136,49 @@ static inline void reset_resource(struct resource *res) * 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) +static void reassign_resources_sorted(struct resource_list_x *realloc_head, + struct resource_list *head) { struct resource *res; - struct pci_dev_resource *add_res, *tmp; - struct pci_dev_resource *dev_res; + struct resource_list_x *list, *tmp, *prev; + struct resource_list *hlist; resource_size_t add_size; int idx; - list_for_each_entry_safe(add_res, tmp, realloc_head, list) { - bool found_match = false; - - res = add_res->res; + prev = realloc_head; + for (list = realloc_head->next; list;) { + res = list->res; /* skip resource that has been reset */ if (!res->flags) goto out; /* 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 */ + for (hlist = head->next; hlist && hlist->res != res; + hlist = hlist->next); + if (!hlist) { /* just skip */ + prev = list; + list = list->next; continue; + } - idx = res - &add_res->dev->resource[0]; - add_size = add_res->add_size; + idx = res - &list->dev->resource[0]; + add_size=list->add_size; if (!resource_size(res)) { - res->start = add_res->start; + res->start = list->start; res->end = res->start + add_size - 1; - if (pci_assign_resource(add_res->dev, idx)) + if(pci_assign_resource(list->dev, idx)) reset_resource(res); } else { - resource_size_t align = add_res->min_align; - 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); + resource_size_t align = list->min_align; + res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); + if (pci_reassign_resource(list->dev, idx, add_size, align)) + dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n", + res); } out: - list_del(&add_res->list); - kfree(add_res); + tmp = list; + prev->next = list = list->next; + kfree(tmp); } } @@ -265,105 +186,41 @@ out: * 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 + * @failed_list : head of the list tracking requests that could * not be allocated * * Satisfy resource requests of each element in the list. Add * requests that could not satisfied to the failed_list. */ -static void assign_requested_resources_sorted(struct list_head *head, - struct list_head *fail_head) +static void assign_requested_resources_sorted(struct resource_list *head, + struct resource_list_x *fail_head) { struct resource *res; - struct pci_dev_resource *dev_res; + struct resource_list *list; 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)) { - if (fail_head && !pci_is_root_bus(dev_res->dev->bus)) { + for (list = head->next; list; list = list->next) { + res = list->res; + idx = res - &list->dev->resource[0]; + if (resource_size(res) && pci_assign_resource(list->dev, idx)) { + if (fail_head && !pci_is_root_bus(list->dev->bus)) { /* * if the failed res is for ROM BAR, and it will * be enabled later, don't add it to the list */ if (!((idx == PCI_ROM_RESOURCE) && (!(res->flags & IORESOURCE_ROM_ENABLE)))) - add_to_list(fail_head, - dev_res->dev, res, - 0 /* dont care */, - 0 /* dont care */); + add_to_failed_list(fail_head, list->dev, res); } reset_resource(res); } } } -static void __assign_resources_sorted(struct list_head *head, - struct list_head *realloc_head, - struct list_head *fail_head) +static void __assign_resources_sorted(struct resource_list *head, + struct resource_list_x *realloc_head, + struct resource_list_x *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. - */ - LIST_HEAD(save_head); - LIST_HEAD(local_fail_head); - struct pci_dev_resource *save_res; - struct pci_dev_resource *dev_res; - - /* Check if optional add_size is there */ - if (!realloc_head || list_empty(realloc_head)) - goto requested_and_reassign; - - /* 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; - } - } - - /* 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); - - /* Try updated head list with add_size added */ - assign_requested_resources_sorted(head, &local_fail_head); - - /* all assigned with add_size ? */ - 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; - } - - 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; - - res->start = save_res->start; - res->end = save_res->end; - res->flags = save_res->flags; - } - free_list(&save_head); - -requested_and_reassign: /* Satisfy the must-have resource requests */ assign_requested_resources_sorted(head, fail_head); @@ -371,27 +228,28 @@ requested_and_reassign: requests */ if (realloc_head) reassign_resources_sorted(realloc_head, head); - free_list(head); + free_list(resource_list, head); } static void pdev_assign_resources_sorted(struct pci_dev *dev, - struct list_head *add_head, - struct list_head *fail_head) + struct resource_list_x *fail_head) { - LIST_HEAD(head); + struct resource_list head; + head.next = NULL; __dev_sort_resources(dev, &head); - __assign_resources_sorted(&head, add_head, fail_head); + __assign_resources_sorted(&head, NULL, fail_head); } static void pbus_assign_resources_sorted(const struct pci_bus *bus, - struct list_head *realloc_head, - struct list_head *fail_head) + struct resource_list_x *realloc_head, + struct resource_list_x *fail_head) { struct pci_dev *dev; - LIST_HEAD(head); + struct resource_list head; + head.next = NULL; list_for_each_entry(dev, &bus->devices, bus_list) __dev_sort_resources(dev, &head); @@ -404,8 +262,8 @@ 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", - &bus->busn_res); + dev_info(&bridge->dev, "CardBus bridge to [bus %02x-%02x]\n", + bus->secondary, bus->subordinate); res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); @@ -469,23 +327,16 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; - unsigned long io_mask; - u8 io_base_lo, io_limit_lo; u32 l, 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; - 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; + l |= (region.start >> 8) & 0x00f0; + l |= region.end & 0xf000; /* 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); @@ -560,8 +411,8 @@ 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); + dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", + bus->secondary, bus->subordinate); if (type & IORESOURCE_IO) pci_setup_bridge_io(bus); @@ -575,7 +426,7 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } -void pci_setup_bridge(struct pci_bus *bus) +static void pci_setup_bridge(struct pci_bus *bus) { unsigned long type = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; @@ -697,36 +548,18 @@ static resource_size_t calculate_memsize(resource_size_t size, return size; } -resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, - unsigned long type) -{ - 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 */ - -static resource_size_t window_alignment(struct pci_bus *bus, - unsigned long type) +static resource_size_t get_res_add_size(struct resource_list_x *realloc_head, + struct resource *res) { - resource_size_t align = 1, arch_align; + struct resource_list_x *list; - 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->io_window_1k) - align = PCI_P2P_DEFAULT_IO_ALIGN_1K; - else - align = PCI_P2P_DEFAULT_IO_ALIGN; - } + /* check if it is in realloc_head list */ + for (list = realloc_head->next; list && list->res != res; + list = list->next); + if (list) + return list->add_size; - arch_align = pcibios_window_alignment(bus, type); - return max(align, arch_align); + return 0; } /** @@ -738,23 +571,21 @@ static resource_size_t window_alignment(struct pci_bus *bus, * @realloc_head : track the additional io 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 + * since these windows have 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. */ 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 resource_list_x *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; resource_size_t children_add_size = 0; - resource_size_t min_align, io_align, align; if (!b_res) return; - io_align = min_align = window_alignment(bus, IORESOURCE_IO); list_for_each_entry(dev, &bus->devices, bus_list) { int i; @@ -772,66 +603,31 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_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); } } - - if (min_align > io_align) - min_align = io_align; - size0 = calculate_iosize(size, min_size, size1, - resource_size(b_res), min_align); + resource_size(b_res), 4096); 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, - resource_size(b_res), min_align); + calculate_iosize(size, min_size+add_size, size1, + resource_size(b_res), 4096); 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); + "%pR to [bus %02x-%02x] (unused)\n", b_res, + bus->secondary, bus->subordinate); b_res->flags = 0; return; } - - b_res->start = min_align; + /* Alignment of the IO window is always 4K */ + b_res->start = 4096; b_res->end = b_res->start + size0 - 1; b_res->flags |= IORESOURCE_STARTALIGN; - if (size1 > size0 && realloc_head) { - add_to_list(realloc_head, bus->self, b_res, size1-size0, - min_align); - dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to %pR add_size %lx\n", b_res, - &bus->busn_res, 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 + 20); - - if (!align) - min_align = align1; - else if (ALIGN(align + min_align, min_align) < align1) - min_align = align1 >> 1; - align += aligns[order]; - } - - return min_align; + if (size1 > size0 && realloc_head) + add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096); } /** @@ -848,7 +644,7 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, 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) + struct resource_list_x *realloc_head) { struct pci_dev *dev; resource_size_t min_align, align, size, size0, size1; @@ -913,32 +709,38 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, children_add_size += get_res_add_size(realloc_head, r); } } + align = 0; + min_align = 0; + for (order = 0; order <= max_order; order++) { + resource_size_t align1 = 1; + + align1 <<= (order + 20); - min_align = calculate_mem_align(aligns, max_order); - min_align = max(min_align, window_alignment(bus, b_res->flags & mask)); + if (!align) + min_align = align1; + else if (ALIGN(align + min_align, min_align) < align1) + min_align = align1 >> 1; + align += aligns[order]; + } 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, + calculate_memsize(size, min_size+add_size, 0, 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); + "%pR to [bus %02x-%02x] (unused)\n", b_res, + bus->secondary, bus->subordinate); 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) { + if (size1 > size0 && realloc_head) add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); - dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to %pR add_size %llx\n", b_res, - &bus->busn_res, (unsigned long long)size1-size0); - } return 1; } @@ -952,48 +754,25 @@ 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 resource_list_x *realloc_head) { struct pci_dev *bridge = bus->self; struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES]; - resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; u16 ctrl; - if (b_res[0].parent) - goto handle_b_res_1; /* * 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; - if (realloc_head) { - b_res[0].end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); - } - -handle_b_res_1: - if (b_res[1].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; - 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[0].start = 0; + b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN; + if (realloc_head) + add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 0 /* dont care */); -handle_b_res_2: - /* 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; - pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - } + b_res[1].start = 0; + b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN; + if (realloc_head) + add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, 0 /* dont care */); /* * Check whether prefetchable memory is supported @@ -1006,46 +785,38 @@ handle_b_res_2: pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); } - if (b_res[2].parent) - goto handle_b_res_3; /* * 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; - 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); - } - - /* reduce that to half */ - b_res_3_size = pci_cardbus_mem_size; - } - -handle_b_res_3: - if (b_res[3].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; - 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[2].start = 0; + b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN; + if (realloc_head) + add_to_list(realloc_head, bridge, b_res+2, pci_cardbus_mem_size, 0 /* dont care */); + + b_res[3].start = 0; + b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN; + if (realloc_head) + add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size, 0 /* dont care */); + } else { + b_res[3].start = 0; + b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN; + if (realloc_head) + add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size * 2, 0 /* dont care */); } -handle_done: - ; + /* set the size of the resource to zero, so that the resource does not + * get assigned during required-resource allocation cycle but gets assigned + * during the optional-resource allocation cycle. + */ + b_res[0].start = b_res[1].start = b_res[2].start = b_res[3].start = 1; + b_res[0].end = b_res[1].end = b_res[2].end = b_res[3].end = 0; } void __ref __pci_bus_size_bridges(struct pci_bus *bus, - struct list_head *realloc_head) + struct resource_list_x *realloc_head) { struct pci_dev *dev; unsigned long mask, prefmask; @@ -1087,8 +858,7 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, * Follow thru */ default: - pbus_size_io(bus, realloc_head ? 0 : additional_io_size, - additional_io_size, realloc_head); + pbus_size_io(bus, 0, 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 @@ -1096,15 +866,11 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, 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)) + if (pbus_size_mem(bus, prefmask, prefmask, 0, 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); + pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head); break; } } @@ -1116,8 +882,8 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) EXPORT_SYMBOL(pci_bus_size_bridges); static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *realloc_head, - struct list_head *fail_head) + struct resource_list_x *realloc_head, + struct resource_list_x *fail_head) { struct pci_bus *b; struct pci_dev *dev; @@ -1156,19 +922,17 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus) EXPORT_SYMBOL(pci_bus_assign_resources); static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, - struct list_head *add_head, - struct list_head *fail_head) + struct resource_list_x *fail_head) { struct pci_bus *b; - pdev_assign_resources_sorted((struct pci_dev *)bridge, - add_head, fail_head); + pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head); b = bridge->subordinate; if (!b) return; - __pci_bus_assign_resources(b, add_head, fail_head); + __pci_bus_assign_resources(b, NULL, fail_head); switch (bridge->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -1331,58 +1095,6 @@ static int __init pci_get_max_depth(void) 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 __initdata = 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 __init pci_realloc_enabled(void) -{ - return pci_realloc_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; - - if (pci_realloc_enable != undefined) - return; - - for_each_pci_dev(dev) { - int i; - - for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { - struct resource *r = &dev->resource[i]; - - /* Not assigned, or rejected by kernel ? */ - if (r->flags && !r->start) { - pci_realloc_enable = auto_enabled; - - return; - } - } - } -#endif -} /* * first try will not touch pci bridge res @@ -1393,57 +1105,59 @@ void __init pci_assign_unassigned_resources(void) { struct pci_bus *bus; - LIST_HEAD(realloc_head); /* list of resources that + struct resource_list_x realloc_list; /* 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; + struct resource_list_x head, *list; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - int pci_try_num = 1; + unsigned long failed_type; + int max_depth = pci_get_max_depth(); + int pci_try_num; - /* don't realloc if asked to do so */ - pci_realloc_detect(); - if (pci_realloc_enabled()) { - int max_depth = pci_get_max_depth(); - pci_try_num = max_depth + 1; - printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", - max_depth, pci_try_num); - } + head.next = NULL; + realloc_list.next = NULL; + + pci_try_num = max_depth + 1; + printk(KERN_DEBUG "PCI: 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) - __pci_bus_size_bridges(bus, add_list); + __pci_bus_size_bridges(bus, &realloc_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)); + __pci_bus_assign_resources(bus, &realloc_list, &head); + BUG_ON(realloc_list.next); tried_times++; /* any device complain? */ - if (list_empty(&fail_head)) + if (!head.next) goto enable_and_dump; - 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"); + /* don't realloc if asked to do so */ + if (!pci_realloc_enabled()) { + free_list(resource_list_x, &head); + goto enable_and_dump; + } - free_list(&fail_head); + failed_type = 0; + for (list = head.next; list;) { + failed_type |= list->flags; + list = list->next; + } + /* + * io port are tight, don't try extra + * or if reach the limit, don't want to try more + */ + failed_type &= type_mask; + if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) { + free_list(resource_list_x, &head); goto enable_and_dump; } @@ -1458,23 +1172,25 @@ again: * 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); + for (list = head.next; list;) { + bus = list->dev->bus; + pci_bus_release_bridge_resources(bus, list->flags & type_mask, + rel_type); + list = list->next; } /* restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; + for (list = head.next; list;) { + struct resource *res = list->res; - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - if (fail_res->dev->subordinate) + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + if (list->dev->subordinate) res->flags = 0; + + list = list->next; } - free_list(&fail_head); + free_list(resource_list_x, &head); goto again; @@ -1491,27 +1207,26 @@ enable_and_dump: 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 */ int tried_times = 0; - LIST_HEAD(fail_head); - struct pci_dev_resource *fail_res; + struct resource_list_x head, *list; int retval; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; + head.next = NULL; + again: - __pci_bus_size_bridges(parent, &add_list); - __pci_bridge_assign_resources(bridge, &add_list, &fail_head); - BUG_ON(!list_empty(&add_list)); + pci_bus_size_bridges(parent); + __pci_bridge_assign_resources(bridge, &head); + tried_times++; - if (list_empty(&fail_head)) + if (!head.next) goto enable_all; if (tried_times >= 2) { /* still fail, don't need to try more */ - free_list(&fail_head); + free_list(resource_list_x, &head); goto enable_all; } @@ -1522,24 +1237,27 @@ again: * 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; + for (list = head.next; list;) { + struct pci_bus *bus = list->dev->bus; + unsigned long flags = list->flags; pci_bus_release_bridge_resources(bus, flags & type_mask, whole_subtree); + list = list->next; } /* restore size and flags */ - list_for_each_entry(fail_res, &fail_head, list) { - struct resource *res = fail_res->res; + for (list = head.next; list;) { + struct resource *res = list->res; - res->start = fail_res->start; - res->end = fail_res->end; - res->flags = fail_res->flags; - if (fail_res->dev->subordinate) + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + if (list->dev->subordinate) res->flags = 0; + + list = list->next; } - free_list(&fail_head); + free_list(resource_list_x, &head); goto again; @@ -1549,21 +1267,3 @@ enable_all: pci_enable_bridges(parent); } 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 */ - - 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); - up_read(&pci_bus_sem); - __pci_bus_assign_resources(bus, &add_list, NULL); - BUG_ON(!list_empty(&add_list)); -} -- cgit v1.2.2