|
@@ -34,6 +34,7 @@ struct resource_list_x {
|
|
|
resource_size_t start;
|
|
|
resource_size_t end;
|
|
|
resource_size_t add_size;
|
|
|
+ resource_size_t min_align;
|
|
|
unsigned long flags;
|
|
|
};
|
|
|
|
|
@@ -65,7 +66,7 @@ void pci_realloc(void)
|
|
|
*/
|
|
|
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 add_size, resource_size_t min_align)
|
|
|
{
|
|
|
struct resource_list_x *list = head;
|
|
|
struct resource_list_x *ln = list->next;
|
|
@@ -84,13 +85,16 @@ static void add_to_list(struct resource_list_x *head,
|
|
|
tmp->end = res->end;
|
|
|
tmp->flags = res->flags;
|
|
|
tmp->add_size = add_size;
|
|
|
+ tmp->min_align = min_align;
|
|
|
list->next = tmp;
|
|
|
}
|
|
|
|
|
|
static void add_to_failed_list(struct resource_list_x *head,
|
|
|
struct pci_dev *dev, struct resource *res)
|
|
|
{
|
|
|
- add_to_list(head, dev, res, 0);
|
|
|
+ add_to_list(head, dev, res,
|
|
|
+ 0 /* dont care */,
|
|
|
+ 0 /* dont care */);
|
|
|
}
|
|
|
|
|
|
static void __dev_sort_resources(struct pci_dev *dev,
|
|
@@ -121,18 +125,18 @@ static inline void reset_resource(struct resource *res)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * adjust_resources_sorted() - satisfy any additional resource requests
|
|
|
+ * reassign_resources_sorted() - satisfy any additional resource requests
|
|
|
*
|
|
|
- * @add_head : head of the list tracking requests requiring additional
|
|
|
+ * @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 add_head and try to procure
|
|
|
+ * 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 adjust_resources_sorted(struct resource_list_x *add_head,
|
|
|
+static void reassign_resources_sorted(struct resource_list_x *realloc_head,
|
|
|
struct resource_list *head)
|
|
|
{
|
|
|
struct resource *res;
|
|
@@ -141,8 +145,8 @@ static void adjust_resources_sorted(struct resource_list_x *add_head,
|
|
|
resource_size_t add_size;
|
|
|
int idx;
|
|
|
|
|
|
- prev = add_head;
|
|
|
- for (list = add_head->next; list;) {
|
|
|
+ prev = realloc_head;
|
|
|
+ for (list = realloc_head->next; list;) {
|
|
|
res = list->res;
|
|
|
/* skip resource that has been reset */
|
|
|
if (!res->flags)
|
|
@@ -159,13 +163,17 @@ static void adjust_resources_sorted(struct resource_list_x *add_head,
|
|
|
|
|
|
idx = res - &list->dev->resource[0];
|
|
|
add_size=list->add_size;
|
|
|
- if (!resource_size(res) && add_size) {
|
|
|
- res->end = res->start + add_size - 1;
|
|
|
- if(pci_assign_resource(list->dev, idx))
|
|
|
+ if (!resource_size(res)) {
|
|
|
+ res->start = list->start;
|
|
|
+ res->end = res->start + add_size - 1;
|
|
|
+ if(pci_assign_resource(list->dev, idx))
|
|
|
reset_resource(res);
|
|
|
- } else if (add_size) {
|
|
|
- adjust_resource(res, res->start,
|
|
|
- resource_size(res) + add_size);
|
|
|
+ } else {
|
|
|
+ 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:
|
|
|
tmp = list;
|
|
@@ -210,16 +218,16 @@ static void assign_requested_resources_sorted(struct resource_list *head,
|
|
|
}
|
|
|
|
|
|
static void __assign_resources_sorted(struct resource_list *head,
|
|
|
- struct resource_list_x *add_head,
|
|
|
+ struct resource_list_x *realloc_head,
|
|
|
struct resource_list_x *fail_head)
|
|
|
{
|
|
|
/* Satisfy the must-have resource requests */
|
|
|
assign_requested_resources_sorted(head, fail_head);
|
|
|
|
|
|
- /* Try to satisfy any additional nice-to-have resource
|
|
|
+ /* Try to satisfy any additional optional resource
|
|
|
requests */
|
|
|
- if (add_head)
|
|
|
- adjust_resources_sorted(add_head, head);
|
|
|
+ if (realloc_head)
|
|
|
+ reassign_resources_sorted(realloc_head, head);
|
|
|
free_list(resource_list, head);
|
|
|
}
|
|
|
|
|
@@ -235,7 +243,7 @@ static void pdev_assign_resources_sorted(struct pci_dev *dev,
|
|
|
}
|
|
|
|
|
|
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
|
|
|
- struct resource_list_x *add_head,
|
|
|
+ struct resource_list_x *realloc_head,
|
|
|
struct resource_list_x *fail_head)
|
|
|
{
|
|
|
struct pci_dev *dev;
|
|
@@ -245,7 +253,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus,
|
|
|
list_for_each_entry(dev, &bus->devices, bus_list)
|
|
|
__dev_sort_resources(dev, &head);
|
|
|
|
|
|
- __assign_resources_sorted(&head, add_head, fail_head);
|
|
|
+ __assign_resources_sorted(&head, realloc_head, fail_head);
|
|
|
}
|
|
|
|
|
|
void pci_setup_cardbus(struct pci_bus *bus)
|
|
@@ -540,13 +548,27 @@ static resource_size_t calculate_memsize(resource_size_t size,
|
|
|
return size;
|
|
|
}
|
|
|
|
|
|
+static resource_size_t get_res_add_size(struct resource_list_x *realloc_head,
|
|
|
+ struct resource *res)
|
|
|
+{
|
|
|
+ struct resource_list_x *list;
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* pbus_size_io() - size the io window of a given bus
|
|
|
*
|
|
|
* @bus : the bus
|
|
|
* @min_size : the minimum io window that must to be allocated
|
|
|
* @add_size : additional optional io window
|
|
|
- * @add_head : track the additional io window on this list
|
|
|
+ * @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 4K granularity and the IO ranges
|
|
@@ -554,11 +576,12 @@ static resource_size_t calculate_memsize(resource_size_t size,
|
|
|
* 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 resource_list_x *add_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;
|
|
|
|
|
|
if (!b_res)
|
|
|
return;
|
|
@@ -579,11 +602,16 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
|
|
size += r_size;
|
|
|
else
|
|
|
size1 += r_size;
|
|
|
+
|
|
|
+ if (realloc_head)
|
|
|
+ children_add_size += get_res_add_size(realloc_head, r);
|
|
|
}
|
|
|
}
|
|
|
size0 = calculate_iosize(size, min_size, size1,
|
|
|
resource_size(b_res), 4096);
|
|
|
- size1 = (!add_head || (add_head && !add_size)) ? size0 :
|
|
|
+ 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), 4096);
|
|
|
if (!size0 && !size1) {
|
|
@@ -598,8 +626,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
|
|
b_res->start = 4096;
|
|
|
b_res->end = b_res->start + size0 - 1;
|
|
|
b_res->flags |= IORESOURCE_STARTALIGN;
|
|
|
- if (size1 > size0 && add_head)
|
|
|
- add_to_list(add_head, bus->self, b_res, size1-size0);
|
|
|
+ if (size1 > size0 && realloc_head)
|
|
|
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -608,7 +636,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
|
|
* @bus : the bus
|
|
|
* @min_size : the minimum memory window that must to be allocated
|
|
|
* @add_size : additional optional memory window
|
|
|
- * @add_head : track the additional memory window on this list
|
|
|
+ * @realloc_head : track the additional memory window on this list
|
|
|
*
|
|
|
* Calculate the size of the bus and minimal alignment which
|
|
|
* guarantees that all child resources fit in this size.
|
|
@@ -616,7 +644,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
|
|
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 resource_list_x *add_head)
|
|
|
+ struct resource_list_x *realloc_head)
|
|
|
{
|
|
|
struct pci_dev *dev;
|
|
|
resource_size_t min_align, align, size, size0, size1;
|
|
@@ -624,6 +652,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
int order, max_order;
|
|
|
struct resource *b_res = find_free_bus_resource(bus, type);
|
|
|
unsigned int mem64_mask = 0;
|
|
|
+ resource_size_t children_add_size = 0;
|
|
|
|
|
|
if (!b_res)
|
|
|
return 0;
|
|
@@ -645,6 +674,16 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
if (r->parent || (r->flags & mask) != type)
|
|
|
continue;
|
|
|
r_size = resource_size(r);
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
|
+ /* put SRIOV requested res to the optional list */
|
|
|
+ if (realloc_head && i >= PCI_IOV_RESOURCES &&
|
|
|
+ i <= PCI_IOV_RESOURCE_END) {
|
|
|
+ r->end = r->start - 1;
|
|
|
+ add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */);
|
|
|
+ children_add_size += r_size;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+#endif
|
|
|
/* For bridges size != alignment */
|
|
|
align = pci_resource_alignment(dev, r);
|
|
|
order = __ffs(align) - 20;
|
|
@@ -665,6 +704,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
if (order > max_order)
|
|
|
max_order = order;
|
|
|
mem64_mask &= r->flags & IORESOURCE_MEM_64;
|
|
|
+
|
|
|
+ if (realloc_head)
|
|
|
+ children_add_size += get_res_add_size(realloc_head, r);
|
|
|
}
|
|
|
}
|
|
|
align = 0;
|
|
@@ -681,7 +723,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
align += aligns[order];
|
|
|
}
|
|
|
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
|
|
|
- size1 = (!add_head || (add_head && !add_size)) ? size0 :
|
|
|
+ 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, 0,
|
|
|
resource_size(b_res), min_align);
|
|
|
if (!size0 && !size1) {
|
|
@@ -695,12 +739,22 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
|
|
b_res->start = min_align;
|
|
|
b_res->end = size0 + min_align - 1;
|
|
|
b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
|
|
|
- if (size1 > size0 && add_head)
|
|
|
- add_to_list(add_head, bus->self, b_res, size1-size0);
|
|
|
+ if (size1 > size0 && realloc_head)
|
|
|
+ add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static void pci_bus_size_cardbus(struct pci_bus *bus)
|
|
|
+unsigned long pci_cardbus_resource_alignment(struct resource *res)
|
|
|
+{
|
|
|
+ if (res->flags & IORESOURCE_IO)
|
|
|
+ return pci_cardbus_io_size;
|
|
|
+ if (res->flags & IORESOURCE_MEM)
|
|
|
+ return pci_cardbus_mem_size;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void pci_bus_size_cardbus(struct pci_bus *bus,
|
|
|
+ struct resource_list_x *realloc_head)
|
|
|
{
|
|
|
struct pci_dev *bridge = bus->self;
|
|
|
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
|
|
@@ -711,12 +765,14 @@ static void pci_bus_size_cardbus(struct pci_bus *bus)
|
|
|
* a fixed amount of bus space for CardBus bridges.
|
|
|
*/
|
|
|
b_res[0].start = 0;
|
|
|
- b_res[0].end = pci_cardbus_io_size - 1;
|
|
|
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 */);
|
|
|
|
|
|
b_res[1].start = 0;
|
|
|
- b_res[1].end = pci_cardbus_io_size - 1;
|
|
|
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
|
|
@@ -736,21 +792,31 @@ static void pci_bus_size_cardbus(struct pci_bus *bus)
|
|
|
*/
|
|
|
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
|
|
|
b_res[2].start = 0;
|
|
|
- b_res[2].end = pci_cardbus_mem_size - 1;
|
|
|
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].end = pci_cardbus_mem_size - 1;
|
|
|
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].end = pci_cardbus_mem_size * 2 - 1;
|
|
|
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 */);
|
|
|
}
|
|
|
+
|
|
|
+ /* 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 resource_list_x *add_head)
|
|
|
+ struct resource_list_x *realloc_head)
|
|
|
{
|
|
|
struct pci_dev *dev;
|
|
|
unsigned long mask, prefmask;
|
|
@@ -763,12 +829,12 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
|
|
|
|
|
switch (dev->class >> 8) {
|
|
|
case PCI_CLASS_BRIDGE_CARDBUS:
|
|
|
- pci_bus_size_cardbus(b);
|
|
|
+ pci_bus_size_cardbus(b, realloc_head);
|
|
|
break;
|
|
|
|
|
|
case PCI_CLASS_BRIDGE_PCI:
|
|
|
default:
|
|
|
- __pci_bus_size_bridges(b, add_head);
|
|
|
+ __pci_bus_size_bridges(b, realloc_head);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -792,7 +858,7 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
|
|
* Follow thru
|
|
|
*/
|
|
|
default:
|
|
|
- pbus_size_io(bus, 0, additional_io_size, add_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
|
|
@@ -800,11 +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, 0, additional_mem_size, add_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, 0, additional_mem_size, add_head);
|
|
|
+ pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -816,20 +882,20 @@ 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 resource_list_x *add_head,
|
|
|
+ struct resource_list_x *realloc_head,
|
|
|
struct resource_list_x *fail_head)
|
|
|
{
|
|
|
struct pci_bus *b;
|
|
|
struct pci_dev *dev;
|
|
|
|
|
|
- pbus_assign_resources_sorted(bus, add_head, fail_head);
|
|
|
+ pbus_assign_resources_sorted(bus, realloc_head, fail_head);
|
|
|
|
|
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
|
|
b = dev->subordinate;
|
|
|
if (!b)
|
|
|
continue;
|
|
|
|
|
|
- __pci_bus_assign_resources(b, add_head, fail_head);
|
|
|
+ __pci_bus_assign_resources(b, realloc_head, fail_head);
|
|
|
|
|
|
switch (dev->class >> 8) {
|
|
|
case PCI_CLASS_BRIDGE_PCI:
|
|
@@ -1039,7 +1105,7 @@ void __init
|
|
|
pci_assign_unassigned_resources(void)
|
|
|
{
|
|
|
struct pci_bus *bus;
|
|
|
- struct resource_list_x add_list; /* list of resources that
|
|
|
+ struct resource_list_x realloc_list; /* list of resources that
|
|
|
want additional resources */
|
|
|
int tried_times = 0;
|
|
|
enum release_type rel_type = leaf_only;
|
|
@@ -1052,7 +1118,7 @@ pci_assign_unassigned_resources(void)
|
|
|
|
|
|
|
|
|
head.next = NULL;
|
|
|
- add_list.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",
|
|
@@ -1062,12 +1128,12 @@ again:
|
|
|
/* 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, &head);
|
|
|
- BUG_ON(add_list.next);
|
|
|
+ __pci_bus_assign_resources(bus, &realloc_list, &head);
|
|
|
+ BUG_ON(realloc_list.next);
|
|
|
tried_times++;
|
|
|
|
|
|
/* any device complain? */
|