Commit 24a4e377 authored by Roland Dreier's avatar Roland Dreier Committed by Greg Kroah-Hartman

[PATCH] PCI: add pci_find_next_capability()

Some devices have more than one capability of the same type.  For
example, the PCI header for the PathScale InfiniPath looks like:

	04:01.0 InfiniBand: Unknown device 1fc1:000d (rev 02)
		Subsystem: Unknown device 1fc1:000d
		Flags: bus master, fast devsel, latency 0, IRQ 193
		Memory at fea00000 (64-bit, non-prefetchable) [size=2M]
		Capabilities: [c0] HyperTransport: Slave or Primary Interface
		Capabilities: [f8] HyperTransport: Interrupt Discovery and Configuration

There are _two_ HyperTransport capabilities, and the PathScale driver
wants to look at both of them.

The current pci_find_capability() API doesn't work for this, since it
only allows us to get to the first capability of a given type.  The
patch below introduces a new pci_find_next_capability(), which can be
used in a loop like

	for (pos = pci_find_capability(pdev, <ID>);
	     pos;
	     pos = pci_find_next_capability(pdev, pos, <ID>)) {
		/* ... */
	}
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
Signed-off-by: default avatarMatthew Wilcox <matthew@wil.cx>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6d23c8bc
...@@ -63,11 +63,38 @@ pci_max_busnr(void) ...@@ -63,11 +63,38 @@ pci_max_busnr(void)
return max; return max;
} }
static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap)
{
u8 id;
int ttl = 48;
while (ttl--) {
pci_bus_read_config_byte(bus, devfn, pos, &pos);
if (pos < 0x40)
break;
pos &= ~3;
pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID,
&id);
if (id == 0xff)
break;
if (id == cap)
return pos;
pos += PCI_CAP_LIST_NEXT;
}
return 0;
}
int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap)
{
return __pci_find_next_cap(dev->bus, dev->devfn,
pos + PCI_CAP_LIST_NEXT, cap);
}
EXPORT_SYMBOL_GPL(pci_find_next_capability);
static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_type, int cap) static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_type, int cap)
{ {
u16 status; u16 status;
u8 pos, id; u8 pos;
int ttl = 48;
pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status); pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);
if (!(status & PCI_STATUS_CAP_LIST)) if (!(status & PCI_STATUS_CAP_LIST))
...@@ -76,24 +103,15 @@ static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_ty ...@@ -76,24 +103,15 @@ static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_ty
switch (hdr_type) { switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE: case PCI_HEADER_TYPE_BRIDGE:
pci_bus_read_config_byte(bus, devfn, PCI_CAPABILITY_LIST, &pos); pos = PCI_CAPABILITY_LIST;
break; break;
case PCI_HEADER_TYPE_CARDBUS: case PCI_HEADER_TYPE_CARDBUS:
pci_bus_read_config_byte(bus, devfn, PCI_CB_CAPABILITY_LIST, &pos); pos = PCI_CB_CAPABILITY_LIST;
break; break;
default: default:
return 0; return 0;
} }
while (ttl-- && pos >= 0x40) { return __pci_find_next_cap(bus, devfn, pos, cap);
pos &= ~3;
pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID, &id);
if (id == 0xff)
break;
if (id == cap)
return pos;
pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_NEXT, &pos);
}
return 0;
} }
/** /**
......
...@@ -338,6 +338,7 @@ struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, const ...@@ -338,6 +338,7 @@ struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, const
struct pci_dev *pci_find_device_reverse (unsigned int vendor, unsigned int device, const struct pci_dev *from); struct pci_dev *pci_find_device_reverse (unsigned int vendor, unsigned int device, const struct pci_dev *from);
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
int pci_find_capability (struct pci_dev *dev, int cap); int pci_find_capability (struct pci_dev *dev, int cap);
int pci_find_next_capability (struct pci_dev *dev, u8 pos, int cap);
int pci_find_ext_capability (struct pci_dev *dev, int cap); int pci_find_ext_capability (struct pci_dev *dev, int cap);
struct pci_bus * pci_find_next_bus(const struct pci_bus *from); struct pci_bus * pci_find_next_bus(const struct pci_bus *from);
...@@ -550,6 +551,7 @@ static inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUS ...@@ -550,6 +551,7 @@ static inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUS
static inline int pci_register_driver(struct pci_driver *drv) { return 0;} static inline int pci_register_driver(struct pci_driver *drv) { return 0;}
static inline void pci_unregister_driver(struct pci_driver *drv) { } static inline void pci_unregister_driver(struct pci_driver *drv) { }
static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; } static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
static inline int pci_find_next_capability (struct pci_dev *dev, u8 post, int cap) { return 0; }
static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; } static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; }
static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; } static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment