usb: dwc2: gadget: stop current transfer on dequeue

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.kaukab@intel.com>
Tested-by: Robert Baldyga <r.baldyga@samsung.com>
Tested-by: Dinh Nguyen <dinguyen@opensource.altera.com>
Tested-by: John Youn <johnyoun@synopsys.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Mian Yousaf Kaukab 2015-09-29 12:08:24 +02:00 committed by Felipe Balbi
parent e525e74339
commit c524dd5f43
1 changed files with 77 additions and 0 deletions

View File

@ -2826,6 +2826,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 (dwc2_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 */
dwc2_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) {
dwc2_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.
@ -2847,6 +2920,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);