]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
USB: xhci: Work around for chain bit in link TRBs.
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Fri, 7 Aug 2009 21:04:36 +0000 (14:04 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 5 Oct 2009 16:32:44 +0000 (09:32 -0700)
commit b0567b3f635db72c881a0d561cebb544ec085073 upstream.

Different sections of the xHCI 0.95 specification had opposing
requirements for the chain bit in a link transaction request buffer (TRB).
The chain bit is used to designate that adjacent TRBs are all part of the
same scatter gather list that should be sent to the device.  Link TRBs can
be in the middle, or at the beginning or end of these chained TRBs.

Sections 4.11.5.1 and 6.4.4.1 both stated the link TRB "shall have the
chain bit set to 1", meaning it is always chained to the next TRB.
However, section 4.6.9 on the stop endpoint command has specific cases for
what the hardware must do for a link TRB with the chain bit set to 0.  The
0.96 specification errata later cleared up this issue by fixing the
4.11.5.1 and 6.4.4.1 sections to state that a link TRB can have the chain
bit set to 1 or 0.

The problem is that the xHCI cancellation code depends on the chain bit of
the link TRB being cleared when it's at the end of a TD, and some 0.95
xHCI hardware simply stops processing the ring when it encounters a link
TRB with the chain bit cleared.

Allow users who are testing 0.95 xHCI prototypes to set a module parameter
(link_quirk) to turn on this link TRB work around.  Cancellation may not
work if the ring is stopped exactly on a link TRB with chain bit set, but
cancellation should be a relatively uncommon case.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/xhci-hcd.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.h

index 816c39caca1cf6bf5e1ca9b4a9c8c5d03684fcf3..994f4c0dda24e1cd1a0f022cf71e058487a3f15b 100644 (file)
 
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 
 #include "xhci.h"
 
 #define DRIVER_AUTHOR "Sarah Sharp"
 #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
 
+/* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
+static int link_quirk;
+module_param(link_quirk, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
+
 /* TODO: copied from ehci-hcd.c - can this be refactored? */
 /*
  * handshake - spin reading hc until handshake completes or fails
@@ -214,6 +220,12 @@ int xhci_init(struct usb_hcd *hcd)
 
        xhci_dbg(xhci, "xhci_init\n");
        spin_lock_init(&xhci->lock);
+       if (link_quirk) {
+               xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n");
+               xhci->quirks |= XHCI_LINK_TRB_QUIRK;
+       } else {
+               xhci_dbg(xhci, "xHCI has no QUIRKS\n");
+       }
        retval = xhci_mem_init(xhci, GFP_KERNEL);
        xhci_dbg(xhci, "Finished xhci_init\n");
 
index e6b9a1c6002d3da5b43c65161cca881d18a8e214..cb2033879ae3e8e9c73e5054fdd872a3abfde3e4 100644 (file)
@@ -94,6 +94,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
                val = prev->trbs[TRBS_PER_SEGMENT-1].link.control;
                val &= ~TRB_TYPE_BITMASK;
                val |= TRB_TYPE(TRB_LINK);
+               /* Always set the chain bit with 0.95 hardware */
+               if (xhci_link_trb_quirk(xhci))
+                       val |= TRB_CHAIN;
                prev->trbs[TRBS_PER_SEGMENT-1].link.control = val;
        }
        xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n",
index aa88a067148bb70584bdaaaf9c56ccb39b32d3c3..011458f4d9ce8439a4fd8b11d27a0ff3dcb6bd02 100644 (file)
@@ -172,8 +172,9 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
  * have their chain bit cleared (so that each Link TRB is a separate TD).
  *
  * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
- * set, but other sections talk about dealing with the chain bit set.
- * Assume section 6.4.4.1 is wrong, and the chain bit can be set in a Link TRB.
+ * set, but other sections talk about dealing with the chain bit set.  This was
+ * fixed in the 0.96 specification errata, but we have to assume that all 0.95
+ * xHCI hardware can't handle the chain bit being cleared on a link TRB.
  */
 static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer)
 {
@@ -191,8 +192,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer
        while (last_trb(xhci, ring, ring->enq_seg, next)) {
                if (!consumer) {
                        if (ring != xhci->event_ring) {
-                               next->link.control &= ~TRB_CHAIN;
-                               next->link.control |= chain;
+                               /* If we're not dealing with 0.95 hardware,
+                                * carry over the chain bit of the previous TRB
+                                * (which may mean the chain bit is cleared).
+                                */
+                               if (!xhci_link_trb_quirk(xhci)) {
+                                       next->link.control &= ~TRB_CHAIN;
+                                       next->link.control |= chain;
+                               }
                                /* Give this link TRB to the hardware */
                                wmb();
                                if (next->link.control & TRB_CYCLE)
index d31d32206ba318fbf28ea23a105a0bfa81f82f92..ce45dc63df3d77550d9678d63ff5fcad29a4987a 100644 (file)
@@ -1058,6 +1058,8 @@ struct xhci_hcd {
        int                     noops_submitted;
        int                     noops_handled;
        int                     error_bitmask;
+       unsigned int            quirks;
+#define        XHCI_LINK_TRB_QUIRK     (1 << 0)
 };
 
 /* For testing purposes */
@@ -1136,6 +1138,13 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
        writel(val_hi, ptr + 1);
 }
 
+static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
+{
+       u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase);
+       return ((HC_VERSION(temp) == 0x95) &&
+                       (xhci->quirks & XHCI_LINK_TRB_QUIRK));
+}
+
 /* xHCI debugging */
 void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num);
 void xhci_print_registers(struct xhci_hcd *xhci);