PCI: Add pci_find_ht_capability() for finding Hypertransport capabilities
authorMichael Ellerman <michael@ellerman.id.au>
Wed, 22 Nov 2006 07:26:18 +0000 (18:26 +1100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 20 Dec 2006 18:54:42 +0000 (10:54 -0800)
There are already several places in the kernel that want to search a PCI
device for a given Hypertransport capability. Although this is possible
using pci_find_capability() etc., it makes sense to encapsulate that
logic in a helper - pci_find_ht_capability().

To cater for searching exhaustively for a capability, we also provide
pci_find_next_ht_capability().

We also need to cater for the fact that the HT capability fields may be
either 3 or 5 bits wide. pci_find_ht_capability() deals with this for you,
but callers using the #defines directly must handle that themselves.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/pci/pci.c
include/linux/pci.h
include/linux/pci_regs.h

index 4627a51d1d6c285b921320709261a95bea4785d7..6bfb942428e41dd733123a3f3993fdc53802f64b 100644 (file)
@@ -68,12 +68,14 @@ pci_max_busnr(void)
 
 #endif  /*  0  */
 
-static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap)
+#define PCI_FIND_CAP_TTL       48
+
+static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
+                                  u8 pos, int cap, int *ttl)
 {
        u8 id;
-       int ttl = 48;
 
-       while (ttl--) {
+       while ((*ttl)--) {
                pci_bus_read_config_byte(bus, devfn, pos, &pos);
                if (pos < 0x40)
                        break;
@@ -89,6 +91,14 @@ static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos,
        return 0;
 }
 
+static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
+                              u8 pos, int cap)
+{
+       int ttl = PCI_FIND_CAP_TTL;
+
+       return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);
+}
+
 int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap)
 {
        return __pci_find_next_cap(dev->bus, dev->devfn,
@@ -224,6 +234,74 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
 }
 EXPORT_SYMBOL_GPL(pci_find_ext_capability);
 
+static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap)
+{
+       int rc, ttl = PCI_FIND_CAP_TTL;
+       u8 cap, mask;
+
+       if (ht_cap == HT_CAPTYPE_SLAVE || ht_cap == HT_CAPTYPE_HOST)
+               mask = HT_3BIT_CAP_MASK;
+       else
+               mask = HT_5BIT_CAP_MASK;
+
+       pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos,
+                                     PCI_CAP_ID_HT, &ttl);
+       while (pos) {
+               rc = pci_read_config_byte(dev, pos + 3, &cap);
+               if (rc != PCIBIOS_SUCCESSFUL)
+                       return 0;
+
+               if ((cap & mask) == ht_cap)
+                       return pos;
+
+               pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos,
+                                             PCI_CAP_ID_HT, &ttl);
+       }
+
+       return 0;
+}
+/**
+ * pci_find_next_ht_capability - query a device's Hypertransport capabilities
+ * @dev: PCI device to query
+ * @pos: Position from which to continue searching
+ * @ht_cap: Hypertransport capability code
+ *
+ * To be used in conjunction with pci_find_ht_capability() to search for
+ * all capabilities matching @ht_cap. @pos should always be a value returned
+ * from pci_find_ht_capability().
+ *
+ * NB. To be 100% safe against broken PCI devices, the caller should take
+ * steps to avoid an infinite loop.
+ */
+int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap)
+{
+       return __pci_find_next_ht_cap(dev, pos + PCI_CAP_LIST_NEXT, ht_cap);
+}
+EXPORT_SYMBOL_GPL(pci_find_next_ht_capability);
+
+/**
+ * pci_find_ht_capability - query a device's Hypertransport capabilities
+ * @dev: PCI device to query
+ * @ht_cap: Hypertransport capability code
+ *
+ * Tell if a device supports a given Hypertransport capability.
+ * Returns an address within the device's PCI configuration space
+ * or 0 in case the device does not support the request capability.
+ * The address points to the PCI capability, of type PCI_CAP_ID_HT,
+ * which has a Hypertransport capability matching @ht_cap.
+ */
+int pci_find_ht_capability(struct pci_dev *dev, int ht_cap)
+{
+       int pos;
+
+       pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);
+       if (pos)
+               pos = __pci_find_next_ht_cap(dev, pos, ht_cap);
+
+       return pos;
+}
+EXPORT_SYMBOL_GPL(pci_find_ht_capability);
+
 /**
  * pci_find_parent_resource - return resource region of parent bus of given region
  * @dev: PCI device structure contains resources to be searched
index 769e132b78d38fc365077b4079377a807d191ba4..b20ffb65300f1e9786098f00242d87d1fd7242d0 100644 (file)
@@ -454,6 +454,8 @@ 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_next_capability (struct pci_dev *dev, u8 pos, int cap);
 int pci_find_ext_capability (struct pci_dev *dev, int cap);
+int pci_find_ht_capability (struct pci_dev *dev, int ht_cap);
+int pci_find_next_ht_capability (struct pci_dev *dev, int pos, int ht_cap);
 struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
 
 struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
index 064b1dc71c228d0edfb29dd18da8477ab445235c..a54b48f7cee2b272c7c670cf70aca148ea98f5d6 100644 (file)
 #define PCI_PWR_CAP            12      /* Capability */
 #define  PCI_PWR_CAP_BUDGET(x) ((x) & 1)       /* Included in system budget */
 
-/* Hypertransport sub capability types */
+/*
+ * Hypertransport sub capability types
+ *
+ * Unfortunately there are both 3 bit and 5 bit capability types defined
+ * in the HT spec, catering for that is a little messy. You probably don't
+ * want to use these directly, just use pci_find_ht_capability() and it
+ * will do the right thing for you.
+ */
+#define HT_3BIT_CAP_MASK       0xE0
 #define HT_CAPTYPE_SLAVE       0x00    /* Slave/Primary link configuration */
 #define HT_CAPTYPE_HOST                0x20    /* Host/Secondary link configuration */
+
+#define HT_5BIT_CAP_MASK       0xF8
 #define HT_CAPTYPE_IRQ         0x80    /* IRQ Configuration */
 #define HT_CAPTYPE_REMAPPING_40        0xA0    /* 40 bit address remapping */
 #define HT_CAPTYPE_REMAPPING_64 0xA2   /* 64 bit address remapping */