diff options
Diffstat (limited to 'drivers/pci/setup-res.c')
| -rw-r--r-- | drivers/pci/setup-res.c | 96 | 
1 files changed, 65 insertions, 31 deletions
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 07f2eddc09c..caed1ce6fac 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -16,7 +16,6 @@   *	     Resource sorting   */ -#include <linux/init.h>  #include <linux/kernel.h>  #include <linux/export.h>  #include <linux/pci.h> @@ -44,6 +43,9 @@ void pci_update_resource(struct pci_dev *dev, int resno)  	if (!res->flags)  		return; +	if (res->flags & IORESOURCE_UNSET) +		return; +  	/*  	 * Ignore non-moveable resources.  This might be legacy resources for  	 * which no functional BAR register exists or another important @@ -52,7 +54,7 @@ void pci_update_resource(struct pci_dev *dev, int resno)  	if (res->flags & IORESOURCE_PCI_FIXED)  		return; -	pcibios_resource_to_bus(dev, ®ion, res); +	pcibios_resource_to_bus(dev->bus, ®ion, res);  	new = region.start | (res->flags & PCI_REGION_FLAG_MASK);  	if (res->flags & IORESOURCE_IO) @@ -94,18 +96,13 @@ void pci_update_resource(struct pci_dev *dev, int resno)  		pci_write_config_dword(dev, reg + 4, new);  		pci_read_config_dword(dev, reg + 4, &check);  		if (check != new) { -			dev_err(&dev->dev, "BAR %d: error updating " -			       "(high %#08x != %#08x)\n", resno, new, check); +			dev_err(&dev->dev, "BAR %d: error updating (high %#08x != %#08x)\n", +				resno, new, check);  		}  	}  	if (disable)  		pci_write_config_word(dev, PCI_COMMAND, cmd); - -	res->flags &= ~IORESOURCE_UNSET; -	dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", -		resno, res, (unsigned long long)region.start, -		(unsigned long long)region.end);  }  int pci_claim_resource(struct pci_dev *dev, int resource) @@ -113,18 +110,23 @@ int pci_claim_resource(struct pci_dev *dev, int resource)  	struct resource *res = &dev->resource[resource];  	struct resource *root, *conflict; +	if (res->flags & IORESOURCE_UNSET) { +		dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n", +			 resource, res); +		return -EINVAL; +	} +  	root = pci_find_parent_resource(dev, res);  	if (!root) { -		dev_info(&dev->dev, "no compatible bridge window for %pR\n", -			 res); +		dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", +			 resource, res);  		return -EINVAL;  	}  	conflict = request_resource_conflict(root, res);  	if (conflict) { -		dev_info(&dev->dev, -			 "address space collision: %pR conflicts with %s %pR\n", -			 res, conflict->name, conflict); +		dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", +			 resource, res, conflict->name, conflict);  		return -EBUSY;  	} @@ -159,7 +161,7 @@ resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)  	return 0;  } -static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,  +static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,  		int resno, resource_size_t size)  {  	struct resource *root, *conflict; @@ -206,21 +208,42 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,  	min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; -	/* First, try exact prefetching match.. */ +	/* +	 * First, try exact prefetching match.  Even if a 64-bit +	 * prefetchable bridge window is below 4GB, we can't put a 32-bit +	 * prefetchable resource in it because pbus_size_mem() assumes a +	 * 64-bit window will contain no 32-bit resources.  If we assign +	 * things differently than they were sized, not everything will fit. +	 */  	ret = pci_bus_alloc_resource(bus, res, size, align, min, -				     IORESOURCE_PREFETCH, +				     IORESOURCE_PREFETCH | IORESOURCE_MEM_64,  				     pcibios_align_resource, dev); +	if (ret == 0) +		return 0; -	if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) { -		/* -		 * That failed. -		 * -		 * But a prefetching area can handle a non-prefetching -		 * window (it will just not perform as well). -		 */ -		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, +	/* +	 * If the prefetchable window is only 32 bits wide, we can put +	 * 64-bit prefetchable resources in it. +	 */ +	if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == +	     (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { +		ret = pci_bus_alloc_resource(bus, res, size, align, min, +					     IORESOURCE_PREFETCH,  					     pcibios_align_resource, dev); +		if (ret == 0) +			return 0;  	} + +	/* +	 * If we didn't find a better match, we can put any memory resource +	 * in a non-prefetchable window.  If this resource is 32 bits and +	 * non-prefetchable, the first call already tried the only possibility +	 * so we don't need to try again. +	 */ +	if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) +		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, +					     pcibios_align_resource, dev); +  	return ret;  } @@ -263,10 +286,11 @@ int pci_assign_resource(struct pci_dev *dev, int resno)  	resource_size_t align, size;  	int ret; +	res->flags |= IORESOURCE_UNSET;  	align = pci_resource_alignment(dev, res);  	if (!align) { -		dev_info(&dev->dev, "BAR %d: can't assign %pR " -			 "(bogus alignment)\n", resno, res); +		dev_info(&dev->dev, "BAR %d: can't assign %pR (bogus alignment)\n", +			 resno, res);  		return -EINVAL;  	} @@ -282,6 +306,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)  		ret = pci_revert_fw_address(res, dev, resno, size);  	if (!ret) { +		res->flags &= ~IORESOURCE_UNSET;  		res->flags &= ~IORESOURCE_STARTALIGN;  		dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);  		if (resno < PCI_BRIDGE_RESOURCES) @@ -289,6 +314,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)  	}  	return ret;  } +EXPORT_SYMBOL(pci_assign_resource);  int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,  			resource_size_t min_align) @@ -297,9 +323,10 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz  	resource_size_t new_size;  	int ret; +	res->flags |= IORESOURCE_UNSET;  	if (!res->parent) { -		dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR " -			 "\n", resno, res); +		dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR\n", +			 resno, res);  		return -EINVAL;  	} @@ -307,6 +334,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz  	new_size = resource_size(res) + addsize;  	ret = _pci_assign_resource(dev, resno, new_size, min_align);  	if (!ret) { +		res->flags &= ~IORESOURCE_UNSET;  		res->flags &= ~IORESOURCE_STARTALIGN;  		dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res);  		if (resno < PCI_BRIDGE_RESOURCES) @@ -336,9 +364,15 @@ int pci_enable_resources(struct pci_dev *dev, int mask)  				(!(r->flags & IORESOURCE_ROM_ENABLE)))  			continue; +		if (r->flags & IORESOURCE_UNSET) { +			dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n", +				i, r); +			return -EINVAL; +		} +  		if (!r->parent) { -			dev_err(&dev->dev, "device not available " -				"(can't reserve %pR)\n", r); +			dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n", +				i, r);  			return -EINVAL;  		}  | 
