From 6da6d476ef22f56297022f6f0e168feacc12c4ef Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Mon, 5 Mar 2018 18:43:38 -0800 Subject: [PATCH] usb: XHCI: Implement xhci_handshake_check_state() API This API is called to handle command timeout which may happen upon XHCI stack removal. Since handshake is performed by polling long duration for status, this can cause watchdog timeout if status does not change for entire duration of polling. Hence add new API which also checks for xhci state and exit the handshake if xhci is removed. Use this api and bailout command abort operation upon XHCI remove. Change-Id: I9a90b2dd83d76f80e3f6fd4546b949c88850edd3 Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 23 ++++++++++++++++++++++- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 142c1eca172c..e10c964b93e4 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -356,7 +356,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * In the future we should distinguish between -ENODEV and -ETIMEDOUT * and try to recover a -ETIMEDOUT with a host controller reset. */ - ret = xhci_handshake(&xhci->op_regs->cmd_ring, + ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring, CMD_RING_RUNNING, 0, 1000 * 1000); if (ret < 0) { xhci_err(xhci, "Abort failed to stop command ring: %d\n", ret); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 462b2a5f4720..ab3be847f759 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -81,6 +81,27 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec) return ret; } +int xhci_handshake_check_state(struct xhci_hcd *xhci, + void __iomem *ptr, u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl_relaxed(ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + /* host removed. Bail out */ + if (xhci->xhc_state & XHCI_STATE_REMOVING) + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + /* * Disable interrupts and begin the xHCI halting process. */ @@ -204,7 +225,7 @@ int xhci_reset(struct xhci_hcd *xhci) if (xhci->quirks & XHCI_INTEL_HOST) udelay(1000); - ret = xhci_handshake(&xhci->op_regs->command, + ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command, CMD_RESET, 0, 10 * 1000 * 1000); if (ret) return ret; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 78f7f2ad8262..e482532970a3 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2061,6 +2061,8 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci, /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec); +int xhci_handshake_check_state(struct xhci_hcd *xhci, + void __iomem *ptr, u32 mask, u32 done, int usec); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_start(struct xhci_hcd *xhci);