diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 163 | 
1 files changed, 95 insertions, 68 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f279826204eb..f2461bf9243d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1803,12 +1803,18 @@ again:  	/* Restore size and flags */  	list_for_each_entry(fail_res, &fail_head, list) {  		struct resource *res = fail_res->res; +		int idx;  		res->start = fail_res->start;  		res->end = fail_res->end;  		res->flags = fail_res->flags; -		if (fail_res->dev->subordinate) -			res->flags = 0; + +		if (pci_is_bridge(fail_res->dev)) { +			idx = res - &fail_res->dev->resource[0]; +			if (idx >= PCI_BRIDGE_RESOURCES && +			    idx <= PCI_BRIDGE_RESOURCE_END) +				res->flags = 0; +		}  	}  	free_list(&fail_head); @@ -1832,56 +1838,72 @@ void __init pci_assign_unassigned_resources(void)  	}  } -static void extend_bridge_window(struct pci_dev *bridge, struct resource *res, +static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,  				 struct list_head *add_list, -				 resource_size_t available) +				 resource_size_t new_size)  { -	struct pci_dev_resource *dev_res; +	resource_size_t add_size, size = resource_size(res);  	if (res->parent)  		return; -	if (resource_size(res) >= available) +	if (!new_size)  		return; -	dev_res = res_to_dev_res(add_list, res); -	if (!dev_res) -		return; - -	/* Is there room to extend the window? */ -	if (available - resource_size(res) <= dev_res->add_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) { +		add_size = size - new_size; +		pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res, +			&add_size); +	} -	dev_res->add_size = available - resource_size(res); -	pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, -		&dev_res->add_size); +	res->end = res->start + new_size - 1; +	remove_from_list(add_list, res);  }  static void pci_bus_distribute_available_resources(struct pci_bus *bus,  					    struct list_head *add_list, -					    resource_size_t available_io, -					    resource_size_t available_mmio, -					    resource_size_t available_mmio_pref) +					    struct resource io, +					    struct resource mmio, +					    struct resource mmio_pref)  { -	resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;  	unsigned int normal_bridges = 0, hotplug_bridges = 0;  	struct resource *io_res, *mmio_res, *mmio_pref_res;  	struct pci_dev *dev, *bridge = bus->self; +	resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align;  	io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];  	mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];  	mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];  	/* -	 * Update additional resource list (add_list) to fill all the -	 * extra resource space available for this port except the space -	 * calculated in __pci_bus_size_bridges() which covers all the -	 * devices currently connected to the port and below. +	 * 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, io_res); +	if (!io_res->parent && align) +		io.start = min(ALIGN(io.start, align), io.end + 1); + +	align = pci_resource_alignment(bridge, mmio_res); +	if (!mmio_res->parent && align) +		mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1); + +	align = pci_resource_alignment(bridge, mmio_pref_res); +	if (!mmio_pref_res->parent && align) +		mmio_pref.start = min(ALIGN(mmio_pref.start, align), +			mmio_pref.end + 1); + +	/* +	 * Now that we have adjusted for alignment, update the bridge window +	 * resources to fill as much remaining resource space as possible.  	 */ -	extend_bridge_window(bridge, io_res, add_list, available_io); -	extend_bridge_window(bridge, mmio_res, add_list, available_mmio); -	extend_bridge_window(bridge, mmio_pref_res, add_list, -			     available_mmio_pref); +	adjust_bridge_window(bridge, io_res, add_list, resource_size(&io)); +	adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); +	adjust_bridge_window(bridge, mmio_pref_res, add_list, +			     resource_size(&mmio_pref));  	/*  	 * Calculate how many hotplug bridges and normal bridges there @@ -1902,11 +1924,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,  	 */  	if (hotplug_bridges + normal_bridges == 1) {  		dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); -		if (dev->subordinate) { +		if (dev->subordinate)  			pci_bus_distribute_available_resources(dev->subordinate, -				add_list, available_io, available_mmio, -				available_mmio_pref); -		} +				add_list, io, mmio, mmio_pref);  		return;  	} @@ -1919,12 +1939,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,  	 * extra space reduced by the minimal required space for the  	 * non-hotplug bridges.  	 */ -	remaining_io = available_io; -	remaining_mmio = available_mmio; -	remaining_mmio_pref = available_mmio_pref; -  	for_each_pci_bridge(dev, bus) { -		const struct resource *res; +		resource_size_t used_size; +		struct resource *res;  		if (dev->is_hotplug_bridge)  			continue; @@ -1934,24 +1951,39 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,  		 * bridge and devices below it occupy.  		 */  		res = &dev->resource[PCI_BRIDGE_RESOURCES + 0]; -		if (!res->parent && available_io > resource_size(res)) -			remaining_io -= resource_size(res); +		align = pci_resource_alignment(dev, res); +		align = align ? ALIGN(io.start, align) - io.start : 0; +		used_size = align + resource_size(res); +		if (!res->parent) +			io.start = min(io.start + used_size, io.end + 1);  		res = &dev->resource[PCI_BRIDGE_RESOURCES + 1]; -		if (!res->parent && available_mmio > resource_size(res)) -			remaining_mmio -= resource_size(res); +		align = pci_resource_alignment(dev, res); +		align = align ? ALIGN(mmio.start, align) - mmio.start : 0; +		used_size = align + resource_size(res); +		if (!res->parent) +			mmio.start = min(mmio.start + used_size, mmio.end + 1);  		res = &dev->resource[PCI_BRIDGE_RESOURCES + 2]; -		if (!res->parent && available_mmio_pref > resource_size(res)) -			remaining_mmio_pref -= resource_size(res); +		align = pci_resource_alignment(dev, res); +		align = align ? ALIGN(mmio_pref.start, align) - +			mmio_pref.start : 0; +		used_size = align + resource_size(res); +		if (!res->parent) +			mmio_pref.start = min(mmio_pref.start + used_size, +				mmio_pref.end + 1);  	} +	io_per_hp = div64_ul(resource_size(&io), hotplug_bridges); +	mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges); +	mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref), +		hotplug_bridges); +  	/*  	 * Go over devices on this bus and distribute the remaining  	 * resource space between hotplug bridges.  	 */  	for_each_pci_bridge(dev, bus) { -		resource_size_t align, io, mmio, mmio_pref;  		struct pci_bus *b;  		b = dev->subordinate; @@ -1963,42 +1995,31 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,  		 * hotplug-capable downstream ports taking alignment into  		 * account.  		 */ -		align = pci_resource_alignment(bridge, io_res); -		io = div64_ul(available_io, hotplug_bridges); -		io = min(ALIGN(io, align), remaining_io); -		remaining_io -= io; - -		align = pci_resource_alignment(bridge, mmio_res); -		mmio = div64_ul(available_mmio, hotplug_bridges); -		mmio = min(ALIGN(mmio, align), remaining_mmio); -		remaining_mmio -= mmio; - -		align = pci_resource_alignment(bridge, mmio_pref_res); -		mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges); -		mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref); -		remaining_mmio_pref -= mmio_pref; +		io.end = io.start + io_per_hp - 1; +		mmio.end = mmio.start + mmio_per_hp - 1; +		mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1;  		pci_bus_distribute_available_resources(b, add_list, io, mmio,  						       mmio_pref); + +		io.start += io_per_hp; +		mmio.start += mmio_per_hp; +		mmio_pref.start += mmio_pref_per_hp;  	}  }  static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,  						     struct list_head *add_list)  { -	resource_size_t available_io, available_mmio, available_mmio_pref; -	const struct resource *res; +	struct resource available_io, available_mmio, available_mmio_pref;  	if (!bridge->is_hotplug_bridge)  		return;  	/* Take the initial extra resources from the hotplug port */ -	res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; -	available_io = resource_size(res); -	res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; -	available_mmio = resource_size(res); -	res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; -	available_mmio_pref = resource_size(res); +	available_io = bridge->resource[PCI_BRIDGE_RESOURCES + 0]; +	available_mmio = bridge->resource[PCI_BRIDGE_RESOURCES + 1]; +	available_mmio_pref = bridge->resource[PCI_BRIDGE_RESOURCES + 2];  	pci_bus_distribute_available_resources(bridge->subordinate,  					       add_list, available_io, @@ -2055,12 +2076,18 @@ again:  	/* Restore size and flags */  	list_for_each_entry(fail_res, &fail_head, list) {  		struct resource *res = fail_res->res; +		int idx;  		res->start = fail_res->start;  		res->end = fail_res->end;  		res->flags = fail_res->flags; -		if (fail_res->dev->subordinate) -			res->flags = 0; + +		if (pci_is_bridge(fail_res->dev)) { +			idx = res - &fail_res->dev->resource[0]; +			if (idx >= PCI_BRIDGE_RESOURCES && +			    idx <= PCI_BRIDGE_RESOURCE_END) +				res->flags = 0; +		}  	}  	free_list(&fail_head);  |