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.S-k@xxxxxxx> Signed-off-by: Nehal Shah <Nehal-bakulchandra.Shah@xxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html