xhci: rework xhci extended capability list parsing functions
authorMathias Nyman <mathias.nyman@linux.intel.com>
Tue, 24 Nov 2015 11:09:58 +0000 (13:09 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 1 Dec 2015 18:45:51 +0000 (10:45 -0800)
Replace the existing two extended capability parsing helper functions with
one called xhci_find_next_ext_cap().

The extended capabilities are read both in pci-quirks before xhci driver is
loaded, and inside the xhci driver when adding ports. The existing helpers
did not suit well for these cases and a lot of custom parsing code was
needed.

The new helper function simplifies these two cases a lot.

The motivation for this rework was that code to support xhci debug
capability needed to parse extended capabilities, and it included
yet another capability parsing helper specific for its needs. With
this solution it debug capability code can use this new  helper as well

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/pci-quirks.c
drivers/usb/host/xhci-ext-caps.h
drivers/usb/host/xhci-mem.c

index f9400564cb721d5e10e2025b23244ef56b789d8b..26cb8c861e6ede633cf6f215c0bd7ac34aeccccc 100644 (file)
@@ -984,24 +984,17 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
         * Find the Legacy Support Capability register -
         * this is optional for xHCI host controllers.
         */
-       ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
-       do {
-               if ((ext_cap_offset + sizeof(val)) > len) {
-                       /* We're reading garbage from the controller */
-                       dev_warn(&pdev->dev,
-                                "xHCI controller failing to respond");
-                       return;
-               }
+       ext_cap_offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_LEGACY);
 
-               if (!ext_cap_offset)
-                       /* We've reached the end of the extended capabilities */
-                       goto hc_init;
+       if (!ext_cap_offset)
+               goto hc_init;
 
-               val = readl(base + ext_cap_offset);
-               if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
-                       break;
-               ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
-       } while (1);
+       if ((ext_cap_offset + sizeof(val)) > len) {
+               /* We're reading garbage from the controller */
+               dev_warn(&pdev->dev, "xHCI controller failing to respond");
+               return;
+       }
+       val = readl(base + ext_cap_offset);
 
        /* If the BIOS owns the HC, signal that the OS wants it, and wait */
        if (val & XHCI_HC_BIOS_OWNED) {
index 9fe3225e6c6140d66139ec00b76f097679b57211..04ce6b156b350e5dd0f9f9a321c79eb4d173b656 100644 (file)
 
 #include <linux/io.h>
 
-/**
- * Return the next extended capability pointer register.
- *
- * @base       PCI register base address.
- *
- * @ext_offset Offset of the 32-bit register that contains the extended
- * capabilites pointer.  If searching for the first extended capability, pass
- * in XHCI_HCC_PARAMS_OFFSET.  If searching for the next extended capability,
- * pass in the offset of the current extended capability register.
- *
- * Returns 0 if there is no next extended capability register or returns the register offset
- * from the PCI registers base address.
- */
-static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
-{
-       u32 next;
-
-       next = readl(base + ext_offset);
-
-       if (ext_offset == XHCI_HCC_PARAMS_OFFSET) {
-               /* Find the first extended capability */
-               next = XHCI_HCC_EXT_CAPS(next);
-               ext_offset = 0;
-       } else {
-               /* Find the next extended capability */
-               next = XHCI_EXT_CAPS_NEXT(next);
-       }
-
-       if (!next)
-               return 0;
-       /*
-        * Address calculation from offset of extended capabilities
-        * (or HCCPARAMS) register - see section 5.3.6 and section 7.
-        */
-       return ext_offset + (next << 2);
-}
-
 /**
  * Find the offset of the extended capabilities with capability ID id.
  *
- * @base PCI MMIO registers base address.
- * @ext_offset Offset from base of the first extended capability to look at,
- *             or the address of HCCPARAMS.
- * @id Extended capability ID to search for.
+ * @base       PCI MMIO registers base address.
+ * @start      address at which to start looking, (0 or HCC_PARAMS to start at
+ *             beginning of list)
+ * @id         Extended capability ID to search for.
  *
- * This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
- * to make sure that the list doesn't contain a loop.
+ * Returns the offset of the next matching extended capability structure.
+ * Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
+ * and this provides a way to find them all.
  */
-static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
+
+static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
 {
        u32 val;
-       int limit = XHCI_MAX_EXT_CAPS;
-
-       while (ext_offset && limit > 0) {
-               val = readl(base + ext_offset);
-               if (XHCI_EXT_CAPS_ID(val) == id)
-                       break;
-               ext_offset = xhci_find_next_cap_offset(base, ext_offset);
-               limit--;
-       }
-       if (limit > 0)
-               return ext_offset;
+       u32 next;
+       u32 offset;
+
+       offset = start;
+       if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
+               val = readl(base + XHCI_HCC_PARAMS_OFFSET);
+               offset = XHCI_HCC_EXT_CAPS(val) << 2;
+               if (!offset)
+                       return 0;
+       };
+       do {
+               val = readl(base + offset);
+               if (XHCI_EXT_CAPS_ID(val) == id && offset != start)
+                       return offset;
+
+               next = XHCI_EXT_CAPS_NEXT(val);
+               offset += next << 2;
+       } while (next);
+
        return 0;
 }
index 536d00f21eed3b25357df7f11ebbb3a7e9055dc9..dc7f915d9a131c658fcd45786570e0884965b9d3 100644 (file)
@@ -2064,17 +2064,19 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
 }
 
 static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
-               __le32 __iomem *addr, u8 major_revision, int max_caps)
+               __le32 __iomem *addr, int max_caps)
 {
        u32 temp, port_offset, port_count;
        int i;
+       u8 major_revision;
        struct xhci_hub *rhub;
 
        temp = readl(addr);
+       major_revision = XHCI_EXT_PORT_MAJOR(temp);
 
-       if (XHCI_EXT_PORT_MAJOR(temp) == 0x03) {
+       if (major_revision == 0x03) {
                rhub = &xhci->usb3_rhub;
-       } else if (XHCI_EXT_PORT_MAJOR(temp) <= 0x02) {
+       } else if (major_revision <= 0x02) {
                rhub = &xhci->usb2_rhub;
        } else {
                xhci_warn(xhci, "Ignoring unknown port speed, "
@@ -2190,19 +2192,12 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
  */
 static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
 {
-       __le32 __iomem *addr, *tmp_addr;
-       u32 offset, tmp_offset;
+       void __iomem *base;
+       u32 offset;
        unsigned int num_ports;
        int i, j, port_index;
        int cap_count = 0;
-
-       addr = &xhci->cap_regs->hcc_params;
-       offset = XHCI_HCC_EXT_CAPS(readl(addr));
-       if (offset == 0) {
-               xhci_err(xhci, "No Extended Capability registers, "
-                               "unable to set up roothub.\n");
-               return -ENODEV;
-       }
+       u32 cap_start;
 
        num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
        xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags);
@@ -2220,48 +2215,34 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
                for (j = 0; j < XHCI_MAX_INTERVAL; j++)
                        INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
        }
+       base = &xhci->cap_regs->hc_capbase;
 
-       /*
-        * For whatever reason, the first capability offset is from the
-        * capability register base, not from the HCCPARAMS register.
-        * See section 5.3.6 for offset calculation.
-        */
-       addr = &xhci->cap_regs->hc_capbase + offset;
-
-       tmp_addr = addr;
-       tmp_offset = offset;
+       cap_start = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_PROTOCOL);
+       if (!cap_start) {
+               xhci_err(xhci, "No Extended Capability registers, unable to set up roothub\n");
+               return -ENODEV;
+       }
 
+       offset = cap_start;
        /* count extended protocol capability entries for later caching */
-       do {
-               u32 cap_id;
-               cap_id = readl(tmp_addr);
-               if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
-                       cap_count++;
-               tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
-               tmp_addr += tmp_offset;
-       } while (tmp_offset);
+       while (offset) {
+               cap_count++;
+               offset = xhci_find_next_ext_cap(base, offset,
+                                                     XHCI_EXT_CAPS_PROTOCOL);
+       }
 
        xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
        if (!xhci->ext_caps)
                return -ENOMEM;
 
-       while (1) {
-               u32 cap_id;
-
-               cap_id = readl(addr);
-               if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
-                       xhci_add_in_port(xhci, num_ports, addr,
-                                       (u8) XHCI_EXT_PORT_MAJOR(cap_id),
-                                       cap_count);
-               offset = XHCI_EXT_CAPS_NEXT(cap_id);
-               if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
-                               == num_ports)
+       offset = cap_start;
+
+       while (offset) {
+               xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
+               if (xhci->num_usb2_ports + xhci->num_usb3_ports == num_ports)
                        break;
-               /*
-                * Once you're into the Extended Capabilities, the offset is
-                * always relative to the register holding the offset.
-                */
-               addr += offset;
+               offset = xhci_find_next_ext_cap(base, offset,
+                                               XHCI_EXT_CAPS_PROTOCOL);
        }
 
        if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {