diff options
Diffstat (limited to 'drivers/pci/setup-res.c')
| -rw-r--r-- | drivers/pci/setup-res.c | 150 | 
1 files changed, 94 insertions, 56 deletions
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 319f359906e..51a9095c7da 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -128,16 +128,16 @@ void pci_disable_bridge_window(struct pci_dev *dev)  }  #endif	/* CONFIG_PCI_QUIRKS */ + +  static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, -				 int resno) +		int resno, resource_size_t size, resource_size_t align)  {  	struct resource *res = dev->resource + resno; -	resource_size_t size, min, align; +	resource_size_t min;  	int ret; -	size = resource_size(res);  	min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; -	align = pci_resource_alignment(dev, res);  	/* First, try exact prefetching match.. */  	ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -154,56 +154,101 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,  		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,  					     pcibios_align_resource, dev);  	} +	return ret; +} -	if (ret < 0 && dev->fw_addr[resno]) { -		struct resource *root, *conflict; -		resource_size_t start, end; +static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,  +		int resno, resource_size_t size) +{ +	struct resource *root, *conflict; +	resource_size_t start, end; +	int ret = 0; -		/* -		 * If we failed to assign anything, let's try the address -		 * where firmware left it.  That at least has a chance of -		 * working, which is better than just leaving it disabled. -		 */ +	if (res->flags & IORESOURCE_IO) +		root = &ioport_resource; +	else +		root = &iomem_resource; + +	start = res->start; +	end = res->end; +	res->start = dev->fw_addr[resno]; +	res->end = res->start + size - 1; +	dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n", +		 resno, res); +	conflict = request_resource_conflict(root, res); +	if (conflict) { +		dev_info(&dev->dev, +			 "BAR %d: %pR conflicts with %s %pR\n", resno, +			 res, conflict->name, conflict); +		res->start = start; +		res->end = end; +		ret = 1; +	} +	return ret; +} -		if (res->flags & IORESOURCE_IO) -			root = &ioport_resource; +static int _pci_assign_resource(struct pci_dev *dev, int resno, int size, resource_size_t min_align) +{ +	struct resource *res = dev->resource + resno; +	struct pci_bus *bus; +	int ret; +	char *type; + +	bus = dev->bus; +	while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { +		if (!bus->parent || !bus->self->transparent) +			break; +		bus = bus->parent; +	} + +	if (ret) { +		if (res->flags & IORESOURCE_MEM) +			if (res->flags & IORESOURCE_PREFETCH) +				type = "mem pref"; +			else +				type = "mem"; +		else if (res->flags & IORESOURCE_IO) +			type = "io";  		else -			root = &iomem_resource; +			type = "unknown"; +		dev_info(&dev->dev, +			 "BAR %d: can't assign %s (size %#llx)\n", +			 resno, type, (unsigned long long) resource_size(res)); +	} + +	return ret; +} -		start = res->start; -		end = res->end; -		res->start = dev->fw_addr[resno]; -		res->end = res->start + size - 1; -		dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n", -			 resno, res); -		conflict = request_resource_conflict(root, res); -		if (conflict) { -			dev_info(&dev->dev, -				 "BAR %d: %pR conflicts with %s %pR\n", resno, -				 res, conflict->name, conflict); -			res->start = start; -			res->end = end; -		} else -			ret = 0; +int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, +			resource_size_t min_align) +{ +	struct resource *res = dev->resource + resno; +	resource_size_t new_size; +	int ret; + +	if (!res->parent) { +		dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR " +			 "\n", resno, res); +		return -EINVAL;  	} +	new_size = resource_size(res) + addsize + min_align; +	ret = _pci_assign_resource(dev, resno, new_size, min_align);  	if (!ret) {  		res->flags &= ~IORESOURCE_STARTALIGN;  		dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);  		if (resno < PCI_BRIDGE_RESOURCES)  			pci_update_resource(dev, resno);  	} -  	return ret;  }  int pci_assign_resource(struct pci_dev *dev, int resno)  {  	struct resource *res = dev->resource + resno; -	resource_size_t align; +	resource_size_t align, size;  	struct pci_bus *bus;  	int ret; -	char *type;  	align = pci_resource_alignment(dev, res);  	if (!align) { @@ -213,34 +258,27 @@ int pci_assign_resource(struct pci_dev *dev, int resno)  	}  	bus = dev->bus; -	while ((ret = __pci_assign_resource(bus, dev, resno))) { -		if (bus->parent && bus->self->transparent) -			bus = bus->parent; -		else -			bus = NULL; -		if (bus) -			continue; -		break; -	} +	size = resource_size(res); +	ret = _pci_assign_resource(dev, resno, size, align); -	if (ret) { -		if (res->flags & IORESOURCE_MEM) -			if (res->flags & IORESOURCE_PREFETCH) -				type = "mem pref"; -			else -				type = "mem"; -		else if (res->flags & IORESOURCE_IO) -			type = "io"; -		else -			type = "unknown"; -		dev_info(&dev->dev, -			 "BAR %d: can't assign %s (size %#llx)\n", -			 resno, type, (unsigned long long) resource_size(res)); -	} +	/* +	 * If we failed to assign anything, let's try the address +	 * where firmware left it.  That at least has a chance of +	 * working, which is better than just leaving it disabled. +	 */ +	if (ret < 0 && dev->fw_addr[resno]) +		ret = pci_revert_fw_address(res, dev, resno, size); +	if (!ret) { +		res->flags &= ~IORESOURCE_STARTALIGN; +		dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); +		if (resno < PCI_BRIDGE_RESOURCES) +			pci_update_resource(dev, resno); +	}  	return ret;  } +  /* Sort resources by alignment */  void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)  {  |