on AMD platforms with SNPS 3.1 USB controller has an issue
if the stop EP command is issued when the controller is not
in running state. If issued, it is leading to a critical RTL bug
because of which controller goes into irrecoverable state.

This patch adds a appropriate checks to make sure that scenario
does not happen.

Signed-off-by: Shyam Sundar S K <shyam-sundar....@amd.com>
Signed-off-by: Nehal Shah <nehal-bakulchandra.s...@amd.com>
---
 drivers/usb/host/xhci-hub.c | 36 ++++++++++++++++++++++++++++++++++--
 drivers/usb/host/xhci-pci.c |  8 ++++++++
 drivers/usb/host/xhci.h     |  1 +
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 5e3e9d4..fec849f 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -380,6 +380,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int 
slot_id, int suspend)
 {
        struct xhci_virt_device *virt_dev;
        struct xhci_command *cmd;
+       struct xhci_ep_ctx *ep_ctx;
        unsigned long flags;
        int ret;
        int i;
@@ -407,11 +408,42 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int 
slot_id, int suspend)
                                return -ENOMEM;
 
                        }
-                       xhci_queue_stop_endpoint(xhci, command, slot_id, i,
+
+                       /* on AMD platforms with SNPS 3.1 USB controller has
+                        * an issue if the stop EP command is issued when
+                        * the controller is not in running state.
+                        * If issued, its leading to a RTL bug because of
+                        * which controller goes to a bad state.
+                        * Adding a conditional check to prevent this.
+                        */
+
+                       if (xhci->quirks & XHCI_BROKEN_STOP) {
+                               ep_ctx = xhci_get_ep_ctx(xhci,
+                                               virt_dev->out_ctx, i);
+
+                               if (GET_EP_CTX_STATE(ep_ctx) ==
+                                                       EP_STATE_RUNNING) {
+                                       xhci_queue_stop_endpoint(xhci, command,
+                                                       slot_id, i, suspend);
+                               }
+                       } else {
+                               xhci_queue_stop_endpoint(xhci, command,
+                                               slot_id, i, suspend);
+                       }
+               }
+       }
+
+       if (xhci->quirks & XHCI_BROKEN_STOP) {
+               ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, 0);
+
+               if (GET_EP_CTX_STATE(ep_ctx) ==  EP_STATE_RUNNING) {
+                       xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0,
                                                 suspend);
                }
+       } else {
+               xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
        }
-       xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
+
        xhci_ring_cmd_db(xhci);
        spin_unlock_irqrestore(&xhci->lock, flags);
 
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 7b86508..403de8f 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -52,6 +52,8 @@
 #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI             0x0aa8
 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI             0x1aa8
 #define PCI_DEVICE_ID_INTEL_APL_XHCI                   0x5aa8
+#define PCI_DEVICE_ID_AMD_XHCI_30                      0x15e0
+#define PCI_DEVICE_ID_AMD_XHCI_31                      0x15e1
 
 static const char hcd_name[] = "xhci_hcd";
 
@@ -205,6 +207,12 @@ static void xhci_pci_quirks(struct device *dev, struct 
xhci_hcd *xhci)
        if (xhci->quirks & XHCI_RESET_ON_RESUME)
                xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                "QUIRK: Resetting on resume");
+
+       if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+           (pdev->device == PCI_DEVICE_ID_AMD_XHCI_30 ||
+               pdev->device == PCI_DEVICE_ID_AMD_XHCI_31))
+               xhci->quirks |= XHCI_BROKEN_STOP;
+
 }
 
 #ifdef CONFIG_ACPI
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a9..c6fa3ca 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1819,6 +1819,7 @@ struct xhci_hcd {
 /* For controller with a broken Port Disable implementation */
 #define XHCI_BROKEN_PORT_PED   (1 << 25)
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
+#define XHCI_BROKEN_STOP       (1 << 27)
 
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to