diff options
| -rw-r--r-- | drivers/pci/setup-bus.c | 27 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 150 | ||||
| -rw-r--r-- | include/linux/pci.h | 1 | 
3 files changed, 112 insertions, 66 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 4409cd0e15f..1796c6ffe91 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, @@ -159,13 +163,16 @@ 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->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; @@ -619,7 +626,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,  	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); +		add_to_list(add_head, bus->self, b_res, size1-size0, 4096);  }  /** @@ -722,7 +729,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  	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); +		add_to_list(add_head, bus->self, b_res, size1-size0, min_align);  	return 1;  } 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)  { diff --git a/include/linux/pci.h b/include/linux/pci.h index 1ff9bbafd93..8c230cbcbb4 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -813,6 +813,7 @@ int __pci_reset_function(struct pci_dev *dev);  int pci_reset_function(struct pci_dev *dev);  void pci_update_resource(struct pci_dev *dev, int resno);  int __must_check pci_assign_resource(struct pci_dev *dev, int i); +int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);  int pci_select_bars(struct pci_dev *dev, unsigned long flags);  /* ROM control related routines */  |