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