Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c524dd5f authored by Mian Yousaf Kaukab's avatar Mian Yousaf Kaukab Committed by Felipe Balbi
Browse files

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: default avatarMian Yousaf Kaukab <yousaf.kaukab@intel.com>
Tested-by: default avatarRobert Baldyga <r.baldyga@samsung.com>
Tested-by: default avatarDinh Nguyen <dinguyen@opensource.altera.com>
Tested-by: default avatarJohn Youn <johnyoun@synopsys.com>
Acked-by: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent e525e743
Loading
Loading
Loading
Loading
+77 −0
Original line number Diff line number Diff line
@@ -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);