diff options
Diffstat (limited to 'drivers/pci/search.c')
| -rw-r--r-- | drivers/pci/search.c | 214 | 
1 files changed, 138 insertions, 76 deletions
diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 9d75dc8ca60..827ad831f1d 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -1,5 +1,5 @@  /* - * 	PCI searching functions. + *	PCI searching functions.   *   *	Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,   *					David Mosberger-Tang @@ -7,7 +7,6 @@   *	Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>   */ -#include <linux/init.h>  #include <linux/pci.h>  #include <linux/slab.h>  #include <linux/module.h> @@ -15,6 +14,95 @@  #include "pci.h"  DECLARE_RWSEM(pci_bus_sem); +EXPORT_SYMBOL_GPL(pci_bus_sem); + +/* + * pci_for_each_dma_alias - Iterate over DMA aliases for a device + * @pdev: starting downstream device + * @fn: function to call for each alias + * @data: opaque data to pass to @fn + * + * Starting @pdev, walk up the bus calling @fn for each possible alias + * of @pdev at the root bus. + */ +int pci_for_each_dma_alias(struct pci_dev *pdev, +			   int (*fn)(struct pci_dev *pdev, +				     u16 alias, void *data), void *data) +{ +	struct pci_bus *bus; +	int ret; + +	ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data); +	if (ret) +		return ret; + +	/* +	 * If the device is broken and uses an alias requester ID for +	 * DMA, iterate over that too. +	 */ +	if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) { +		ret = fn(pdev, PCI_DEVID(pdev->bus->number, +					 pdev->dma_alias_devfn), data); +		if (ret) +			return ret; +	} + +	for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) { +		struct pci_dev *tmp; + +		/* Skip virtual buses */ +		if (!bus->self) +			continue; + +		tmp = bus->self; + +		/* +		 * PCIe-to-PCI/X bridges alias transactions from downstream +		 * devices using the subordinate bus number (PCI Express to +		 * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3).  For all cases +		 * where the upstream bus is PCI/X we alias to the bridge +		 * (there are various conditions in the previous reference +		 * where the bridge may take ownership of transactions, even +		 * when the secondary interface is PCI-X). +		 */ +		if (pci_is_pcie(tmp)) { +			switch (pci_pcie_type(tmp)) { +			case PCI_EXP_TYPE_ROOT_PORT: +			case PCI_EXP_TYPE_UPSTREAM: +			case PCI_EXP_TYPE_DOWNSTREAM: +				continue; +			case PCI_EXP_TYPE_PCI_BRIDGE: +				ret = fn(tmp, +					 PCI_DEVID(tmp->subordinate->number, +						   PCI_DEVFN(0, 0)), data); +				if (ret) +					return ret; +				continue; +			case PCI_EXP_TYPE_PCIE_BRIDGE: +				ret = fn(tmp, +					 PCI_DEVID(tmp->bus->number, +						   tmp->devfn), data); +				if (ret) +					return ret; +				continue; +			} +		} else { +			if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS) +				ret = fn(tmp, +					 PCI_DEVID(tmp->subordinate->number, +						   PCI_DEVFN(0, 0)), data); +			else +				ret = fn(tmp, +					 PCI_DEVID(tmp->bus->number, +						   tmp->devfn), data); +			if (ret) +				return ret; +		} +	} + +	return ret; +} +  /*   * find the upstream PCIe-to-PCI bridge of a PCI device   * if the device is PCIE, return NULL @@ -22,8 +110,7 @@ DECLARE_RWSEM(pci_bus_sem);   * legacy PCI bridge and the bridge is directly connected to bus 0), return its   * parent   */ -struct pci_dev * -pci_find_upstream_pcie_bridge(struct pci_dev *pdev) +struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)  {  	struct pci_dev *tmp = NULL; @@ -39,7 +126,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)  			continue;  		}  		/* PCI device should connect to a PCIe bridge */ -		if (pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE) { +		if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {  			/* Busted hardware? */  			WARN_ON_ONCE(1);  			return NULL; @@ -52,15 +139,15 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)  static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)  { -	struct pci_bus* child; -	struct list_head *tmp; +	struct pci_bus *child; +	struct pci_bus *tmp; -	if(bus->number == busnr) +	if (bus->number == busnr)  		return bus; -	list_for_each(tmp, &bus->children) { -		child = pci_do_find_bus(pci_bus_b(tmp), busnr); -		if(child) +	list_for_each_entry(tmp, &bus->children, node) { +		child = pci_do_find_bus(tmp, busnr); +		if (child)  			return child;  	}  	return NULL; @@ -75,7 +162,7 @@ static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)   * in the global list of PCI buses.  If the bus is found, a pointer to its   * data structure is returned.  If no bus is found, %NULL is returned.   */ -struct pci_bus * pci_find_bus(int domain, int busnr) +struct pci_bus *pci_find_bus(int domain, int busnr)  {  	struct pci_bus *bus = NULL;  	struct pci_bus *tmp_bus; @@ -89,18 +176,18 @@ struct pci_bus * pci_find_bus(int domain, int busnr)  	}  	return NULL;  } +EXPORT_SYMBOL(pci_find_bus);  /**   * pci_find_next_bus - begin or continue searching for a PCI bus   * @from: Previous PCI bus found, or %NULL for new search.   * - * Iterates through the list of known PCI busses.  A new search is + * Iterates through the list of known PCI buses.  A new search is   * initiated by passing %NULL as the @from argument.  Otherwise if   * @from is not %NULL, searches continue from next device on the   * global list.   */ -struct pci_bus *  -pci_find_next_bus(const struct pci_bus *from) +struct pci_bus *pci_find_next_bus(const struct pci_bus *from)  {  	struct list_head *n;  	struct pci_bus *b = NULL; @@ -109,35 +196,34 @@ pci_find_next_bus(const struct pci_bus *from)  	down_read(&pci_bus_sem);  	n = from ? from->node.next : pci_root_buses.next;  	if (n != &pci_root_buses) -		b = pci_bus_b(n); +		b = list_entry(n, struct pci_bus, node);  	up_read(&pci_bus_sem);  	return b;  } +EXPORT_SYMBOL(pci_find_next_bus);  /**   * pci_get_slot - locate PCI device for a given PCI slot   * @bus: PCI bus on which desired PCI device resides - * @devfn: encodes number of PCI slot in which the desired PCI  - * device resides and the logical device number within that slot  + * @devfn: encodes number of PCI slot in which the desired PCI + * device resides and the logical device number within that slot   * in case of multi-function devices.   * - * Given a PCI bus and slot/function number, the desired PCI device  + * Given a PCI bus and slot/function number, the desired PCI device   * is located in the list of PCI devices.   * If the device is found, its reference count is increased and this   * function returns a pointer to its data structure.  The caller must   * decrement the reference count by calling pci_dev_put().   * If no device is found, %NULL is returned.   */ -struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) +struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)  { -	struct list_head *tmp;  	struct pci_dev *dev;  	WARN_ON(in_interrupt());  	down_read(&pci_bus_sem); -	list_for_each(tmp, &bus->devices) { -		dev = pci_dev_b(tmp); +	list_for_each_entry(dev, &bus->devices, bus_list) {  		if (dev->devfn == devfn)  			goto out;  	} @@ -148,6 +234,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)  	up_read(&pci_bus_sem);  	return dev;  } +EXPORT_SYMBOL(pci_get_slot);  /**   * pci_get_domain_bus_and_slot - locate PCI device for a given PCI domain (segment), bus, and slot @@ -243,31 +330,16 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,  			       unsigned int ss_vendor, unsigned int ss_device,  			       struct pci_dev *from)  { -	struct pci_dev *pdev; -	struct pci_device_id *id; - -	/* -	 * pci_find_subsys() can be called on the ide_setup() path, -	 * super-early in boot.  But the down_read() will enable local -	 * interrupts, which can cause some machines to crash.  So here we -	 * detect and flag that situation and bail out early. -	 */ -	if (unlikely(no_pci_devices())) -		return NULL; - -	id = kzalloc(sizeof(*id), GFP_KERNEL); -	if (!id) -		return NULL; -	id->vendor = vendor; -	id->device = device; -	id->subvendor = ss_vendor; -	id->subdevice = ss_device; - -	pdev = pci_get_dev_by_id(id, from); -	kfree(id); - -	return pdev; +	struct pci_device_id id = { +		.vendor = vendor, +		.device = device, +		.subvendor = ss_vendor, +		.subdevice = ss_device, +	}; + +	return pci_get_dev_by_id(&id, from);  } +EXPORT_SYMBOL(pci_get_subsys);  /**   * pci_get_device - begin or continue searching for a PCI device by vendor/device id @@ -283,11 +355,12 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,   * from next device on the global list.  The reference count for @from is   * always decremented if it is not %NULL.   */ -struct pci_dev * -pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) +struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, +			       struct pci_dev *from)  {  	return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);  } +EXPORT_SYMBOL(pci_get_device);  /**   * pci_get_class - begin or continue searching for a PCI device by class @@ -305,20 +378,18 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)   */  struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)  { -	struct pci_dev *dev; -	struct pci_device_id *id; - -	id = kzalloc(sizeof(*id), GFP_KERNEL); -	if (!id) -		return NULL; -	id->vendor = id->device = id->subvendor = id->subdevice = PCI_ANY_ID; -	id->class_mask = PCI_ANY_ID; -	id->class = class; - -	dev = pci_get_dev_by_id(id, from); -	kfree(id); -	return dev; +	struct pci_device_id id = { +		.vendor = PCI_ANY_ID, +		.device = PCI_ANY_ID, +		.subvendor = PCI_ANY_ID, +		.subdevice = PCI_ANY_ID, +		.class_mask = PCI_ANY_ID, +		.class = class, +	}; + +	return pci_get_dev_by_id(&id, from);  } +EXPORT_SYMBOL(pci_get_class);  /**   * pci_dev_present - Returns 1 if device matching the device list is present, 0 if not. @@ -338,22 +409,13 @@ int pci_dev_present(const struct pci_device_id *ids)  	WARN_ON(in_interrupt());  	while (ids->vendor || ids->subvendor || ids->class_mask) {  		found = pci_get_dev_by_id(ids, NULL); -		if (found) -			goto exit; +		if (found) { +			pci_dev_put(found); +			return 1; +		}  		ids++;  	} -exit: -	if (found) -		return 1; +  	return 0;  }  EXPORT_SYMBOL(pci_dev_present); - -/* For boot time work */ -EXPORT_SYMBOL(pci_find_bus); -EXPORT_SYMBOL(pci_find_next_bus); -/* For everyone */ -EXPORT_SYMBOL(pci_get_device); -EXPORT_SYMBOL(pci_get_subsys); -EXPORT_SYMBOL(pci_get_slot); -EXPORT_SYMBOL(pci_get_class);  | 
