If the request being dequeued is already started, disable endpoint
to stop the transfer and then call dwc2_hsotg_complete_request().
Endpoint will be re-enabled on next call to dwc2_hsotg_start_req().

Signed-off-by: Mian Yousaf Kaukab <yousaf.kau...@intel.com>
---
 drivers/usb/dwc2/gadget.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 77 insertions(+)

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 60b50d0..c7da6b7 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -2829,6 +2829,79 @@ static bool on_list(struct dwc2_hsotg_ep *ep, struct 
dwc2_hsotg_req *test)
        return false;
 }
 
+static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg,
+                                                       u32 bit, u32 timeout)
+{
+       u32 i;
+
+       for (i = 0; i < timeout; i++) {
+               if (readl(hs_otg->regs + reg) & bit)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
+                                               struct dwc2_hsotg_ep *hs_ep)
+{
+       u32 epctrl_reg;
+       u32 epint_reg;
+
+       epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) :
+                                          DOEPCTL(hs_ep->index);
+       epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) :
+                                       DOEPINT(hs_ep->index);
+
+       dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__,
+                                                               hs_ep->name);
+       if (hs_ep->dir_in) {
+               __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK);
+               /* Wait for Nak effect */
+               if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
+                                               DXEPINT_INEPNAKEFF, 100))
+                       dev_warn(hsotg->dev,
+                               "%s: timeout DIEPINT.NAKEFF\n", __func__);
+       } else {
+               /* Clear any pending nak effect interrupt */
+               writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS);
+
+               __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+
+               /* Wait for global nak to take effect */
+               if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
+                                               GINTSTS_GINNAKEFF, 100))
+                       dev_warn(hsotg->dev,
+                               "%s: timeout GINTSTS.GINNAKEFF\n", __func__);
+       }
+
+       /* Disable ep */
+       __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
+
+       /* Wait for ep to be disabled */
+       if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
+               dev_warn(hsotg->dev,
+                       "%s: timeout DOEPCTL.EPDisable\n", __func__);
+
+       if (hs_ep->dir_in) {
+               if (hsotg->dedicated_fifos) {
+                       writel(GRSTCTL_TXFNUM(hs_ep->fifo_index) |
+                               GRSTCTL_TXFFLSH, hsotg->regs + GRSTCTL);
+                       /* Wait for fifo flush */
+                       if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
+                                                       GRSTCTL_TXFFLSH, 100))
+                               dev_warn(hsotg->dev,
+                                       "%s: timeout flushing fifos\n",
+                                       __func__);
+               }
+               /* TODO: Flush shared tx fifo */
+       } else {
+               /* Remove global NAKs */
+               __bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+       }
+}
+
 /**
  * dwc2_hsotg_ep_dequeue - dequeue given endpoint
  * @ep: The endpoint to dequeue.
@@ -2850,6 +2923,10 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, 
struct usb_request *req)
                return -EINVAL;
        }
 
+       /* Dequeue already started request */
+       if (req == &hs_ep->req->req)
+               dwc2_hsotg_ep_stop_xfr(hs, hs_ep);
+
        dwc2_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
        spin_unlock_irqrestore(&hs->lock, flags);
 
-- 
2.3.3

--
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