]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
xhci: Intel Panther Point BEI quirk.
authorMaxim Uvarov <maxim.uvarov@oracle.com>
Mon, 8 Oct 2012 15:15:01 +0000 (08:15 -0700)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Mon, 8 Oct 2012 15:15:01 +0000 (08:15 -0700)
commit 80fab3b244a22e0ca539d2439bdda50e81e5666f upstream.

When a device with an isochronous endpoint is behind a hub plugged into
the Intel Panther Point xHCI host controller, and the driver submits
multiple frames per URB, the xHCI driver will set the Block Event
Interrupt (BEI) flag on all but the last TD for the URB.  This causes
the host controller to place an event on the event ring, but not send an
interrupt.  When the last TD for the URB completes, BEI is cleared, and
we get an interrupt for the whole URB.

However, under a Panther Point xHCI host controller, if the parent hub
is unplugged when one or more events from transfers with BEI set are on
the event ring, a port status change event is placed on the event ring,
but no interrupt is generated.  This means URBs stop completing, and the
USB device disconnect is not noticed.  Something like a USB headset will
cause mplayer to hang when the device is disconnected.

If another transfer is sent (such as running `sudo lsusb -v`), the next
transfer event seems to "unstick" the event ring, the xHCI driver gets
an interrupt, and the disconnect is reported to the USB core.

The fix is not to use the BEI flag under the Panther Point xHCI host.
This will impact power consumption and system responsiveness, because
the xHCI driver will receive an interrupt for every frame in all
isochronous URBs instead of once per URB.

Intel chipset developers confirm that this bug will be hit if the BEI
flag is used on any endpoint, not just ones that are behind a hub.

This patch should be backported to kernels as old as 3.0, that contain
the commit 69e848c2090aebba5698a1620604c7dccb448684 "Intel xhci: Support
EHCI/xHCI port switching."

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Conflicts:

drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci.h

Signed-off-by: Maxim Uvarov <maxim.uvarov@oracle.com>
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.h

index 39e230f2dd1b5b08aa5f8ceab14309b76359bd79..291b0527460eefe6a427c5abe184e1ef64a5cc70 100644 (file)
@@ -140,6 +140,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
                xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
                xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
                xhci->limit_active_eps = 64;
+               xhci->quirks |= XHCI_AVOID_BEI;
        }
        if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
                        pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
index de3c1513fdb246915274c69d3b3ac0a14d5e6e37..ef3633d0d67eb7a639249256d2abe70ee8126625 100644 (file)
@@ -3356,7 +3356,9 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
                        } else {
                                td->last_trb = ep_ring->enqueue;
                                field |= TRB_IOC;
-                               if (xhci->hci_version == 0x100) {
+                               if (xhci->hci_version == 0x100 &&
+                                               !(xhci->quirks &
+                                                       XHCI_AVOID_BEI)) {
                                        /* Set BEI bit except for the last td */
                                        if (i < num_tds - 1)
                                                field |= TRB_BEI;
index 25c3e26b999c76cee5cd3c88166602e2fa4dfc1e..819f01fbf327b876878f6f4d09189d3e34bdca09 100644 (file)
@@ -1316,6 +1316,7 @@ struct xhci_hcd {
 #define XHCI_RESET_ON_RESUME   (1 << 7)
 #define XHCI_AMD_0x96_HOST     (1 << 9)
 #define XHCI_TRUST_TX_LENGTH   (1 << 10)
+#define XHCI_AVOID_BEI         (1 << 15)
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */