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