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