xref: /openbmc/u-boot/drivers/usb/dwc3/gadget.c (revision 401c2540445aad08816382c0dc208d2516d90d89)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0
285d5e707SKishon Vijay Abraham I /**
385d5e707SKishon Vijay Abraham I  * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
485d5e707SKishon Vijay Abraham I  *
530c31d58SKishon Vijay Abraham I  * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
685d5e707SKishon Vijay Abraham I  *
785d5e707SKishon Vijay Abraham I  * Authors: Felipe Balbi <balbi@ti.com>,
885d5e707SKishon Vijay Abraham I  *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
985d5e707SKishon Vijay Abraham I  *
1030c31d58SKishon Vijay Abraham I  * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/gadget.c) and ported
1130c31d58SKishon Vijay Abraham I  * to uboot.
1285d5e707SKishon Vijay Abraham I  *
1330c31d58SKishon Vijay Abraham I  * commit 8e74475b0e : usb: dwc3: gadget: use udc-core's reset notifier
1485d5e707SKishon Vijay Abraham I  */
1585d5e707SKishon Vijay Abraham I 
16747a0a5bSKishon Vijay Abraham I #include <common.h>
17747a0a5bSKishon Vijay Abraham I #include <malloc.h>
18747a0a5bSKishon Vijay Abraham I #include <asm/dma-mapping.h>
1984b8bf6dSMasahiro Yamada #include <linux/bug.h>
2085d5e707SKishon Vijay Abraham I #include <linux/list.h>
2185d5e707SKishon Vijay Abraham I 
2285d5e707SKishon Vijay Abraham I #include <linux/usb/ch9.h>
2385d5e707SKishon Vijay Abraham I #include <linux/usb/gadget.h>
2485d5e707SKishon Vijay Abraham I 
2585d5e707SKishon Vijay Abraham I #include "core.h"
2685d5e707SKishon Vijay Abraham I #include "gadget.h"
2785d5e707SKishon Vijay Abraham I #include "io.h"
2885d5e707SKishon Vijay Abraham I 
29747a0a5bSKishon Vijay Abraham I #include "linux-compat.h"
30747a0a5bSKishon Vijay Abraham I 
3185d5e707SKishon Vijay Abraham I /**
3285d5e707SKishon Vijay Abraham I  * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
3385d5e707SKishon Vijay Abraham I  * @dwc: pointer to our context structure
3485d5e707SKishon Vijay Abraham I  * @mode: the mode to set (J, K SE0 NAK, Force Enable)
3585d5e707SKishon Vijay Abraham I  *
3685d5e707SKishon Vijay Abraham I  * Caller should take care of locking. This function will
3785d5e707SKishon Vijay Abraham I  * return 0 on success or -EINVAL if wrong Test Selector
3885d5e707SKishon Vijay Abraham I  * is passed
3985d5e707SKishon Vijay Abraham I  */
dwc3_gadget_set_test_mode(struct dwc3 * dwc,int mode)4085d5e707SKishon Vijay Abraham I int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
4185d5e707SKishon Vijay Abraham I {
4285d5e707SKishon Vijay Abraham I 	u32		reg;
4385d5e707SKishon Vijay Abraham I 
4485d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
4585d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DCTL_TSTCTRL_MASK;
4685d5e707SKishon Vijay Abraham I 
4785d5e707SKishon Vijay Abraham I 	switch (mode) {
4885d5e707SKishon Vijay Abraham I 	case TEST_J:
4985d5e707SKishon Vijay Abraham I 	case TEST_K:
5085d5e707SKishon Vijay Abraham I 	case TEST_SE0_NAK:
5185d5e707SKishon Vijay Abraham I 	case TEST_PACKET:
5285d5e707SKishon Vijay Abraham I 	case TEST_FORCE_EN:
5385d5e707SKishon Vijay Abraham I 		reg |= mode << 1;
5485d5e707SKishon Vijay Abraham I 		break;
5585d5e707SKishon Vijay Abraham I 	default:
5685d5e707SKishon Vijay Abraham I 		return -EINVAL;
5785d5e707SKishon Vijay Abraham I 	}
5885d5e707SKishon Vijay Abraham I 
5985d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
6085d5e707SKishon Vijay Abraham I 
6185d5e707SKishon Vijay Abraham I 	return 0;
6285d5e707SKishon Vijay Abraham I }
6385d5e707SKishon Vijay Abraham I 
6485d5e707SKishon Vijay Abraham I /**
6585d5e707SKishon Vijay Abraham I  * dwc3_gadget_get_link_state - Gets current state of USB Link
6685d5e707SKishon Vijay Abraham I  * @dwc: pointer to our context structure
6785d5e707SKishon Vijay Abraham I  *
6885d5e707SKishon Vijay Abraham I  * Caller should take care of locking. This function will
6985d5e707SKishon Vijay Abraham I  * return the link state on success (>= 0) or -ETIMEDOUT.
7085d5e707SKishon Vijay Abraham I  */
dwc3_gadget_get_link_state(struct dwc3 * dwc)7185d5e707SKishon Vijay Abraham I int dwc3_gadget_get_link_state(struct dwc3 *dwc)
7285d5e707SKishon Vijay Abraham I {
7385d5e707SKishon Vijay Abraham I 	u32		reg;
7485d5e707SKishon Vijay Abraham I 
7585d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
7685d5e707SKishon Vijay Abraham I 
7785d5e707SKishon Vijay Abraham I 	return DWC3_DSTS_USBLNKST(reg);
7885d5e707SKishon Vijay Abraham I }
7985d5e707SKishon Vijay Abraham I 
8085d5e707SKishon Vijay Abraham I /**
8185d5e707SKishon Vijay Abraham I  * dwc3_gadget_set_link_state - Sets USB Link to a particular State
8285d5e707SKishon Vijay Abraham I  * @dwc: pointer to our context structure
8385d5e707SKishon Vijay Abraham I  * @state: the state to put link into
8485d5e707SKishon Vijay Abraham I  *
8585d5e707SKishon Vijay Abraham I  * Caller should take care of locking. This function will
8685d5e707SKishon Vijay Abraham I  * return 0 on success or -ETIMEDOUT.
8785d5e707SKishon Vijay Abraham I  */
dwc3_gadget_set_link_state(struct dwc3 * dwc,enum dwc3_link_state state)8885d5e707SKishon Vijay Abraham I int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
8985d5e707SKishon Vijay Abraham I {
9085d5e707SKishon Vijay Abraham I 	int		retries = 10000;
9185d5e707SKishon Vijay Abraham I 	u32		reg;
9285d5e707SKishon Vijay Abraham I 
9385d5e707SKishon Vijay Abraham I 	/*
9485d5e707SKishon Vijay Abraham I 	 * Wait until device controller is ready. Only applies to 1.94a and
9585d5e707SKishon Vijay Abraham I 	 * later RTL.
9685d5e707SKishon Vijay Abraham I 	 */
9785d5e707SKishon Vijay Abraham I 	if (dwc->revision >= DWC3_REVISION_194A) {
9885d5e707SKishon Vijay Abraham I 		while (--retries) {
9985d5e707SKishon Vijay Abraham I 			reg = dwc3_readl(dwc->regs, DWC3_DSTS);
10085d5e707SKishon Vijay Abraham I 			if (reg & DWC3_DSTS_DCNRD)
10185d5e707SKishon Vijay Abraham I 				udelay(5);
10285d5e707SKishon Vijay Abraham I 			else
10385d5e707SKishon Vijay Abraham I 				break;
10485d5e707SKishon Vijay Abraham I 		}
10585d5e707SKishon Vijay Abraham I 
10685d5e707SKishon Vijay Abraham I 		if (retries <= 0)
10785d5e707SKishon Vijay Abraham I 			return -ETIMEDOUT;
10885d5e707SKishon Vijay Abraham I 	}
10985d5e707SKishon Vijay Abraham I 
11085d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
11185d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
11285d5e707SKishon Vijay Abraham I 
11385d5e707SKishon Vijay Abraham I 	/* set requested state */
11485d5e707SKishon Vijay Abraham I 	reg |= DWC3_DCTL_ULSTCHNGREQ(state);
11585d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
11685d5e707SKishon Vijay Abraham I 
11785d5e707SKishon Vijay Abraham I 	/*
11885d5e707SKishon Vijay Abraham I 	 * The following code is racy when called from dwc3_gadget_wakeup,
11985d5e707SKishon Vijay Abraham I 	 * and is not needed, at least on newer versions
12085d5e707SKishon Vijay Abraham I 	 */
12185d5e707SKishon Vijay Abraham I 	if (dwc->revision >= DWC3_REVISION_194A)
12285d5e707SKishon Vijay Abraham I 		return 0;
12385d5e707SKishon Vijay Abraham I 
12485d5e707SKishon Vijay Abraham I 	/* wait for a change in DSTS */
12585d5e707SKishon Vijay Abraham I 	retries = 10000;
12685d5e707SKishon Vijay Abraham I 	while (--retries) {
12785d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
12885d5e707SKishon Vijay Abraham I 
12985d5e707SKishon Vijay Abraham I 		if (DWC3_DSTS_USBLNKST(reg) == state)
13085d5e707SKishon Vijay Abraham I 			return 0;
13185d5e707SKishon Vijay Abraham I 
13285d5e707SKishon Vijay Abraham I 		udelay(5);
13385d5e707SKishon Vijay Abraham I 	}
13485d5e707SKishon Vijay Abraham I 
13585d5e707SKishon Vijay Abraham I 	dev_vdbg(dwc->dev, "link state change request timed out\n");
13685d5e707SKishon Vijay Abraham I 
13785d5e707SKishon Vijay Abraham I 	return -ETIMEDOUT;
13885d5e707SKishon Vijay Abraham I }
13985d5e707SKishon Vijay Abraham I 
14085d5e707SKishon Vijay Abraham I /**
14185d5e707SKishon Vijay Abraham I  * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
14285d5e707SKishon Vijay Abraham I  * @dwc: pointer to our context structure
14385d5e707SKishon Vijay Abraham I  *
14485d5e707SKishon Vijay Abraham I  * This function will a best effort FIFO allocation in order
14585d5e707SKishon Vijay Abraham I  * to improve FIFO usage and throughput, while still allowing
14685d5e707SKishon Vijay Abraham I  * us to enable as many endpoints as possible.
14785d5e707SKishon Vijay Abraham I  *
14885d5e707SKishon Vijay Abraham I  * Keep in mind that this operation will be highly dependent
14985d5e707SKishon Vijay Abraham I  * on the configured size for RAM1 - which contains TxFifo -,
15085d5e707SKishon Vijay Abraham I  * the amount of endpoints enabled on coreConsultant tool, and
15185d5e707SKishon Vijay Abraham I  * the width of the Master Bus.
15285d5e707SKishon Vijay Abraham I  *
15385d5e707SKishon Vijay Abraham I  * In the ideal world, we would always be able to satisfy the
15485d5e707SKishon Vijay Abraham I  * following equation:
15585d5e707SKishon Vijay Abraham I  *
15685d5e707SKishon Vijay Abraham I  * ((512 + 2 * MDWIDTH-Bytes) + (Number of IN Endpoints - 1) * \
15785d5e707SKishon Vijay Abraham I  * (3 * (1024 + MDWIDTH-Bytes) + MDWIDTH-Bytes)) / MDWIDTH-Bytes
15885d5e707SKishon Vijay Abraham I  *
15985d5e707SKishon Vijay Abraham I  * Unfortunately, due to many variables that's not always the case.
16085d5e707SKishon Vijay Abraham I  */
dwc3_gadget_resize_tx_fifos(struct dwc3 * dwc)16185d5e707SKishon Vijay Abraham I int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
16285d5e707SKishon Vijay Abraham I {
16385d5e707SKishon Vijay Abraham I 	int		last_fifo_depth = 0;
16485d5e707SKishon Vijay Abraham I 	int		fifo_size;
16585d5e707SKishon Vijay Abraham I 	int		mdwidth;
16685d5e707SKishon Vijay Abraham I 	int		num;
16785d5e707SKishon Vijay Abraham I 
16885d5e707SKishon Vijay Abraham I 	if (!dwc->needs_fifo_resize)
16985d5e707SKishon Vijay Abraham I 		return 0;
17085d5e707SKishon Vijay Abraham I 
17185d5e707SKishon Vijay Abraham I 	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
17285d5e707SKishon Vijay Abraham I 
17385d5e707SKishon Vijay Abraham I 	/* MDWIDTH is represented in bits, we need it in bytes */
17485d5e707SKishon Vijay Abraham I 	mdwidth >>= 3;
17585d5e707SKishon Vijay Abraham I 
17685d5e707SKishon Vijay Abraham I 	/*
17785d5e707SKishon Vijay Abraham I 	 * FIXME For now we will only allocate 1 wMaxPacketSize space
17885d5e707SKishon Vijay Abraham I 	 * for each enabled endpoint, later patches will come to
17985d5e707SKishon Vijay Abraham I 	 * improve this algorithm so that we better use the internal
18085d5e707SKishon Vijay Abraham I 	 * FIFO space
18185d5e707SKishon Vijay Abraham I 	 */
18285d5e707SKishon Vijay Abraham I 	for (num = 0; num < dwc->num_in_eps; num++) {
18385d5e707SKishon Vijay Abraham I 		/* bit0 indicates direction; 1 means IN ep */
18485d5e707SKishon Vijay Abraham I 		struct dwc3_ep	*dep = dwc->eps[(num << 1) | 1];
18585d5e707SKishon Vijay Abraham I 		int		mult = 1;
18685d5e707SKishon Vijay Abraham I 		int		tmp;
18785d5e707SKishon Vijay Abraham I 
18885d5e707SKishon Vijay Abraham I 		if (!(dep->flags & DWC3_EP_ENABLED))
18985d5e707SKishon Vijay Abraham I 			continue;
19085d5e707SKishon Vijay Abraham I 
19185d5e707SKishon Vijay Abraham I 		if (usb_endpoint_xfer_bulk(dep->endpoint.desc)
19285d5e707SKishon Vijay Abraham I 				|| usb_endpoint_xfer_isoc(dep->endpoint.desc))
19385d5e707SKishon Vijay Abraham I 			mult = 3;
19485d5e707SKishon Vijay Abraham I 
19585d5e707SKishon Vijay Abraham I 		/*
19685d5e707SKishon Vijay Abraham I 		 * REVISIT: the following assumes we will always have enough
19785d5e707SKishon Vijay Abraham I 		 * space available on the FIFO RAM for all possible use cases.
19885d5e707SKishon Vijay Abraham I 		 * Make sure that's true somehow and change FIFO allocation
19985d5e707SKishon Vijay Abraham I 		 * accordingly.
20085d5e707SKishon Vijay Abraham I 		 *
20185d5e707SKishon Vijay Abraham I 		 * If we have Bulk or Isochronous endpoints, we want
20285d5e707SKishon Vijay Abraham I 		 * them to be able to be very, very fast. So we're giving
20385d5e707SKishon Vijay Abraham I 		 * those endpoints a fifo_size which is enough for 3 full
20485d5e707SKishon Vijay Abraham I 		 * packets
20585d5e707SKishon Vijay Abraham I 		 */
20685d5e707SKishon Vijay Abraham I 		tmp = mult * (dep->endpoint.maxpacket + mdwidth);
20785d5e707SKishon Vijay Abraham I 		tmp += mdwidth;
20885d5e707SKishon Vijay Abraham I 
20985d5e707SKishon Vijay Abraham I 		fifo_size = DIV_ROUND_UP(tmp, mdwidth);
21085d5e707SKishon Vijay Abraham I 
21185d5e707SKishon Vijay Abraham I 		fifo_size |= (last_fifo_depth << 16);
21285d5e707SKishon Vijay Abraham I 
21385d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
21485d5e707SKishon Vijay Abraham I 				dep->name, last_fifo_depth, fifo_size & 0xffff);
21585d5e707SKishon Vijay Abraham I 
21685d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
21785d5e707SKishon Vijay Abraham I 
21885d5e707SKishon Vijay Abraham I 		last_fifo_depth += (fifo_size & 0xffff);
21985d5e707SKishon Vijay Abraham I 	}
22085d5e707SKishon Vijay Abraham I 
22185d5e707SKishon Vijay Abraham I 	return 0;
22285d5e707SKishon Vijay Abraham I }
22385d5e707SKishon Vijay Abraham I 
dwc3_gadget_giveback(struct dwc3_ep * dep,struct dwc3_request * req,int status)22485d5e707SKishon Vijay Abraham I void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
22585d5e707SKishon Vijay Abraham I 		int status)
22685d5e707SKishon Vijay Abraham I {
22785d5e707SKishon Vijay Abraham I 	struct dwc3			*dwc = dep->dwc;
22885d5e707SKishon Vijay Abraham I 
22985d5e707SKishon Vijay Abraham I 	if (req->queued) {
23085d5e707SKishon Vijay Abraham I 		dep->busy_slot++;
23185d5e707SKishon Vijay Abraham I 		/*
23285d5e707SKishon Vijay Abraham I 		 * Skip LINK TRB. We can't use req->trb and check for
23385d5e707SKishon Vijay Abraham I 		 * DWC3_TRBCTL_LINK_TRB because it points the TRB we
23485d5e707SKishon Vijay Abraham I 		 * just completed (not the LINK TRB).
23585d5e707SKishon Vijay Abraham I 		 */
23685d5e707SKishon Vijay Abraham I 		if (((dep->busy_slot & DWC3_TRB_MASK) ==
23785d5e707SKishon Vijay Abraham I 			DWC3_TRB_NUM- 1) &&
23885d5e707SKishon Vijay Abraham I 			usb_endpoint_xfer_isoc(dep->endpoint.desc))
23985d5e707SKishon Vijay Abraham I 			dep->busy_slot++;
24085d5e707SKishon Vijay Abraham I 		req->queued = false;
24185d5e707SKishon Vijay Abraham I 	}
242747a0a5bSKishon Vijay Abraham I 
24385d5e707SKishon Vijay Abraham I 	list_del(&req->list);
24485d5e707SKishon Vijay Abraham I 	req->trb = NULL;
245b7bf4a95SPhilipp Tomsich 	dwc3_flush_cache((uintptr_t)req->request.dma, req->request.length);
24685d5e707SKishon Vijay Abraham I 
24785d5e707SKishon Vijay Abraham I 	if (req->request.status == -EINPROGRESS)
24885d5e707SKishon Vijay Abraham I 		req->request.status = status;
24985d5e707SKishon Vijay Abraham I 
25085d5e707SKishon Vijay Abraham I 	if (dwc->ep0_bounced && dep->number == 0)
25185d5e707SKishon Vijay Abraham I 		dwc->ep0_bounced = false;
25285d5e707SKishon Vijay Abraham I 	else
25385d5e707SKishon Vijay Abraham I 		usb_gadget_unmap_request(&dwc->gadget, &req->request,
25485d5e707SKishon Vijay Abraham I 				req->direction);
25585d5e707SKishon Vijay Abraham I 
25685d5e707SKishon Vijay Abraham I 	dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
25785d5e707SKishon Vijay Abraham I 			req, dep->name, req->request.actual,
25885d5e707SKishon Vijay Abraham I 			req->request.length, status);
25985d5e707SKishon Vijay Abraham I 
26085d5e707SKishon Vijay Abraham I 	spin_unlock(&dwc->lock);
26185d5e707SKishon Vijay Abraham I 	usb_gadget_giveback_request(&dep->endpoint, &req->request);
26285d5e707SKishon Vijay Abraham I 	spin_lock(&dwc->lock);
26385d5e707SKishon Vijay Abraham I }
26485d5e707SKishon Vijay Abraham I 
dwc3_send_gadget_generic_command(struct dwc3 * dwc,unsigned cmd,u32 param)26585d5e707SKishon Vijay Abraham I int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
26685d5e707SKishon Vijay Abraham I {
26785d5e707SKishon Vijay Abraham I 	u32		timeout = 500;
26885d5e707SKishon Vijay Abraham I 	u32		reg;
26985d5e707SKishon Vijay Abraham I 
27085d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
27185d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
27285d5e707SKishon Vijay Abraham I 
27385d5e707SKishon Vijay Abraham I 	do {
27485d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
27585d5e707SKishon Vijay Abraham I 		if (!(reg & DWC3_DGCMD_CMDACT)) {
27685d5e707SKishon Vijay Abraham I 			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
27785d5e707SKishon Vijay Abraham I 					DWC3_DGCMD_STATUS(reg));
27885d5e707SKishon Vijay Abraham I 			return 0;
27985d5e707SKishon Vijay Abraham I 		}
28085d5e707SKishon Vijay Abraham I 
28185d5e707SKishon Vijay Abraham I 		/*
28285d5e707SKishon Vijay Abraham I 		 * We can't sleep here, because it's also called from
28385d5e707SKishon Vijay Abraham I 		 * interrupt context.
28485d5e707SKishon Vijay Abraham I 		 */
28585d5e707SKishon Vijay Abraham I 		timeout--;
28685d5e707SKishon Vijay Abraham I 		if (!timeout)
28785d5e707SKishon Vijay Abraham I 			return -ETIMEDOUT;
28885d5e707SKishon Vijay Abraham I 		udelay(1);
28985d5e707SKishon Vijay Abraham I 	} while (1);
29085d5e707SKishon Vijay Abraham I }
29185d5e707SKishon Vijay Abraham I 
dwc3_send_gadget_ep_cmd(struct dwc3 * dwc,unsigned ep,unsigned cmd,struct dwc3_gadget_ep_cmd_params * params)29285d5e707SKishon Vijay Abraham I int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
29385d5e707SKishon Vijay Abraham I 		unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
29485d5e707SKishon Vijay Abraham I {
29585d5e707SKishon Vijay Abraham I 	u32			timeout = 500;
29685d5e707SKishon Vijay Abraham I 	u32			reg;
29785d5e707SKishon Vijay Abraham I 
29885d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
29985d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
30085d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2);
30185d5e707SKishon Vijay Abraham I 
30285d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT);
30385d5e707SKishon Vijay Abraham I 	do {
30485d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
30585d5e707SKishon Vijay Abraham I 		if (!(reg & DWC3_DEPCMD_CMDACT)) {
30685d5e707SKishon Vijay Abraham I 			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
30785d5e707SKishon Vijay Abraham I 					DWC3_DEPCMD_STATUS(reg));
30885d5e707SKishon Vijay Abraham I 			return 0;
30985d5e707SKishon Vijay Abraham I 		}
31085d5e707SKishon Vijay Abraham I 
31185d5e707SKishon Vijay Abraham I 		/*
31285d5e707SKishon Vijay Abraham I 		 * We can't sleep here, because it is also called from
31385d5e707SKishon Vijay Abraham I 		 * interrupt context.
31485d5e707SKishon Vijay Abraham I 		 */
31585d5e707SKishon Vijay Abraham I 		timeout--;
31685d5e707SKishon Vijay Abraham I 		if (!timeout)
31785d5e707SKishon Vijay Abraham I 			return -ETIMEDOUT;
31885d5e707SKishon Vijay Abraham I 
31985d5e707SKishon Vijay Abraham I 		udelay(1);
32085d5e707SKishon Vijay Abraham I 	} while (1);
32185d5e707SKishon Vijay Abraham I }
32285d5e707SKishon Vijay Abraham I 
dwc3_trb_dma_offset(struct dwc3_ep * dep,struct dwc3_trb * trb)32385d5e707SKishon Vijay Abraham I static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
32485d5e707SKishon Vijay Abraham I 		struct dwc3_trb *trb)
32585d5e707SKishon Vijay Abraham I {
32685d5e707SKishon Vijay Abraham I 	u32		offset = (char *) trb - (char *) dep->trb_pool;
32785d5e707SKishon Vijay Abraham I 
32885d5e707SKishon Vijay Abraham I 	return dep->trb_pool_dma + offset;
32985d5e707SKishon Vijay Abraham I }
33085d5e707SKishon Vijay Abraham I 
dwc3_alloc_trb_pool(struct dwc3_ep * dep)33185d5e707SKishon Vijay Abraham I static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
33285d5e707SKishon Vijay Abraham I {
33385d5e707SKishon Vijay Abraham I 	if (dep->trb_pool)
33485d5e707SKishon Vijay Abraham I 		return 0;
33585d5e707SKishon Vijay Abraham I 
33685d5e707SKishon Vijay Abraham I 	if (dep->number == 0 || dep->number == 1)
33785d5e707SKishon Vijay Abraham I 		return 0;
33885d5e707SKishon Vijay Abraham I 
339747a0a5bSKishon Vijay Abraham I 	dep->trb_pool = dma_alloc_coherent(sizeof(struct dwc3_trb) *
340747a0a5bSKishon Vijay Abraham I 					   DWC3_TRB_NUM,
341747a0a5bSKishon Vijay Abraham I 					   (unsigned long *)&dep->trb_pool_dma);
34285d5e707SKishon Vijay Abraham I 	if (!dep->trb_pool) {
34385d5e707SKishon Vijay Abraham I 		dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
34485d5e707SKishon Vijay Abraham I 				dep->name);
34585d5e707SKishon Vijay Abraham I 		return -ENOMEM;
34685d5e707SKishon Vijay Abraham I 	}
34785d5e707SKishon Vijay Abraham I 
34885d5e707SKishon Vijay Abraham I 	return 0;
34985d5e707SKishon Vijay Abraham I }
35085d5e707SKishon Vijay Abraham I 
dwc3_free_trb_pool(struct dwc3_ep * dep)35185d5e707SKishon Vijay Abraham I static void dwc3_free_trb_pool(struct dwc3_ep *dep)
35285d5e707SKishon Vijay Abraham I {
353747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dep->trb_pool);
35485d5e707SKishon Vijay Abraham I 
35585d5e707SKishon Vijay Abraham I 	dep->trb_pool = NULL;
35685d5e707SKishon Vijay Abraham I 	dep->trb_pool_dma = 0;
35785d5e707SKishon Vijay Abraham I }
35885d5e707SKishon Vijay Abraham I 
dwc3_gadget_start_config(struct dwc3 * dwc,struct dwc3_ep * dep)35985d5e707SKishon Vijay Abraham I static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
36085d5e707SKishon Vijay Abraham I {
36185d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params params;
36285d5e707SKishon Vijay Abraham I 	u32			cmd;
36385d5e707SKishon Vijay Abraham I 
36485d5e707SKishon Vijay Abraham I 	memset(&params, 0x00, sizeof(params));
36585d5e707SKishon Vijay Abraham I 
36685d5e707SKishon Vijay Abraham I 	if (dep->number != 1) {
36785d5e707SKishon Vijay Abraham I 		cmd = DWC3_DEPCMD_DEPSTARTCFG;
36885d5e707SKishon Vijay Abraham I 		/* XferRscIdx == 0 for ep0 and 2 for the remaining */
36985d5e707SKishon Vijay Abraham I 		if (dep->number > 1) {
37085d5e707SKishon Vijay Abraham I 			if (dwc->start_config_issued)
37185d5e707SKishon Vijay Abraham I 				return 0;
37285d5e707SKishon Vijay Abraham I 			dwc->start_config_issued = true;
37385d5e707SKishon Vijay Abraham I 			cmd |= DWC3_DEPCMD_PARAM(2);
37485d5e707SKishon Vijay Abraham I 		}
37585d5e707SKishon Vijay Abraham I 
37685d5e707SKishon Vijay Abraham I 		return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, &params);
37785d5e707SKishon Vijay Abraham I 	}
37885d5e707SKishon Vijay Abraham I 
37985d5e707SKishon Vijay Abraham I 	return 0;
38085d5e707SKishon Vijay Abraham I }
38185d5e707SKishon Vijay Abraham I 
dwc3_gadget_set_ep_config(struct dwc3 * dwc,struct dwc3_ep * dep,const struct usb_endpoint_descriptor * desc,const struct usb_ss_ep_comp_descriptor * comp_desc,bool ignore,bool restore)38285d5e707SKishon Vijay Abraham I static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
38385d5e707SKishon Vijay Abraham I 		const struct usb_endpoint_descriptor *desc,
38485d5e707SKishon Vijay Abraham I 		const struct usb_ss_ep_comp_descriptor *comp_desc,
38585d5e707SKishon Vijay Abraham I 		bool ignore, bool restore)
38685d5e707SKishon Vijay Abraham I {
38785d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params params;
38885d5e707SKishon Vijay Abraham I 
38985d5e707SKishon Vijay Abraham I 	memset(&params, 0x00, sizeof(params));
39085d5e707SKishon Vijay Abraham I 
39185d5e707SKishon Vijay Abraham I 	params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
39285d5e707SKishon Vijay Abraham I 		| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
39385d5e707SKishon Vijay Abraham I 
39485d5e707SKishon Vijay Abraham I 	/* Burst size is only needed in SuperSpeed mode */
39585d5e707SKishon Vijay Abraham I 	if (dwc->gadget.speed == USB_SPEED_SUPER) {
39685d5e707SKishon Vijay Abraham I 		u32 burst = dep->endpoint.maxburst - 1;
39785d5e707SKishon Vijay Abraham I 
39885d5e707SKishon Vijay Abraham I 		params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
39985d5e707SKishon Vijay Abraham I 	}
40085d5e707SKishon Vijay Abraham I 
40185d5e707SKishon Vijay Abraham I 	if (ignore)
40285d5e707SKishon Vijay Abraham I 		params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
40385d5e707SKishon Vijay Abraham I 
40485d5e707SKishon Vijay Abraham I 	if (restore) {
40585d5e707SKishon Vijay Abraham I 		params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
40685d5e707SKishon Vijay Abraham I 		params.param2 |= dep->saved_state;
40785d5e707SKishon Vijay Abraham I 	}
40885d5e707SKishon Vijay Abraham I 
40985d5e707SKishon Vijay Abraham I 	params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
41085d5e707SKishon Vijay Abraham I 		| DWC3_DEPCFG_XFER_NOT_READY_EN;
41185d5e707SKishon Vijay Abraham I 
41285d5e707SKishon Vijay Abraham I 	if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
41385d5e707SKishon Vijay Abraham I 		params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
41485d5e707SKishon Vijay Abraham I 			| DWC3_DEPCFG_STREAM_EVENT_EN;
41585d5e707SKishon Vijay Abraham I 		dep->stream_capable = true;
41685d5e707SKishon Vijay Abraham I 	}
41785d5e707SKishon Vijay Abraham I 
41885d5e707SKishon Vijay Abraham I 	if (!usb_endpoint_xfer_control(desc))
41985d5e707SKishon Vijay Abraham I 		params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN;
42085d5e707SKishon Vijay Abraham I 
42185d5e707SKishon Vijay Abraham I 	/*
42285d5e707SKishon Vijay Abraham I 	 * We are doing 1:1 mapping for endpoints, meaning
42385d5e707SKishon Vijay Abraham I 	 * Physical Endpoints 2 maps to Logical Endpoint 2 and
42485d5e707SKishon Vijay Abraham I 	 * so on. We consider the direction bit as part of the physical
42585d5e707SKishon Vijay Abraham I 	 * endpoint number. So USB endpoint 0x81 is 0x03.
42685d5e707SKishon Vijay Abraham I 	 */
42785d5e707SKishon Vijay Abraham I 	params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number);
42885d5e707SKishon Vijay Abraham I 
42985d5e707SKishon Vijay Abraham I 	/*
43085d5e707SKishon Vijay Abraham I 	 * We must use the lower 16 TX FIFOs even though
43185d5e707SKishon Vijay Abraham I 	 * HW might have more
43285d5e707SKishon Vijay Abraham I 	 */
43385d5e707SKishon Vijay Abraham I 	if (dep->direction)
43485d5e707SKishon Vijay Abraham I 		params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1);
43585d5e707SKishon Vijay Abraham I 
43685d5e707SKishon Vijay Abraham I 	if (desc->bInterval) {
43785d5e707SKishon Vijay Abraham I 		params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1);
43885d5e707SKishon Vijay Abraham I 		dep->interval = 1 << (desc->bInterval - 1);
43985d5e707SKishon Vijay Abraham I 	}
44085d5e707SKishon Vijay Abraham I 
44185d5e707SKishon Vijay Abraham I 	return dwc3_send_gadget_ep_cmd(dwc, dep->number,
44285d5e707SKishon Vijay Abraham I 			DWC3_DEPCMD_SETEPCONFIG, &params);
44385d5e707SKishon Vijay Abraham I }
44485d5e707SKishon Vijay Abraham I 
dwc3_gadget_set_xfer_resource(struct dwc3 * dwc,struct dwc3_ep * dep)44585d5e707SKishon Vijay Abraham I static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
44685d5e707SKishon Vijay Abraham I {
44785d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params params;
44885d5e707SKishon Vijay Abraham I 
44985d5e707SKishon Vijay Abraham I 	memset(&params, 0x00, sizeof(params));
45085d5e707SKishon Vijay Abraham I 
45185d5e707SKishon Vijay Abraham I 	params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1);
45285d5e707SKishon Vijay Abraham I 
45385d5e707SKishon Vijay Abraham I 	return dwc3_send_gadget_ep_cmd(dwc, dep->number,
45485d5e707SKishon Vijay Abraham I 			DWC3_DEPCMD_SETTRANSFRESOURCE, &params);
45585d5e707SKishon Vijay Abraham I }
45685d5e707SKishon Vijay Abraham I 
45785d5e707SKishon Vijay Abraham I /**
45885d5e707SKishon Vijay Abraham I  * __dwc3_gadget_ep_enable - Initializes a HW endpoint
45985d5e707SKishon Vijay Abraham I  * @dep: endpoint to be initialized
46085d5e707SKishon Vijay Abraham I  * @desc: USB Endpoint Descriptor
46185d5e707SKishon Vijay Abraham I  *
46285d5e707SKishon Vijay Abraham I  * Caller should take care of locking
46385d5e707SKishon Vijay Abraham I  */
__dwc3_gadget_ep_enable(struct dwc3_ep * dep,const struct usb_endpoint_descriptor * desc,const struct usb_ss_ep_comp_descriptor * comp_desc,bool ignore,bool restore)46485d5e707SKishon Vijay Abraham I static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
46585d5e707SKishon Vijay Abraham I 		const struct usb_endpoint_descriptor *desc,
46685d5e707SKishon Vijay Abraham I 		const struct usb_ss_ep_comp_descriptor *comp_desc,
46785d5e707SKishon Vijay Abraham I 		bool ignore, bool restore)
46885d5e707SKishon Vijay Abraham I {
46985d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = dep->dwc;
47085d5e707SKishon Vijay Abraham I 	u32			reg;
47185d5e707SKishon Vijay Abraham I 	int			ret;
47285d5e707SKishon Vijay Abraham I 
47385d5e707SKishon Vijay Abraham I 	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
47485d5e707SKishon Vijay Abraham I 
47585d5e707SKishon Vijay Abraham I 	if (!(dep->flags & DWC3_EP_ENABLED)) {
47685d5e707SKishon Vijay Abraham I 		ret = dwc3_gadget_start_config(dwc, dep);
47785d5e707SKishon Vijay Abraham I 		if (ret)
47885d5e707SKishon Vijay Abraham I 			return ret;
47985d5e707SKishon Vijay Abraham I 	}
48085d5e707SKishon Vijay Abraham I 
48185d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
48285d5e707SKishon Vijay Abraham I 			restore);
48385d5e707SKishon Vijay Abraham I 	if (ret)
48485d5e707SKishon Vijay Abraham I 		return ret;
48585d5e707SKishon Vijay Abraham I 
48685d5e707SKishon Vijay Abraham I 	if (!(dep->flags & DWC3_EP_ENABLED)) {
48785d5e707SKishon Vijay Abraham I 		struct dwc3_trb	*trb_st_hw;
48885d5e707SKishon Vijay Abraham I 		struct dwc3_trb	*trb_link;
48985d5e707SKishon Vijay Abraham I 
49085d5e707SKishon Vijay Abraham I 		ret = dwc3_gadget_set_xfer_resource(dwc, dep);
49185d5e707SKishon Vijay Abraham I 		if (ret)
49285d5e707SKishon Vijay Abraham I 			return ret;
49385d5e707SKishon Vijay Abraham I 
49485d5e707SKishon Vijay Abraham I 		dep->endpoint.desc = desc;
49585d5e707SKishon Vijay Abraham I 		dep->comp_desc = comp_desc;
49685d5e707SKishon Vijay Abraham I 		dep->type = usb_endpoint_type(desc);
49785d5e707SKishon Vijay Abraham I 		dep->flags |= DWC3_EP_ENABLED;
49885d5e707SKishon Vijay Abraham I 
49985d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
50085d5e707SKishon Vijay Abraham I 		reg |= DWC3_DALEPENA_EP(dep->number);
50185d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
50285d5e707SKishon Vijay Abraham I 
50385d5e707SKishon Vijay Abraham I 		if (!usb_endpoint_xfer_isoc(desc))
50485d5e707SKishon Vijay Abraham I 			return 0;
50585d5e707SKishon Vijay Abraham I 
50685d5e707SKishon Vijay Abraham I 		/* Link TRB for ISOC. The HWO bit is never reset */
50785d5e707SKishon Vijay Abraham I 		trb_st_hw = &dep->trb_pool[0];
50885d5e707SKishon Vijay Abraham I 
50985d5e707SKishon Vijay Abraham I 		trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
51085d5e707SKishon Vijay Abraham I 		memset(trb_link, 0, sizeof(*trb_link));
51185d5e707SKishon Vijay Abraham I 
51285d5e707SKishon Vijay Abraham I 		trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
51385d5e707SKishon Vijay Abraham I 		trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
51485d5e707SKishon Vijay Abraham I 		trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB;
51585d5e707SKishon Vijay Abraham I 		trb_link->ctrl |= DWC3_TRB_CTRL_HWO;
51685d5e707SKishon Vijay Abraham I 	}
51785d5e707SKishon Vijay Abraham I 
51885d5e707SKishon Vijay Abraham I 	return 0;
51985d5e707SKishon Vijay Abraham I }
52085d5e707SKishon Vijay Abraham I 
52185d5e707SKishon Vijay Abraham I static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
dwc3_remove_requests(struct dwc3 * dwc,struct dwc3_ep * dep)52285d5e707SKishon Vijay Abraham I static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
52385d5e707SKishon Vijay Abraham I {
52485d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req;
52585d5e707SKishon Vijay Abraham I 
52685d5e707SKishon Vijay Abraham I 	if (!list_empty(&dep->req_queued)) {
52785d5e707SKishon Vijay Abraham I 		dwc3_stop_active_transfer(dwc, dep->number, true);
52885d5e707SKishon Vijay Abraham I 
52985d5e707SKishon Vijay Abraham I 		/* - giveback all requests to gadget driver */
53085d5e707SKishon Vijay Abraham I 		while (!list_empty(&dep->req_queued)) {
53185d5e707SKishon Vijay Abraham I 			req = next_request(&dep->req_queued);
53285d5e707SKishon Vijay Abraham I 
53385d5e707SKishon Vijay Abraham I 			dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
53485d5e707SKishon Vijay Abraham I 		}
53585d5e707SKishon Vijay Abraham I 	}
53685d5e707SKishon Vijay Abraham I 
53785d5e707SKishon Vijay Abraham I 	while (!list_empty(&dep->request_list)) {
53885d5e707SKishon Vijay Abraham I 		req = next_request(&dep->request_list);
53985d5e707SKishon Vijay Abraham I 
54085d5e707SKishon Vijay Abraham I 		dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
54185d5e707SKishon Vijay Abraham I 	}
54285d5e707SKishon Vijay Abraham I }
54385d5e707SKishon Vijay Abraham I 
54485d5e707SKishon Vijay Abraham I /**
54585d5e707SKishon Vijay Abraham I  * __dwc3_gadget_ep_disable - Disables a HW endpoint
54685d5e707SKishon Vijay Abraham I  * @dep: the endpoint to disable
54785d5e707SKishon Vijay Abraham I  *
54885d5e707SKishon Vijay Abraham I  * This function also removes requests which are currently processed ny the
54985d5e707SKishon Vijay Abraham I  * hardware and those which are not yet scheduled.
55085d5e707SKishon Vijay Abraham I  * Caller should take care of locking.
55185d5e707SKishon Vijay Abraham I  */
__dwc3_gadget_ep_disable(struct dwc3_ep * dep)55285d5e707SKishon Vijay Abraham I static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
55385d5e707SKishon Vijay Abraham I {
55485d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = dep->dwc;
55585d5e707SKishon Vijay Abraham I 	u32			reg;
55685d5e707SKishon Vijay Abraham I 
55785d5e707SKishon Vijay Abraham I 	dwc3_remove_requests(dwc, dep);
55885d5e707SKishon Vijay Abraham I 
55985d5e707SKishon Vijay Abraham I 	/* make sure HW endpoint isn't stalled */
56085d5e707SKishon Vijay Abraham I 	if (dep->flags & DWC3_EP_STALL)
56185d5e707SKishon Vijay Abraham I 		__dwc3_gadget_ep_set_halt(dep, 0, false);
56285d5e707SKishon Vijay Abraham I 
56385d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
56485d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DALEPENA_EP(dep->number);
56585d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
56685d5e707SKishon Vijay Abraham I 
56785d5e707SKishon Vijay Abraham I 	dep->stream_capable = false;
56885d5e707SKishon Vijay Abraham I 	dep->endpoint.desc = NULL;
56985d5e707SKishon Vijay Abraham I 	dep->comp_desc = NULL;
57085d5e707SKishon Vijay Abraham I 	dep->type = 0;
57185d5e707SKishon Vijay Abraham I 	dep->flags = 0;
57285d5e707SKishon Vijay Abraham I 
57385d5e707SKishon Vijay Abraham I 	return 0;
57485d5e707SKishon Vijay Abraham I }
57585d5e707SKishon Vijay Abraham I 
57685d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
57785d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep0_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)57885d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep0_enable(struct usb_ep *ep,
57985d5e707SKishon Vijay Abraham I 		const struct usb_endpoint_descriptor *desc)
58085d5e707SKishon Vijay Abraham I {
58185d5e707SKishon Vijay Abraham I 	return -EINVAL;
58285d5e707SKishon Vijay Abraham I }
58385d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep0_disable(struct usb_ep * ep)58485d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep0_disable(struct usb_ep *ep)
58585d5e707SKishon Vijay Abraham I {
58685d5e707SKishon Vijay Abraham I 	return -EINVAL;
58785d5e707SKishon Vijay Abraham I }
58885d5e707SKishon Vijay Abraham I 
58985d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
59085d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)59185d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_enable(struct usb_ep *ep,
59285d5e707SKishon Vijay Abraham I 		const struct usb_endpoint_descriptor *desc)
59385d5e707SKishon Vijay Abraham I {
59485d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep;
59585d5e707SKishon Vijay Abraham I 	unsigned long			flags;
59685d5e707SKishon Vijay Abraham I 	int				ret;
59785d5e707SKishon Vijay Abraham I 
59885d5e707SKishon Vijay Abraham I 	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
59985d5e707SKishon Vijay Abraham I 		pr_debug("dwc3: invalid parameters\n");
60085d5e707SKishon Vijay Abraham I 		return -EINVAL;
60185d5e707SKishon Vijay Abraham I 	}
60285d5e707SKishon Vijay Abraham I 
60385d5e707SKishon Vijay Abraham I 	if (!desc->wMaxPacketSize) {
60485d5e707SKishon Vijay Abraham I 		pr_debug("dwc3: missing wMaxPacketSize\n");
60585d5e707SKishon Vijay Abraham I 		return -EINVAL;
60685d5e707SKishon Vijay Abraham I 	}
60785d5e707SKishon Vijay Abraham I 
60885d5e707SKishon Vijay Abraham I 	dep = to_dwc3_ep(ep);
60985d5e707SKishon Vijay Abraham I 
61085d5e707SKishon Vijay Abraham I 	if (dep->flags & DWC3_EP_ENABLED) {
611747a0a5bSKishon Vijay Abraham I 		WARN(true, "%s is already enabled\n",
61285d5e707SKishon Vijay Abraham I 				dep->name);
61385d5e707SKishon Vijay Abraham I 		return 0;
61485d5e707SKishon Vijay Abraham I 	}
61585d5e707SKishon Vijay Abraham I 
61685d5e707SKishon Vijay Abraham I 	switch (usb_endpoint_type(desc)) {
61785d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_CONTROL:
61885d5e707SKishon Vijay Abraham I 		strlcat(dep->name, "-control", sizeof(dep->name));
61985d5e707SKishon Vijay Abraham I 		break;
62085d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_ISOC:
62185d5e707SKishon Vijay Abraham I 		strlcat(dep->name, "-isoc", sizeof(dep->name));
62285d5e707SKishon Vijay Abraham I 		break;
62385d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_BULK:
62485d5e707SKishon Vijay Abraham I 		strlcat(dep->name, "-bulk", sizeof(dep->name));
62585d5e707SKishon Vijay Abraham I 		break;
62685d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_INT:
62785d5e707SKishon Vijay Abraham I 		strlcat(dep->name, "-int", sizeof(dep->name));
62885d5e707SKishon Vijay Abraham I 		break;
62985d5e707SKishon Vijay Abraham I 	default:
63085d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "invalid endpoint transfer type\n");
63185d5e707SKishon Vijay Abraham I 	}
63285d5e707SKishon Vijay Abraham I 
63385d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
63485d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
63585d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
63685d5e707SKishon Vijay Abraham I 
63785d5e707SKishon Vijay Abraham I 	return ret;
63885d5e707SKishon Vijay Abraham I }
63985d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_disable(struct usb_ep * ep)64085d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_disable(struct usb_ep *ep)
64185d5e707SKishon Vijay Abraham I {
64285d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep;
64385d5e707SKishon Vijay Abraham I 	unsigned long			flags;
64485d5e707SKishon Vijay Abraham I 	int				ret;
64585d5e707SKishon Vijay Abraham I 
64685d5e707SKishon Vijay Abraham I 	if (!ep) {
64785d5e707SKishon Vijay Abraham I 		pr_debug("dwc3: invalid parameters\n");
64885d5e707SKishon Vijay Abraham I 		return -EINVAL;
64985d5e707SKishon Vijay Abraham I 	}
65085d5e707SKishon Vijay Abraham I 
65185d5e707SKishon Vijay Abraham I 	dep = to_dwc3_ep(ep);
65285d5e707SKishon Vijay Abraham I 
65385d5e707SKishon Vijay Abraham I 	if (!(dep->flags & DWC3_EP_ENABLED)) {
654747a0a5bSKishon Vijay Abraham I 		WARN(true, "%s is already disabled\n",
65585d5e707SKishon Vijay Abraham I 				dep->name);
65685d5e707SKishon Vijay Abraham I 		return 0;
65785d5e707SKishon Vijay Abraham I 	}
65885d5e707SKishon Vijay Abraham I 
65985d5e707SKishon Vijay Abraham I 	snprintf(dep->name, sizeof(dep->name), "ep%d%s",
66085d5e707SKishon Vijay Abraham I 			dep->number >> 1,
66185d5e707SKishon Vijay Abraham I 			(dep->number & 1) ? "in" : "out");
66285d5e707SKishon Vijay Abraham I 
66385d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
66485d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_disable(dep);
66585d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
66685d5e707SKishon Vijay Abraham I 
66785d5e707SKishon Vijay Abraham I 	return ret;
66885d5e707SKishon Vijay Abraham I }
66985d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_alloc_request(struct usb_ep * ep,gfp_t gfp_flags)67085d5e707SKishon Vijay Abraham I static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
67185d5e707SKishon Vijay Abraham I 	gfp_t gfp_flags)
67285d5e707SKishon Vijay Abraham I {
67385d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req;
67485d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
67585d5e707SKishon Vijay Abraham I 
67685d5e707SKishon Vijay Abraham I 	req = kzalloc(sizeof(*req), gfp_flags);
67785d5e707SKishon Vijay Abraham I 	if (!req)
67885d5e707SKishon Vijay Abraham I 		return NULL;
67985d5e707SKishon Vijay Abraham I 
68085d5e707SKishon Vijay Abraham I 	req->epnum	= dep->number;
68185d5e707SKishon Vijay Abraham I 	req->dep	= dep;
68285d5e707SKishon Vijay Abraham I 
68385d5e707SKishon Vijay Abraham I 	return &req->request;
68485d5e707SKishon Vijay Abraham I }
68585d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_free_request(struct usb_ep * ep,struct usb_request * request)68685d5e707SKishon Vijay Abraham I static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
68785d5e707SKishon Vijay Abraham I 		struct usb_request *request)
68885d5e707SKishon Vijay Abraham I {
68985d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req = to_dwc3_request(request);
69085d5e707SKishon Vijay Abraham I 
69185d5e707SKishon Vijay Abraham I 	kfree(req);
69285d5e707SKishon Vijay Abraham I }
69385d5e707SKishon Vijay Abraham I 
69485d5e707SKishon Vijay Abraham I /**
69585d5e707SKishon Vijay Abraham I  * dwc3_prepare_one_trb - setup one TRB from one request
69685d5e707SKishon Vijay Abraham I  * @dep: endpoint for which this request is prepared
69785d5e707SKishon Vijay Abraham I  * @req: dwc3_request pointer
69885d5e707SKishon Vijay Abraham I  */
dwc3_prepare_one_trb(struct dwc3_ep * dep,struct dwc3_request * req,dma_addr_t dma,unsigned length,unsigned last,unsigned chain,unsigned node)69985d5e707SKishon Vijay Abraham I static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
70085d5e707SKishon Vijay Abraham I 		struct dwc3_request *req, dma_addr_t dma,
70185d5e707SKishon Vijay Abraham I 		unsigned length, unsigned last, unsigned chain, unsigned node)
70285d5e707SKishon Vijay Abraham I {
70385d5e707SKishon Vijay Abraham I 	struct dwc3_trb		*trb;
70485d5e707SKishon Vijay Abraham I 
70585d5e707SKishon Vijay Abraham I 	dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
70685d5e707SKishon Vijay Abraham I 			dep->name, req, (unsigned long long) dma,
70785d5e707SKishon Vijay Abraham I 			length, last ? " last" : "",
70885d5e707SKishon Vijay Abraham I 			chain ? " chain" : "");
70985d5e707SKishon Vijay Abraham I 
71085d5e707SKishon Vijay Abraham I 
71185d5e707SKishon Vijay Abraham I 	trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
71285d5e707SKishon Vijay Abraham I 
71385d5e707SKishon Vijay Abraham I 	if (!req->trb) {
71485d5e707SKishon Vijay Abraham I 		dwc3_gadget_move_request_queued(req);
71585d5e707SKishon Vijay Abraham I 		req->trb = trb;
71685d5e707SKishon Vijay Abraham I 		req->trb_dma = dwc3_trb_dma_offset(dep, trb);
71785d5e707SKishon Vijay Abraham I 		req->start_slot = dep->free_slot & DWC3_TRB_MASK;
71885d5e707SKishon Vijay Abraham I 	}
71985d5e707SKishon Vijay Abraham I 
72085d5e707SKishon Vijay Abraham I 	dep->free_slot++;
72185d5e707SKishon Vijay Abraham I 	/* Skip the LINK-TRB on ISOC */
72285d5e707SKishon Vijay Abraham I 	if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
72385d5e707SKishon Vijay Abraham I 			usb_endpoint_xfer_isoc(dep->endpoint.desc))
72485d5e707SKishon Vijay Abraham I 		dep->free_slot++;
72585d5e707SKishon Vijay Abraham I 
72685d5e707SKishon Vijay Abraham I 	trb->size = DWC3_TRB_SIZE_LENGTH(length);
72785d5e707SKishon Vijay Abraham I 	trb->bpl = lower_32_bits(dma);
72885d5e707SKishon Vijay Abraham I 	trb->bph = upper_32_bits(dma);
72985d5e707SKishon Vijay Abraham I 
73085d5e707SKishon Vijay Abraham I 	switch (usb_endpoint_type(dep->endpoint.desc)) {
73185d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_CONTROL:
73285d5e707SKishon Vijay Abraham I 		trb->ctrl = DWC3_TRBCTL_CONTROL_SETUP;
73385d5e707SKishon Vijay Abraham I 		break;
73485d5e707SKishon Vijay Abraham I 
73585d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_ISOC:
73685d5e707SKishon Vijay Abraham I 		if (!node)
73785d5e707SKishon Vijay Abraham I 			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
73885d5e707SKishon Vijay Abraham I 		else
73985d5e707SKishon Vijay Abraham I 			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
74085d5e707SKishon Vijay Abraham I 		break;
74185d5e707SKishon Vijay Abraham I 
74285d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_BULK:
74385d5e707SKishon Vijay Abraham I 	case USB_ENDPOINT_XFER_INT:
74485d5e707SKishon Vijay Abraham I 		trb->ctrl = DWC3_TRBCTL_NORMAL;
74585d5e707SKishon Vijay Abraham I 		break;
74685d5e707SKishon Vijay Abraham I 	default:
74785d5e707SKishon Vijay Abraham I 		/*
74885d5e707SKishon Vijay Abraham I 		 * This is only possible with faulty memory because we
74985d5e707SKishon Vijay Abraham I 		 * checked it already :)
75085d5e707SKishon Vijay Abraham I 		 */
75185d5e707SKishon Vijay Abraham I 		BUG();
75285d5e707SKishon Vijay Abraham I 	}
75385d5e707SKishon Vijay Abraham I 
75485d5e707SKishon Vijay Abraham I 	if (!req->request.no_interrupt && !chain)
75585d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_IOC;
75685d5e707SKishon Vijay Abraham I 
75785d5e707SKishon Vijay Abraham I 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
75885d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
75985d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_CSP;
76085d5e707SKishon Vijay Abraham I 	} else if (last) {
76185d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_LST;
76285d5e707SKishon Vijay Abraham I 	}
76385d5e707SKishon Vijay Abraham I 
76485d5e707SKishon Vijay Abraham I 	if (chain)
76585d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_CHN;
76685d5e707SKishon Vijay Abraham I 
76785d5e707SKishon Vijay Abraham I 	if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
76885d5e707SKishon Vijay Abraham I 		trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
76985d5e707SKishon Vijay Abraham I 
77085d5e707SKishon Vijay Abraham I 	trb->ctrl |= DWC3_TRB_CTRL_HWO;
771526a50f8SKishon Vijay Abraham I 
772b7bf4a95SPhilipp Tomsich 	dwc3_flush_cache((uintptr_t)dma, length);
773b7bf4a95SPhilipp Tomsich 	dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
77485d5e707SKishon Vijay Abraham I }
77585d5e707SKishon Vijay Abraham I 
77685d5e707SKishon Vijay Abraham I /*
77785d5e707SKishon Vijay Abraham I  * dwc3_prepare_trbs - setup TRBs from requests
77885d5e707SKishon Vijay Abraham I  * @dep: endpoint for which requests are being prepared
77985d5e707SKishon Vijay Abraham I  * @starting: true if the endpoint is idle and no requests are queued.
78085d5e707SKishon Vijay Abraham I  *
78185d5e707SKishon Vijay Abraham I  * The function goes through the requests list and sets up TRBs for the
78285d5e707SKishon Vijay Abraham I  * transfers. The function returns once there are no more TRBs available or
78385d5e707SKishon Vijay Abraham I  * it runs out of requests.
78485d5e707SKishon Vijay Abraham I  */
dwc3_prepare_trbs(struct dwc3_ep * dep,bool starting)78585d5e707SKishon Vijay Abraham I static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
78685d5e707SKishon Vijay Abraham I {
78785d5e707SKishon Vijay Abraham I 	struct dwc3_request	*req, *n;
78885d5e707SKishon Vijay Abraham I 	u32			trbs_left;
78985d5e707SKishon Vijay Abraham I 	u32			max;
79085d5e707SKishon Vijay Abraham I 
79185d5e707SKishon Vijay Abraham I 	BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
79285d5e707SKishon Vijay Abraham I 
79385d5e707SKishon Vijay Abraham I 	/* the first request must not be queued */
79485d5e707SKishon Vijay Abraham I 	trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK;
79585d5e707SKishon Vijay Abraham I 
79685d5e707SKishon Vijay Abraham I 	/* Can't wrap around on a non-isoc EP since there's no link TRB */
79785d5e707SKishon Vijay Abraham I 	if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
79885d5e707SKishon Vijay Abraham I 		max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK);
79985d5e707SKishon Vijay Abraham I 		if (trbs_left > max)
80085d5e707SKishon Vijay Abraham I 			trbs_left = max;
80185d5e707SKishon Vijay Abraham I 	}
80285d5e707SKishon Vijay Abraham I 
80385d5e707SKishon Vijay Abraham I 	/*
80485d5e707SKishon Vijay Abraham I 	 * If busy & slot are equal than it is either full or empty. If we are
80585d5e707SKishon Vijay Abraham I 	 * starting to process requests then we are empty. Otherwise we are
80685d5e707SKishon Vijay Abraham I 	 * full and don't do anything
80785d5e707SKishon Vijay Abraham I 	 */
80885d5e707SKishon Vijay Abraham I 	if (!trbs_left) {
80985d5e707SKishon Vijay Abraham I 		if (!starting)
81085d5e707SKishon Vijay Abraham I 			return;
81185d5e707SKishon Vijay Abraham I 		trbs_left = DWC3_TRB_NUM;
81285d5e707SKishon Vijay Abraham I 		/*
81385d5e707SKishon Vijay Abraham I 		 * In case we start from scratch, we queue the ISOC requests
81485d5e707SKishon Vijay Abraham I 		 * starting from slot 1. This is done because we use ring
81585d5e707SKishon Vijay Abraham I 		 * buffer and have no LST bit to stop us. Instead, we place
81685d5e707SKishon Vijay Abraham I 		 * IOC bit every TRB_NUM/4. We try to avoid having an interrupt
81785d5e707SKishon Vijay Abraham I 		 * after the first request so we start at slot 1 and have
81885d5e707SKishon Vijay Abraham I 		 * 7 requests proceed before we hit the first IOC.
81985d5e707SKishon Vijay Abraham I 		 * Other transfer types don't use the ring buffer and are
82085d5e707SKishon Vijay Abraham I 		 * processed from the first TRB until the last one. Since we
82185d5e707SKishon Vijay Abraham I 		 * don't wrap around we have to start at the beginning.
82285d5e707SKishon Vijay Abraham I 		 */
82385d5e707SKishon Vijay Abraham I 		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
82485d5e707SKishon Vijay Abraham I 			dep->busy_slot = 1;
82585d5e707SKishon Vijay Abraham I 			dep->free_slot = 1;
82685d5e707SKishon Vijay Abraham I 		} else {
82785d5e707SKishon Vijay Abraham I 			dep->busy_slot = 0;
82885d5e707SKishon Vijay Abraham I 			dep->free_slot = 0;
82985d5e707SKishon Vijay Abraham I 		}
83085d5e707SKishon Vijay Abraham I 	}
83185d5e707SKishon Vijay Abraham I 
83285d5e707SKishon Vijay Abraham I 	/* The last TRB is a link TRB, not used for xfer */
83385d5e707SKishon Vijay Abraham I 	if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc))
83485d5e707SKishon Vijay Abraham I 		return;
83585d5e707SKishon Vijay Abraham I 
83685d5e707SKishon Vijay Abraham I 	list_for_each_entry_safe(req, n, &dep->request_list, list) {
83785d5e707SKishon Vijay Abraham I 		unsigned	length;
83885d5e707SKishon Vijay Abraham I 		dma_addr_t	dma;
83985d5e707SKishon Vijay Abraham I 
84085d5e707SKishon Vijay Abraham I 		dma = req->request.dma;
84185d5e707SKishon Vijay Abraham I 		length = req->request.length;
84285d5e707SKishon Vijay Abraham I 
84385d5e707SKishon Vijay Abraham I 		dwc3_prepare_one_trb(dep, req, dma, length,
84429e7fc19SLukasz Majewski 				     true, false, 0);
84585d5e707SKishon Vijay Abraham I 
84685d5e707SKishon Vijay Abraham I 		break;
84785d5e707SKishon Vijay Abraham I 	}
84885d5e707SKishon Vijay Abraham I }
84985d5e707SKishon Vijay Abraham I 
__dwc3_gadget_kick_transfer(struct dwc3_ep * dep,u16 cmd_param,int start_new)85085d5e707SKishon Vijay Abraham I static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
85185d5e707SKishon Vijay Abraham I 		int start_new)
85285d5e707SKishon Vijay Abraham I {
85385d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params params;
85485d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req;
85585d5e707SKishon Vijay Abraham I 	struct dwc3			*dwc = dep->dwc;
85685d5e707SKishon Vijay Abraham I 	int				ret;
85785d5e707SKishon Vijay Abraham I 	u32				cmd;
85885d5e707SKishon Vijay Abraham I 
85985d5e707SKishon Vijay Abraham I 	if (start_new && (dep->flags & DWC3_EP_BUSY)) {
86085d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name);
86185d5e707SKishon Vijay Abraham I 		return -EBUSY;
86285d5e707SKishon Vijay Abraham I 	}
86385d5e707SKishon Vijay Abraham I 	dep->flags &= ~DWC3_EP_PENDING_REQUEST;
86485d5e707SKishon Vijay Abraham I 
86585d5e707SKishon Vijay Abraham I 	/*
86685d5e707SKishon Vijay Abraham I 	 * If we are getting here after a short-out-packet we don't enqueue any
86785d5e707SKishon Vijay Abraham I 	 * new requests as we try to set the IOC bit only on the last request.
86885d5e707SKishon Vijay Abraham I 	 */
86985d5e707SKishon Vijay Abraham I 	if (start_new) {
87085d5e707SKishon Vijay Abraham I 		if (list_empty(&dep->req_queued))
87185d5e707SKishon Vijay Abraham I 			dwc3_prepare_trbs(dep, start_new);
87285d5e707SKishon Vijay Abraham I 
87385d5e707SKishon Vijay Abraham I 		/* req points to the first request which will be sent */
87485d5e707SKishon Vijay Abraham I 		req = next_request(&dep->req_queued);
87585d5e707SKishon Vijay Abraham I 	} else {
87685d5e707SKishon Vijay Abraham I 		dwc3_prepare_trbs(dep, start_new);
87785d5e707SKishon Vijay Abraham I 
87885d5e707SKishon Vijay Abraham I 		/*
87985d5e707SKishon Vijay Abraham I 		 * req points to the first request where HWO changed from 0 to 1
88085d5e707SKishon Vijay Abraham I 		 */
88185d5e707SKishon Vijay Abraham I 		req = next_request(&dep->req_queued);
88285d5e707SKishon Vijay Abraham I 	}
88385d5e707SKishon Vijay Abraham I 	if (!req) {
88485d5e707SKishon Vijay Abraham I 		dep->flags |= DWC3_EP_PENDING_REQUEST;
88585d5e707SKishon Vijay Abraham I 		return 0;
88685d5e707SKishon Vijay Abraham I 	}
88785d5e707SKishon Vijay Abraham I 
88885d5e707SKishon Vijay Abraham I 	memset(&params, 0, sizeof(params));
88985d5e707SKishon Vijay Abraham I 
89085d5e707SKishon Vijay Abraham I 	if (start_new) {
89185d5e707SKishon Vijay Abraham I 		params.param0 = upper_32_bits(req->trb_dma);
89285d5e707SKishon Vijay Abraham I 		params.param1 = lower_32_bits(req->trb_dma);
89385d5e707SKishon Vijay Abraham I 		cmd = DWC3_DEPCMD_STARTTRANSFER;
89485d5e707SKishon Vijay Abraham I 	} else {
89585d5e707SKishon Vijay Abraham I 		cmd = DWC3_DEPCMD_UPDATETRANSFER;
89685d5e707SKishon Vijay Abraham I 	}
89785d5e707SKishon Vijay Abraham I 
89885d5e707SKishon Vijay Abraham I 	cmd |= DWC3_DEPCMD_PARAM(cmd_param);
89985d5e707SKishon Vijay Abraham I 	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
90085d5e707SKishon Vijay Abraham I 	if (ret < 0) {
90185d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
90285d5e707SKishon Vijay Abraham I 
90385d5e707SKishon Vijay Abraham I 		/*
90485d5e707SKishon Vijay Abraham I 		 * FIXME we need to iterate over the list of requests
90585d5e707SKishon Vijay Abraham I 		 * here and stop, unmap, free and del each of the linked
90685d5e707SKishon Vijay Abraham I 		 * requests instead of what we do now.
90785d5e707SKishon Vijay Abraham I 		 */
90885d5e707SKishon Vijay Abraham I 		usb_gadget_unmap_request(&dwc->gadget, &req->request,
90985d5e707SKishon Vijay Abraham I 				req->direction);
91085d5e707SKishon Vijay Abraham I 		list_del(&req->list);
91185d5e707SKishon Vijay Abraham I 		return ret;
91285d5e707SKishon Vijay Abraham I 	}
91385d5e707SKishon Vijay Abraham I 
91485d5e707SKishon Vijay Abraham I 	dep->flags |= DWC3_EP_BUSY;
91585d5e707SKishon Vijay Abraham I 
91685d5e707SKishon Vijay Abraham I 	if (start_new) {
91785d5e707SKishon Vijay Abraham I 		dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
91885d5e707SKishon Vijay Abraham I 				dep->number);
91985d5e707SKishon Vijay Abraham I 		WARN_ON_ONCE(!dep->resource_index);
92085d5e707SKishon Vijay Abraham I 	}
92185d5e707SKishon Vijay Abraham I 
92285d5e707SKishon Vijay Abraham I 	return 0;
92385d5e707SKishon Vijay Abraham I }
92485d5e707SKishon Vijay Abraham I 
__dwc3_gadget_start_isoc(struct dwc3 * dwc,struct dwc3_ep * dep,u32 cur_uf)92585d5e707SKishon Vijay Abraham I static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
92685d5e707SKishon Vijay Abraham I 		struct dwc3_ep *dep, u32 cur_uf)
92785d5e707SKishon Vijay Abraham I {
92885d5e707SKishon Vijay Abraham I 	u32 uf;
92985d5e707SKishon Vijay Abraham I 
93085d5e707SKishon Vijay Abraham I 	if (list_empty(&dep->request_list)) {
93185d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
93285d5e707SKishon Vijay Abraham I 			dep->name);
93385d5e707SKishon Vijay Abraham I 		dep->flags |= DWC3_EP_PENDING_REQUEST;
93485d5e707SKishon Vijay Abraham I 		return;
93585d5e707SKishon Vijay Abraham I 	}
93685d5e707SKishon Vijay Abraham I 
93785d5e707SKishon Vijay Abraham I 	/* 4 micro frames in the future */
93885d5e707SKishon Vijay Abraham I 	uf = cur_uf + dep->interval * 4;
93985d5e707SKishon Vijay Abraham I 
94085d5e707SKishon Vijay Abraham I 	__dwc3_gadget_kick_transfer(dep, uf, 1);
94185d5e707SKishon Vijay Abraham I }
94285d5e707SKishon Vijay Abraham I 
dwc3_gadget_start_isoc(struct dwc3 * dwc,struct dwc3_ep * dep,const struct dwc3_event_depevt * event)94385d5e707SKishon Vijay Abraham I static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
94485d5e707SKishon Vijay Abraham I 		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
94585d5e707SKishon Vijay Abraham I {
94685d5e707SKishon Vijay Abraham I 	u32 cur_uf, mask;
94785d5e707SKishon Vijay Abraham I 
94885d5e707SKishon Vijay Abraham I 	mask = ~(dep->interval - 1);
94985d5e707SKishon Vijay Abraham I 	cur_uf = event->parameters & mask;
95085d5e707SKishon Vijay Abraham I 
95185d5e707SKishon Vijay Abraham I 	__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
95285d5e707SKishon Vijay Abraham I }
95385d5e707SKishon Vijay Abraham I 
__dwc3_gadget_ep_queue(struct dwc3_ep * dep,struct dwc3_request * req)95485d5e707SKishon Vijay Abraham I static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
95585d5e707SKishon Vijay Abraham I {
95685d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = dep->dwc;
95785d5e707SKishon Vijay Abraham I 	int			ret;
95885d5e707SKishon Vijay Abraham I 
95985d5e707SKishon Vijay Abraham I 	req->request.actual	= 0;
96085d5e707SKishon Vijay Abraham I 	req->request.status	= -EINPROGRESS;
96185d5e707SKishon Vijay Abraham I 	req->direction		= dep->direction;
96285d5e707SKishon Vijay Abraham I 	req->epnum		= dep->number;
96385d5e707SKishon Vijay Abraham I 
96485d5e707SKishon Vijay Abraham I 	/*
9655dc4538bSMarek Szyprowski 	 * DWC3 hangs on OUT requests smaller than maxpacket size,
9665dc4538bSMarek Szyprowski 	 * so HACK the request length
9675dc4538bSMarek Szyprowski 	 */
9685dc4538bSMarek Szyprowski 	if (dep->direction == 0 &&
9695dc4538bSMarek Szyprowski 	    req->request.length < dep->endpoint.maxpacket)
9705dc4538bSMarek Szyprowski 		req->request.length = dep->endpoint.maxpacket;
9715dc4538bSMarek Szyprowski 
9725dc4538bSMarek Szyprowski 	/*
97385d5e707SKishon Vijay Abraham I 	 * We only add to our list of requests now and
97485d5e707SKishon Vijay Abraham I 	 * start consuming the list once we get XferNotReady
97585d5e707SKishon Vijay Abraham I 	 * IRQ.
97685d5e707SKishon Vijay Abraham I 	 *
97785d5e707SKishon Vijay Abraham I 	 * That way, we avoid doing anything that we don't need
97885d5e707SKishon Vijay Abraham I 	 * to do now and defer it until the point we receive a
97985d5e707SKishon Vijay Abraham I 	 * particular token from the Host side.
98085d5e707SKishon Vijay Abraham I 	 *
98185d5e707SKishon Vijay Abraham I 	 * This will also avoid Host cancelling URBs due to too
98285d5e707SKishon Vijay Abraham I 	 * many NAKs.
98385d5e707SKishon Vijay Abraham I 	 */
98485d5e707SKishon Vijay Abraham I 	ret = usb_gadget_map_request(&dwc->gadget, &req->request,
98585d5e707SKishon Vijay Abraham I 			dep->direction);
98685d5e707SKishon Vijay Abraham I 	if (ret)
98785d5e707SKishon Vijay Abraham I 		return ret;
98885d5e707SKishon Vijay Abraham I 
98985d5e707SKishon Vijay Abraham I 	list_add_tail(&req->list, &dep->request_list);
99085d5e707SKishon Vijay Abraham I 
99185d5e707SKishon Vijay Abraham I 	/*
99285d5e707SKishon Vijay Abraham I 	 * There are a few special cases:
99385d5e707SKishon Vijay Abraham I 	 *
99485d5e707SKishon Vijay Abraham I 	 * 1. XferNotReady with empty list of requests. We need to kick the
99585d5e707SKishon Vijay Abraham I 	 *    transfer here in that situation, otherwise we will be NAKing
99685d5e707SKishon Vijay Abraham I 	 *    forever. If we get XferNotReady before gadget driver has a
99785d5e707SKishon Vijay Abraham I 	 *    chance to queue a request, we will ACK the IRQ but won't be
99885d5e707SKishon Vijay Abraham I 	 *    able to receive the data until the next request is queued.
99985d5e707SKishon Vijay Abraham I 	 *    The following code is handling exactly that.
100085d5e707SKishon Vijay Abraham I 	 *
100185d5e707SKishon Vijay Abraham I 	 */
100285d5e707SKishon Vijay Abraham I 	if (dep->flags & DWC3_EP_PENDING_REQUEST) {
100385d5e707SKishon Vijay Abraham I 		/*
100485d5e707SKishon Vijay Abraham I 		 * If xfernotready is already elapsed and it is a case
100585d5e707SKishon Vijay Abraham I 		 * of isoc transfer, then issue END TRANSFER, so that
100685d5e707SKishon Vijay Abraham I 		 * you can receive xfernotready again and can have
100785d5e707SKishon Vijay Abraham I 		 * notion of current microframe.
100885d5e707SKishon Vijay Abraham I 		 */
100985d5e707SKishon Vijay Abraham I 		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
101085d5e707SKishon Vijay Abraham I 			if (list_empty(&dep->req_queued)) {
101185d5e707SKishon Vijay Abraham I 				dwc3_stop_active_transfer(dwc, dep->number, true);
101285d5e707SKishon Vijay Abraham I 				dep->flags = DWC3_EP_ENABLED;
101385d5e707SKishon Vijay Abraham I 			}
101485d5e707SKishon Vijay Abraham I 			return 0;
101585d5e707SKishon Vijay Abraham I 		}
101685d5e707SKishon Vijay Abraham I 
101785d5e707SKishon Vijay Abraham I 		ret = __dwc3_gadget_kick_transfer(dep, 0, true);
101885d5e707SKishon Vijay Abraham I 		if (ret && ret != -EBUSY)
101985d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
102085d5e707SKishon Vijay Abraham I 					dep->name);
102185d5e707SKishon Vijay Abraham I 		return ret;
102285d5e707SKishon Vijay Abraham I 	}
102385d5e707SKishon Vijay Abraham I 
102485d5e707SKishon Vijay Abraham I 	/*
102585d5e707SKishon Vijay Abraham I 	 * 2. XferInProgress on Isoc EP with an active transfer. We need to
102685d5e707SKishon Vijay Abraham I 	 *    kick the transfer here after queuing a request, otherwise the
102785d5e707SKishon Vijay Abraham I 	 *    core may not see the modified TRB(s).
102885d5e707SKishon Vijay Abraham I 	 */
102985d5e707SKishon Vijay Abraham I 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
103085d5e707SKishon Vijay Abraham I 			(dep->flags & DWC3_EP_BUSY) &&
103185d5e707SKishon Vijay Abraham I 			!(dep->flags & DWC3_EP_MISSED_ISOC)) {
103285d5e707SKishon Vijay Abraham I 		WARN_ON_ONCE(!dep->resource_index);
103385d5e707SKishon Vijay Abraham I 		ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
103485d5e707SKishon Vijay Abraham I 				false);
103585d5e707SKishon Vijay Abraham I 		if (ret && ret != -EBUSY)
103685d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
103785d5e707SKishon Vijay Abraham I 					dep->name);
103885d5e707SKishon Vijay Abraham I 		return ret;
103985d5e707SKishon Vijay Abraham I 	}
104085d5e707SKishon Vijay Abraham I 
104185d5e707SKishon Vijay Abraham I 	/*
104285d5e707SKishon Vijay Abraham I 	 * 4. Stream Capable Bulk Endpoints. We need to start the transfer
104385d5e707SKishon Vijay Abraham I 	 * right away, otherwise host will not know we have streams to be
104485d5e707SKishon Vijay Abraham I 	 * handled.
104585d5e707SKishon Vijay Abraham I 	 */
104685d5e707SKishon Vijay Abraham I 	if (dep->stream_capable) {
104785d5e707SKishon Vijay Abraham I 		int	ret;
104885d5e707SKishon Vijay Abraham I 
104985d5e707SKishon Vijay Abraham I 		ret = __dwc3_gadget_kick_transfer(dep, 0, true);
105085d5e707SKishon Vijay Abraham I 		if (ret && ret != -EBUSY) {
105185d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
105285d5e707SKishon Vijay Abraham I 					dep->name);
105385d5e707SKishon Vijay Abraham I 		}
105485d5e707SKishon Vijay Abraham I 	}
105585d5e707SKishon Vijay Abraham I 
105685d5e707SKishon Vijay Abraham I 	return 0;
105785d5e707SKishon Vijay Abraham I }
105885d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_queue(struct usb_ep * ep,struct usb_request * request,gfp_t gfp_flags)105985d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
106085d5e707SKishon Vijay Abraham I 	gfp_t gfp_flags)
106185d5e707SKishon Vijay Abraham I {
106285d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req = to_dwc3_request(request);
106385d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
106485d5e707SKishon Vijay Abraham I 
106585d5e707SKishon Vijay Abraham I 	unsigned long			flags;
106685d5e707SKishon Vijay Abraham I 
106785d5e707SKishon Vijay Abraham I 	int				ret;
106885d5e707SKishon Vijay Abraham I 
106985d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
107085d5e707SKishon Vijay Abraham I 	if (!dep->endpoint.desc) {
107185d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
107285d5e707SKishon Vijay Abraham I 				request, ep->name);
107385d5e707SKishon Vijay Abraham I 		ret = -ESHUTDOWN;
107485d5e707SKishon Vijay Abraham I 		goto out;
107585d5e707SKishon Vijay Abraham I 	}
107685d5e707SKishon Vijay Abraham I 
1077747a0a5bSKishon Vijay Abraham I 	if (req->dep != dep) {
1078747a0a5bSKishon Vijay Abraham I 		WARN(true, "request %p belongs to '%s'\n",
1079747a0a5bSKishon Vijay Abraham I 				request, req->dep->name);
108085d5e707SKishon Vijay Abraham I 		ret = -EINVAL;
108185d5e707SKishon Vijay Abraham I 		goto out;
108285d5e707SKishon Vijay Abraham I 	}
108385d5e707SKishon Vijay Abraham I 
108485d5e707SKishon Vijay Abraham I 	dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
108585d5e707SKishon Vijay Abraham I 			request, ep->name, request->length);
108685d5e707SKishon Vijay Abraham I 
108785d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_queue(dep, req);
108885d5e707SKishon Vijay Abraham I 
108985d5e707SKishon Vijay Abraham I out:
109085d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
109185d5e707SKishon Vijay Abraham I 
109285d5e707SKishon Vijay Abraham I 	return ret;
109385d5e707SKishon Vijay Abraham I }
109485d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_dequeue(struct usb_ep * ep,struct usb_request * request)109585d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
109685d5e707SKishon Vijay Abraham I 		struct usb_request *request)
109785d5e707SKishon Vijay Abraham I {
109885d5e707SKishon Vijay Abraham I 	struct dwc3_request		*req = to_dwc3_request(request);
109985d5e707SKishon Vijay Abraham I 	struct dwc3_request		*r = NULL;
110085d5e707SKishon Vijay Abraham I 
110185d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
110285d5e707SKishon Vijay Abraham I 	struct dwc3			*dwc = dep->dwc;
110385d5e707SKishon Vijay Abraham I 
110485d5e707SKishon Vijay Abraham I 	unsigned long			flags;
110585d5e707SKishon Vijay Abraham I 	int				ret = 0;
110685d5e707SKishon Vijay Abraham I 
110785d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
110885d5e707SKishon Vijay Abraham I 
110985d5e707SKishon Vijay Abraham I 	list_for_each_entry(r, &dep->request_list, list) {
111085d5e707SKishon Vijay Abraham I 		if (r == req)
111185d5e707SKishon Vijay Abraham I 			break;
111285d5e707SKishon Vijay Abraham I 	}
111385d5e707SKishon Vijay Abraham I 
111485d5e707SKishon Vijay Abraham I 	if (r != req) {
111585d5e707SKishon Vijay Abraham I 		list_for_each_entry(r, &dep->req_queued, list) {
111685d5e707SKishon Vijay Abraham I 			if (r == req)
111785d5e707SKishon Vijay Abraham I 				break;
111885d5e707SKishon Vijay Abraham I 		}
111985d5e707SKishon Vijay Abraham I 		if (r == req) {
112085d5e707SKishon Vijay Abraham I 			/* wait until it is processed */
112185d5e707SKishon Vijay Abraham I 			dwc3_stop_active_transfer(dwc, dep->number, true);
112285d5e707SKishon Vijay Abraham I 			goto out1;
112385d5e707SKishon Vijay Abraham I 		}
112485d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "request %p was not queued to %s\n",
112585d5e707SKishon Vijay Abraham I 				request, ep->name);
112685d5e707SKishon Vijay Abraham I 		ret = -EINVAL;
112785d5e707SKishon Vijay Abraham I 		goto out0;
112885d5e707SKishon Vijay Abraham I 	}
112985d5e707SKishon Vijay Abraham I 
113085d5e707SKishon Vijay Abraham I out1:
113185d5e707SKishon Vijay Abraham I 	/* giveback the request */
113285d5e707SKishon Vijay Abraham I 	dwc3_gadget_giveback(dep, req, -ECONNRESET);
113385d5e707SKishon Vijay Abraham I 
113485d5e707SKishon Vijay Abraham I out0:
113585d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
113685d5e707SKishon Vijay Abraham I 
113785d5e707SKishon Vijay Abraham I 	return ret;
113885d5e707SKishon Vijay Abraham I }
113985d5e707SKishon Vijay Abraham I 
__dwc3_gadget_ep_set_halt(struct dwc3_ep * dep,int value,int protocol)114085d5e707SKishon Vijay Abraham I int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
114185d5e707SKishon Vijay Abraham I {
114285d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params	params;
114385d5e707SKishon Vijay Abraham I 	struct dwc3				*dwc = dep->dwc;
114485d5e707SKishon Vijay Abraham I 	int					ret;
114585d5e707SKishon Vijay Abraham I 
114685d5e707SKishon Vijay Abraham I 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
114785d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
114885d5e707SKishon Vijay Abraham I 		return -EINVAL;
114985d5e707SKishon Vijay Abraham I 	}
115085d5e707SKishon Vijay Abraham I 
115185d5e707SKishon Vijay Abraham I 	memset(&params, 0x00, sizeof(params));
115285d5e707SKishon Vijay Abraham I 
115385d5e707SKishon Vijay Abraham I 	if (value) {
115485d5e707SKishon Vijay Abraham I 		if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
115585d5e707SKishon Vijay Abraham I 				(!list_empty(&dep->req_queued) ||
115685d5e707SKishon Vijay Abraham I 				 !list_empty(&dep->request_list)))) {
115785d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
115885d5e707SKishon Vijay Abraham I 					dep->name);
115985d5e707SKishon Vijay Abraham I 			return -EAGAIN;
116085d5e707SKishon Vijay Abraham I 		}
116185d5e707SKishon Vijay Abraham I 
116285d5e707SKishon Vijay Abraham I 		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
116385d5e707SKishon Vijay Abraham I 			DWC3_DEPCMD_SETSTALL, &params);
116485d5e707SKishon Vijay Abraham I 		if (ret)
116585d5e707SKishon Vijay Abraham I 			dev_err(dwc->dev, "failed to set STALL on %s\n",
116685d5e707SKishon Vijay Abraham I 					dep->name);
116785d5e707SKishon Vijay Abraham I 		else
116885d5e707SKishon Vijay Abraham I 			dep->flags |= DWC3_EP_STALL;
116985d5e707SKishon Vijay Abraham I 	} else {
117085d5e707SKishon Vijay Abraham I 		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
117185d5e707SKishon Vijay Abraham I 			DWC3_DEPCMD_CLEARSTALL, &params);
117285d5e707SKishon Vijay Abraham I 		if (ret)
117385d5e707SKishon Vijay Abraham I 			dev_err(dwc->dev, "failed to clear STALL on %s\n",
117485d5e707SKishon Vijay Abraham I 					dep->name);
117585d5e707SKishon Vijay Abraham I 		else
117685d5e707SKishon Vijay Abraham I 			dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
117785d5e707SKishon Vijay Abraham I 	}
117885d5e707SKishon Vijay Abraham I 
117985d5e707SKishon Vijay Abraham I 	return ret;
118085d5e707SKishon Vijay Abraham I }
118185d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_set_halt(struct usb_ep * ep,int value)118285d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
118385d5e707SKishon Vijay Abraham I {
118485d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
118585d5e707SKishon Vijay Abraham I 
118685d5e707SKishon Vijay Abraham I 	unsigned long			flags;
118785d5e707SKishon Vijay Abraham I 
118885d5e707SKishon Vijay Abraham I 	int				ret;
118985d5e707SKishon Vijay Abraham I 
119085d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
119185d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_set_halt(dep, value, false);
119285d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
119385d5e707SKishon Vijay Abraham I 
119485d5e707SKishon Vijay Abraham I 	return ret;
119585d5e707SKishon Vijay Abraham I }
119685d5e707SKishon Vijay Abraham I 
dwc3_gadget_ep_set_wedge(struct usb_ep * ep)119785d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
119885d5e707SKishon Vijay Abraham I {
119985d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep = to_dwc3_ep(ep);
120085d5e707SKishon Vijay Abraham I 	unsigned long			flags;
120185d5e707SKishon Vijay Abraham I 	int				ret;
120285d5e707SKishon Vijay Abraham I 
120385d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
120485d5e707SKishon Vijay Abraham I 	dep->flags |= DWC3_EP_WEDGE;
120585d5e707SKishon Vijay Abraham I 
120685d5e707SKishon Vijay Abraham I 	if (dep->number == 0 || dep->number == 1)
120785d5e707SKishon Vijay Abraham I 		ret = __dwc3_gadget_ep0_set_halt(ep, 1);
120885d5e707SKishon Vijay Abraham I 	else
120985d5e707SKishon Vijay Abraham I 		ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
121085d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
121185d5e707SKishon Vijay Abraham I 
121285d5e707SKishon Vijay Abraham I 	return ret;
121385d5e707SKishon Vijay Abraham I }
121485d5e707SKishon Vijay Abraham I 
121585d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
121685d5e707SKishon Vijay Abraham I 
121785d5e707SKishon Vijay Abraham I static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
121885d5e707SKishon Vijay Abraham I 	.bLength	= USB_DT_ENDPOINT_SIZE,
121985d5e707SKishon Vijay Abraham I 	.bDescriptorType = USB_DT_ENDPOINT,
122085d5e707SKishon Vijay Abraham I 	.bmAttributes	= USB_ENDPOINT_XFER_CONTROL,
122185d5e707SKishon Vijay Abraham I };
122285d5e707SKishon Vijay Abraham I 
122385d5e707SKishon Vijay Abraham I static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
122485d5e707SKishon Vijay Abraham I 	.enable		= dwc3_gadget_ep0_enable,
122585d5e707SKishon Vijay Abraham I 	.disable	= dwc3_gadget_ep0_disable,
122685d5e707SKishon Vijay Abraham I 	.alloc_request	= dwc3_gadget_ep_alloc_request,
122785d5e707SKishon Vijay Abraham I 	.free_request	= dwc3_gadget_ep_free_request,
122885d5e707SKishon Vijay Abraham I 	.queue		= dwc3_gadget_ep0_queue,
122985d5e707SKishon Vijay Abraham I 	.dequeue	= dwc3_gadget_ep_dequeue,
123085d5e707SKishon Vijay Abraham I 	.set_halt	= dwc3_gadget_ep0_set_halt,
123185d5e707SKishon Vijay Abraham I 	.set_wedge	= dwc3_gadget_ep_set_wedge,
123285d5e707SKishon Vijay Abraham I };
123385d5e707SKishon Vijay Abraham I 
123485d5e707SKishon Vijay Abraham I static const struct usb_ep_ops dwc3_gadget_ep_ops = {
123585d5e707SKishon Vijay Abraham I 	.enable		= dwc3_gadget_ep_enable,
123685d5e707SKishon Vijay Abraham I 	.disable	= dwc3_gadget_ep_disable,
123785d5e707SKishon Vijay Abraham I 	.alloc_request	= dwc3_gadget_ep_alloc_request,
123885d5e707SKishon Vijay Abraham I 	.free_request	= dwc3_gadget_ep_free_request,
123985d5e707SKishon Vijay Abraham I 	.queue		= dwc3_gadget_ep_queue,
124085d5e707SKishon Vijay Abraham I 	.dequeue	= dwc3_gadget_ep_dequeue,
124185d5e707SKishon Vijay Abraham I 	.set_halt	= dwc3_gadget_ep_set_halt,
124285d5e707SKishon Vijay Abraham I 	.set_wedge	= dwc3_gadget_ep_set_wedge,
124385d5e707SKishon Vijay Abraham I };
124485d5e707SKishon Vijay Abraham I 
124585d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
124685d5e707SKishon Vijay Abraham I 
dwc3_gadget_get_frame(struct usb_gadget * g)124785d5e707SKishon Vijay Abraham I static int dwc3_gadget_get_frame(struct usb_gadget *g)
124885d5e707SKishon Vijay Abraham I {
124985d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
125085d5e707SKishon Vijay Abraham I 	u32			reg;
125185d5e707SKishon Vijay Abraham I 
125285d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
125385d5e707SKishon Vijay Abraham I 	return DWC3_DSTS_SOFFN(reg);
125485d5e707SKishon Vijay Abraham I }
125585d5e707SKishon Vijay Abraham I 
dwc3_gadget_wakeup(struct usb_gadget * g)125685d5e707SKishon Vijay Abraham I static int dwc3_gadget_wakeup(struct usb_gadget *g)
125785d5e707SKishon Vijay Abraham I {
125885d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
125985d5e707SKishon Vijay Abraham I 
126085d5e707SKishon Vijay Abraham I 	unsigned long		timeout;
126185d5e707SKishon Vijay Abraham I 	unsigned long		flags;
126285d5e707SKishon Vijay Abraham I 
126385d5e707SKishon Vijay Abraham I 	u32			reg;
126485d5e707SKishon Vijay Abraham I 
126585d5e707SKishon Vijay Abraham I 	int			ret = 0;
126685d5e707SKishon Vijay Abraham I 
126785d5e707SKishon Vijay Abraham I 	u8			link_state;
126885d5e707SKishon Vijay Abraham I 	u8			speed;
126985d5e707SKishon Vijay Abraham I 
127085d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
127185d5e707SKishon Vijay Abraham I 
127285d5e707SKishon Vijay Abraham I 	/*
127385d5e707SKishon Vijay Abraham I 	 * According to the Databook Remote wakeup request should
127485d5e707SKishon Vijay Abraham I 	 * be issued only when the device is in early suspend state.
127585d5e707SKishon Vijay Abraham I 	 *
127685d5e707SKishon Vijay Abraham I 	 * We can check that via USB Link State bits in DSTS register.
127785d5e707SKishon Vijay Abraham I 	 */
127885d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
127985d5e707SKishon Vijay Abraham I 
128085d5e707SKishon Vijay Abraham I 	speed = reg & DWC3_DSTS_CONNECTSPD;
128185d5e707SKishon Vijay Abraham I 	if (speed == DWC3_DSTS_SUPERSPEED) {
128285d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
128385d5e707SKishon Vijay Abraham I 		ret = -EINVAL;
128485d5e707SKishon Vijay Abraham I 		goto out;
128585d5e707SKishon Vijay Abraham I 	}
128685d5e707SKishon Vijay Abraham I 
128785d5e707SKishon Vijay Abraham I 	link_state = DWC3_DSTS_USBLNKST(reg);
128885d5e707SKishon Vijay Abraham I 
128985d5e707SKishon Vijay Abraham I 	switch (link_state) {
129085d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_RX_DET:	/* in HS, means Early Suspend */
129185d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_U3:	/* in HS, means SUSPEND */
129285d5e707SKishon Vijay Abraham I 		break;
129385d5e707SKishon Vijay Abraham I 	default:
129485d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
129585d5e707SKishon Vijay Abraham I 				link_state);
129685d5e707SKishon Vijay Abraham I 		ret = -EINVAL;
129785d5e707SKishon Vijay Abraham I 		goto out;
129885d5e707SKishon Vijay Abraham I 	}
129985d5e707SKishon Vijay Abraham I 
130085d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
130185d5e707SKishon Vijay Abraham I 	if (ret < 0) {
130285d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to put link in Recovery\n");
130385d5e707SKishon Vijay Abraham I 		goto out;
130485d5e707SKishon Vijay Abraham I 	}
130585d5e707SKishon Vijay Abraham I 
130685d5e707SKishon Vijay Abraham I 	/* Recent versions do this automatically */
130785d5e707SKishon Vijay Abraham I 	if (dwc->revision < DWC3_REVISION_194A) {
130885d5e707SKishon Vijay Abraham I 		/* write zeroes to Link Change Request */
130985d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
131085d5e707SKishon Vijay Abraham I 		reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
131185d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
131285d5e707SKishon Vijay Abraham I 	}
131385d5e707SKishon Vijay Abraham I 
131485d5e707SKishon Vijay Abraham I 	/* poll until Link State changes to ON */
1315747a0a5bSKishon Vijay Abraham I 	timeout = 1000;
131685d5e707SKishon Vijay Abraham I 
1317747a0a5bSKishon Vijay Abraham I 	while (timeout--) {
131885d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
131985d5e707SKishon Vijay Abraham I 
132085d5e707SKishon Vijay Abraham I 		/* in HS, means ON */
132185d5e707SKishon Vijay Abraham I 		if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
132285d5e707SKishon Vijay Abraham I 			break;
132385d5e707SKishon Vijay Abraham I 	}
132485d5e707SKishon Vijay Abraham I 
132585d5e707SKishon Vijay Abraham I 	if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
132685d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to send remote wakeup\n");
132785d5e707SKishon Vijay Abraham I 		ret = -EINVAL;
132885d5e707SKishon Vijay Abraham I 	}
132985d5e707SKishon Vijay Abraham I 
133085d5e707SKishon Vijay Abraham I out:
133185d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
133285d5e707SKishon Vijay Abraham I 
133385d5e707SKishon Vijay Abraham I 	return ret;
133485d5e707SKishon Vijay Abraham I }
133585d5e707SKishon Vijay Abraham I 
dwc3_gadget_set_selfpowered(struct usb_gadget * g,int is_selfpowered)133685d5e707SKishon Vijay Abraham I static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
133785d5e707SKishon Vijay Abraham I 		int is_selfpowered)
133885d5e707SKishon Vijay Abraham I {
133985d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
134085d5e707SKishon Vijay Abraham I 	unsigned long		flags;
134185d5e707SKishon Vijay Abraham I 
134285d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
134385d5e707SKishon Vijay Abraham I 	dwc->is_selfpowered = !!is_selfpowered;
134485d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
134585d5e707SKishon Vijay Abraham I 
134685d5e707SKishon Vijay Abraham I 	return 0;
134785d5e707SKishon Vijay Abraham I }
134885d5e707SKishon Vijay Abraham I 
dwc3_gadget_run_stop(struct dwc3 * dwc,int is_on,int suspend)134985d5e707SKishon Vijay Abraham I static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
135085d5e707SKishon Vijay Abraham I {
135185d5e707SKishon Vijay Abraham I 	u32			reg;
135285d5e707SKishon Vijay Abraham I 	u32			timeout = 500;
135385d5e707SKishon Vijay Abraham I 
135485d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
135585d5e707SKishon Vijay Abraham I 	if (is_on) {
135685d5e707SKishon Vijay Abraham I 		if (dwc->revision <= DWC3_REVISION_187A) {
135785d5e707SKishon Vijay Abraham I 			reg &= ~DWC3_DCTL_TRGTULST_MASK;
135885d5e707SKishon Vijay Abraham I 			reg |= DWC3_DCTL_TRGTULST_RX_DET;
135985d5e707SKishon Vijay Abraham I 		}
136085d5e707SKishon Vijay Abraham I 
136185d5e707SKishon Vijay Abraham I 		if (dwc->revision >= DWC3_REVISION_194A)
136285d5e707SKishon Vijay Abraham I 			reg &= ~DWC3_DCTL_KEEP_CONNECT;
136385d5e707SKishon Vijay Abraham I 		reg |= DWC3_DCTL_RUN_STOP;
136485d5e707SKishon Vijay Abraham I 
136585d5e707SKishon Vijay Abraham I 		if (dwc->has_hibernation)
136685d5e707SKishon Vijay Abraham I 			reg |= DWC3_DCTL_KEEP_CONNECT;
136785d5e707SKishon Vijay Abraham I 
136885d5e707SKishon Vijay Abraham I 		dwc->pullups_connected = true;
136985d5e707SKishon Vijay Abraham I 	} else {
137085d5e707SKishon Vijay Abraham I 		reg &= ~DWC3_DCTL_RUN_STOP;
137185d5e707SKishon Vijay Abraham I 
137285d5e707SKishon Vijay Abraham I 		if (dwc->has_hibernation && !suspend)
137385d5e707SKishon Vijay Abraham I 			reg &= ~DWC3_DCTL_KEEP_CONNECT;
137485d5e707SKishon Vijay Abraham I 
137585d5e707SKishon Vijay Abraham I 		dwc->pullups_connected = false;
137685d5e707SKishon Vijay Abraham I 	}
137785d5e707SKishon Vijay Abraham I 
137885d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
137985d5e707SKishon Vijay Abraham I 
138085d5e707SKishon Vijay Abraham I 	do {
138185d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DSTS);
138285d5e707SKishon Vijay Abraham I 		if (is_on) {
138385d5e707SKishon Vijay Abraham I 			if (!(reg & DWC3_DSTS_DEVCTRLHLT))
138485d5e707SKishon Vijay Abraham I 				break;
138585d5e707SKishon Vijay Abraham I 		} else {
138685d5e707SKishon Vijay Abraham I 			if (reg & DWC3_DSTS_DEVCTRLHLT)
138785d5e707SKishon Vijay Abraham I 				break;
138885d5e707SKishon Vijay Abraham I 		}
138985d5e707SKishon Vijay Abraham I 		timeout--;
139085d5e707SKishon Vijay Abraham I 		if (!timeout)
139185d5e707SKishon Vijay Abraham I 			return -ETIMEDOUT;
139285d5e707SKishon Vijay Abraham I 		udelay(1);
139385d5e707SKishon Vijay Abraham I 	} while (1);
139485d5e707SKishon Vijay Abraham I 
139585d5e707SKishon Vijay Abraham I 	dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
139685d5e707SKishon Vijay Abraham I 			dwc->gadget_driver
139785d5e707SKishon Vijay Abraham I 			? dwc->gadget_driver->function : "no-function",
139885d5e707SKishon Vijay Abraham I 			is_on ? "connect" : "disconnect");
139985d5e707SKishon Vijay Abraham I 
140085d5e707SKishon Vijay Abraham I 	return 0;
140185d5e707SKishon Vijay Abraham I }
140285d5e707SKishon Vijay Abraham I 
dwc3_gadget_pullup(struct usb_gadget * g,int is_on)140385d5e707SKishon Vijay Abraham I static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
140485d5e707SKishon Vijay Abraham I {
140585d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
140685d5e707SKishon Vijay Abraham I 	unsigned long		flags;
140785d5e707SKishon Vijay Abraham I 	int			ret;
140885d5e707SKishon Vijay Abraham I 
140985d5e707SKishon Vijay Abraham I 	is_on = !!is_on;
141085d5e707SKishon Vijay Abraham I 
141185d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
141285d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_run_stop(dwc, is_on, false);
141385d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
141485d5e707SKishon Vijay Abraham I 
141585d5e707SKishon Vijay Abraham I 	return ret;
141685d5e707SKishon Vijay Abraham I }
141785d5e707SKishon Vijay Abraham I 
dwc3_gadget_enable_irq(struct dwc3 * dwc)141885d5e707SKishon Vijay Abraham I static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
141985d5e707SKishon Vijay Abraham I {
142085d5e707SKishon Vijay Abraham I 	u32			reg;
142185d5e707SKishon Vijay Abraham I 
142285d5e707SKishon Vijay Abraham I 	/* Enable all but Start and End of Frame IRQs */
142385d5e707SKishon Vijay Abraham I 	reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
142485d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_EVNTOVERFLOWEN |
142585d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_CMDCMPLTEN |
142685d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_ERRTICERREN |
142785d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_WKUPEVTEN |
142885d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_ULSTCNGEN |
142985d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_CONNECTDONEEN |
143085d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_USBRSTEN |
143185d5e707SKishon Vijay Abraham I 			DWC3_DEVTEN_DISCONNEVTEN);
143285d5e707SKishon Vijay Abraham I 
143385d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
143485d5e707SKishon Vijay Abraham I }
143585d5e707SKishon Vijay Abraham I 
dwc3_gadget_disable_irq(struct dwc3 * dwc)143685d5e707SKishon Vijay Abraham I static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
143785d5e707SKishon Vijay Abraham I {
143885d5e707SKishon Vijay Abraham I 	/* mask all interrupts */
143985d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
144085d5e707SKishon Vijay Abraham I }
144185d5e707SKishon Vijay Abraham I 
dwc3_gadget_start(struct usb_gadget * g,struct usb_gadget_driver * driver)144285d5e707SKishon Vijay Abraham I static int dwc3_gadget_start(struct usb_gadget *g,
144385d5e707SKishon Vijay Abraham I 		struct usb_gadget_driver *driver)
144485d5e707SKishon Vijay Abraham I {
144585d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
144685d5e707SKishon Vijay Abraham I 	struct dwc3_ep		*dep;
144785d5e707SKishon Vijay Abraham I 	unsigned long		flags;
144885d5e707SKishon Vijay Abraham I 	int			ret = 0;
144985d5e707SKishon Vijay Abraham I 	u32			reg;
145085d5e707SKishon Vijay Abraham I 
145185d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
145285d5e707SKishon Vijay Abraham I 
145385d5e707SKishon Vijay Abraham I 	if (dwc->gadget_driver) {
145485d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "%s is already bound to %s\n",
145585d5e707SKishon Vijay Abraham I 				dwc->gadget.name,
1456747a0a5bSKishon Vijay Abraham I 				dwc->gadget_driver->function);
145785d5e707SKishon Vijay Abraham I 		ret = -EBUSY;
145885d5e707SKishon Vijay Abraham I 		goto err1;
145985d5e707SKishon Vijay Abraham I 	}
146085d5e707SKishon Vijay Abraham I 
146185d5e707SKishon Vijay Abraham I 	dwc->gadget_driver	= driver;
146285d5e707SKishon Vijay Abraham I 
146385d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
146485d5e707SKishon Vijay Abraham I 	reg &= ~(DWC3_DCFG_SPEED_MASK);
146585d5e707SKishon Vijay Abraham I 
146685d5e707SKishon Vijay Abraham I 	/**
146785d5e707SKishon Vijay Abraham I 	 * WORKAROUND: DWC3 revision < 2.20a have an issue
146885d5e707SKishon Vijay Abraham I 	 * which would cause metastability state on Run/Stop
146985d5e707SKishon Vijay Abraham I 	 * bit if we try to force the IP to USB2-only mode.
147085d5e707SKishon Vijay Abraham I 	 *
147185d5e707SKishon Vijay Abraham I 	 * Because of that, we cannot configure the IP to any
147285d5e707SKishon Vijay Abraham I 	 * speed other than the SuperSpeed
147385d5e707SKishon Vijay Abraham I 	 *
147485d5e707SKishon Vijay Abraham I 	 * Refers to:
147585d5e707SKishon Vijay Abraham I 	 *
147685d5e707SKishon Vijay Abraham I 	 * STAR#9000525659: Clock Domain Crossing on DCTL in
147785d5e707SKishon Vijay Abraham I 	 * USB 2.0 Mode
147885d5e707SKishon Vijay Abraham I 	 */
147985d5e707SKishon Vijay Abraham I 	if (dwc->revision < DWC3_REVISION_220A) {
148085d5e707SKishon Vijay Abraham I 		reg |= DWC3_DCFG_SUPERSPEED;
148185d5e707SKishon Vijay Abraham I 	} else {
148285d5e707SKishon Vijay Abraham I 		switch (dwc->maximum_speed) {
148385d5e707SKishon Vijay Abraham I 		case USB_SPEED_LOW:
148485d5e707SKishon Vijay Abraham I 			reg |= DWC3_DSTS_LOWSPEED;
148585d5e707SKishon Vijay Abraham I 			break;
148685d5e707SKishon Vijay Abraham I 		case USB_SPEED_FULL:
148785d5e707SKishon Vijay Abraham I 			reg |= DWC3_DSTS_FULLSPEED1;
148885d5e707SKishon Vijay Abraham I 			break;
148985d5e707SKishon Vijay Abraham I 		case USB_SPEED_HIGH:
149085d5e707SKishon Vijay Abraham I 			reg |= DWC3_DSTS_HIGHSPEED;
149185d5e707SKishon Vijay Abraham I 			break;
149285d5e707SKishon Vijay Abraham I 		case USB_SPEED_SUPER:	/* FALLTHROUGH */
149385d5e707SKishon Vijay Abraham I 		case USB_SPEED_UNKNOWN:	/* FALTHROUGH */
149485d5e707SKishon Vijay Abraham I 		default:
149585d5e707SKishon Vijay Abraham I 			reg |= DWC3_DSTS_SUPERSPEED;
149685d5e707SKishon Vijay Abraham I 		}
149785d5e707SKishon Vijay Abraham I 	}
149885d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
149985d5e707SKishon Vijay Abraham I 
150085d5e707SKishon Vijay Abraham I 	dwc->start_config_issued = false;
150185d5e707SKishon Vijay Abraham I 
150285d5e707SKishon Vijay Abraham I 	/* Start with SuperSpeed Default */
150385d5e707SKishon Vijay Abraham I 	dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
150485d5e707SKishon Vijay Abraham I 
150585d5e707SKishon Vijay Abraham I 	dep = dwc->eps[0];
150685d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
150785d5e707SKishon Vijay Abraham I 			false);
150885d5e707SKishon Vijay Abraham I 	if (ret) {
150985d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
151085d5e707SKishon Vijay Abraham I 		goto err2;
151185d5e707SKishon Vijay Abraham I 	}
151285d5e707SKishon Vijay Abraham I 
151385d5e707SKishon Vijay Abraham I 	dep = dwc->eps[1];
151485d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
151585d5e707SKishon Vijay Abraham I 			false);
151685d5e707SKishon Vijay Abraham I 	if (ret) {
151785d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
151885d5e707SKishon Vijay Abraham I 		goto err3;
151985d5e707SKishon Vijay Abraham I 	}
152085d5e707SKishon Vijay Abraham I 
152185d5e707SKishon Vijay Abraham I 	/* begin to receive SETUP packets */
152285d5e707SKishon Vijay Abraham I 	dwc->ep0state = EP0_SETUP_PHASE;
152385d5e707SKishon Vijay Abraham I 	dwc3_ep0_out_start(dwc);
152485d5e707SKishon Vijay Abraham I 
152585d5e707SKishon Vijay Abraham I 	dwc3_gadget_enable_irq(dwc);
152685d5e707SKishon Vijay Abraham I 
152785d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
152885d5e707SKishon Vijay Abraham I 
152985d5e707SKishon Vijay Abraham I 	return 0;
153085d5e707SKishon Vijay Abraham I 
153185d5e707SKishon Vijay Abraham I err3:
153285d5e707SKishon Vijay Abraham I 	__dwc3_gadget_ep_disable(dwc->eps[0]);
153385d5e707SKishon Vijay Abraham I 
153485d5e707SKishon Vijay Abraham I err2:
153585d5e707SKishon Vijay Abraham I 	dwc->gadget_driver = NULL;
153685d5e707SKishon Vijay Abraham I 
153785d5e707SKishon Vijay Abraham I err1:
153885d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
153985d5e707SKishon Vijay Abraham I 
154085d5e707SKishon Vijay Abraham I 	return ret;
154185d5e707SKishon Vijay Abraham I }
154285d5e707SKishon Vijay Abraham I 
dwc3_gadget_stop(struct usb_gadget * g)154385d5e707SKishon Vijay Abraham I static int dwc3_gadget_stop(struct usb_gadget *g)
154485d5e707SKishon Vijay Abraham I {
154585d5e707SKishon Vijay Abraham I 	struct dwc3		*dwc = gadget_to_dwc(g);
154685d5e707SKishon Vijay Abraham I 	unsigned long		flags;
154785d5e707SKishon Vijay Abraham I 
154885d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
154985d5e707SKishon Vijay Abraham I 
155085d5e707SKishon Vijay Abraham I 	dwc3_gadget_disable_irq(dwc);
155185d5e707SKishon Vijay Abraham I 	__dwc3_gadget_ep_disable(dwc->eps[0]);
155285d5e707SKishon Vijay Abraham I 	__dwc3_gadget_ep_disable(dwc->eps[1]);
155385d5e707SKishon Vijay Abraham I 
155485d5e707SKishon Vijay Abraham I 	dwc->gadget_driver	= NULL;
155585d5e707SKishon Vijay Abraham I 
155685d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
155785d5e707SKishon Vijay Abraham I 
155885d5e707SKishon Vijay Abraham I 	return 0;
155985d5e707SKishon Vijay Abraham I }
156085d5e707SKishon Vijay Abraham I 
156185d5e707SKishon Vijay Abraham I static const struct usb_gadget_ops dwc3_gadget_ops = {
156285d5e707SKishon Vijay Abraham I 	.get_frame		= dwc3_gadget_get_frame,
156385d5e707SKishon Vijay Abraham I 	.wakeup			= dwc3_gadget_wakeup,
156485d5e707SKishon Vijay Abraham I 	.set_selfpowered	= dwc3_gadget_set_selfpowered,
156585d5e707SKishon Vijay Abraham I 	.pullup			= dwc3_gadget_pullup,
156685d5e707SKishon Vijay Abraham I 	.udc_start		= dwc3_gadget_start,
156785d5e707SKishon Vijay Abraham I 	.udc_stop		= dwc3_gadget_stop,
156885d5e707SKishon Vijay Abraham I };
156985d5e707SKishon Vijay Abraham I 
157085d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
157185d5e707SKishon Vijay Abraham I 
dwc3_gadget_init_hw_endpoints(struct dwc3 * dwc,u8 num,u32 direction)157285d5e707SKishon Vijay Abraham I static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
157385d5e707SKishon Vijay Abraham I 		u8 num, u32 direction)
157485d5e707SKishon Vijay Abraham I {
157585d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep;
157685d5e707SKishon Vijay Abraham I 	u8				i;
157785d5e707SKishon Vijay Abraham I 
157885d5e707SKishon Vijay Abraham I 	for (i = 0; i < num; i++) {
157985d5e707SKishon Vijay Abraham I 		u8 epnum = (i << 1) | (!!direction);
158085d5e707SKishon Vijay Abraham I 
158185d5e707SKishon Vijay Abraham I 		dep = kzalloc(sizeof(*dep), GFP_KERNEL);
158285d5e707SKishon Vijay Abraham I 		if (!dep)
158385d5e707SKishon Vijay Abraham I 			return -ENOMEM;
158485d5e707SKishon Vijay Abraham I 
158585d5e707SKishon Vijay Abraham I 		dep->dwc = dwc;
158685d5e707SKishon Vijay Abraham I 		dep->number = epnum;
158785d5e707SKishon Vijay Abraham I 		dep->direction = !!direction;
158885d5e707SKishon Vijay Abraham I 		dwc->eps[epnum] = dep;
158985d5e707SKishon Vijay Abraham I 
159085d5e707SKishon Vijay Abraham I 		snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
159185d5e707SKishon Vijay Abraham I 				(epnum & 1) ? "in" : "out");
159285d5e707SKishon Vijay Abraham I 
159385d5e707SKishon Vijay Abraham I 		dep->endpoint.name = dep->name;
159485d5e707SKishon Vijay Abraham I 
159585d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
159685d5e707SKishon Vijay Abraham I 
159785d5e707SKishon Vijay Abraham I 		if (epnum == 0 || epnum == 1) {
159885d5e707SKishon Vijay Abraham I 			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
159985d5e707SKishon Vijay Abraham I 			dep->endpoint.maxburst = 1;
160085d5e707SKishon Vijay Abraham I 			dep->endpoint.ops = &dwc3_gadget_ep0_ops;
160185d5e707SKishon Vijay Abraham I 			if (!epnum)
160285d5e707SKishon Vijay Abraham I 				dwc->gadget.ep0 = &dep->endpoint;
160385d5e707SKishon Vijay Abraham I 		} else {
160485d5e707SKishon Vijay Abraham I 			int		ret;
160585d5e707SKishon Vijay Abraham I 
1606afa093bfSLukasz Majewski 			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
160785d5e707SKishon Vijay Abraham I 			dep->endpoint.max_streams = 15;
160885d5e707SKishon Vijay Abraham I 			dep->endpoint.ops = &dwc3_gadget_ep_ops;
160985d5e707SKishon Vijay Abraham I 			list_add_tail(&dep->endpoint.ep_list,
161085d5e707SKishon Vijay Abraham I 					&dwc->gadget.ep_list);
161185d5e707SKishon Vijay Abraham I 
161285d5e707SKishon Vijay Abraham I 			ret = dwc3_alloc_trb_pool(dep);
161385d5e707SKishon Vijay Abraham I 			if (ret)
161485d5e707SKishon Vijay Abraham I 				return ret;
161585d5e707SKishon Vijay Abraham I 		}
161685d5e707SKishon Vijay Abraham I 
161785d5e707SKishon Vijay Abraham I 		INIT_LIST_HEAD(&dep->request_list);
161885d5e707SKishon Vijay Abraham I 		INIT_LIST_HEAD(&dep->req_queued);
161985d5e707SKishon Vijay Abraham I 	}
162085d5e707SKishon Vijay Abraham I 
162185d5e707SKishon Vijay Abraham I 	return 0;
162285d5e707SKishon Vijay Abraham I }
162385d5e707SKishon Vijay Abraham I 
dwc3_gadget_init_endpoints(struct dwc3 * dwc)162485d5e707SKishon Vijay Abraham I static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
162585d5e707SKishon Vijay Abraham I {
162685d5e707SKishon Vijay Abraham I 	int				ret;
162785d5e707SKishon Vijay Abraham I 
162885d5e707SKishon Vijay Abraham I 	INIT_LIST_HEAD(&dwc->gadget.ep_list);
162985d5e707SKishon Vijay Abraham I 
163085d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
163185d5e707SKishon Vijay Abraham I 	if (ret < 0) {
163285d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
163385d5e707SKishon Vijay Abraham I 		return ret;
163485d5e707SKishon Vijay Abraham I 	}
163585d5e707SKishon Vijay Abraham I 
163685d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
163785d5e707SKishon Vijay Abraham I 	if (ret < 0) {
163885d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
163985d5e707SKishon Vijay Abraham I 		return ret;
164085d5e707SKishon Vijay Abraham I 	}
164185d5e707SKishon Vijay Abraham I 
164285d5e707SKishon Vijay Abraham I 	return 0;
164385d5e707SKishon Vijay Abraham I }
164485d5e707SKishon Vijay Abraham I 
dwc3_gadget_free_endpoints(struct dwc3 * dwc)164585d5e707SKishon Vijay Abraham I static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
164685d5e707SKishon Vijay Abraham I {
164785d5e707SKishon Vijay Abraham I 	struct dwc3_ep			*dep;
164885d5e707SKishon Vijay Abraham I 	u8				epnum;
164985d5e707SKishon Vijay Abraham I 
165085d5e707SKishon Vijay Abraham I 	for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
165185d5e707SKishon Vijay Abraham I 		dep = dwc->eps[epnum];
165285d5e707SKishon Vijay Abraham I 		if (!dep)
165385d5e707SKishon Vijay Abraham I 			continue;
165485d5e707SKishon Vijay Abraham I 		/*
165585d5e707SKishon Vijay Abraham I 		 * Physical endpoints 0 and 1 are special; they form the
165685d5e707SKishon Vijay Abraham I 		 * bi-directional USB endpoint 0.
165785d5e707SKishon Vijay Abraham I 		 *
165885d5e707SKishon Vijay Abraham I 		 * For those two physical endpoints, we don't allocate a TRB
165985d5e707SKishon Vijay Abraham I 		 * pool nor do we add them the endpoints list. Due to that, we
166085d5e707SKishon Vijay Abraham I 		 * shouldn't do these two operations otherwise we would end up
166185d5e707SKishon Vijay Abraham I 		 * with all sorts of bugs when removing dwc3.ko.
166285d5e707SKishon Vijay Abraham I 		 */
166385d5e707SKishon Vijay Abraham I 		if (epnum != 0 && epnum != 1) {
166485d5e707SKishon Vijay Abraham I 			dwc3_free_trb_pool(dep);
166585d5e707SKishon Vijay Abraham I 			list_del(&dep->endpoint.ep_list);
166685d5e707SKishon Vijay Abraham I 		}
166785d5e707SKishon Vijay Abraham I 
166885d5e707SKishon Vijay Abraham I 		kfree(dep);
166985d5e707SKishon Vijay Abraham I 	}
167085d5e707SKishon Vijay Abraham I }
167185d5e707SKishon Vijay Abraham I 
167285d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
167385d5e707SKishon Vijay Abraham I 
__dwc3_cleanup_done_trbs(struct dwc3 * dwc,struct dwc3_ep * dep,struct dwc3_request * req,struct dwc3_trb * trb,const struct dwc3_event_depevt * event,int status)167485d5e707SKishon Vijay Abraham I static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
167585d5e707SKishon Vijay Abraham I 		struct dwc3_request *req, struct dwc3_trb *trb,
167685d5e707SKishon Vijay Abraham I 		const struct dwc3_event_depevt *event, int status)
167785d5e707SKishon Vijay Abraham I {
167885d5e707SKishon Vijay Abraham I 	unsigned int		count;
167985d5e707SKishon Vijay Abraham I 	unsigned int		s_pkt = 0;
168085d5e707SKishon Vijay Abraham I 	unsigned int		trb_status;
168185d5e707SKishon Vijay Abraham I 
168285d5e707SKishon Vijay Abraham I 	if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
168385d5e707SKishon Vijay Abraham I 		/*
168485d5e707SKishon Vijay Abraham I 		 * We continue despite the error. There is not much we
168585d5e707SKishon Vijay Abraham I 		 * can do. If we don't clean it up we loop forever. If
168685d5e707SKishon Vijay Abraham I 		 * we skip the TRB then it gets overwritten after a
168785d5e707SKishon Vijay Abraham I 		 * while since we use them in a ring buffer. A BUG()
168885d5e707SKishon Vijay Abraham I 		 * would help. Lets hope that if this occurs, someone
168985d5e707SKishon Vijay Abraham I 		 * fixes the root cause instead of looking away :)
169085d5e707SKishon Vijay Abraham I 		 */
169185d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
169285d5e707SKishon Vijay Abraham I 				dep->name, trb);
169385d5e707SKishon Vijay Abraham I 	count = trb->size & DWC3_TRB_SIZE_MASK;
169485d5e707SKishon Vijay Abraham I 
169585d5e707SKishon Vijay Abraham I 	if (dep->direction) {
169685d5e707SKishon Vijay Abraham I 		if (count) {
169785d5e707SKishon Vijay Abraham I 			trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
169885d5e707SKishon Vijay Abraham I 			if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
169985d5e707SKishon Vijay Abraham I 				dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
170085d5e707SKishon Vijay Abraham I 						dep->name);
170185d5e707SKishon Vijay Abraham I 				/*
170285d5e707SKishon Vijay Abraham I 				 * If missed isoc occurred and there is
170385d5e707SKishon Vijay Abraham I 				 * no request queued then issue END
170485d5e707SKishon Vijay Abraham I 				 * TRANSFER, so that core generates
170585d5e707SKishon Vijay Abraham I 				 * next xfernotready and we will issue
170685d5e707SKishon Vijay Abraham I 				 * a fresh START TRANSFER.
170785d5e707SKishon Vijay Abraham I 				 * If there are still queued request
170885d5e707SKishon Vijay Abraham I 				 * then wait, do not issue either END
170985d5e707SKishon Vijay Abraham I 				 * or UPDATE TRANSFER, just attach next
171085d5e707SKishon Vijay Abraham I 				 * request in request_list during
171185d5e707SKishon Vijay Abraham I 				 * giveback.If any future queued request
171285d5e707SKishon Vijay Abraham I 				 * is successfully transferred then we
171385d5e707SKishon Vijay Abraham I 				 * will issue UPDATE TRANSFER for all
171485d5e707SKishon Vijay Abraham I 				 * request in the request_list.
171585d5e707SKishon Vijay Abraham I 				 */
171685d5e707SKishon Vijay Abraham I 				dep->flags |= DWC3_EP_MISSED_ISOC;
171785d5e707SKishon Vijay Abraham I 			} else {
171885d5e707SKishon Vijay Abraham I 				dev_err(dwc->dev, "incomplete IN transfer %s\n",
171985d5e707SKishon Vijay Abraham I 						dep->name);
172085d5e707SKishon Vijay Abraham I 				status = -ECONNRESET;
172185d5e707SKishon Vijay Abraham I 			}
172285d5e707SKishon Vijay Abraham I 		} else {
172385d5e707SKishon Vijay Abraham I 			dep->flags &= ~DWC3_EP_MISSED_ISOC;
172485d5e707SKishon Vijay Abraham I 		}
172585d5e707SKishon Vijay Abraham I 	} else {
172685d5e707SKishon Vijay Abraham I 		if (count && (event->status & DEPEVT_STATUS_SHORT))
172785d5e707SKishon Vijay Abraham I 			s_pkt = 1;
172885d5e707SKishon Vijay Abraham I 	}
172985d5e707SKishon Vijay Abraham I 
173085d5e707SKishon Vijay Abraham I 	/*
173185d5e707SKishon Vijay Abraham I 	 * We assume here we will always receive the entire data block
173285d5e707SKishon Vijay Abraham I 	 * which we should receive. Meaning, if we program RX to
173385d5e707SKishon Vijay Abraham I 	 * receive 4K but we receive only 2K, we assume that's all we
173485d5e707SKishon Vijay Abraham I 	 * should receive and we simply bounce the request back to the
173585d5e707SKishon Vijay Abraham I 	 * gadget driver for further processing.
173685d5e707SKishon Vijay Abraham I 	 */
173785d5e707SKishon Vijay Abraham I 	req->request.actual += req->request.length - count;
173885d5e707SKishon Vijay Abraham I 	if (s_pkt)
173985d5e707SKishon Vijay Abraham I 		return 1;
174085d5e707SKishon Vijay Abraham I 	if ((event->status & DEPEVT_STATUS_LST) &&
174185d5e707SKishon Vijay Abraham I 			(trb->ctrl & (DWC3_TRB_CTRL_LST |
174285d5e707SKishon Vijay Abraham I 				DWC3_TRB_CTRL_HWO)))
174385d5e707SKishon Vijay Abraham I 		return 1;
174485d5e707SKishon Vijay Abraham I 	if ((event->status & DEPEVT_STATUS_IOC) &&
174585d5e707SKishon Vijay Abraham I 			(trb->ctrl & DWC3_TRB_CTRL_IOC))
174685d5e707SKishon Vijay Abraham I 		return 1;
174785d5e707SKishon Vijay Abraham I 	return 0;
174885d5e707SKishon Vijay Abraham I }
174985d5e707SKishon Vijay Abraham I 
dwc3_cleanup_done_reqs(struct dwc3 * dwc,struct dwc3_ep * dep,const struct dwc3_event_depevt * event,int status)175085d5e707SKishon Vijay Abraham I static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
175185d5e707SKishon Vijay Abraham I 		const struct dwc3_event_depevt *event, int status)
175285d5e707SKishon Vijay Abraham I {
175385d5e707SKishon Vijay Abraham I 	struct dwc3_request	*req;
175485d5e707SKishon Vijay Abraham I 	struct dwc3_trb		*trb;
175585d5e707SKishon Vijay Abraham I 	unsigned int		slot;
175685d5e707SKishon Vijay Abraham I 
175785d5e707SKishon Vijay Abraham I 	req = next_request(&dep->req_queued);
175885d5e707SKishon Vijay Abraham I 	if (!req) {
175985d5e707SKishon Vijay Abraham I 		WARN_ON_ONCE(1);
176085d5e707SKishon Vijay Abraham I 		return 1;
176185d5e707SKishon Vijay Abraham I 	}
1762747a0a5bSKishon Vijay Abraham I 
1763747a0a5bSKishon Vijay Abraham I 	slot = req->start_slot;
176485d5e707SKishon Vijay Abraham I 	if ((slot == DWC3_TRB_NUM - 1) &&
176585d5e707SKishon Vijay Abraham I 	    usb_endpoint_xfer_isoc(dep->endpoint.desc))
176685d5e707SKishon Vijay Abraham I 		slot++;
176785d5e707SKishon Vijay Abraham I 	slot %= DWC3_TRB_NUM;
176885d5e707SKishon Vijay Abraham I 	trb = &dep->trb_pool[slot];
176985d5e707SKishon Vijay Abraham I 
1770b7bf4a95SPhilipp Tomsich 	dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
17713621b3b8SLukasz Majewski 	__dwc3_cleanup_done_trbs(dwc, dep, req, trb, event, status);
177285d5e707SKishon Vijay Abraham I 	dwc3_gadget_giveback(dep, req, status);
177385d5e707SKishon Vijay Abraham I 
177485d5e707SKishon Vijay Abraham I 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
177585d5e707SKishon Vijay Abraham I 			list_empty(&dep->req_queued)) {
177685d5e707SKishon Vijay Abraham I 		if (list_empty(&dep->request_list)) {
177785d5e707SKishon Vijay Abraham I 			/*
177885d5e707SKishon Vijay Abraham I 			 * If there is no entry in request list then do
177985d5e707SKishon Vijay Abraham I 			 * not issue END TRANSFER now. Just set PENDING
178085d5e707SKishon Vijay Abraham I 			 * flag, so that END TRANSFER is issued when an
178185d5e707SKishon Vijay Abraham I 			 * entry is added into request list.
178285d5e707SKishon Vijay Abraham I 			 */
178385d5e707SKishon Vijay Abraham I 			dep->flags = DWC3_EP_PENDING_REQUEST;
178485d5e707SKishon Vijay Abraham I 		} else {
178585d5e707SKishon Vijay Abraham I 			dwc3_stop_active_transfer(dwc, dep->number, true);
178685d5e707SKishon Vijay Abraham I 			dep->flags = DWC3_EP_ENABLED;
178785d5e707SKishon Vijay Abraham I 		}
178885d5e707SKishon Vijay Abraham I 		return 1;
178985d5e707SKishon Vijay Abraham I 	}
179085d5e707SKishon Vijay Abraham I 
179185d5e707SKishon Vijay Abraham I 	return 1;
179285d5e707SKishon Vijay Abraham I }
179385d5e707SKishon Vijay Abraham I 
dwc3_endpoint_transfer_complete(struct dwc3 * dwc,struct dwc3_ep * dep,const struct dwc3_event_depevt * event)179485d5e707SKishon Vijay Abraham I static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
179585d5e707SKishon Vijay Abraham I 		struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
179685d5e707SKishon Vijay Abraham I {
179785d5e707SKishon Vijay Abraham I 	unsigned		status = 0;
179885d5e707SKishon Vijay Abraham I 	int			clean_busy;
179985d5e707SKishon Vijay Abraham I 
180085d5e707SKishon Vijay Abraham I 	if (event->status & DEPEVT_STATUS_BUSERR)
180185d5e707SKishon Vijay Abraham I 		status = -ECONNRESET;
180285d5e707SKishon Vijay Abraham I 
180385d5e707SKishon Vijay Abraham I 	clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
180485d5e707SKishon Vijay Abraham I 	if (clean_busy)
180585d5e707SKishon Vijay Abraham I 		dep->flags &= ~DWC3_EP_BUSY;
180685d5e707SKishon Vijay Abraham I 
180785d5e707SKishon Vijay Abraham I 	/*
180885d5e707SKishon Vijay Abraham I 	 * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
180985d5e707SKishon Vijay Abraham I 	 * See dwc3_gadget_linksts_change_interrupt() for 1st half.
181085d5e707SKishon Vijay Abraham I 	 */
181185d5e707SKishon Vijay Abraham I 	if (dwc->revision < DWC3_REVISION_183A) {
181285d5e707SKishon Vijay Abraham I 		u32		reg;
181385d5e707SKishon Vijay Abraham I 		int		i;
181485d5e707SKishon Vijay Abraham I 
181585d5e707SKishon Vijay Abraham I 		for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
181685d5e707SKishon Vijay Abraham I 			dep = dwc->eps[i];
181785d5e707SKishon Vijay Abraham I 
181885d5e707SKishon Vijay Abraham I 			if (!(dep->flags & DWC3_EP_ENABLED))
181985d5e707SKishon Vijay Abraham I 				continue;
182085d5e707SKishon Vijay Abraham I 
182185d5e707SKishon Vijay Abraham I 			if (!list_empty(&dep->req_queued))
182285d5e707SKishon Vijay Abraham I 				return;
182385d5e707SKishon Vijay Abraham I 		}
182485d5e707SKishon Vijay Abraham I 
182585d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
182685d5e707SKishon Vijay Abraham I 		reg |= dwc->u1u2;
182785d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
182885d5e707SKishon Vijay Abraham I 
182985d5e707SKishon Vijay Abraham I 		dwc->u1u2 = 0;
183085d5e707SKishon Vijay Abraham I 	}
183185d5e707SKishon Vijay Abraham I }
183285d5e707SKishon Vijay Abraham I 
dwc3_endpoint_interrupt(struct dwc3 * dwc,const struct dwc3_event_depevt * event)183385d5e707SKishon Vijay Abraham I static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
183485d5e707SKishon Vijay Abraham I 		const struct dwc3_event_depevt *event)
183585d5e707SKishon Vijay Abraham I {
183685d5e707SKishon Vijay Abraham I 	struct dwc3_ep		*dep;
183785d5e707SKishon Vijay Abraham I 	u8			epnum = event->endpoint_number;
183885d5e707SKishon Vijay Abraham I 
183985d5e707SKishon Vijay Abraham I 	dep = dwc->eps[epnum];
184085d5e707SKishon Vijay Abraham I 
184185d5e707SKishon Vijay Abraham I 	if (!(dep->flags & DWC3_EP_ENABLED))
184285d5e707SKishon Vijay Abraham I 		return;
184385d5e707SKishon Vijay Abraham I 
184485d5e707SKishon Vijay Abraham I 	if (epnum == 0 || epnum == 1) {
184585d5e707SKishon Vijay Abraham I 		dwc3_ep0_interrupt(dwc, event);
184685d5e707SKishon Vijay Abraham I 		return;
184785d5e707SKishon Vijay Abraham I 	}
184885d5e707SKishon Vijay Abraham I 
184985d5e707SKishon Vijay Abraham I 	switch (event->endpoint_event) {
185085d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_XFERCOMPLETE:
185185d5e707SKishon Vijay Abraham I 		dep->resource_index = 0;
185285d5e707SKishon Vijay Abraham I 
185385d5e707SKishon Vijay Abraham I 		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
185485d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
185585d5e707SKishon Vijay Abraham I 					dep->name);
185685d5e707SKishon Vijay Abraham I 			return;
185785d5e707SKishon Vijay Abraham I 		}
185885d5e707SKishon Vijay Abraham I 
185985d5e707SKishon Vijay Abraham I 		dwc3_endpoint_transfer_complete(dwc, dep, event);
186085d5e707SKishon Vijay Abraham I 		break;
186185d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_XFERINPROGRESS:
186285d5e707SKishon Vijay Abraham I 		dwc3_endpoint_transfer_complete(dwc, dep, event);
186385d5e707SKishon Vijay Abraham I 		break;
186485d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_XFERNOTREADY:
186585d5e707SKishon Vijay Abraham I 		if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
186685d5e707SKishon Vijay Abraham I 			dwc3_gadget_start_isoc(dwc, dep, event);
186785d5e707SKishon Vijay Abraham I 		} else {
186885d5e707SKishon Vijay Abraham I 			int ret;
186985d5e707SKishon Vijay Abraham I 
187085d5e707SKishon Vijay Abraham I 			dev_vdbg(dwc->dev, "%s: reason %s\n",
187185d5e707SKishon Vijay Abraham I 					dep->name, event->status &
187285d5e707SKishon Vijay Abraham I 					DEPEVT_STATUS_TRANSFER_ACTIVE
187385d5e707SKishon Vijay Abraham I 					? "Transfer Active"
187485d5e707SKishon Vijay Abraham I 					: "Transfer Not Active");
187585d5e707SKishon Vijay Abraham I 
187685d5e707SKishon Vijay Abraham I 			ret = __dwc3_gadget_kick_transfer(dep, 0, 1);
187785d5e707SKishon Vijay Abraham I 			if (!ret || ret == -EBUSY)
187885d5e707SKishon Vijay Abraham I 				return;
187985d5e707SKishon Vijay Abraham I 
188085d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
188185d5e707SKishon Vijay Abraham I 					dep->name);
188285d5e707SKishon Vijay Abraham I 		}
188385d5e707SKishon Vijay Abraham I 
188485d5e707SKishon Vijay Abraham I 		break;
188585d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_STREAMEVT:
188685d5e707SKishon Vijay Abraham I 		if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) {
188785d5e707SKishon Vijay Abraham I 			dev_err(dwc->dev, "Stream event for non-Bulk %s\n",
188885d5e707SKishon Vijay Abraham I 					dep->name);
188985d5e707SKishon Vijay Abraham I 			return;
189085d5e707SKishon Vijay Abraham I 		}
189185d5e707SKishon Vijay Abraham I 
189285d5e707SKishon Vijay Abraham I 		switch (event->status) {
189385d5e707SKishon Vijay Abraham I 		case DEPEVT_STREAMEVT_FOUND:
189485d5e707SKishon Vijay Abraham I 			dev_vdbg(dwc->dev, "Stream %d found and started\n",
189585d5e707SKishon Vijay Abraham I 					event->parameters);
189685d5e707SKishon Vijay Abraham I 
189785d5e707SKishon Vijay Abraham I 			break;
189885d5e707SKishon Vijay Abraham I 		case DEPEVT_STREAMEVT_NOTFOUND:
189985d5e707SKishon Vijay Abraham I 			/* FALLTHROUGH */
190085d5e707SKishon Vijay Abraham I 		default:
190185d5e707SKishon Vijay Abraham I 			dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
190285d5e707SKishon Vijay Abraham I 		}
190385d5e707SKishon Vijay Abraham I 		break;
190485d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_RXTXFIFOEVT:
190585d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
190685d5e707SKishon Vijay Abraham I 		break;
190785d5e707SKishon Vijay Abraham I 	case DWC3_DEPEVT_EPCMDCMPLT:
190885d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
190985d5e707SKishon Vijay Abraham I 		break;
191085d5e707SKishon Vijay Abraham I 	}
191185d5e707SKishon Vijay Abraham I }
191285d5e707SKishon Vijay Abraham I 
dwc3_disconnect_gadget(struct dwc3 * dwc)191385d5e707SKishon Vijay Abraham I static void dwc3_disconnect_gadget(struct dwc3 *dwc)
191485d5e707SKishon Vijay Abraham I {
191585d5e707SKishon Vijay Abraham I 	if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
191685d5e707SKishon Vijay Abraham I 		spin_unlock(&dwc->lock);
191785d5e707SKishon Vijay Abraham I 		dwc->gadget_driver->disconnect(&dwc->gadget);
191885d5e707SKishon Vijay Abraham I 		spin_lock(&dwc->lock);
191985d5e707SKishon Vijay Abraham I 	}
192085d5e707SKishon Vijay Abraham I }
192185d5e707SKishon Vijay Abraham I 
dwc3_suspend_gadget(struct dwc3 * dwc)192285d5e707SKishon Vijay Abraham I static void dwc3_suspend_gadget(struct dwc3 *dwc)
192385d5e707SKishon Vijay Abraham I {
192485d5e707SKishon Vijay Abraham I 	if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
192585d5e707SKishon Vijay Abraham I 		spin_unlock(&dwc->lock);
192685d5e707SKishon Vijay Abraham I 		dwc->gadget_driver->suspend(&dwc->gadget);
192785d5e707SKishon Vijay Abraham I 		spin_lock(&dwc->lock);
192885d5e707SKishon Vijay Abraham I 	}
192985d5e707SKishon Vijay Abraham I }
193085d5e707SKishon Vijay Abraham I 
dwc3_resume_gadget(struct dwc3 * dwc)193185d5e707SKishon Vijay Abraham I static void dwc3_resume_gadget(struct dwc3 *dwc)
193285d5e707SKishon Vijay Abraham I {
193385d5e707SKishon Vijay Abraham I 	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
193485d5e707SKishon Vijay Abraham I 		spin_unlock(&dwc->lock);
193585d5e707SKishon Vijay Abraham I 		dwc->gadget_driver->resume(&dwc->gadget);
193685d5e707SKishon Vijay Abraham I 	}
193785d5e707SKishon Vijay Abraham I }
193885d5e707SKishon Vijay Abraham I 
dwc3_reset_gadget(struct dwc3 * dwc)193985d5e707SKishon Vijay Abraham I static void dwc3_reset_gadget(struct dwc3 *dwc)
194085d5e707SKishon Vijay Abraham I {
194185d5e707SKishon Vijay Abraham I 	if (!dwc->gadget_driver)
194285d5e707SKishon Vijay Abraham I 		return;
194385d5e707SKishon Vijay Abraham I 
194485d5e707SKishon Vijay Abraham I 	if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
194585d5e707SKishon Vijay Abraham I 		spin_unlock(&dwc->lock);
194685d5e707SKishon Vijay Abraham I 		usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
194785d5e707SKishon Vijay Abraham I 		spin_lock(&dwc->lock);
194885d5e707SKishon Vijay Abraham I 	}
194985d5e707SKishon Vijay Abraham I }
195085d5e707SKishon Vijay Abraham I 
dwc3_stop_active_transfer(struct dwc3 * dwc,u32 epnum,bool force)195185d5e707SKishon Vijay Abraham I static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
195285d5e707SKishon Vijay Abraham I {
195385d5e707SKishon Vijay Abraham I 	struct dwc3_ep *dep;
195485d5e707SKishon Vijay Abraham I 	struct dwc3_gadget_ep_cmd_params params;
195585d5e707SKishon Vijay Abraham I 	u32 cmd;
195685d5e707SKishon Vijay Abraham I 	int ret;
195785d5e707SKishon Vijay Abraham I 
195885d5e707SKishon Vijay Abraham I 	dep = dwc->eps[epnum];
195985d5e707SKishon Vijay Abraham I 
196085d5e707SKishon Vijay Abraham I 	if (!dep->resource_index)
196185d5e707SKishon Vijay Abraham I 		return;
196285d5e707SKishon Vijay Abraham I 
196385d5e707SKishon Vijay Abraham I 	/*
196485d5e707SKishon Vijay Abraham I 	 * NOTICE: We are violating what the Databook says about the
196585d5e707SKishon Vijay Abraham I 	 * EndTransfer command. Ideally we would _always_ wait for the
196685d5e707SKishon Vijay Abraham I 	 * EndTransfer Command Completion IRQ, but that's causing too
196785d5e707SKishon Vijay Abraham I 	 * much trouble synchronizing between us and gadget driver.
196885d5e707SKishon Vijay Abraham I 	 *
196985d5e707SKishon Vijay Abraham I 	 * We have discussed this with the IP Provider and it was
197085d5e707SKishon Vijay Abraham I 	 * suggested to giveback all requests here, but give HW some
197185d5e707SKishon Vijay Abraham I 	 * extra time to synchronize with the interconnect. We're using
197285d5e707SKishon Vijay Abraham I 	 * an arbitraty 100us delay for that.
197385d5e707SKishon Vijay Abraham I 	 *
197485d5e707SKishon Vijay Abraham I 	 * Note also that a similar handling was tested by Synopsys
197585d5e707SKishon Vijay Abraham I 	 * (thanks a lot Paul) and nothing bad has come out of it.
197685d5e707SKishon Vijay Abraham I 	 * In short, what we're doing is:
197785d5e707SKishon Vijay Abraham I 	 *
197885d5e707SKishon Vijay Abraham I 	 * - Issue EndTransfer WITH CMDIOC bit set
197985d5e707SKishon Vijay Abraham I 	 * - Wait 100us
198085d5e707SKishon Vijay Abraham I 	 */
198185d5e707SKishon Vijay Abraham I 
198285d5e707SKishon Vijay Abraham I 	cmd = DWC3_DEPCMD_ENDTRANSFER;
198385d5e707SKishon Vijay Abraham I 	cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
198485d5e707SKishon Vijay Abraham I 	cmd |= DWC3_DEPCMD_CMDIOC;
198585d5e707SKishon Vijay Abraham I 	cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
198685d5e707SKishon Vijay Abraham I 	memset(&params, 0, sizeof(params));
198785d5e707SKishon Vijay Abraham I 	ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
198885d5e707SKishon Vijay Abraham I 	WARN_ON_ONCE(ret);
198985d5e707SKishon Vijay Abraham I 	dep->resource_index = 0;
199085d5e707SKishon Vijay Abraham I 	dep->flags &= ~DWC3_EP_BUSY;
199185d5e707SKishon Vijay Abraham I 	udelay(100);
199285d5e707SKishon Vijay Abraham I }
199385d5e707SKishon Vijay Abraham I 
dwc3_stop_active_transfers(struct dwc3 * dwc)199485d5e707SKishon Vijay Abraham I static void dwc3_stop_active_transfers(struct dwc3 *dwc)
199585d5e707SKishon Vijay Abraham I {
199685d5e707SKishon Vijay Abraham I 	u32 epnum;
199785d5e707SKishon Vijay Abraham I 
199885d5e707SKishon Vijay Abraham I 	for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
199985d5e707SKishon Vijay Abraham I 		struct dwc3_ep *dep;
200085d5e707SKishon Vijay Abraham I 
200185d5e707SKishon Vijay Abraham I 		dep = dwc->eps[epnum];
200285d5e707SKishon Vijay Abraham I 		if (!dep)
200385d5e707SKishon Vijay Abraham I 			continue;
200485d5e707SKishon Vijay Abraham I 
200585d5e707SKishon Vijay Abraham I 		if (!(dep->flags & DWC3_EP_ENABLED))
200685d5e707SKishon Vijay Abraham I 			continue;
200785d5e707SKishon Vijay Abraham I 
200885d5e707SKishon Vijay Abraham I 		dwc3_remove_requests(dwc, dep);
200985d5e707SKishon Vijay Abraham I 	}
201085d5e707SKishon Vijay Abraham I }
201185d5e707SKishon Vijay Abraham I 
dwc3_clear_stall_all_ep(struct dwc3 * dwc)201285d5e707SKishon Vijay Abraham I static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
201385d5e707SKishon Vijay Abraham I {
201485d5e707SKishon Vijay Abraham I 	u32 epnum;
201585d5e707SKishon Vijay Abraham I 
201685d5e707SKishon Vijay Abraham I 	for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
201785d5e707SKishon Vijay Abraham I 		struct dwc3_ep *dep;
201885d5e707SKishon Vijay Abraham I 		struct dwc3_gadget_ep_cmd_params params;
201985d5e707SKishon Vijay Abraham I 		int ret;
202085d5e707SKishon Vijay Abraham I 
202185d5e707SKishon Vijay Abraham I 		dep = dwc->eps[epnum];
202285d5e707SKishon Vijay Abraham I 		if (!dep)
202385d5e707SKishon Vijay Abraham I 			continue;
202485d5e707SKishon Vijay Abraham I 
202585d5e707SKishon Vijay Abraham I 		if (!(dep->flags & DWC3_EP_STALL))
202685d5e707SKishon Vijay Abraham I 			continue;
202785d5e707SKishon Vijay Abraham I 
202885d5e707SKishon Vijay Abraham I 		dep->flags &= ~DWC3_EP_STALL;
202985d5e707SKishon Vijay Abraham I 
203085d5e707SKishon Vijay Abraham I 		memset(&params, 0, sizeof(params));
203185d5e707SKishon Vijay Abraham I 		ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
203285d5e707SKishon Vijay Abraham I 				DWC3_DEPCMD_CLEARSTALL, &params);
203385d5e707SKishon Vijay Abraham I 		WARN_ON_ONCE(ret);
203485d5e707SKishon Vijay Abraham I 	}
203585d5e707SKishon Vijay Abraham I }
203685d5e707SKishon Vijay Abraham I 
dwc3_gadget_disconnect_interrupt(struct dwc3 * dwc)203785d5e707SKishon Vijay Abraham I static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
203885d5e707SKishon Vijay Abraham I {
203985d5e707SKishon Vijay Abraham I 	int			reg;
204085d5e707SKishon Vijay Abraham I 
204185d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
204285d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DCTL_INITU1ENA;
204385d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
204485d5e707SKishon Vijay Abraham I 
204585d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DCTL_INITU2ENA;
204685d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
204785d5e707SKishon Vijay Abraham I 
204885d5e707SKishon Vijay Abraham I 	dwc3_disconnect_gadget(dwc);
204985d5e707SKishon Vijay Abraham I 	dwc->start_config_issued = false;
205085d5e707SKishon Vijay Abraham I 
205185d5e707SKishon Vijay Abraham I 	dwc->gadget.speed = USB_SPEED_UNKNOWN;
205285d5e707SKishon Vijay Abraham I 	dwc->setup_packet_pending = false;
205385d5e707SKishon Vijay Abraham I 	usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
205485d5e707SKishon Vijay Abraham I }
205585d5e707SKishon Vijay Abraham I 
dwc3_gadget_reset_interrupt(struct dwc3 * dwc)205685d5e707SKishon Vijay Abraham I static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
205785d5e707SKishon Vijay Abraham I {
205885d5e707SKishon Vijay Abraham I 	u32			reg;
205985d5e707SKishon Vijay Abraham I 
206085d5e707SKishon Vijay Abraham I 	/*
206185d5e707SKishon Vijay Abraham I 	 * WORKAROUND: DWC3 revisions <1.88a have an issue which
206285d5e707SKishon Vijay Abraham I 	 * would cause a missing Disconnect Event if there's a
206385d5e707SKishon Vijay Abraham I 	 * pending Setup Packet in the FIFO.
206485d5e707SKishon Vijay Abraham I 	 *
206585d5e707SKishon Vijay Abraham I 	 * There's no suggested workaround on the official Bug
206685d5e707SKishon Vijay Abraham I 	 * report, which states that "unless the driver/application
206785d5e707SKishon Vijay Abraham I 	 * is doing any special handling of a disconnect event,
206885d5e707SKishon Vijay Abraham I 	 * there is no functional issue".
206985d5e707SKishon Vijay Abraham I 	 *
207085d5e707SKishon Vijay Abraham I 	 * Unfortunately, it turns out that we _do_ some special
207185d5e707SKishon Vijay Abraham I 	 * handling of a disconnect event, namely complete all
207285d5e707SKishon Vijay Abraham I 	 * pending transfers, notify gadget driver of the
207385d5e707SKishon Vijay Abraham I 	 * disconnection, and so on.
207485d5e707SKishon Vijay Abraham I 	 *
207585d5e707SKishon Vijay Abraham I 	 * Our suggested workaround is to follow the Disconnect
207685d5e707SKishon Vijay Abraham I 	 * Event steps here, instead, based on a setup_packet_pending
207785d5e707SKishon Vijay Abraham I 	 * flag. Such flag gets set whenever we have a XferNotReady
207885d5e707SKishon Vijay Abraham I 	 * event on EP0 and gets cleared on XferComplete for the
207985d5e707SKishon Vijay Abraham I 	 * same endpoint.
208085d5e707SKishon Vijay Abraham I 	 *
208185d5e707SKishon Vijay Abraham I 	 * Refers to:
208285d5e707SKishon Vijay Abraham I 	 *
208385d5e707SKishon Vijay Abraham I 	 * STAR#9000466709: RTL: Device : Disconnect event not
208485d5e707SKishon Vijay Abraham I 	 * generated if setup packet pending in FIFO
208585d5e707SKishon Vijay Abraham I 	 */
208685d5e707SKishon Vijay Abraham I 	if (dwc->revision < DWC3_REVISION_188A) {
208785d5e707SKishon Vijay Abraham I 		if (dwc->setup_packet_pending)
208885d5e707SKishon Vijay Abraham I 			dwc3_gadget_disconnect_interrupt(dwc);
208985d5e707SKishon Vijay Abraham I 	}
209085d5e707SKishon Vijay Abraham I 
209185d5e707SKishon Vijay Abraham I 	dwc3_reset_gadget(dwc);
209285d5e707SKishon Vijay Abraham I 
209385d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
209485d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_DCTL_TSTCTRL_MASK;
209585d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
209685d5e707SKishon Vijay Abraham I 	dwc->test_mode = false;
209785d5e707SKishon Vijay Abraham I 
209885d5e707SKishon Vijay Abraham I 	dwc3_stop_active_transfers(dwc);
209985d5e707SKishon Vijay Abraham I 	dwc3_clear_stall_all_ep(dwc);
210085d5e707SKishon Vijay Abraham I 	dwc->start_config_issued = false;
210185d5e707SKishon Vijay Abraham I 
210285d5e707SKishon Vijay Abraham I 	/* Reset device address to zero */
210385d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
210485d5e707SKishon Vijay Abraham I 	reg &= ~(DWC3_DCFG_DEVADDR_MASK);
210585d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
210685d5e707SKishon Vijay Abraham I }
210785d5e707SKishon Vijay Abraham I 
dwc3_update_ram_clk_sel(struct dwc3 * dwc,u32 speed)210885d5e707SKishon Vijay Abraham I static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
210985d5e707SKishon Vijay Abraham I {
211085d5e707SKishon Vijay Abraham I 	u32 reg;
211185d5e707SKishon Vijay Abraham I 	u32 usb30_clock = DWC3_GCTL_CLK_BUS;
211285d5e707SKishon Vijay Abraham I 
211385d5e707SKishon Vijay Abraham I 	/*
211485d5e707SKishon Vijay Abraham I 	 * We change the clock only at SS but I dunno why I would want to do
211585d5e707SKishon Vijay Abraham I 	 * this. Maybe it becomes part of the power saving plan.
211685d5e707SKishon Vijay Abraham I 	 */
211785d5e707SKishon Vijay Abraham I 
211885d5e707SKishon Vijay Abraham I 	if (speed != DWC3_DSTS_SUPERSPEED)
211985d5e707SKishon Vijay Abraham I 		return;
212085d5e707SKishon Vijay Abraham I 
212185d5e707SKishon Vijay Abraham I 	/*
212285d5e707SKishon Vijay Abraham I 	 * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
212385d5e707SKishon Vijay Abraham I 	 * each time on Connect Done.
212485d5e707SKishon Vijay Abraham I 	 */
212585d5e707SKishon Vijay Abraham I 	if (!usb30_clock)
212685d5e707SKishon Vijay Abraham I 		return;
212785d5e707SKishon Vijay Abraham I 
212885d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
212985d5e707SKishon Vijay Abraham I 	reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock);
213085d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_GCTL, reg);
213185d5e707SKishon Vijay Abraham I }
213285d5e707SKishon Vijay Abraham I 
dwc3_gadget_conndone_interrupt(struct dwc3 * dwc)213385d5e707SKishon Vijay Abraham I static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
213485d5e707SKishon Vijay Abraham I {
213585d5e707SKishon Vijay Abraham I 	struct dwc3_ep		*dep;
213685d5e707SKishon Vijay Abraham I 	int			ret;
213785d5e707SKishon Vijay Abraham I 	u32			reg;
213885d5e707SKishon Vijay Abraham I 	u8			speed;
213985d5e707SKishon Vijay Abraham I 
214085d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
214185d5e707SKishon Vijay Abraham I 	speed = reg & DWC3_DSTS_CONNECTSPD;
214285d5e707SKishon Vijay Abraham I 	dwc->speed = speed;
214385d5e707SKishon Vijay Abraham I 
214485d5e707SKishon Vijay Abraham I 	dwc3_update_ram_clk_sel(dwc, speed);
214585d5e707SKishon Vijay Abraham I 
214685d5e707SKishon Vijay Abraham I 	switch (speed) {
214785d5e707SKishon Vijay Abraham I 	case DWC3_DCFG_SUPERSPEED:
214885d5e707SKishon Vijay Abraham I 		/*
214985d5e707SKishon Vijay Abraham I 		 * WORKAROUND: DWC3 revisions <1.90a have an issue which
215085d5e707SKishon Vijay Abraham I 		 * would cause a missing USB3 Reset event.
215185d5e707SKishon Vijay Abraham I 		 *
215285d5e707SKishon Vijay Abraham I 		 * In such situations, we should force a USB3 Reset
215385d5e707SKishon Vijay Abraham I 		 * event by calling our dwc3_gadget_reset_interrupt()
215485d5e707SKishon Vijay Abraham I 		 * routine.
215585d5e707SKishon Vijay Abraham I 		 *
215685d5e707SKishon Vijay Abraham I 		 * Refers to:
215785d5e707SKishon Vijay Abraham I 		 *
215885d5e707SKishon Vijay Abraham I 		 * STAR#9000483510: RTL: SS : USB3 reset event may
215985d5e707SKishon Vijay Abraham I 		 * not be generated always when the link enters poll
216085d5e707SKishon Vijay Abraham I 		 */
216185d5e707SKishon Vijay Abraham I 		if (dwc->revision < DWC3_REVISION_190A)
216285d5e707SKishon Vijay Abraham I 			dwc3_gadget_reset_interrupt(dwc);
216385d5e707SKishon Vijay Abraham I 
216485d5e707SKishon Vijay Abraham I 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
216585d5e707SKishon Vijay Abraham I 		dwc->gadget.ep0->maxpacket = 512;
216685d5e707SKishon Vijay Abraham I 		dwc->gadget.speed = USB_SPEED_SUPER;
216785d5e707SKishon Vijay Abraham I 		break;
216885d5e707SKishon Vijay Abraham I 	case DWC3_DCFG_HIGHSPEED:
216985d5e707SKishon Vijay Abraham I 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
217085d5e707SKishon Vijay Abraham I 		dwc->gadget.ep0->maxpacket = 64;
217185d5e707SKishon Vijay Abraham I 		dwc->gadget.speed = USB_SPEED_HIGH;
217285d5e707SKishon Vijay Abraham I 		break;
217385d5e707SKishon Vijay Abraham I 	case DWC3_DCFG_FULLSPEED2:
217485d5e707SKishon Vijay Abraham I 	case DWC3_DCFG_FULLSPEED1:
217585d5e707SKishon Vijay Abraham I 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
217685d5e707SKishon Vijay Abraham I 		dwc->gadget.ep0->maxpacket = 64;
217785d5e707SKishon Vijay Abraham I 		dwc->gadget.speed = USB_SPEED_FULL;
217885d5e707SKishon Vijay Abraham I 		break;
217985d5e707SKishon Vijay Abraham I 	case DWC3_DCFG_LOWSPEED:
218085d5e707SKishon Vijay Abraham I 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
218185d5e707SKishon Vijay Abraham I 		dwc->gadget.ep0->maxpacket = 8;
218285d5e707SKishon Vijay Abraham I 		dwc->gadget.speed = USB_SPEED_LOW;
218385d5e707SKishon Vijay Abraham I 		break;
218485d5e707SKishon Vijay Abraham I 	}
218585d5e707SKishon Vijay Abraham I 
218685d5e707SKishon Vijay Abraham I 	/* Enable USB2 LPM Capability */
218785d5e707SKishon Vijay Abraham I 
218885d5e707SKishon Vijay Abraham I 	if ((dwc->revision > DWC3_REVISION_194A)
218985d5e707SKishon Vijay Abraham I 			&& (speed != DWC3_DCFG_SUPERSPEED)) {
219085d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DCFG);
219185d5e707SKishon Vijay Abraham I 		reg |= DWC3_DCFG_LPM_CAP;
219285d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DCFG, reg);
219385d5e707SKishon Vijay Abraham I 
219485d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
219585d5e707SKishon Vijay Abraham I 		reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
219685d5e707SKishon Vijay Abraham I 
219785d5e707SKishon Vijay Abraham I 		reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
219885d5e707SKishon Vijay Abraham I 
219985d5e707SKishon Vijay Abraham I 		/*
220085d5e707SKishon Vijay Abraham I 		 * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
220185d5e707SKishon Vijay Abraham I 		 * DCFG.LPMCap is set, core responses with an ACK and the
220285d5e707SKishon Vijay Abraham I 		 * BESL value in the LPM token is less than or equal to LPM
220385d5e707SKishon Vijay Abraham I 		 * NYET threshold.
220485d5e707SKishon Vijay Abraham I 		 */
2205747a0a5bSKishon Vijay Abraham I 		if (dwc->revision < DWC3_REVISION_240A 	&& dwc->has_lpm_erratum)
2206747a0a5bSKishon Vijay Abraham I 			WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
220785d5e707SKishon Vijay Abraham I 
220885d5e707SKishon Vijay Abraham I 		if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
220985d5e707SKishon Vijay Abraham I 			reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
221085d5e707SKishon Vijay Abraham I 
221185d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
221285d5e707SKishon Vijay Abraham I 	} else {
221385d5e707SKishon Vijay Abraham I 		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
221485d5e707SKishon Vijay Abraham I 		reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
221585d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
221685d5e707SKishon Vijay Abraham I 	}
221785d5e707SKishon Vijay Abraham I 
221885d5e707SKishon Vijay Abraham I 	dep = dwc->eps[0];
221985d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
222085d5e707SKishon Vijay Abraham I 			false);
222185d5e707SKishon Vijay Abraham I 	if (ret) {
222285d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
222385d5e707SKishon Vijay Abraham I 		return;
222485d5e707SKishon Vijay Abraham I 	}
222585d5e707SKishon Vijay Abraham I 
222685d5e707SKishon Vijay Abraham I 	dep = dwc->eps[1];
222785d5e707SKishon Vijay Abraham I 	ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
222885d5e707SKishon Vijay Abraham I 			false);
222985d5e707SKishon Vijay Abraham I 	if (ret) {
223085d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to enable %s\n", dep->name);
223185d5e707SKishon Vijay Abraham I 		return;
223285d5e707SKishon Vijay Abraham I 	}
223385d5e707SKishon Vijay Abraham I 
223485d5e707SKishon Vijay Abraham I 	/*
223585d5e707SKishon Vijay Abraham I 	 * Configure PHY via GUSB3PIPECTLn if required.
223685d5e707SKishon Vijay Abraham I 	 *
223785d5e707SKishon Vijay Abraham I 	 * Update GTXFIFOSIZn
223885d5e707SKishon Vijay Abraham I 	 *
223985d5e707SKishon Vijay Abraham I 	 * In both cases reset values should be sufficient.
224085d5e707SKishon Vijay Abraham I 	 */
224185d5e707SKishon Vijay Abraham I }
224285d5e707SKishon Vijay Abraham I 
dwc3_gadget_wakeup_interrupt(struct dwc3 * dwc)224385d5e707SKishon Vijay Abraham I static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
224485d5e707SKishon Vijay Abraham I {
224585d5e707SKishon Vijay Abraham I 	/*
224685d5e707SKishon Vijay Abraham I 	 * TODO take core out of low power mode when that's
224785d5e707SKishon Vijay Abraham I 	 * implemented.
224885d5e707SKishon Vijay Abraham I 	 */
224985d5e707SKishon Vijay Abraham I 
225085d5e707SKishon Vijay Abraham I 	dwc->gadget_driver->resume(&dwc->gadget);
225185d5e707SKishon Vijay Abraham I }
225285d5e707SKishon Vijay Abraham I 
dwc3_gadget_linksts_change_interrupt(struct dwc3 * dwc,unsigned int evtinfo)225385d5e707SKishon Vijay Abraham I static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
225485d5e707SKishon Vijay Abraham I 		unsigned int evtinfo)
225585d5e707SKishon Vijay Abraham I {
225685d5e707SKishon Vijay Abraham I 	enum dwc3_link_state	next = evtinfo & DWC3_LINK_STATE_MASK;
225785d5e707SKishon Vijay Abraham I 	unsigned int		pwropt;
225885d5e707SKishon Vijay Abraham I 
225985d5e707SKishon Vijay Abraham I 	/*
226085d5e707SKishon Vijay Abraham I 	 * WORKAROUND: DWC3 < 2.50a have an issue when configured without
226185d5e707SKishon Vijay Abraham I 	 * Hibernation mode enabled which would show up when device detects
226285d5e707SKishon Vijay Abraham I 	 * host-initiated U3 exit.
226385d5e707SKishon Vijay Abraham I 	 *
226485d5e707SKishon Vijay Abraham I 	 * In that case, device will generate a Link State Change Interrupt
226585d5e707SKishon Vijay Abraham I 	 * from U3 to RESUME which is only necessary if Hibernation is
226685d5e707SKishon Vijay Abraham I 	 * configured in.
226785d5e707SKishon Vijay Abraham I 	 *
226885d5e707SKishon Vijay Abraham I 	 * There are no functional changes due to such spurious event and we
226985d5e707SKishon Vijay Abraham I 	 * just need to ignore it.
227085d5e707SKishon Vijay Abraham I 	 *
227185d5e707SKishon Vijay Abraham I 	 * Refers to:
227285d5e707SKishon Vijay Abraham I 	 *
227385d5e707SKishon Vijay Abraham I 	 * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation
227485d5e707SKishon Vijay Abraham I 	 * operational mode
227585d5e707SKishon Vijay Abraham I 	 */
227685d5e707SKishon Vijay Abraham I 	pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
227785d5e707SKishon Vijay Abraham I 	if ((dwc->revision < DWC3_REVISION_250A) &&
227885d5e707SKishon Vijay Abraham I 			(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
227985d5e707SKishon Vijay Abraham I 		if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
228085d5e707SKishon Vijay Abraham I 				(next == DWC3_LINK_STATE_RESUME)) {
228185d5e707SKishon Vijay Abraham I 			dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
228285d5e707SKishon Vijay Abraham I 			return;
228385d5e707SKishon Vijay Abraham I 		}
228485d5e707SKishon Vijay Abraham I 	}
228585d5e707SKishon Vijay Abraham I 
228685d5e707SKishon Vijay Abraham I 	/*
228785d5e707SKishon Vijay Abraham I 	 * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
228885d5e707SKishon Vijay Abraham I 	 * on the link partner, the USB session might do multiple entry/exit
228985d5e707SKishon Vijay Abraham I 	 * of low power states before a transfer takes place.
229085d5e707SKishon Vijay Abraham I 	 *
229185d5e707SKishon Vijay Abraham I 	 * Due to this problem, we might experience lower throughput. The
229285d5e707SKishon Vijay Abraham I 	 * suggested workaround is to disable DCTL[12:9] bits if we're
229385d5e707SKishon Vijay Abraham I 	 * transitioning from U1/U2 to U0 and enable those bits again
229485d5e707SKishon Vijay Abraham I 	 * after a transfer completes and there are no pending transfers
229585d5e707SKishon Vijay Abraham I 	 * on any of the enabled endpoints.
229685d5e707SKishon Vijay Abraham I 	 *
229785d5e707SKishon Vijay Abraham I 	 * This is the first half of that workaround.
229885d5e707SKishon Vijay Abraham I 	 *
229985d5e707SKishon Vijay Abraham I 	 * Refers to:
230085d5e707SKishon Vijay Abraham I 	 *
230185d5e707SKishon Vijay Abraham I 	 * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
230285d5e707SKishon Vijay Abraham I 	 * core send LGO_Ux entering U0
230385d5e707SKishon Vijay Abraham I 	 */
230485d5e707SKishon Vijay Abraham I 	if (dwc->revision < DWC3_REVISION_183A) {
230585d5e707SKishon Vijay Abraham I 		if (next == DWC3_LINK_STATE_U0) {
230685d5e707SKishon Vijay Abraham I 			u32	u1u2;
230785d5e707SKishon Vijay Abraham I 			u32	reg;
230885d5e707SKishon Vijay Abraham I 
230985d5e707SKishon Vijay Abraham I 			switch (dwc->link_state) {
231085d5e707SKishon Vijay Abraham I 			case DWC3_LINK_STATE_U1:
231185d5e707SKishon Vijay Abraham I 			case DWC3_LINK_STATE_U2:
231285d5e707SKishon Vijay Abraham I 				reg = dwc3_readl(dwc->regs, DWC3_DCTL);
231385d5e707SKishon Vijay Abraham I 				u1u2 = reg & (DWC3_DCTL_INITU2ENA
231485d5e707SKishon Vijay Abraham I 						| DWC3_DCTL_ACCEPTU2ENA
231585d5e707SKishon Vijay Abraham I 						| DWC3_DCTL_INITU1ENA
231685d5e707SKishon Vijay Abraham I 						| DWC3_DCTL_ACCEPTU1ENA);
231785d5e707SKishon Vijay Abraham I 
231885d5e707SKishon Vijay Abraham I 				if (!dwc->u1u2)
231985d5e707SKishon Vijay Abraham I 					dwc->u1u2 = reg & u1u2;
232085d5e707SKishon Vijay Abraham I 
232185d5e707SKishon Vijay Abraham I 				reg &= ~u1u2;
232285d5e707SKishon Vijay Abraham I 
232385d5e707SKishon Vijay Abraham I 				dwc3_writel(dwc->regs, DWC3_DCTL, reg);
232485d5e707SKishon Vijay Abraham I 				break;
232585d5e707SKishon Vijay Abraham I 			default:
232685d5e707SKishon Vijay Abraham I 				/* do nothing */
232785d5e707SKishon Vijay Abraham I 				break;
232885d5e707SKishon Vijay Abraham I 			}
232985d5e707SKishon Vijay Abraham I 		}
233085d5e707SKishon Vijay Abraham I 	}
233185d5e707SKishon Vijay Abraham I 
233285d5e707SKishon Vijay Abraham I 	switch (next) {
233385d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_U1:
233485d5e707SKishon Vijay Abraham I 		if (dwc->speed == USB_SPEED_SUPER)
233585d5e707SKishon Vijay Abraham I 			dwc3_suspend_gadget(dwc);
233685d5e707SKishon Vijay Abraham I 		break;
233785d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_U2:
233885d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_U3:
233985d5e707SKishon Vijay Abraham I 		dwc3_suspend_gadget(dwc);
234085d5e707SKishon Vijay Abraham I 		break;
234185d5e707SKishon Vijay Abraham I 	case DWC3_LINK_STATE_RESUME:
234285d5e707SKishon Vijay Abraham I 		dwc3_resume_gadget(dwc);
234385d5e707SKishon Vijay Abraham I 		break;
234485d5e707SKishon Vijay Abraham I 	default:
234585d5e707SKishon Vijay Abraham I 		/* do nothing */
234685d5e707SKishon Vijay Abraham I 		break;
234785d5e707SKishon Vijay Abraham I 	}
234885d5e707SKishon Vijay Abraham I 
234985d5e707SKishon Vijay Abraham I 	dwc->link_state = next;
235085d5e707SKishon Vijay Abraham I }
235185d5e707SKishon Vijay Abraham I 
dwc3_gadget_hibernation_interrupt(struct dwc3 * dwc,unsigned int evtinfo)235285d5e707SKishon Vijay Abraham I static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
235385d5e707SKishon Vijay Abraham I 		unsigned int evtinfo)
235485d5e707SKishon Vijay Abraham I {
23552252d150SLukasz Majewski 	unsigned int is_ss = evtinfo & (1UL << 4);
235685d5e707SKishon Vijay Abraham I 
235785d5e707SKishon Vijay Abraham I 	/**
235885d5e707SKishon Vijay Abraham I 	 * WORKAROUND: DWC3 revison 2.20a with hibernation support
235985d5e707SKishon Vijay Abraham I 	 * have a known issue which can cause USB CV TD.9.23 to fail
236085d5e707SKishon Vijay Abraham I 	 * randomly.
236185d5e707SKishon Vijay Abraham I 	 *
236285d5e707SKishon Vijay Abraham I 	 * Because of this issue, core could generate bogus hibernation
236385d5e707SKishon Vijay Abraham I 	 * events which SW needs to ignore.
236485d5e707SKishon Vijay Abraham I 	 *
236585d5e707SKishon Vijay Abraham I 	 * Refers to:
236685d5e707SKishon Vijay Abraham I 	 *
236785d5e707SKishon Vijay Abraham I 	 * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
236885d5e707SKishon Vijay Abraham I 	 * Device Fallback from SuperSpeed
236985d5e707SKishon Vijay Abraham I 	 */
237085d5e707SKishon Vijay Abraham I 	if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
237185d5e707SKishon Vijay Abraham I 		return;
237285d5e707SKishon Vijay Abraham I 
237385d5e707SKishon Vijay Abraham I 	/* enter hibernation here */
237485d5e707SKishon Vijay Abraham I }
237585d5e707SKishon Vijay Abraham I 
dwc3_gadget_interrupt(struct dwc3 * dwc,const struct dwc3_event_devt * event)237685d5e707SKishon Vijay Abraham I static void dwc3_gadget_interrupt(struct dwc3 *dwc,
237785d5e707SKishon Vijay Abraham I 		const struct dwc3_event_devt *event)
237885d5e707SKishon Vijay Abraham I {
237985d5e707SKishon Vijay Abraham I 	switch (event->type) {
238085d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_DISCONNECT:
238185d5e707SKishon Vijay Abraham I 		dwc3_gadget_disconnect_interrupt(dwc);
238285d5e707SKishon Vijay Abraham I 		break;
238385d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_RESET:
238485d5e707SKishon Vijay Abraham I 		dwc3_gadget_reset_interrupt(dwc);
238585d5e707SKishon Vijay Abraham I 		break;
238685d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_CONNECT_DONE:
238785d5e707SKishon Vijay Abraham I 		dwc3_gadget_conndone_interrupt(dwc);
238885d5e707SKishon Vijay Abraham I 		break;
238985d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_WAKEUP:
239085d5e707SKishon Vijay Abraham I 		dwc3_gadget_wakeup_interrupt(dwc);
239185d5e707SKishon Vijay Abraham I 		break;
239285d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_HIBER_REQ:
2393747a0a5bSKishon Vijay Abraham I 		if (!dwc->has_hibernation) {
2394747a0a5bSKishon Vijay Abraham I 			WARN(1 ,"unexpected hibernation event\n");
239585d5e707SKishon Vijay Abraham I 			break;
2396747a0a5bSKishon Vijay Abraham I 		}
239785d5e707SKishon Vijay Abraham I 		dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
239885d5e707SKishon Vijay Abraham I 		break;
239985d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
240085d5e707SKishon Vijay Abraham I 		dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
240185d5e707SKishon Vijay Abraham I 		break;
240285d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_EOPF:
240385d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "End of Periodic Frame\n");
240485d5e707SKishon Vijay Abraham I 		break;
240585d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_SOF:
240685d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
240785d5e707SKishon Vijay Abraham I 		break;
240885d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
240985d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "Erratic Error\n");
241085d5e707SKishon Vijay Abraham I 		break;
241185d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_CMD_CMPL:
241285d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "Command Complete\n");
241385d5e707SKishon Vijay Abraham I 		break;
241485d5e707SKishon Vijay Abraham I 	case DWC3_DEVICE_EVENT_OVERFLOW:
241585d5e707SKishon Vijay Abraham I 		dev_vdbg(dwc->dev, "Overflow\n");
241685d5e707SKishon Vijay Abraham I 		break;
241785d5e707SKishon Vijay Abraham I 	default:
241885d5e707SKishon Vijay Abraham I 		dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
241985d5e707SKishon Vijay Abraham I 	}
242085d5e707SKishon Vijay Abraham I }
242185d5e707SKishon Vijay Abraham I 
dwc3_process_event_entry(struct dwc3 * dwc,const union dwc3_event * event)242285d5e707SKishon Vijay Abraham I static void dwc3_process_event_entry(struct dwc3 *dwc,
242385d5e707SKishon Vijay Abraham I 		const union dwc3_event *event)
242485d5e707SKishon Vijay Abraham I {
242585d5e707SKishon Vijay Abraham I 	/* Endpoint IRQ, handle it and return early */
242685d5e707SKishon Vijay Abraham I 	if (event->type.is_devspec == 0) {
242785d5e707SKishon Vijay Abraham I 		/* depevt */
242885d5e707SKishon Vijay Abraham I 		return dwc3_endpoint_interrupt(dwc, &event->depevt);
242985d5e707SKishon Vijay Abraham I 	}
243085d5e707SKishon Vijay Abraham I 
243185d5e707SKishon Vijay Abraham I 	switch (event->type.type) {
243285d5e707SKishon Vijay Abraham I 	case DWC3_EVENT_TYPE_DEV:
243385d5e707SKishon Vijay Abraham I 		dwc3_gadget_interrupt(dwc, &event->devt);
243485d5e707SKishon Vijay Abraham I 		break;
243585d5e707SKishon Vijay Abraham I 	/* REVISIT what to do with Carkit and I2C events ? */
243685d5e707SKishon Vijay Abraham I 	default:
243785d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw);
243885d5e707SKishon Vijay Abraham I 	}
243985d5e707SKishon Vijay Abraham I }
244085d5e707SKishon Vijay Abraham I 
dwc3_process_event_buf(struct dwc3 * dwc,u32 buf)244185d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
244285d5e707SKishon Vijay Abraham I {
244385d5e707SKishon Vijay Abraham I 	struct dwc3_event_buffer *evt;
244485d5e707SKishon Vijay Abraham I 	irqreturn_t ret = IRQ_NONE;
244585d5e707SKishon Vijay Abraham I 	int left;
244685d5e707SKishon Vijay Abraham I 	u32 reg;
244785d5e707SKishon Vijay Abraham I 
244885d5e707SKishon Vijay Abraham I 	evt = dwc->ev_buffs[buf];
244985d5e707SKishon Vijay Abraham I 	left = evt->count;
245085d5e707SKishon Vijay Abraham I 
245185d5e707SKishon Vijay Abraham I 	if (!(evt->flags & DWC3_EVENT_PENDING))
245285d5e707SKishon Vijay Abraham I 		return IRQ_NONE;
245385d5e707SKishon Vijay Abraham I 
245485d5e707SKishon Vijay Abraham I 	while (left > 0) {
245585d5e707SKishon Vijay Abraham I 		union dwc3_event event;
245685d5e707SKishon Vijay Abraham I 
245785d5e707SKishon Vijay Abraham I 		event.raw = *(u32 *) (evt->buf + evt->lpos);
245885d5e707SKishon Vijay Abraham I 
245985d5e707SKishon Vijay Abraham I 		dwc3_process_event_entry(dwc, &event);
246085d5e707SKishon Vijay Abraham I 
246185d5e707SKishon Vijay Abraham I 		/*
246285d5e707SKishon Vijay Abraham I 		 * FIXME we wrap around correctly to the next entry as
246385d5e707SKishon Vijay Abraham I 		 * almost all entries are 4 bytes in size. There is one
246485d5e707SKishon Vijay Abraham I 		 * entry which has 12 bytes which is a regular entry
246585d5e707SKishon Vijay Abraham I 		 * followed by 8 bytes data. ATM I don't know how
246685d5e707SKishon Vijay Abraham I 		 * things are organized if we get next to the a
246785d5e707SKishon Vijay Abraham I 		 * boundary so I worry about that once we try to handle
246885d5e707SKishon Vijay Abraham I 		 * that.
246985d5e707SKishon Vijay Abraham I 		 */
247085d5e707SKishon Vijay Abraham I 		evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
247185d5e707SKishon Vijay Abraham I 		left -= 4;
247285d5e707SKishon Vijay Abraham I 
247385d5e707SKishon Vijay Abraham I 		dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
247485d5e707SKishon Vijay Abraham I 	}
247585d5e707SKishon Vijay Abraham I 
247685d5e707SKishon Vijay Abraham I 	evt->count = 0;
247785d5e707SKishon Vijay Abraham I 	evt->flags &= ~DWC3_EVENT_PENDING;
247885d5e707SKishon Vijay Abraham I 	ret = IRQ_HANDLED;
247985d5e707SKishon Vijay Abraham I 
248085d5e707SKishon Vijay Abraham I 	/* Unmask interrupt */
248185d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
248285d5e707SKishon Vijay Abraham I 	reg &= ~DWC3_GEVNTSIZ_INTMASK;
248385d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
248485d5e707SKishon Vijay Abraham I 
248585d5e707SKishon Vijay Abraham I 	return ret;
248685d5e707SKishon Vijay Abraham I }
248785d5e707SKishon Vijay Abraham I 
dwc3_thread_interrupt(int irq,void * _dwc)248885d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
248985d5e707SKishon Vijay Abraham I {
249085d5e707SKishon Vijay Abraham I 	struct dwc3 *dwc = _dwc;
249185d5e707SKishon Vijay Abraham I 	unsigned long flags;
249285d5e707SKishon Vijay Abraham I 	irqreturn_t ret = IRQ_NONE;
249385d5e707SKishon Vijay Abraham I 	int i;
249485d5e707SKishon Vijay Abraham I 
249585d5e707SKishon Vijay Abraham I 	spin_lock_irqsave(&dwc->lock, flags);
249685d5e707SKishon Vijay Abraham I 
249785d5e707SKishon Vijay Abraham I 	for (i = 0; i < dwc->num_event_buffers; i++)
249885d5e707SKishon Vijay Abraham I 		ret |= dwc3_process_event_buf(dwc, i);
249985d5e707SKishon Vijay Abraham I 
250085d5e707SKishon Vijay Abraham I 	spin_unlock_irqrestore(&dwc->lock, flags);
250185d5e707SKishon Vijay Abraham I 
250285d5e707SKishon Vijay Abraham I 	return ret;
250385d5e707SKishon Vijay Abraham I }
250485d5e707SKishon Vijay Abraham I 
dwc3_check_event_buf(struct dwc3 * dwc,u32 buf)250585d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
250685d5e707SKishon Vijay Abraham I {
250785d5e707SKishon Vijay Abraham I 	struct dwc3_event_buffer *evt;
250885d5e707SKishon Vijay Abraham I 	u32 count;
250985d5e707SKishon Vijay Abraham I 	u32 reg;
251085d5e707SKishon Vijay Abraham I 
251185d5e707SKishon Vijay Abraham I 	evt = dwc->ev_buffs[buf];
251285d5e707SKishon Vijay Abraham I 
251385d5e707SKishon Vijay Abraham I 	count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
251485d5e707SKishon Vijay Abraham I 	count &= DWC3_GEVNTCOUNT_MASK;
251585d5e707SKishon Vijay Abraham I 	if (!count)
251685d5e707SKishon Vijay Abraham I 		return IRQ_NONE;
251785d5e707SKishon Vijay Abraham I 
251885d5e707SKishon Vijay Abraham I 	evt->count = count;
251985d5e707SKishon Vijay Abraham I 	evt->flags |= DWC3_EVENT_PENDING;
252085d5e707SKishon Vijay Abraham I 
252185d5e707SKishon Vijay Abraham I 	/* Mask interrupt */
252285d5e707SKishon Vijay Abraham I 	reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf));
252385d5e707SKishon Vijay Abraham I 	reg |= DWC3_GEVNTSIZ_INTMASK;
252485d5e707SKishon Vijay Abraham I 	dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
252585d5e707SKishon Vijay Abraham I 
252685d5e707SKishon Vijay Abraham I 	return IRQ_WAKE_THREAD;
252785d5e707SKishon Vijay Abraham I }
252885d5e707SKishon Vijay Abraham I 
dwc3_interrupt(int irq,void * _dwc)252985d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
253085d5e707SKishon Vijay Abraham I {
253185d5e707SKishon Vijay Abraham I 	struct dwc3			*dwc = _dwc;
253285d5e707SKishon Vijay Abraham I 	int				i;
253385d5e707SKishon Vijay Abraham I 	irqreturn_t			ret = IRQ_NONE;
253485d5e707SKishon Vijay Abraham I 
253585d5e707SKishon Vijay Abraham I 	spin_lock(&dwc->lock);
253685d5e707SKishon Vijay Abraham I 
253785d5e707SKishon Vijay Abraham I 	for (i = 0; i < dwc->num_event_buffers; i++) {
253885d5e707SKishon Vijay Abraham I 		irqreturn_t status;
253985d5e707SKishon Vijay Abraham I 
254085d5e707SKishon Vijay Abraham I 		status = dwc3_check_event_buf(dwc, i);
254185d5e707SKishon Vijay Abraham I 		if (status == IRQ_WAKE_THREAD)
254285d5e707SKishon Vijay Abraham I 			ret = status;
254385d5e707SKishon Vijay Abraham I 	}
254485d5e707SKishon Vijay Abraham I 
254585d5e707SKishon Vijay Abraham I 	spin_unlock(&dwc->lock);
254685d5e707SKishon Vijay Abraham I 
254785d5e707SKishon Vijay Abraham I 	return ret;
254885d5e707SKishon Vijay Abraham I }
254985d5e707SKishon Vijay Abraham I 
255085d5e707SKishon Vijay Abraham I /**
255185d5e707SKishon Vijay Abraham I  * dwc3_gadget_init - Initializes gadget related registers
255285d5e707SKishon Vijay Abraham I  * @dwc: pointer to our controller context structure
255385d5e707SKishon Vijay Abraham I  *
255485d5e707SKishon Vijay Abraham I  * Returns 0 on success otherwise negative errno.
255585d5e707SKishon Vijay Abraham I  */
dwc3_gadget_init(struct dwc3 * dwc)255685d5e707SKishon Vijay Abraham I int dwc3_gadget_init(struct dwc3 *dwc)
255785d5e707SKishon Vijay Abraham I {
255885d5e707SKishon Vijay Abraham I 	int					ret;
255985d5e707SKishon Vijay Abraham I 
2560747a0a5bSKishon Vijay Abraham I 	dwc->ctrl_req = dma_alloc_coherent(sizeof(*dwc->ctrl_req),
2561747a0a5bSKishon Vijay Abraham I 					(unsigned long *)&dwc->ctrl_req_addr);
256285d5e707SKishon Vijay Abraham I 	if (!dwc->ctrl_req) {
256385d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to allocate ctrl request\n");
256485d5e707SKishon Vijay Abraham I 		ret = -ENOMEM;
256585d5e707SKishon Vijay Abraham I 		goto err0;
256685d5e707SKishon Vijay Abraham I 	}
256785d5e707SKishon Vijay Abraham I 
25688d488f3eSKishon Vijay Abraham I 	dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2,
2569747a0a5bSKishon Vijay Abraham I 					  (unsigned long *)&dwc->ep0_trb_addr);
257085d5e707SKishon Vijay Abraham I 	if (!dwc->ep0_trb) {
257185d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to allocate ep0 trb\n");
257285d5e707SKishon Vijay Abraham I 		ret = -ENOMEM;
257385d5e707SKishon Vijay Abraham I 		goto err1;
257485d5e707SKishon Vijay Abraham I 	}
257585d5e707SKishon Vijay Abraham I 
2576526a50f8SKishon Vijay Abraham I 	dwc->setup_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
2577526a50f8SKishon Vijay Abraham I 				  DWC3_EP0_BOUNCE_SIZE);
257885d5e707SKishon Vijay Abraham I 	if (!dwc->setup_buf) {
257985d5e707SKishon Vijay Abraham I 		ret = -ENOMEM;
258085d5e707SKishon Vijay Abraham I 		goto err2;
258185d5e707SKishon Vijay Abraham I 	}
258285d5e707SKishon Vijay Abraham I 
2583747a0a5bSKishon Vijay Abraham I 	dwc->ep0_bounce = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE,
2584747a0a5bSKishon Vijay Abraham I 					(unsigned long *)&dwc->ep0_bounce_addr);
258585d5e707SKishon Vijay Abraham I 	if (!dwc->ep0_bounce) {
258685d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
258785d5e707SKishon Vijay Abraham I 		ret = -ENOMEM;
258885d5e707SKishon Vijay Abraham I 		goto err3;
258985d5e707SKishon Vijay Abraham I 	}
259085d5e707SKishon Vijay Abraham I 
259185d5e707SKishon Vijay Abraham I 	dwc->gadget.ops			= &dwc3_gadget_ops;
259285d5e707SKishon Vijay Abraham I 	dwc->gadget.max_speed		= USB_SPEED_SUPER;
259385d5e707SKishon Vijay Abraham I 	dwc->gadget.speed		= USB_SPEED_UNKNOWN;
259485d5e707SKishon Vijay Abraham I 	dwc->gadget.name		= "dwc3-gadget";
259585d5e707SKishon Vijay Abraham I 
259685d5e707SKishon Vijay Abraham I 	/*
259785d5e707SKishon Vijay Abraham I 	 * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
259885d5e707SKishon Vijay Abraham I 	 * on ep out.
259985d5e707SKishon Vijay Abraham I 	 */
260085d5e707SKishon Vijay Abraham I 	dwc->gadget.quirk_ep_out_aligned_size = true;
260185d5e707SKishon Vijay Abraham I 
260285d5e707SKishon Vijay Abraham I 	/*
260385d5e707SKishon Vijay Abraham I 	 * REVISIT: Here we should clear all pending IRQs to be
260485d5e707SKishon Vijay Abraham I 	 * sure we're starting from a well known location.
260585d5e707SKishon Vijay Abraham I 	 */
260685d5e707SKishon Vijay Abraham I 
260785d5e707SKishon Vijay Abraham I 	ret = dwc3_gadget_init_endpoints(dwc);
260885d5e707SKishon Vijay Abraham I 	if (ret)
260985d5e707SKishon Vijay Abraham I 		goto err4;
261085d5e707SKishon Vijay Abraham I 
2611*23ba2d63SMugunthan V N 	ret = usb_add_gadget_udc((struct device *)dwc->dev, &dwc->gadget);
261285d5e707SKishon Vijay Abraham I 	if (ret) {
261385d5e707SKishon Vijay Abraham I 		dev_err(dwc->dev, "failed to register udc\n");
261485d5e707SKishon Vijay Abraham I 		goto err4;
261585d5e707SKishon Vijay Abraham I 	}
261685d5e707SKishon Vijay Abraham I 
261785d5e707SKishon Vijay Abraham I 	return 0;
261885d5e707SKishon Vijay Abraham I 
261985d5e707SKishon Vijay Abraham I err4:
262085d5e707SKishon Vijay Abraham I 	dwc3_gadget_free_endpoints(dwc);
2621747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ep0_bounce);
262285d5e707SKishon Vijay Abraham I 
262385d5e707SKishon Vijay Abraham I err3:
262485d5e707SKishon Vijay Abraham I 	kfree(dwc->setup_buf);
262585d5e707SKishon Vijay Abraham I 
262685d5e707SKishon Vijay Abraham I err2:
2627747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ep0_trb);
262885d5e707SKishon Vijay Abraham I 
262985d5e707SKishon Vijay Abraham I err1:
2630747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ctrl_req);
263185d5e707SKishon Vijay Abraham I 
263285d5e707SKishon Vijay Abraham I err0:
263385d5e707SKishon Vijay Abraham I 	return ret;
263485d5e707SKishon Vijay Abraham I }
263585d5e707SKishon Vijay Abraham I 
263685d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */
263785d5e707SKishon Vijay Abraham I 
dwc3_gadget_exit(struct dwc3 * dwc)263885d5e707SKishon Vijay Abraham I void dwc3_gadget_exit(struct dwc3 *dwc)
263985d5e707SKishon Vijay Abraham I {
264085d5e707SKishon Vijay Abraham I 	usb_del_gadget_udc(&dwc->gadget);
264185d5e707SKishon Vijay Abraham I 
264285d5e707SKishon Vijay Abraham I 	dwc3_gadget_free_endpoints(dwc);
264385d5e707SKishon Vijay Abraham I 
2644747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ep0_bounce);
264585d5e707SKishon Vijay Abraham I 
264685d5e707SKishon Vijay Abraham I 	kfree(dwc->setup_buf);
264785d5e707SKishon Vijay Abraham I 
2648747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ep0_trb);
264985d5e707SKishon Vijay Abraham I 
2650747a0a5bSKishon Vijay Abraham I 	dma_free_coherent(dwc->ctrl_req);
2651747a0a5bSKishon Vijay Abraham I }
2652747a0a5bSKishon Vijay Abraham I 
2653747a0a5bSKishon Vijay Abraham I /**
2654747a0a5bSKishon Vijay Abraham I  * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt
2655747a0a5bSKishon Vijay Abraham I  * @dwc: struct dwce *
2656747a0a5bSKishon Vijay Abraham I  *
2657747a0a5bSKishon Vijay Abraham I  * Handles ep0 and gadget interrupt
2658747a0a5bSKishon Vijay Abraham I  *
2659747a0a5bSKishon Vijay Abraham I  * Should be called from dwc3 core.
2660747a0a5bSKishon Vijay Abraham I  */
dwc3_gadget_uboot_handle_interrupt(struct dwc3 * dwc)2661747a0a5bSKishon Vijay Abraham I void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc)
2662747a0a5bSKishon Vijay Abraham I {
2663137f7c59SMarek Szyprowski 	int ret = dwc3_interrupt(0, dwc);
2664137f7c59SMarek Szyprowski 
2665137f7c59SMarek Szyprowski 	if (ret == IRQ_WAKE_THREAD) {
2666137f7c59SMarek Szyprowski 		int i;
2667137f7c59SMarek Szyprowski 		struct dwc3_event_buffer *evt;
2668137f7c59SMarek Szyprowski 
2669889239d6SPhilipp Tomsich 		dwc3_thread_interrupt(0, dwc);
2670889239d6SPhilipp Tomsich 
2671889239d6SPhilipp Tomsich 		/* Clean + Invalidate the buffers after touching them */
2672137f7c59SMarek Szyprowski 		for (i = 0; i < dwc->num_event_buffers; i++) {
2673137f7c59SMarek Szyprowski 			evt = dwc->ev_buffs[i];
2674b7bf4a95SPhilipp Tomsich 			dwc3_flush_cache((uintptr_t)evt->buf, evt->length);
2675137f7c59SMarek Szyprowski 		}
267685d5e707SKishon Vijay Abraham I 	}
2677137f7c59SMarek Szyprowski }
2678