diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 169 | 
1 files changed, 116 insertions, 53 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9995842e45b..784da9d3602 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -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) @@ -336,7 +344,6 @@ static void pci_setup_bridge_io(struct pci_bus *bus)  		/* Clear upper 16 bits of I/O base/limit. */  		io_upper16 = 0;  		l = 0x00f0; -		dev_info(&bridge->dev, "  bridge window [io  disabled]\n");  	}  	/* Temporarily disable the I/O range before updating PCI_IO_BASE. */  	pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); @@ -362,7 +369,6 @@ static void pci_setup_bridge_mmio(struct pci_bus *bus)  		dev_info(&bridge->dev, "  bridge window %pR\n", res);  	} else {  		l = 0x0000fff0; -		dev_info(&bridge->dev, "  bridge window [mem disabled]\n");  	}  	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);  } @@ -393,7 +399,6 @@ static void pci_setup_bridge_mmio_pref(struct pci_bus *bus)  		dev_info(&bridge->dev, "  bridge window %pR\n", res);  	} else {  		l = 0x0000fff0; -		dev_info(&bridge->dev, "  bridge window [mem pref disabled]\n");  	}  	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); @@ -543,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 @@ -557,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; @@ -582,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) { @@ -601,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);  }  /** @@ -611,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. @@ -619,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; @@ -627,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; @@ -648,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; @@ -668,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; @@ -684,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) { @@ -698,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]; @@ -714,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 @@ -739,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; @@ -766,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;  		}  	} @@ -795,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 @@ -803,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;  	}  } @@ -819,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: @@ -1042,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; @@ -1055,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", @@ -1065,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? */  |