]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
xhci-hcd: support soft retry on SS transfer error
authorPetlozu Pravareshwar <petlozup@nvidia.com>
Tue, 28 Mar 2017 14:02:35 +0000 (19:32 +0530)
committermobile promotions <svcmobile_promotions@nvidia.com>
Wed, 3 May 2017 13:00:32 +0000 (06:00 -0700)
This commit implements XHCI "soft retry" for SuperSpeed endpoints which
encounters transfer errors.
When transfer error happens on a SuperSpeed endpoint, XHCI driver will
1. queue a "reset endpoint" command with TSP=1 (Transfer State Preserve)

2. invoke a HCD driver specific callback "->endpoint_soft_retry()" to let
HCD driver has a chance to configure its hardware

3. ring door bell for the endpoint upon seeing the command completion.

bug 200162414
bug 200227028

Change-Id: Idad81a1ce32c95fd9ac9784630a7b8464bde3e7e
Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Reviewed-on: http://git-master/r/1326817
(cherry picked from commit 4b5fca1880d888fa3811999e1c0547876e32fa29)
Reviewed-on: http://git-master/r/1474187
Reviewed-by: Rajkumar Kasirajan <rkasirajan@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: BH Hsieh <bhsieh@nvidia.com>
GVS: Gerrit_Virtual_Submit
Tested-by: Rajkumar Kasirajan <rkasirajan@nvidia.com>
Reviewed-by: Shreshtha Sahu <ssahu@nvidia.com>
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.h
include/linux/usb/hcd.h

index d487b73871305c1c8de1adae4a0c47c0dfdc6da1..5679cd96e156dce56388640248a1113c153d01c7 100644 (file)
@@ -1090,6 +1090,13 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
        } else {
                /* Clear our internal halted state */
                xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
+
+               /* ring doorbell for the endpoint under soft-retry */
+               if (TRB_TSP & le32_to_cpu(trb->generic.field[3])) {
+                       xhci_dbg(xhci, "Ring doorbell for slot_id %d ep_index 0x%x\n",
+                                slot_id, ep_index);
+                       xhci_ring_ep_doorbell(xhci, slot_id, ep_index, 0);
+               }
        }
 }
 
@@ -2273,6 +2280,30 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
        return finish_td(xhci, td, event_trb, event, ep, status, false);
 }
 
+static void xhci_endpoint_soft_retry(struct xhci_hcd *xhci, unsigned slot_id,
+                       unsigned dci, bool on)
+{
+       struct xhci_virt_device *xdev = xhci->devs[slot_id];
+       struct usb_host_endpoint *ep;
+
+       if (!xhci->shared_hcd || !xhci->shared_hcd->driver ||
+                       !xhci->shared_hcd->driver->endpoint_soft_retry)
+               return;
+
+       if (xdev->udev->speed != USB_SPEED_SUPER)
+               return;
+
+       if (dci & 0x1)
+               ep = xdev->udev->ep_in[(dci - 1)/2];
+       else
+               ep = xdev->udev->ep_out[dci/2];
+
+       if (!ep)
+               return;
+
+       xhci->shared_hcd->driver->endpoint_soft_retry(xhci->shared_hcd, ep, on);
+}
+
 /*
  * If this function returns an error condition, it means it got a Transfer
  * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2301,6 +2332,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
        int ret = 0;
        int td_num = 0;
        bool handling_skipped_tds = false;
+       bool disable_u0_ts1_detect;
 
        slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
        xdev = xhci->devs[slot_id];
@@ -2319,6 +2351,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                return -ENODEV;
        }
 
+       disable_u0_ts1_detect =
+                       xhci->shared_hcd->driver->is_u0_ts1_detect_disabled(
+                                       xhci->shared_hcd);
+
        /* Endpoint ID is 1 based, our index is zero based */
        ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
        ep = &xdev->eps[ep_index];
@@ -2356,14 +2392,25 @@ static int handle_tx_event(struct xhci_hcd *xhci,
         * transfer type
         */
        case COMP_SUCCESS:
-               if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
-                       break;
+               if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
+                       if (disable_u0_ts1_detect)
+                               goto check_soft_try;
+                       else
+                               break;
+               }
                if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
                        trb_comp_code = COMP_SHORT_TX;
                else
                        xhci_warn_ratelimited(xhci,
                                        "WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk?\n");
        case COMP_SHORT_TX:
+check_soft_try:
+               if (disable_u0_ts1_detect && ep_ring->soft_try) {
+                       xhci_dbg(xhci, "soft retry completed successfully\n");
+                       ep_ring->soft_try = false;
+                       xhci_endpoint_soft_retry(xhci,
+                                               slot_id, ep_index + 1, false);
+               }
                break;
        case COMP_STOP:
                xhci_dbg(xhci, "Stopped on Transfer TRB\n");
@@ -2384,7 +2431,31 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                status = -EILSEQ;
                break;
        case COMP_SPLIT_ERR:
+               xhci_dbg(xhci, "Transfer error on endpoint\n");
+               status = -EPROTO;
+               break;
        case COMP_TX_ERR:
+               if (disable_u0_ts1_detect &&
+                               xdev->udev->speed == USB_SPEED_SUPER &&
+                               ep_ring->type != TYPE_ISOC) {
+                       if (!ep_ring->soft_try) {
+                               xhci_dbg(xhci, "SuperSpeed transfer error, do soft retry\n");
+                               ret = xhci_queue_soft_retry(xhci,
+                                               slot_id, ep_index);
+                               if (!ret) {
+                                       xhci_endpoint_soft_retry(xhci,
+                                               slot_id, ep_index + 1, true);
+                                       xhci_ring_cmd_db(xhci);
+                                       ep_ring->soft_try = true;
+                                       goto cleanup;
+                               }
+                       } else {
+                               xhci_dbg(xhci, "soft retry complete but transfer still failed\n");
+                               ep_ring->soft_try = false;
+                       }
+                       xhci_endpoint_soft_retry(xhci,
+                                               slot_id, ep_index + 1, false);
+               }
                xhci_dbg(xhci, "Transfer error on endpoint\n");
                status = -EPROTO;
                break;
@@ -4274,3 +4345,19 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
        return queue_command(xhci, cmd, 0, 0, 0,
                        trb_slot_id | trb_ep_index | type, false);
 }
+
+int xhci_queue_soft_retry(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index)
+{
+       struct xhci_command *command;
+       u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
+       u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+       u32 type = TRB_TYPE(TRB_RESET_EP) | TRB_TSP;
+
+       command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
+       if (!command)
+               return -EINVAL;
+
+       return queue_command(xhci, command, 0, 0, 0,
+                       trb_slot_id | trb_ep_index | type, false);
+}
index 7912c6552b5ab43a64346d6ca71e9989d324f7a8..3dab274166184c1f8ba0c142d423f39e84c00b6f 100644 (file)
@@ -3,6 +3,7 @@
  * xHCI host controller driver
  *
  * Copyright (C) 2008 Intel Corp.
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
  *
  * Author: Sarah Sharp
  * Some code borrowed from the Linux EHCI driver.
@@ -311,6 +312,7 @@ struct xhci_op_regs {
 #define XDEV_U0                (0x0 << 5)
 #define XDEV_U2                (0x2 << 5)
 #define XDEV_U3                (0x3 << 5)
+#define XDEV_RXDETECT  (0x5 << 5)
 #define XDEV_INACTIVE  (0x6 << 5)
 #define XDEV_RESUME    (0xf << 5)
 /* true: port has power (see HCC_PPC) */
@@ -1208,6 +1210,8 @@ enum xhci_setup_dev {
 
 /* Block Event Interrupt */
 #define        TRB_BEI                 (1<<9)
+/* Transfer State Preserve */
+#define TRB_TSP                        (1<<9)
 
 /* Control transfer TRB specific fields */
 #define TRB_DIR_IN             (1<<16)
@@ -1396,6 +1400,7 @@ struct xhci_ring {
        enum xhci_ring_type     type;
        bool                    last_td_was_short;
        struct radix_tree_root  *trb_address_map;
+       bool                    soft_try;
 };
 
 struct xhci_erst_entry {
@@ -1898,6 +1903,8 @@ int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd,
                dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed);
 int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
                int slot_id, unsigned int ep_index);
+int xhci_queue_soft_retry(struct xhci_hcd *xhci, int slot_id,
+               unsigned int ep_index);
 int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
                u32 slot_id);
 void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
index 8e9859eb5febae31f3d654632f3b87b54032f6ec..3e5e0ab92a2136c8f1c0ab598fbd086879bea3d2 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2001-2002 by David Brownell
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -396,6 +397,13 @@ struct hc_driver {
        int     (*find_raw_port_number)(struct usb_hcd *, int);
        /* Call for power on/off the port if necessary */
        int     (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
+       /* (optional) called from xhci ISR before (on=1) and after (on=0)
+        * soft retry. It gives HCD driver a chance to configure it hardware to
+        * deal with intermittent SuperSpeed transfer errors.
+        */
+       void (*endpoint_soft_retry)(struct usb_hcd *,
+                       struct usb_host_endpoint *, bool);
+       bool (*is_u0_ts1_detect_disabled)(struct usb_hcd *);
 };
 
 /**