diff options
Diffstat (limited to 'drivers/pci/pci.c')
| -rw-r--r-- | drivers/pci/pci.c | 133 | 
1 files changed, 130 insertions, 3 deletions
| diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index af295bb21d62..815674415267 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -94,6 +94,9 @@ u8 pci_cache_line_size;   */  unsigned int pcibios_max_latency = 255; +/* If set, the PCIe ARI capability will not be used. */ +static bool pcie_ari_disabled; +  /**   * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children   * @bus: pointer to PCI bus structure to search @@ -825,6 +828,19 @@ EXPORT_SYMBOL(pci_choose_state);  #define pcie_cap_has_sltctl2(type, flags)		\  		((flags & PCI_EXP_FLAGS_VERS) > 1) +static struct pci_cap_saved_state *pci_find_saved_cap( +	struct pci_dev *pci_dev, char cap) +{ +	struct pci_cap_saved_state *tmp; +	struct hlist_node *pos; + +	hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) { +		if (tmp->cap.cap_nr == cap) +			return tmp; +	} +	return NULL; +} +  static int pci_save_pcie_state(struct pci_dev *dev)  {  	int pos, i = 0; @@ -959,6 +975,7 @@ void pci_restore_state(struct pci_dev *dev)  {  	int i;  	u32 val; +	int tries;  	if (!dev->state_saved)  		return; @@ -973,12 +990,16 @@ void pci_restore_state(struct pci_dev *dev)  	 */  	for (i = 15; i >= 0; i--) {  		pci_read_config_dword(dev, i * 4, &val); -		if (val != dev->saved_config_space[i]) { +		tries = 10;		 +		while (tries && val != dev->saved_config_space[i]) {  			dev_dbg(&dev->dev, "restoring config "  				"space at offset %#x (was %#x, writing %#x)\n",  				i, val, (int)dev->saved_config_space[i]);  			pci_write_config_dword(dev,i * 4,  				dev->saved_config_space[i]); +			pci_read_config_dword(dev, i * 4, &val); +			mdelay(10); +			tries--;  		}  	}  	pci_restore_pcix_state(dev); @@ -1864,6 +1885,12 @@ void platform_pci_wakeup_init(struct pci_dev *dev)  	platform_pci_sleep_wake(dev, false);  } +static void pci_add_saved_cap(struct pci_dev *pci_dev, +	struct pci_cap_saved_state *new_cap) +{ +	hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); +} +  /**   * pci_add_save_buffer - allocate buffer for saving given capability registers   * @dev: the PCI device @@ -1911,6 +1938,15 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)  			"unable to preallocate PCI-X save buffer\n");  } +void pci_free_cap_save_buffers(struct pci_dev *dev) +{ +	struct pci_cap_saved_state *tmp; +	struct hlist_node *pos, *n; + +	hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next) +		kfree(tmp); +} +  /**   * pci_enable_ari - enable ARI forwarding if hardware support it   * @dev: the PCI device @@ -1922,7 +1958,7 @@ void pci_enable_ari(struct pci_dev *dev)  	u16 flags, ctrl;  	struct pci_dev *bridge; -	if (!pci_is_pcie(dev) || dev->devfn) +	if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)  		return;  	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); @@ -3163,6 +3199,31 @@ int __pci_reset_function(struct pci_dev *dev)  EXPORT_SYMBOL_GPL(__pci_reset_function);  /** + * __pci_reset_function_locked - reset a PCI device function while holding + * the @dev mutex lock. + * @dev: PCI device to reset + * + * Some devices allow an individual function to be reset without affecting + * other functions in the same device.  The PCI device must be responsive + * to PCI config space in order to use this function. + * + * The device function is presumed to be unused and the caller is holding + * the device mutex lock when this function is called. + * Resetting the device will make the contents of PCI configuration space + * random, so any caller of this must be prepared to reinitialise the + * device including MSI, bus mastering, BARs, decoding IO and memory spaces, + * etc. + * + * Returns 0 if the device function was successfully reset or negative if the + * device doesn't support resetting a single function. + */ +int __pci_reset_function_locked(struct pci_dev *dev) +{ +	return pci_dev_reset(dev, 1); +} +EXPORT_SYMBOL_GPL(__pci_reset_function_locked); + +/**   * pci_probe_reset_function - check whether the device can be safely reset   * @dev: PCI device to reset   * @@ -3636,6 +3697,68 @@ int pci_is_reassigndev(struct pci_dev *dev)  	return (pci_specified_resource_alignment(dev) != 0);  } +/* + * This function disables memory decoding and releases memory resources + * of the device specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to the device. + */ +void pci_reassigndev_resource_alignment(struct pci_dev *dev) +{ +	int i; +	struct resource *r; +	resource_size_t align, size; +	u16 command; + +	if (!pci_is_reassigndev(dev)) +		return; + +	if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && +	    (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { +		dev_warn(&dev->dev, +			"Can't reassign resources to host bridge.\n"); +		return; +	} + +	dev_info(&dev->dev, +		"Disabling memory decoding and releasing memory resources.\n"); +	pci_read_config_word(dev, PCI_COMMAND, &command); +	command &= ~PCI_COMMAND_MEMORY; +	pci_write_config_word(dev, PCI_COMMAND, command); + +	align = pci_specified_resource_alignment(dev); +	for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { +		r = &dev->resource[i]; +		if (!(r->flags & IORESOURCE_MEM)) +			continue; +		size = resource_size(r); +		if (size < align) { +			size = align; +			dev_info(&dev->dev, +				"Rounding up size of resource #%d to %#llx.\n", +				i, (unsigned long long)size); +		} +		r->end = size - 1; +		r->start = 0; +	} +	/* Need to disable bridge's resource window, +	 * to enable the kernel to reassign new resource +	 * window later on. +	 */ +	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && +	    (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { +		for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { +			r = &dev->resource[i]; +			if (!(r->flags & IORESOURCE_MEM)) +				continue; +			r->end = resource_size(r) - 1; +			r->start = 0; +		} +		pci_disable_bridge_window(dev); +	} +} +  ssize_t pci_set_resource_alignment_param(const char *buf, size_t count)  {  	if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) @@ -3714,10 +3837,14 @@ static int __init pci_setup(char *str)  				pci_no_msi();  			} else if (!strcmp(str, "noaer")) {  				pci_no_aer(); +			} else if (!strncmp(str, "realloc=", 8)) { +				pci_realloc_get_opt(str + 8);  			} else if (!strncmp(str, "realloc", 7)) { -				pci_realloc(); +				pci_realloc_get_opt("on");  			} else if (!strcmp(str, "nodomains")) {  				pci_no_domains(); +			} else if (!strncmp(str, "noari", 5)) { +				pcie_ari_disabled = true;  			} else if (!strncmp(str, "cbiosize=", 9)) {  				pci_cardbus_io_size = memparse(str + 9, &str);  			} else if (!strncmp(str, "cbmemsize=", 10)) { | 
