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> 2185d5e707SKishon Vijay Abraham I #include <linux/list.h> 2285d5e707SKishon Vijay Abraham I 2385d5e707SKishon Vijay Abraham I #include <linux/usb/ch9.h> 2485d5e707SKishon Vijay Abraham I #include <linux/usb/gadget.h> 25747a0a5bSKishon Vijay Abraham I #include <asm/arch/sys_proto.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; 247526a50f8SKishon Vijay Abraham I dwc3_flush_cache((int)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 774526a50f8SKishon Vijay Abraham I dwc3_flush_cache((int)dma, length); 775526a50f8SKishon Vijay Abraham I dwc3_flush_cache((int)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 unsigned int last_one = 0; 79385d5e707SKishon Vijay Abraham I 79485d5e707SKishon Vijay Abraham I BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); 79585d5e707SKishon Vijay Abraham I 79685d5e707SKishon Vijay Abraham I /* the first request must not be queued */ 79785d5e707SKishon Vijay Abraham I trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; 79885d5e707SKishon Vijay Abraham I 79985d5e707SKishon Vijay Abraham I /* Can't wrap around on a non-isoc EP since there's no link TRB */ 80085d5e707SKishon Vijay Abraham I if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 80185d5e707SKishon Vijay Abraham I max = DWC3_TRB_NUM - (dep->free_slot & DWC3_TRB_MASK); 80285d5e707SKishon Vijay Abraham I if (trbs_left > max) 80385d5e707SKishon Vijay Abraham I trbs_left = max; 80485d5e707SKishon Vijay Abraham I } 80585d5e707SKishon Vijay Abraham I 80685d5e707SKishon Vijay Abraham I /* 80785d5e707SKishon Vijay Abraham I * If busy & slot are equal than it is either full or empty. If we are 80885d5e707SKishon Vijay Abraham I * starting to process requests then we are empty. Otherwise we are 80985d5e707SKishon Vijay Abraham I * full and don't do anything 81085d5e707SKishon Vijay Abraham I */ 81185d5e707SKishon Vijay Abraham I if (!trbs_left) { 81285d5e707SKishon Vijay Abraham I if (!starting) 81385d5e707SKishon Vijay Abraham I return; 81485d5e707SKishon Vijay Abraham I trbs_left = DWC3_TRB_NUM; 81585d5e707SKishon Vijay Abraham I /* 81685d5e707SKishon Vijay Abraham I * In case we start from scratch, we queue the ISOC requests 81785d5e707SKishon Vijay Abraham I * starting from slot 1. This is done because we use ring 81885d5e707SKishon Vijay Abraham I * buffer and have no LST bit to stop us. Instead, we place 81985d5e707SKishon Vijay Abraham I * IOC bit every TRB_NUM/4. We try to avoid having an interrupt 82085d5e707SKishon Vijay Abraham I * after the first request so we start at slot 1 and have 82185d5e707SKishon Vijay Abraham I * 7 requests proceed before we hit the first IOC. 82285d5e707SKishon Vijay Abraham I * Other transfer types don't use the ring buffer and are 82385d5e707SKishon Vijay Abraham I * processed from the first TRB until the last one. Since we 82485d5e707SKishon Vijay Abraham I * don't wrap around we have to start at the beginning. 82585d5e707SKishon Vijay Abraham I */ 82685d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 82785d5e707SKishon Vijay Abraham I dep->busy_slot = 1; 82885d5e707SKishon Vijay Abraham I dep->free_slot = 1; 82985d5e707SKishon Vijay Abraham I } else { 83085d5e707SKishon Vijay Abraham I dep->busy_slot = 0; 83185d5e707SKishon Vijay Abraham I dep->free_slot = 0; 83285d5e707SKishon Vijay Abraham I } 83385d5e707SKishon Vijay Abraham I } 83485d5e707SKishon Vijay Abraham I 83585d5e707SKishon Vijay Abraham I /* The last TRB is a link TRB, not used for xfer */ 83685d5e707SKishon Vijay Abraham I if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) 83785d5e707SKishon Vijay Abraham I return; 83885d5e707SKishon Vijay Abraham I 83985d5e707SKishon Vijay Abraham I list_for_each_entry_safe(req, n, &dep->request_list, list) { 84085d5e707SKishon Vijay Abraham I unsigned length; 84185d5e707SKishon Vijay Abraham I dma_addr_t dma; 84285d5e707SKishon Vijay Abraham I last_one = false; 84385d5e707SKishon Vijay Abraham I 84485d5e707SKishon Vijay Abraham I dma = req->request.dma; 84585d5e707SKishon Vijay Abraham I length = req->request.length; 84685d5e707SKishon Vijay Abraham I trbs_left--; 84785d5e707SKishon Vijay Abraham I 84885d5e707SKishon Vijay Abraham I if (!trbs_left) 84985d5e707SKishon Vijay Abraham I last_one = 1; 85085d5e707SKishon Vijay Abraham I 85185d5e707SKishon Vijay Abraham I /* Is this the last request? */ 85285d5e707SKishon Vijay Abraham I if (list_is_last(&req->list, &dep->request_list)) 85385d5e707SKishon Vijay Abraham I last_one = 1; 85485d5e707SKishon Vijay Abraham I 85585d5e707SKishon Vijay Abraham I dwc3_prepare_one_trb(dep, req, dma, length, 85685d5e707SKishon Vijay Abraham I last_one, false, 0); 85785d5e707SKishon Vijay Abraham I 85885d5e707SKishon Vijay Abraham I if (last_one) 85985d5e707SKishon Vijay Abraham I break; 86085d5e707SKishon Vijay Abraham I } 86185d5e707SKishon Vijay Abraham I } 86285d5e707SKishon Vijay Abraham I 86385d5e707SKishon Vijay Abraham I static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, 86485d5e707SKishon Vijay Abraham I int start_new) 86585d5e707SKishon Vijay Abraham I { 86685d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params; 86785d5e707SKishon Vijay Abraham I struct dwc3_request *req; 86885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc; 86985d5e707SKishon Vijay Abraham I int ret; 87085d5e707SKishon Vijay Abraham I u32 cmd; 87185d5e707SKishon Vijay Abraham I 87285d5e707SKishon Vijay Abraham I if (start_new && (dep->flags & DWC3_EP_BUSY)) { 87385d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); 87485d5e707SKishon Vijay Abraham I return -EBUSY; 87585d5e707SKishon Vijay Abraham I } 87685d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_PENDING_REQUEST; 87785d5e707SKishon Vijay Abraham I 87885d5e707SKishon Vijay Abraham I /* 87985d5e707SKishon Vijay Abraham I * If we are getting here after a short-out-packet we don't enqueue any 88085d5e707SKishon Vijay Abraham I * new requests as we try to set the IOC bit only on the last request. 88185d5e707SKishon Vijay Abraham I */ 88285d5e707SKishon Vijay Abraham I if (start_new) { 88385d5e707SKishon Vijay Abraham I if (list_empty(&dep->req_queued)) 88485d5e707SKishon Vijay Abraham I dwc3_prepare_trbs(dep, start_new); 88585d5e707SKishon Vijay Abraham I 88685d5e707SKishon Vijay Abraham I /* req points to the first request which will be sent */ 88785d5e707SKishon Vijay Abraham I req = next_request(&dep->req_queued); 88885d5e707SKishon Vijay Abraham I } else { 88985d5e707SKishon Vijay Abraham I dwc3_prepare_trbs(dep, start_new); 89085d5e707SKishon Vijay Abraham I 89185d5e707SKishon Vijay Abraham I /* 89285d5e707SKishon Vijay Abraham I * req points to the first request where HWO changed from 0 to 1 89385d5e707SKishon Vijay Abraham I */ 89485d5e707SKishon Vijay Abraham I req = next_request(&dep->req_queued); 89585d5e707SKishon Vijay Abraham I } 89685d5e707SKishon Vijay Abraham I if (!req) { 89785d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_PENDING_REQUEST; 89885d5e707SKishon Vijay Abraham I return 0; 89985d5e707SKishon Vijay Abraham I } 90085d5e707SKishon Vijay Abraham I 90185d5e707SKishon Vijay Abraham I memset(¶ms, 0, sizeof(params)); 90285d5e707SKishon Vijay Abraham I 90385d5e707SKishon Vijay Abraham I if (start_new) { 90485d5e707SKishon Vijay Abraham I params.param0 = upper_32_bits(req->trb_dma); 90585d5e707SKishon Vijay Abraham I params.param1 = lower_32_bits(req->trb_dma); 90685d5e707SKishon Vijay Abraham I cmd = DWC3_DEPCMD_STARTTRANSFER; 90785d5e707SKishon Vijay Abraham I } else { 90885d5e707SKishon Vijay Abraham I cmd = DWC3_DEPCMD_UPDATETRANSFER; 90985d5e707SKishon Vijay Abraham I } 91085d5e707SKishon Vijay Abraham I 91185d5e707SKishon Vijay Abraham I cmd |= DWC3_DEPCMD_PARAM(cmd_param); 91285d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); 91385d5e707SKishon Vijay Abraham I if (ret < 0) { 91485d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); 91585d5e707SKishon Vijay Abraham I 91685d5e707SKishon Vijay Abraham I /* 91785d5e707SKishon Vijay Abraham I * FIXME we need to iterate over the list of requests 91885d5e707SKishon Vijay Abraham I * here and stop, unmap, free and del each of the linked 91985d5e707SKishon Vijay Abraham I * requests instead of what we do now. 92085d5e707SKishon Vijay Abraham I */ 92185d5e707SKishon Vijay Abraham I usb_gadget_unmap_request(&dwc->gadget, &req->request, 92285d5e707SKishon Vijay Abraham I req->direction); 92385d5e707SKishon Vijay Abraham I list_del(&req->list); 92485d5e707SKishon Vijay Abraham I return ret; 92585d5e707SKishon Vijay Abraham I } 92685d5e707SKishon Vijay Abraham I 92785d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_BUSY; 92885d5e707SKishon Vijay Abraham I 92985d5e707SKishon Vijay Abraham I if (start_new) { 93085d5e707SKishon Vijay Abraham I dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, 93185d5e707SKishon Vijay Abraham I dep->number); 93285d5e707SKishon Vijay Abraham I WARN_ON_ONCE(!dep->resource_index); 93385d5e707SKishon Vijay Abraham I } 93485d5e707SKishon Vijay Abraham I 93585d5e707SKishon Vijay Abraham I return 0; 93685d5e707SKishon Vijay Abraham I } 93785d5e707SKishon Vijay Abraham I 93885d5e707SKishon Vijay Abraham I static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, 93985d5e707SKishon Vijay Abraham I struct dwc3_ep *dep, u32 cur_uf) 94085d5e707SKishon Vijay Abraham I { 94185d5e707SKishon Vijay Abraham I u32 uf; 94285d5e707SKishon Vijay Abraham I 94385d5e707SKishon Vijay Abraham I if (list_empty(&dep->request_list)) { 94485d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", 94585d5e707SKishon Vijay Abraham I dep->name); 94685d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_PENDING_REQUEST; 94785d5e707SKishon Vijay Abraham I return; 94885d5e707SKishon Vijay Abraham I } 94985d5e707SKishon Vijay Abraham I 95085d5e707SKishon Vijay Abraham I /* 4 micro frames in the future */ 95185d5e707SKishon Vijay Abraham I uf = cur_uf + dep->interval * 4; 95285d5e707SKishon Vijay Abraham I 95385d5e707SKishon Vijay Abraham I __dwc3_gadget_kick_transfer(dep, uf, 1); 95485d5e707SKishon Vijay Abraham I } 95585d5e707SKishon Vijay Abraham I 95685d5e707SKishon Vijay Abraham I static void dwc3_gadget_start_isoc(struct dwc3 *dwc, 95785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep, const struct dwc3_event_depevt *event) 95885d5e707SKishon Vijay Abraham I { 95985d5e707SKishon Vijay Abraham I u32 cur_uf, mask; 96085d5e707SKishon Vijay Abraham I 96185d5e707SKishon Vijay Abraham I mask = ~(dep->interval - 1); 96285d5e707SKishon Vijay Abraham I cur_uf = event->parameters & mask; 96385d5e707SKishon Vijay Abraham I 96485d5e707SKishon Vijay Abraham I __dwc3_gadget_start_isoc(dwc, dep, cur_uf); 96585d5e707SKishon Vijay Abraham I } 96685d5e707SKishon Vijay Abraham I 96785d5e707SKishon Vijay Abraham I static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) 96885d5e707SKishon Vijay Abraham I { 96985d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc; 97085d5e707SKishon Vijay Abraham I int ret; 97185d5e707SKishon Vijay Abraham I 97285d5e707SKishon Vijay Abraham I req->request.actual = 0; 97385d5e707SKishon Vijay Abraham I req->request.status = -EINPROGRESS; 97485d5e707SKishon Vijay Abraham I req->direction = dep->direction; 97585d5e707SKishon Vijay Abraham I req->epnum = dep->number; 97685d5e707SKishon Vijay Abraham I 97785d5e707SKishon Vijay Abraham I /* 9785dc4538bSMarek Szyprowski * DWC3 hangs on OUT requests smaller than maxpacket size, 9795dc4538bSMarek Szyprowski * so HACK the request length 9805dc4538bSMarek Szyprowski */ 9815dc4538bSMarek Szyprowski if (dep->direction == 0 && 9825dc4538bSMarek Szyprowski req->request.length < dep->endpoint.maxpacket) 9835dc4538bSMarek Szyprowski req->request.length = dep->endpoint.maxpacket; 9845dc4538bSMarek Szyprowski 9855dc4538bSMarek Szyprowski /* 98685d5e707SKishon Vijay Abraham I * We only add to our list of requests now and 98785d5e707SKishon Vijay Abraham I * start consuming the list once we get XferNotReady 98885d5e707SKishon Vijay Abraham I * IRQ. 98985d5e707SKishon Vijay Abraham I * 99085d5e707SKishon Vijay Abraham I * That way, we avoid doing anything that we don't need 99185d5e707SKishon Vijay Abraham I * to do now and defer it until the point we receive a 99285d5e707SKishon Vijay Abraham I * particular token from the Host side. 99385d5e707SKishon Vijay Abraham I * 99485d5e707SKishon Vijay Abraham I * This will also avoid Host cancelling URBs due to too 99585d5e707SKishon Vijay Abraham I * many NAKs. 99685d5e707SKishon Vijay Abraham I */ 99785d5e707SKishon Vijay Abraham I ret = usb_gadget_map_request(&dwc->gadget, &req->request, 99885d5e707SKishon Vijay Abraham I dep->direction); 99985d5e707SKishon Vijay Abraham I if (ret) 100085d5e707SKishon Vijay Abraham I return ret; 100185d5e707SKishon Vijay Abraham I 100285d5e707SKishon Vijay Abraham I list_add_tail(&req->list, &dep->request_list); 100385d5e707SKishon Vijay Abraham I 100485d5e707SKishon Vijay Abraham I /* 100585d5e707SKishon Vijay Abraham I * There are a few special cases: 100685d5e707SKishon Vijay Abraham I * 100785d5e707SKishon Vijay Abraham I * 1. XferNotReady with empty list of requests. We need to kick the 100885d5e707SKishon Vijay Abraham I * transfer here in that situation, otherwise we will be NAKing 100985d5e707SKishon Vijay Abraham I * forever. If we get XferNotReady before gadget driver has a 101085d5e707SKishon Vijay Abraham I * chance to queue a request, we will ACK the IRQ but won't be 101185d5e707SKishon Vijay Abraham I * able to receive the data until the next request is queued. 101285d5e707SKishon Vijay Abraham I * The following code is handling exactly that. 101385d5e707SKishon Vijay Abraham I * 101485d5e707SKishon Vijay Abraham I */ 101585d5e707SKishon Vijay Abraham I if (dep->flags & DWC3_EP_PENDING_REQUEST) { 101685d5e707SKishon Vijay Abraham I /* 101785d5e707SKishon Vijay Abraham I * If xfernotready is already elapsed and it is a case 101885d5e707SKishon Vijay Abraham I * of isoc transfer, then issue END TRANSFER, so that 101985d5e707SKishon Vijay Abraham I * you can receive xfernotready again and can have 102085d5e707SKishon Vijay Abraham I * notion of current microframe. 102185d5e707SKishon Vijay Abraham I */ 102285d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 102385d5e707SKishon Vijay Abraham I if (list_empty(&dep->req_queued)) { 102485d5e707SKishon Vijay Abraham I dwc3_stop_active_transfer(dwc, dep->number, true); 102585d5e707SKishon Vijay Abraham I dep->flags = DWC3_EP_ENABLED; 102685d5e707SKishon Vijay Abraham I } 102785d5e707SKishon Vijay Abraham I return 0; 102885d5e707SKishon Vijay Abraham I } 102985d5e707SKishon Vijay Abraham I 103085d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_kick_transfer(dep, 0, true); 103185d5e707SKishon Vijay Abraham I if (ret && ret != -EBUSY) 103285d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s: failed to kick transfers\n", 103385d5e707SKishon Vijay Abraham I dep->name); 103485d5e707SKishon Vijay Abraham I return ret; 103585d5e707SKishon Vijay Abraham I } 103685d5e707SKishon Vijay Abraham I 103785d5e707SKishon Vijay Abraham I /* 103885d5e707SKishon Vijay Abraham I * 2. XferInProgress on Isoc EP with an active transfer. We need to 103985d5e707SKishon Vijay Abraham I * kick the transfer here after queuing a request, otherwise the 104085d5e707SKishon Vijay Abraham I * core may not see the modified TRB(s). 104185d5e707SKishon Vijay Abraham I */ 104285d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && 104385d5e707SKishon Vijay Abraham I (dep->flags & DWC3_EP_BUSY) && 104485d5e707SKishon Vijay Abraham I !(dep->flags & DWC3_EP_MISSED_ISOC)) { 104585d5e707SKishon Vijay Abraham I WARN_ON_ONCE(!dep->resource_index); 104685d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, 104785d5e707SKishon Vijay Abraham I false); 104885d5e707SKishon Vijay Abraham I if (ret && ret != -EBUSY) 104985d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s: failed to kick transfers\n", 105085d5e707SKishon Vijay Abraham I dep->name); 105185d5e707SKishon Vijay Abraham I return ret; 105285d5e707SKishon Vijay Abraham I } 105385d5e707SKishon Vijay Abraham I 105485d5e707SKishon Vijay Abraham I /* 105585d5e707SKishon Vijay Abraham I * 4. Stream Capable Bulk Endpoints. We need to start the transfer 105685d5e707SKishon Vijay Abraham I * right away, otherwise host will not know we have streams to be 105785d5e707SKishon Vijay Abraham I * handled. 105885d5e707SKishon Vijay Abraham I */ 105985d5e707SKishon Vijay Abraham I if (dep->stream_capable) { 106085d5e707SKishon Vijay Abraham I int ret; 106185d5e707SKishon Vijay Abraham I 106285d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_kick_transfer(dep, 0, true); 106385d5e707SKishon Vijay Abraham I if (ret && ret != -EBUSY) { 106485d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s: failed to kick transfers\n", 106585d5e707SKishon Vijay Abraham I dep->name); 106685d5e707SKishon Vijay Abraham I } 106785d5e707SKishon Vijay Abraham I } 106885d5e707SKishon Vijay Abraham I 106985d5e707SKishon Vijay Abraham I return 0; 107085d5e707SKishon Vijay Abraham I } 107185d5e707SKishon Vijay Abraham I 107285d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, 107385d5e707SKishon Vijay Abraham I gfp_t gfp_flags) 107485d5e707SKishon Vijay Abraham I { 107585d5e707SKishon Vijay Abraham I struct dwc3_request *req = to_dwc3_request(request); 107685d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep); 107785d5e707SKishon Vijay Abraham I 107885d5e707SKishon Vijay Abraham I unsigned long flags; 107985d5e707SKishon Vijay Abraham I 108085d5e707SKishon Vijay Abraham I int ret; 108185d5e707SKishon Vijay Abraham I 108285d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 108385d5e707SKishon Vijay Abraham I if (!dep->endpoint.desc) { 108485d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", 108585d5e707SKishon Vijay Abraham I request, ep->name); 108685d5e707SKishon Vijay Abraham I ret = -ESHUTDOWN; 108785d5e707SKishon Vijay Abraham I goto out; 108885d5e707SKishon Vijay Abraham I } 108985d5e707SKishon Vijay Abraham I 1090747a0a5bSKishon Vijay Abraham I if (req->dep != dep) { 1091747a0a5bSKishon Vijay Abraham I WARN(true, "request %p belongs to '%s'\n", 1092747a0a5bSKishon Vijay Abraham I request, req->dep->name); 109385d5e707SKishon Vijay Abraham I ret = -EINVAL; 109485d5e707SKishon Vijay Abraham I goto out; 109585d5e707SKishon Vijay Abraham I } 109685d5e707SKishon Vijay Abraham I 109785d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", 109885d5e707SKishon Vijay Abraham I request, ep->name, request->length); 109985d5e707SKishon Vijay Abraham I 110085d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_queue(dep, req); 110185d5e707SKishon Vijay Abraham I 110285d5e707SKishon Vijay Abraham I out: 110385d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 110485d5e707SKishon Vijay Abraham I 110585d5e707SKishon Vijay Abraham I return ret; 110685d5e707SKishon Vijay Abraham I } 110785d5e707SKishon Vijay Abraham I 110885d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, 110985d5e707SKishon Vijay Abraham I struct usb_request *request) 111085d5e707SKishon Vijay Abraham I { 111185d5e707SKishon Vijay Abraham I struct dwc3_request *req = to_dwc3_request(request); 111285d5e707SKishon Vijay Abraham I struct dwc3_request *r = NULL; 111385d5e707SKishon Vijay Abraham I 111485d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep); 111585d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc; 111685d5e707SKishon Vijay Abraham I 111785d5e707SKishon Vijay Abraham I unsigned long flags; 111885d5e707SKishon Vijay Abraham I int ret = 0; 111985d5e707SKishon Vijay Abraham I 112085d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 112185d5e707SKishon Vijay Abraham I 112285d5e707SKishon Vijay Abraham I list_for_each_entry(r, &dep->request_list, list) { 112385d5e707SKishon Vijay Abraham I if (r == req) 112485d5e707SKishon Vijay Abraham I break; 112585d5e707SKishon Vijay Abraham I } 112685d5e707SKishon Vijay Abraham I 112785d5e707SKishon Vijay Abraham I if (r != req) { 112885d5e707SKishon Vijay Abraham I list_for_each_entry(r, &dep->req_queued, list) { 112985d5e707SKishon Vijay Abraham I if (r == req) 113085d5e707SKishon Vijay Abraham I break; 113185d5e707SKishon Vijay Abraham I } 113285d5e707SKishon Vijay Abraham I if (r == req) { 113385d5e707SKishon Vijay Abraham I /* wait until it is processed */ 113485d5e707SKishon Vijay Abraham I dwc3_stop_active_transfer(dwc, dep->number, true); 113585d5e707SKishon Vijay Abraham I goto out1; 113685d5e707SKishon Vijay Abraham I } 113785d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "request %p was not queued to %s\n", 113885d5e707SKishon Vijay Abraham I request, ep->name); 113985d5e707SKishon Vijay Abraham I ret = -EINVAL; 114085d5e707SKishon Vijay Abraham I goto out0; 114185d5e707SKishon Vijay Abraham I } 114285d5e707SKishon Vijay Abraham I 114385d5e707SKishon Vijay Abraham I out1: 114485d5e707SKishon Vijay Abraham I /* giveback the request */ 114585d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(dep, req, -ECONNRESET); 114685d5e707SKishon Vijay Abraham I 114785d5e707SKishon Vijay Abraham I out0: 114885d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 114985d5e707SKishon Vijay Abraham I 115085d5e707SKishon Vijay Abraham I return ret; 115185d5e707SKishon Vijay Abraham I } 115285d5e707SKishon Vijay Abraham I 115385d5e707SKishon Vijay Abraham I int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) 115485d5e707SKishon Vijay Abraham I { 115585d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params; 115685d5e707SKishon Vijay Abraham I struct dwc3 *dwc = dep->dwc; 115785d5e707SKishon Vijay Abraham I int ret; 115885d5e707SKishon Vijay Abraham I 115985d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 116085d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); 116185d5e707SKishon Vijay Abraham I return -EINVAL; 116285d5e707SKishon Vijay Abraham I } 116385d5e707SKishon Vijay Abraham I 116485d5e707SKishon Vijay Abraham I memset(¶ms, 0x00, sizeof(params)); 116585d5e707SKishon Vijay Abraham I 116685d5e707SKishon Vijay Abraham I if (value) { 116785d5e707SKishon Vijay Abraham I if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) || 116885d5e707SKishon Vijay Abraham I (!list_empty(&dep->req_queued) || 116985d5e707SKishon Vijay Abraham I !list_empty(&dep->request_list)))) { 117085d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s: pending request, cannot halt\n", 117185d5e707SKishon Vijay Abraham I dep->name); 117285d5e707SKishon Vijay Abraham I return -EAGAIN; 117385d5e707SKishon Vijay Abraham I } 117485d5e707SKishon Vijay Abraham I 117585d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, 117685d5e707SKishon Vijay Abraham I DWC3_DEPCMD_SETSTALL, ¶ms); 117785d5e707SKishon Vijay Abraham I if (ret) 117885d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to set STALL on %s\n", 117985d5e707SKishon Vijay Abraham I dep->name); 118085d5e707SKishon Vijay Abraham I else 118185d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_STALL; 118285d5e707SKishon Vijay Abraham I } else { 118385d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, 118485d5e707SKishon Vijay Abraham I DWC3_DEPCMD_CLEARSTALL, ¶ms); 118585d5e707SKishon Vijay Abraham I if (ret) 118685d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to clear STALL on %s\n", 118785d5e707SKishon Vijay Abraham I dep->name); 118885d5e707SKishon Vijay Abraham I else 118985d5e707SKishon Vijay Abraham I dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE); 119085d5e707SKishon Vijay Abraham I } 119185d5e707SKishon Vijay Abraham I 119285d5e707SKishon Vijay Abraham I return ret; 119385d5e707SKishon Vijay Abraham I } 119485d5e707SKishon Vijay Abraham I 119585d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) 119685d5e707SKishon Vijay Abraham I { 119785d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep); 119885d5e707SKishon Vijay Abraham I 119985d5e707SKishon Vijay Abraham I unsigned long flags; 120085d5e707SKishon Vijay Abraham I 120185d5e707SKishon Vijay Abraham I int ret; 120285d5e707SKishon Vijay Abraham I 120385d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 120485d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_set_halt(dep, value, false); 120585d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 120685d5e707SKishon Vijay Abraham I 120785d5e707SKishon Vijay Abraham I return ret; 120885d5e707SKishon Vijay Abraham I } 120985d5e707SKishon Vijay Abraham I 121085d5e707SKishon Vijay Abraham I static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) 121185d5e707SKishon Vijay Abraham I { 121285d5e707SKishon Vijay Abraham I struct dwc3_ep *dep = to_dwc3_ep(ep); 121385d5e707SKishon Vijay Abraham I unsigned long flags; 121485d5e707SKishon Vijay Abraham I int ret; 121585d5e707SKishon Vijay Abraham I 121685d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 121785d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_WEDGE; 121885d5e707SKishon Vijay Abraham I 121985d5e707SKishon Vijay Abraham I if (dep->number == 0 || dep->number == 1) 122085d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep0_set_halt(ep, 1); 122185d5e707SKishon Vijay Abraham I else 122285d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_set_halt(dep, 1, false); 122385d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 122485d5e707SKishon Vijay Abraham I 122585d5e707SKishon Vijay Abraham I return ret; 122685d5e707SKishon Vijay Abraham I } 122785d5e707SKishon Vijay Abraham I 122885d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */ 122985d5e707SKishon Vijay Abraham I 123085d5e707SKishon Vijay Abraham I static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { 123185d5e707SKishon Vijay Abraham I .bLength = USB_DT_ENDPOINT_SIZE, 123285d5e707SKishon Vijay Abraham I .bDescriptorType = USB_DT_ENDPOINT, 123385d5e707SKishon Vijay Abraham I .bmAttributes = USB_ENDPOINT_XFER_CONTROL, 123485d5e707SKishon Vijay Abraham I }; 123585d5e707SKishon Vijay Abraham I 123685d5e707SKishon Vijay Abraham I static const struct usb_ep_ops dwc3_gadget_ep0_ops = { 123785d5e707SKishon Vijay Abraham I .enable = dwc3_gadget_ep0_enable, 123885d5e707SKishon Vijay Abraham I .disable = dwc3_gadget_ep0_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_ep0_queue, 124285d5e707SKishon Vijay Abraham I .dequeue = dwc3_gadget_ep_dequeue, 124385d5e707SKishon Vijay Abraham I .set_halt = dwc3_gadget_ep0_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 static const struct usb_ep_ops dwc3_gadget_ep_ops = { 124885d5e707SKishon Vijay Abraham I .enable = dwc3_gadget_ep_enable, 124985d5e707SKishon Vijay Abraham I .disable = dwc3_gadget_ep_disable, 125085d5e707SKishon Vijay Abraham I .alloc_request = dwc3_gadget_ep_alloc_request, 125185d5e707SKishon Vijay Abraham I .free_request = dwc3_gadget_ep_free_request, 125285d5e707SKishon Vijay Abraham I .queue = dwc3_gadget_ep_queue, 125385d5e707SKishon Vijay Abraham I .dequeue = dwc3_gadget_ep_dequeue, 125485d5e707SKishon Vijay Abraham I .set_halt = dwc3_gadget_ep_set_halt, 125585d5e707SKishon Vijay Abraham I .set_wedge = dwc3_gadget_ep_set_wedge, 125685d5e707SKishon Vijay Abraham I }; 125785d5e707SKishon Vijay Abraham I 125885d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */ 125985d5e707SKishon Vijay Abraham I 126085d5e707SKishon Vijay Abraham I static int dwc3_gadget_get_frame(struct usb_gadget *g) 126185d5e707SKishon Vijay Abraham I { 126285d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 126385d5e707SKishon Vijay Abraham I u32 reg; 126485d5e707SKishon Vijay Abraham I 126585d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DSTS); 126685d5e707SKishon Vijay Abraham I return DWC3_DSTS_SOFFN(reg); 126785d5e707SKishon Vijay Abraham I } 126885d5e707SKishon Vijay Abraham I 126985d5e707SKishon Vijay Abraham I static int dwc3_gadget_wakeup(struct usb_gadget *g) 127085d5e707SKishon Vijay Abraham I { 127185d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 127285d5e707SKishon Vijay Abraham I 127385d5e707SKishon Vijay Abraham I unsigned long timeout; 127485d5e707SKishon Vijay Abraham I unsigned long flags; 127585d5e707SKishon Vijay Abraham I 127685d5e707SKishon Vijay Abraham I u32 reg; 127785d5e707SKishon Vijay Abraham I 127885d5e707SKishon Vijay Abraham I int ret = 0; 127985d5e707SKishon Vijay Abraham I 128085d5e707SKishon Vijay Abraham I u8 link_state; 128185d5e707SKishon Vijay Abraham I u8 speed; 128285d5e707SKishon Vijay Abraham I 128385d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 128485d5e707SKishon Vijay Abraham I 128585d5e707SKishon Vijay Abraham I /* 128685d5e707SKishon Vijay Abraham I * According to the Databook Remote wakeup request should 128785d5e707SKishon Vijay Abraham I * be issued only when the device is in early suspend state. 128885d5e707SKishon Vijay Abraham I * 128985d5e707SKishon Vijay Abraham I * We can check that via USB Link State bits in DSTS register. 129085d5e707SKishon Vijay Abraham I */ 129185d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DSTS); 129285d5e707SKishon Vijay Abraham I 129385d5e707SKishon Vijay Abraham I speed = reg & DWC3_DSTS_CONNECTSPD; 129485d5e707SKishon Vijay Abraham I if (speed == DWC3_DSTS_SUPERSPEED) { 129585d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); 129685d5e707SKishon Vijay Abraham I ret = -EINVAL; 129785d5e707SKishon Vijay Abraham I goto out; 129885d5e707SKishon Vijay Abraham I } 129985d5e707SKishon Vijay Abraham I 130085d5e707SKishon Vijay Abraham I link_state = DWC3_DSTS_USBLNKST(reg); 130185d5e707SKishon Vijay Abraham I 130285d5e707SKishon Vijay Abraham I switch (link_state) { 130385d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ 130485d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ 130585d5e707SKishon Vijay Abraham I break; 130685d5e707SKishon Vijay Abraham I default: 130785d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "can't wakeup from link state %d\n", 130885d5e707SKishon Vijay Abraham I link_state); 130985d5e707SKishon Vijay Abraham I ret = -EINVAL; 131085d5e707SKishon Vijay Abraham I goto out; 131185d5e707SKishon Vijay Abraham I } 131285d5e707SKishon Vijay Abraham I 131385d5e707SKishon Vijay Abraham I ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV); 131485d5e707SKishon Vijay Abraham I if (ret < 0) { 131585d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to put link in Recovery\n"); 131685d5e707SKishon Vijay Abraham I goto out; 131785d5e707SKishon Vijay Abraham I } 131885d5e707SKishon Vijay Abraham I 131985d5e707SKishon Vijay Abraham I /* Recent versions do this automatically */ 132085d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_194A) { 132185d5e707SKishon Vijay Abraham I /* write zeroes to Link Change Request */ 132285d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 132385d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; 132485d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 132585d5e707SKishon Vijay Abraham I } 132685d5e707SKishon Vijay Abraham I 132785d5e707SKishon Vijay Abraham I /* poll until Link State changes to ON */ 1328747a0a5bSKishon Vijay Abraham I timeout = 1000; 132985d5e707SKishon Vijay Abraham I 1330747a0a5bSKishon Vijay Abraham I while (timeout--) { 133185d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DSTS); 133285d5e707SKishon Vijay Abraham I 133385d5e707SKishon Vijay Abraham I /* in HS, means ON */ 133485d5e707SKishon Vijay Abraham I if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) 133585d5e707SKishon Vijay Abraham I break; 133685d5e707SKishon Vijay Abraham I } 133785d5e707SKishon Vijay Abraham I 133885d5e707SKishon Vijay Abraham I if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { 133985d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to send remote wakeup\n"); 134085d5e707SKishon Vijay Abraham I ret = -EINVAL; 134185d5e707SKishon Vijay Abraham I } 134285d5e707SKishon Vijay Abraham I 134385d5e707SKishon Vijay Abraham I out: 134485d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 134585d5e707SKishon Vijay Abraham I 134685d5e707SKishon Vijay Abraham I return ret; 134785d5e707SKishon Vijay Abraham I } 134885d5e707SKishon Vijay Abraham I 134985d5e707SKishon Vijay Abraham I static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, 135085d5e707SKishon Vijay Abraham I int is_selfpowered) 135185d5e707SKishon Vijay Abraham I { 135285d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 135385d5e707SKishon Vijay Abraham I unsigned long flags; 135485d5e707SKishon Vijay Abraham I 135585d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 135685d5e707SKishon Vijay Abraham I dwc->is_selfpowered = !!is_selfpowered; 135785d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 135885d5e707SKishon Vijay Abraham I 135985d5e707SKishon Vijay Abraham I return 0; 136085d5e707SKishon Vijay Abraham I } 136185d5e707SKishon Vijay Abraham I 136285d5e707SKishon Vijay Abraham I static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) 136385d5e707SKishon Vijay Abraham I { 136485d5e707SKishon Vijay Abraham I u32 reg; 136585d5e707SKishon Vijay Abraham I u32 timeout = 500; 136685d5e707SKishon Vijay Abraham I 136785d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 136885d5e707SKishon Vijay Abraham I if (is_on) { 136985d5e707SKishon Vijay Abraham I if (dwc->revision <= DWC3_REVISION_187A) { 137085d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_TRGTULST_MASK; 137185d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_TRGTULST_RX_DET; 137285d5e707SKishon Vijay Abraham I } 137385d5e707SKishon Vijay Abraham I 137485d5e707SKishon Vijay Abraham I if (dwc->revision >= DWC3_REVISION_194A) 137585d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_KEEP_CONNECT; 137685d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_RUN_STOP; 137785d5e707SKishon Vijay Abraham I 137885d5e707SKishon Vijay Abraham I if (dwc->has_hibernation) 137985d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_KEEP_CONNECT; 138085d5e707SKishon Vijay Abraham I 138185d5e707SKishon Vijay Abraham I dwc->pullups_connected = true; 138285d5e707SKishon Vijay Abraham I } else { 138385d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_RUN_STOP; 138485d5e707SKishon Vijay Abraham I 138585d5e707SKishon Vijay Abraham I if (dwc->has_hibernation && !suspend) 138685d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_KEEP_CONNECT; 138785d5e707SKishon Vijay Abraham I 138885d5e707SKishon Vijay Abraham I dwc->pullups_connected = false; 138985d5e707SKishon Vijay Abraham I } 139085d5e707SKishon Vijay Abraham I 139185d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 139285d5e707SKishon Vijay Abraham I 139385d5e707SKishon Vijay Abraham I do { 139485d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DSTS); 139585d5e707SKishon Vijay Abraham I if (is_on) { 139685d5e707SKishon Vijay Abraham I if (!(reg & DWC3_DSTS_DEVCTRLHLT)) 139785d5e707SKishon Vijay Abraham I break; 139885d5e707SKishon Vijay Abraham I } else { 139985d5e707SKishon Vijay Abraham I if (reg & DWC3_DSTS_DEVCTRLHLT) 140085d5e707SKishon Vijay Abraham I break; 140185d5e707SKishon Vijay Abraham I } 140285d5e707SKishon Vijay Abraham I timeout--; 140385d5e707SKishon Vijay Abraham I if (!timeout) 140485d5e707SKishon Vijay Abraham I return -ETIMEDOUT; 140585d5e707SKishon Vijay Abraham I udelay(1); 140685d5e707SKishon Vijay Abraham I } while (1); 140785d5e707SKishon Vijay Abraham I 140885d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", 140985d5e707SKishon Vijay Abraham I dwc->gadget_driver 141085d5e707SKishon Vijay Abraham I ? dwc->gadget_driver->function : "no-function", 141185d5e707SKishon Vijay Abraham I is_on ? "connect" : "disconnect"); 141285d5e707SKishon Vijay Abraham I 141385d5e707SKishon Vijay Abraham I return 0; 141485d5e707SKishon Vijay Abraham I } 141585d5e707SKishon Vijay Abraham I 141685d5e707SKishon Vijay Abraham I static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) 141785d5e707SKishon Vijay Abraham I { 141885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 141985d5e707SKishon Vijay Abraham I unsigned long flags; 142085d5e707SKishon Vijay Abraham I int ret; 142185d5e707SKishon Vijay Abraham I 142285d5e707SKishon Vijay Abraham I is_on = !!is_on; 142385d5e707SKishon Vijay Abraham I 142485d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 142585d5e707SKishon Vijay Abraham I ret = dwc3_gadget_run_stop(dwc, is_on, false); 142685d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 142785d5e707SKishon Vijay Abraham I 142885d5e707SKishon Vijay Abraham I return ret; 142985d5e707SKishon Vijay Abraham I } 143085d5e707SKishon Vijay Abraham I 143185d5e707SKishon Vijay Abraham I static void dwc3_gadget_enable_irq(struct dwc3 *dwc) 143285d5e707SKishon Vijay Abraham I { 143385d5e707SKishon Vijay Abraham I u32 reg; 143485d5e707SKishon Vijay Abraham I 143585d5e707SKishon Vijay Abraham I /* Enable all but Start and End of Frame IRQs */ 143685d5e707SKishon Vijay Abraham I reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | 143785d5e707SKishon Vijay Abraham I DWC3_DEVTEN_EVNTOVERFLOWEN | 143885d5e707SKishon Vijay Abraham I DWC3_DEVTEN_CMDCMPLTEN | 143985d5e707SKishon Vijay Abraham I DWC3_DEVTEN_ERRTICERREN | 144085d5e707SKishon Vijay Abraham I DWC3_DEVTEN_WKUPEVTEN | 144185d5e707SKishon Vijay Abraham I DWC3_DEVTEN_ULSTCNGEN | 144285d5e707SKishon Vijay Abraham I DWC3_DEVTEN_CONNECTDONEEN | 144385d5e707SKishon Vijay Abraham I DWC3_DEVTEN_USBRSTEN | 144485d5e707SKishon Vijay Abraham I DWC3_DEVTEN_DISCONNEVTEN); 144585d5e707SKishon Vijay Abraham I 144685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); 144785d5e707SKishon Vijay Abraham I } 144885d5e707SKishon Vijay Abraham I 144985d5e707SKishon Vijay Abraham I static void dwc3_gadget_disable_irq(struct dwc3 *dwc) 145085d5e707SKishon Vijay Abraham I { 145185d5e707SKishon Vijay Abraham I /* mask all interrupts */ 145285d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); 145385d5e707SKishon Vijay Abraham I } 145485d5e707SKishon Vijay Abraham I 145585d5e707SKishon Vijay Abraham I static int dwc3_gadget_start(struct usb_gadget *g, 145685d5e707SKishon Vijay Abraham I struct usb_gadget_driver *driver) 145785d5e707SKishon Vijay Abraham I { 145885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 145985d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 146085d5e707SKishon Vijay Abraham I unsigned long flags; 146185d5e707SKishon Vijay Abraham I int ret = 0; 146285d5e707SKishon Vijay Abraham I u32 reg; 146385d5e707SKishon Vijay Abraham I 146485d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 146585d5e707SKishon Vijay Abraham I 146685d5e707SKishon Vijay Abraham I if (dwc->gadget_driver) { 146785d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "%s is already bound to %s\n", 146885d5e707SKishon Vijay Abraham I dwc->gadget.name, 1469747a0a5bSKishon Vijay Abraham I dwc->gadget_driver->function); 147085d5e707SKishon Vijay Abraham I ret = -EBUSY; 147185d5e707SKishon Vijay Abraham I goto err1; 147285d5e707SKishon Vijay Abraham I } 147385d5e707SKishon Vijay Abraham I 147485d5e707SKishon Vijay Abraham I dwc->gadget_driver = driver; 147585d5e707SKishon Vijay Abraham I 147685d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCFG); 147785d5e707SKishon Vijay Abraham I reg &= ~(DWC3_DCFG_SPEED_MASK); 147885d5e707SKishon Vijay Abraham I 147985d5e707SKishon Vijay Abraham I /** 148085d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 revision < 2.20a have an issue 148185d5e707SKishon Vijay Abraham I * which would cause metastability state on Run/Stop 148285d5e707SKishon Vijay Abraham I * bit if we try to force the IP to USB2-only mode. 148385d5e707SKishon Vijay Abraham I * 148485d5e707SKishon Vijay Abraham I * Because of that, we cannot configure the IP to any 148585d5e707SKishon Vijay Abraham I * speed other than the SuperSpeed 148685d5e707SKishon Vijay Abraham I * 148785d5e707SKishon Vijay Abraham I * Refers to: 148885d5e707SKishon Vijay Abraham I * 148985d5e707SKishon Vijay Abraham I * STAR#9000525659: Clock Domain Crossing on DCTL in 149085d5e707SKishon Vijay Abraham I * USB 2.0 Mode 149185d5e707SKishon Vijay Abraham I */ 149285d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_220A) { 149385d5e707SKishon Vijay Abraham I reg |= DWC3_DCFG_SUPERSPEED; 149485d5e707SKishon Vijay Abraham I } else { 149585d5e707SKishon Vijay Abraham I switch (dwc->maximum_speed) { 149685d5e707SKishon Vijay Abraham I case USB_SPEED_LOW: 149785d5e707SKishon Vijay Abraham I reg |= DWC3_DSTS_LOWSPEED; 149885d5e707SKishon Vijay Abraham I break; 149985d5e707SKishon Vijay Abraham I case USB_SPEED_FULL: 150085d5e707SKishon Vijay Abraham I reg |= DWC3_DSTS_FULLSPEED1; 150185d5e707SKishon Vijay Abraham I break; 150285d5e707SKishon Vijay Abraham I case USB_SPEED_HIGH: 150385d5e707SKishon Vijay Abraham I reg |= DWC3_DSTS_HIGHSPEED; 150485d5e707SKishon Vijay Abraham I break; 150585d5e707SKishon Vijay Abraham I case USB_SPEED_SUPER: /* FALLTHROUGH */ 150685d5e707SKishon Vijay Abraham I case USB_SPEED_UNKNOWN: /* FALTHROUGH */ 150785d5e707SKishon Vijay Abraham I default: 150885d5e707SKishon Vijay Abraham I reg |= DWC3_DSTS_SUPERSPEED; 150985d5e707SKishon Vijay Abraham I } 151085d5e707SKishon Vijay Abraham I } 151185d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCFG, reg); 151285d5e707SKishon Vijay Abraham I 151385d5e707SKishon Vijay Abraham I dwc->start_config_issued = false; 151485d5e707SKishon Vijay Abraham I 151585d5e707SKishon Vijay Abraham I /* Start with SuperSpeed Default */ 151685d5e707SKishon Vijay Abraham I dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); 151785d5e707SKishon Vijay Abraham I 151885d5e707SKishon Vijay Abraham I dep = dwc->eps[0]; 151985d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, 152085d5e707SKishon Vijay Abraham I false); 152185d5e707SKishon Vijay Abraham I if (ret) { 152285d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to enable %s\n", dep->name); 152385d5e707SKishon Vijay Abraham I goto err2; 152485d5e707SKishon Vijay Abraham I } 152585d5e707SKishon Vijay Abraham I 152685d5e707SKishon Vijay Abraham I dep = dwc->eps[1]; 152785d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, 152885d5e707SKishon Vijay Abraham I false); 152985d5e707SKishon Vijay Abraham I if (ret) { 153085d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to enable %s\n", dep->name); 153185d5e707SKishon Vijay Abraham I goto err3; 153285d5e707SKishon Vijay Abraham I } 153385d5e707SKishon Vijay Abraham I 153485d5e707SKishon Vijay Abraham I /* begin to receive SETUP packets */ 153585d5e707SKishon Vijay Abraham I dwc->ep0state = EP0_SETUP_PHASE; 153685d5e707SKishon Vijay Abraham I dwc3_ep0_out_start(dwc); 153785d5e707SKishon Vijay Abraham I 153885d5e707SKishon Vijay Abraham I dwc3_gadget_enable_irq(dwc); 153985d5e707SKishon Vijay Abraham I 154085d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 154185d5e707SKishon Vijay Abraham I 154285d5e707SKishon Vijay Abraham I return 0; 154385d5e707SKishon Vijay Abraham I 154485d5e707SKishon Vijay Abraham I err3: 154585d5e707SKishon Vijay Abraham I __dwc3_gadget_ep_disable(dwc->eps[0]); 154685d5e707SKishon Vijay Abraham I 154785d5e707SKishon Vijay Abraham I err2: 154885d5e707SKishon Vijay Abraham I dwc->gadget_driver = NULL; 154985d5e707SKishon Vijay Abraham I 155085d5e707SKishon Vijay Abraham I err1: 155185d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 155285d5e707SKishon Vijay Abraham I 155385d5e707SKishon Vijay Abraham I return ret; 155485d5e707SKishon Vijay Abraham I } 155585d5e707SKishon Vijay Abraham I 155685d5e707SKishon Vijay Abraham I static int dwc3_gadget_stop(struct usb_gadget *g) 155785d5e707SKishon Vijay Abraham I { 155885d5e707SKishon Vijay Abraham I struct dwc3 *dwc = gadget_to_dwc(g); 155985d5e707SKishon Vijay Abraham I unsigned long flags; 156085d5e707SKishon Vijay Abraham I 156185d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 156285d5e707SKishon Vijay Abraham I 156385d5e707SKishon Vijay Abraham I dwc3_gadget_disable_irq(dwc); 156485d5e707SKishon Vijay Abraham I __dwc3_gadget_ep_disable(dwc->eps[0]); 156585d5e707SKishon Vijay Abraham I __dwc3_gadget_ep_disable(dwc->eps[1]); 156685d5e707SKishon Vijay Abraham I 156785d5e707SKishon Vijay Abraham I dwc->gadget_driver = NULL; 156885d5e707SKishon Vijay Abraham I 156985d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 157085d5e707SKishon Vijay Abraham I 157185d5e707SKishon Vijay Abraham I return 0; 157285d5e707SKishon Vijay Abraham I } 157385d5e707SKishon Vijay Abraham I 157485d5e707SKishon Vijay Abraham I static const struct usb_gadget_ops dwc3_gadget_ops = { 157585d5e707SKishon Vijay Abraham I .get_frame = dwc3_gadget_get_frame, 157685d5e707SKishon Vijay Abraham I .wakeup = dwc3_gadget_wakeup, 157785d5e707SKishon Vijay Abraham I .set_selfpowered = dwc3_gadget_set_selfpowered, 157885d5e707SKishon Vijay Abraham I .pullup = dwc3_gadget_pullup, 157985d5e707SKishon Vijay Abraham I .udc_start = dwc3_gadget_start, 158085d5e707SKishon Vijay Abraham I .udc_stop = dwc3_gadget_stop, 158185d5e707SKishon Vijay Abraham I }; 158285d5e707SKishon Vijay Abraham I 158385d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */ 158485d5e707SKishon Vijay Abraham I 158585d5e707SKishon Vijay Abraham I static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, 158685d5e707SKishon Vijay Abraham I u8 num, u32 direction) 158785d5e707SKishon Vijay Abraham I { 158885d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 158985d5e707SKishon Vijay Abraham I u8 i; 159085d5e707SKishon Vijay Abraham I 159185d5e707SKishon Vijay Abraham I for (i = 0; i < num; i++) { 159285d5e707SKishon Vijay Abraham I u8 epnum = (i << 1) | (!!direction); 159385d5e707SKishon Vijay Abraham I 159485d5e707SKishon Vijay Abraham I dep = kzalloc(sizeof(*dep), GFP_KERNEL); 159585d5e707SKishon Vijay Abraham I if (!dep) 159685d5e707SKishon Vijay Abraham I return -ENOMEM; 159785d5e707SKishon Vijay Abraham I 159885d5e707SKishon Vijay Abraham I dep->dwc = dwc; 159985d5e707SKishon Vijay Abraham I dep->number = epnum; 160085d5e707SKishon Vijay Abraham I dep->direction = !!direction; 160185d5e707SKishon Vijay Abraham I dwc->eps[epnum] = dep; 160285d5e707SKishon Vijay Abraham I 160385d5e707SKishon Vijay Abraham I snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, 160485d5e707SKishon Vijay Abraham I (epnum & 1) ? "in" : "out"); 160585d5e707SKishon Vijay Abraham I 160685d5e707SKishon Vijay Abraham I dep->endpoint.name = dep->name; 160785d5e707SKishon Vijay Abraham I 160885d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "initializing %s\n", dep->name); 160985d5e707SKishon Vijay Abraham I 161085d5e707SKishon Vijay Abraham I if (epnum == 0 || epnum == 1) { 161185d5e707SKishon Vijay Abraham I usb_ep_set_maxpacket_limit(&dep->endpoint, 512); 161285d5e707SKishon Vijay Abraham I dep->endpoint.maxburst = 1; 161385d5e707SKishon Vijay Abraham I dep->endpoint.ops = &dwc3_gadget_ep0_ops; 161485d5e707SKishon Vijay Abraham I if (!epnum) 161585d5e707SKishon Vijay Abraham I dwc->gadget.ep0 = &dep->endpoint; 161685d5e707SKishon Vijay Abraham I } else { 161785d5e707SKishon Vijay Abraham I int ret; 161885d5e707SKishon Vijay Abraham I 161985d5e707SKishon Vijay Abraham I usb_ep_set_maxpacket_limit(&dep->endpoint, 1024); 162085d5e707SKishon Vijay Abraham I dep->endpoint.max_streams = 15; 162185d5e707SKishon Vijay Abraham I dep->endpoint.ops = &dwc3_gadget_ep_ops; 162285d5e707SKishon Vijay Abraham I list_add_tail(&dep->endpoint.ep_list, 162385d5e707SKishon Vijay Abraham I &dwc->gadget.ep_list); 162485d5e707SKishon Vijay Abraham I 162585d5e707SKishon Vijay Abraham I ret = dwc3_alloc_trb_pool(dep); 162685d5e707SKishon Vijay Abraham I if (ret) 162785d5e707SKishon Vijay Abraham I return ret; 162885d5e707SKishon Vijay Abraham I } 162985d5e707SKishon Vijay Abraham I 163085d5e707SKishon Vijay Abraham I INIT_LIST_HEAD(&dep->request_list); 163185d5e707SKishon Vijay Abraham I INIT_LIST_HEAD(&dep->req_queued); 163285d5e707SKishon Vijay Abraham I } 163385d5e707SKishon Vijay Abraham I 163485d5e707SKishon Vijay Abraham I return 0; 163585d5e707SKishon Vijay Abraham I } 163685d5e707SKishon Vijay Abraham I 163785d5e707SKishon Vijay Abraham I static int dwc3_gadget_init_endpoints(struct dwc3 *dwc) 163885d5e707SKishon Vijay Abraham I { 163985d5e707SKishon Vijay Abraham I int ret; 164085d5e707SKishon Vijay Abraham I 164185d5e707SKishon Vijay Abraham I INIT_LIST_HEAD(&dwc->gadget.ep_list); 164285d5e707SKishon Vijay Abraham I 164385d5e707SKishon Vijay Abraham I ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0); 164485d5e707SKishon Vijay Abraham I if (ret < 0) { 164585d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n"); 164685d5e707SKishon Vijay Abraham I return ret; 164785d5e707SKishon Vijay Abraham I } 164885d5e707SKishon Vijay Abraham I 164985d5e707SKishon Vijay Abraham I ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1); 165085d5e707SKishon Vijay Abraham I if (ret < 0) { 165185d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n"); 165285d5e707SKishon Vijay Abraham I return ret; 165385d5e707SKishon Vijay Abraham I } 165485d5e707SKishon Vijay Abraham I 165585d5e707SKishon Vijay Abraham I return 0; 165685d5e707SKishon Vijay Abraham I } 165785d5e707SKishon Vijay Abraham I 165885d5e707SKishon Vijay Abraham I static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) 165985d5e707SKishon Vijay Abraham I { 166085d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 166185d5e707SKishon Vijay Abraham I u8 epnum; 166285d5e707SKishon Vijay Abraham I 166385d5e707SKishon Vijay Abraham I for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { 166485d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum]; 166585d5e707SKishon Vijay Abraham I if (!dep) 166685d5e707SKishon Vijay Abraham I continue; 166785d5e707SKishon Vijay Abraham I /* 166885d5e707SKishon Vijay Abraham I * Physical endpoints 0 and 1 are special; they form the 166985d5e707SKishon Vijay Abraham I * bi-directional USB endpoint 0. 167085d5e707SKishon Vijay Abraham I * 167185d5e707SKishon Vijay Abraham I * For those two physical endpoints, we don't allocate a TRB 167285d5e707SKishon Vijay Abraham I * pool nor do we add them the endpoints list. Due to that, we 167385d5e707SKishon Vijay Abraham I * shouldn't do these two operations otherwise we would end up 167485d5e707SKishon Vijay Abraham I * with all sorts of bugs when removing dwc3.ko. 167585d5e707SKishon Vijay Abraham I */ 167685d5e707SKishon Vijay Abraham I if (epnum != 0 && epnum != 1) { 167785d5e707SKishon Vijay Abraham I dwc3_free_trb_pool(dep); 167885d5e707SKishon Vijay Abraham I list_del(&dep->endpoint.ep_list); 167985d5e707SKishon Vijay Abraham I } 168085d5e707SKishon Vijay Abraham I 168185d5e707SKishon Vijay Abraham I kfree(dep); 168285d5e707SKishon Vijay Abraham I } 168385d5e707SKishon Vijay Abraham I } 168485d5e707SKishon Vijay Abraham I 168585d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */ 168685d5e707SKishon Vijay Abraham I 168785d5e707SKishon Vijay Abraham I static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, 168885d5e707SKishon Vijay Abraham I struct dwc3_request *req, struct dwc3_trb *trb, 168985d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event, int status) 169085d5e707SKishon Vijay Abraham I { 169185d5e707SKishon Vijay Abraham I unsigned int count; 169285d5e707SKishon Vijay Abraham I unsigned int s_pkt = 0; 169385d5e707SKishon Vijay Abraham I unsigned int trb_status; 169485d5e707SKishon Vijay Abraham I 169585d5e707SKishon Vijay Abraham I if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) 169685d5e707SKishon Vijay Abraham I /* 169785d5e707SKishon Vijay Abraham I * We continue despite the error. There is not much we 169885d5e707SKishon Vijay Abraham I * can do. If we don't clean it up we loop forever. If 169985d5e707SKishon Vijay Abraham I * we skip the TRB then it gets overwritten after a 170085d5e707SKishon Vijay Abraham I * while since we use them in a ring buffer. A BUG() 170185d5e707SKishon Vijay Abraham I * would help. Lets hope that if this occurs, someone 170285d5e707SKishon Vijay Abraham I * fixes the root cause instead of looking away :) 170385d5e707SKishon Vijay Abraham I */ 170485d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", 170585d5e707SKishon Vijay Abraham I dep->name, trb); 170685d5e707SKishon Vijay Abraham I count = trb->size & DWC3_TRB_SIZE_MASK; 170785d5e707SKishon Vijay Abraham I 170885d5e707SKishon Vijay Abraham I if (dep->direction) { 170985d5e707SKishon Vijay Abraham I if (count) { 171085d5e707SKishon Vijay Abraham I trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); 171185d5e707SKishon Vijay Abraham I if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { 171285d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "incomplete IN transfer %s\n", 171385d5e707SKishon Vijay Abraham I dep->name); 171485d5e707SKishon Vijay Abraham I /* 171585d5e707SKishon Vijay Abraham I * If missed isoc occurred and there is 171685d5e707SKishon Vijay Abraham I * no request queued then issue END 171785d5e707SKishon Vijay Abraham I * TRANSFER, so that core generates 171885d5e707SKishon Vijay Abraham I * next xfernotready and we will issue 171985d5e707SKishon Vijay Abraham I * a fresh START TRANSFER. 172085d5e707SKishon Vijay Abraham I * If there are still queued request 172185d5e707SKishon Vijay Abraham I * then wait, do not issue either END 172285d5e707SKishon Vijay Abraham I * or UPDATE TRANSFER, just attach next 172385d5e707SKishon Vijay Abraham I * request in request_list during 172485d5e707SKishon Vijay Abraham I * giveback.If any future queued request 172585d5e707SKishon Vijay Abraham I * is successfully transferred then we 172685d5e707SKishon Vijay Abraham I * will issue UPDATE TRANSFER for all 172785d5e707SKishon Vijay Abraham I * request in the request_list. 172885d5e707SKishon Vijay Abraham I */ 172985d5e707SKishon Vijay Abraham I dep->flags |= DWC3_EP_MISSED_ISOC; 173085d5e707SKishon Vijay Abraham I } else { 173185d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "incomplete IN transfer %s\n", 173285d5e707SKishon Vijay Abraham I dep->name); 173385d5e707SKishon Vijay Abraham I status = -ECONNRESET; 173485d5e707SKishon Vijay Abraham I } 173585d5e707SKishon Vijay Abraham I } else { 173685d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_MISSED_ISOC; 173785d5e707SKishon Vijay Abraham I } 173885d5e707SKishon Vijay Abraham I } else { 173985d5e707SKishon Vijay Abraham I if (count && (event->status & DEPEVT_STATUS_SHORT)) 174085d5e707SKishon Vijay Abraham I s_pkt = 1; 174185d5e707SKishon Vijay Abraham I } 174285d5e707SKishon Vijay Abraham I 174385d5e707SKishon Vijay Abraham I /* 174485d5e707SKishon Vijay Abraham I * We assume here we will always receive the entire data block 174585d5e707SKishon Vijay Abraham I * which we should receive. Meaning, if we program RX to 174685d5e707SKishon Vijay Abraham I * receive 4K but we receive only 2K, we assume that's all we 174785d5e707SKishon Vijay Abraham I * should receive and we simply bounce the request back to the 174885d5e707SKishon Vijay Abraham I * gadget driver for further processing. 174985d5e707SKishon Vijay Abraham I */ 175085d5e707SKishon Vijay Abraham I req->request.actual += req->request.length - count; 175185d5e707SKishon Vijay Abraham I if (s_pkt) 175285d5e707SKishon Vijay Abraham I return 1; 175385d5e707SKishon Vijay Abraham I if ((event->status & DEPEVT_STATUS_LST) && 175485d5e707SKishon Vijay Abraham I (trb->ctrl & (DWC3_TRB_CTRL_LST | 175585d5e707SKishon Vijay Abraham I DWC3_TRB_CTRL_HWO))) 175685d5e707SKishon Vijay Abraham I return 1; 175785d5e707SKishon Vijay Abraham I if ((event->status & DEPEVT_STATUS_IOC) && 175885d5e707SKishon Vijay Abraham I (trb->ctrl & DWC3_TRB_CTRL_IOC)) 175985d5e707SKishon Vijay Abraham I return 1; 176085d5e707SKishon Vijay Abraham I return 0; 176185d5e707SKishon Vijay Abraham I } 176285d5e707SKishon Vijay Abraham I 176385d5e707SKishon Vijay Abraham I static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, 176485d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event, int status) 176585d5e707SKishon Vijay Abraham I { 176685d5e707SKishon Vijay Abraham I struct dwc3_request *req; 176785d5e707SKishon Vijay Abraham I struct dwc3_trb *trb; 176885d5e707SKishon Vijay Abraham I unsigned int slot; 176985d5e707SKishon Vijay Abraham I int ret; 177085d5e707SKishon Vijay Abraham I 177185d5e707SKishon Vijay Abraham I do { 177285d5e707SKishon Vijay Abraham I req = next_request(&dep->req_queued); 177385d5e707SKishon Vijay Abraham I if (!req) { 177485d5e707SKishon Vijay Abraham I WARN_ON_ONCE(1); 177585d5e707SKishon Vijay Abraham I return 1; 177685d5e707SKishon Vijay Abraham I } 1777747a0a5bSKishon Vijay Abraham I 1778747a0a5bSKishon Vijay Abraham I slot = req->start_slot; 177985d5e707SKishon Vijay Abraham I if ((slot == DWC3_TRB_NUM - 1) && 178085d5e707SKishon Vijay Abraham I usb_endpoint_xfer_isoc(dep->endpoint.desc)) 178185d5e707SKishon Vijay Abraham I slot++; 178285d5e707SKishon Vijay Abraham I slot %= DWC3_TRB_NUM; 178385d5e707SKishon Vijay Abraham I trb = &dep->trb_pool[slot]; 178485d5e707SKishon Vijay Abraham I 1785526a50f8SKishon Vijay Abraham I dwc3_flush_cache((int)trb, sizeof(*trb)); 178685d5e707SKishon Vijay Abraham I ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, 178785d5e707SKishon Vijay Abraham I event, status); 178885d5e707SKishon Vijay Abraham I if (ret) 178985d5e707SKishon Vijay Abraham I break; 179085d5e707SKishon Vijay Abraham I 179185d5e707SKishon Vijay Abraham I dwc3_gadget_giveback(dep, req, status); 179285d5e707SKishon Vijay Abraham I 179385d5e707SKishon Vijay Abraham I if (ret) 179485d5e707SKishon Vijay Abraham I break; 179585d5e707SKishon Vijay Abraham I } while (1); 179685d5e707SKishon Vijay Abraham I 179785d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && 179885d5e707SKishon Vijay Abraham I list_empty(&dep->req_queued)) { 179985d5e707SKishon Vijay Abraham I if (list_empty(&dep->request_list)) { 180085d5e707SKishon Vijay Abraham I /* 180185d5e707SKishon Vijay Abraham I * If there is no entry in request list then do 180285d5e707SKishon Vijay Abraham I * not issue END TRANSFER now. Just set PENDING 180385d5e707SKishon Vijay Abraham I * flag, so that END TRANSFER is issued when an 180485d5e707SKishon Vijay Abraham I * entry is added into request list. 180585d5e707SKishon Vijay Abraham I */ 180685d5e707SKishon Vijay Abraham I dep->flags = DWC3_EP_PENDING_REQUEST; 180785d5e707SKishon Vijay Abraham I } else { 180885d5e707SKishon Vijay Abraham I dwc3_stop_active_transfer(dwc, dep->number, true); 180985d5e707SKishon Vijay Abraham I dep->flags = DWC3_EP_ENABLED; 181085d5e707SKishon Vijay Abraham I } 181185d5e707SKishon Vijay Abraham I return 1; 181285d5e707SKishon Vijay Abraham I } 181385d5e707SKishon Vijay Abraham I 181485d5e707SKishon Vijay Abraham I return 1; 181585d5e707SKishon Vijay Abraham I } 181685d5e707SKishon Vijay Abraham I 181785d5e707SKishon Vijay Abraham I static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, 181885d5e707SKishon Vijay Abraham I struct dwc3_ep *dep, const struct dwc3_event_depevt *event) 181985d5e707SKishon Vijay Abraham I { 182085d5e707SKishon Vijay Abraham I unsigned status = 0; 182185d5e707SKishon Vijay Abraham I int clean_busy; 182285d5e707SKishon Vijay Abraham I 182385d5e707SKishon Vijay Abraham I if (event->status & DEPEVT_STATUS_BUSERR) 182485d5e707SKishon Vijay Abraham I status = -ECONNRESET; 182585d5e707SKishon Vijay Abraham I 182685d5e707SKishon Vijay Abraham I clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); 182785d5e707SKishon Vijay Abraham I if (clean_busy) 182885d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_BUSY; 182985d5e707SKishon Vijay Abraham I 183085d5e707SKishon Vijay Abraham I /* 183185d5e707SKishon Vijay Abraham I * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. 183285d5e707SKishon Vijay Abraham I * See dwc3_gadget_linksts_change_interrupt() for 1st half. 183385d5e707SKishon Vijay Abraham I */ 183485d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_183A) { 183585d5e707SKishon Vijay Abraham I u32 reg; 183685d5e707SKishon Vijay Abraham I int i; 183785d5e707SKishon Vijay Abraham I 183885d5e707SKishon Vijay Abraham I for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { 183985d5e707SKishon Vijay Abraham I dep = dwc->eps[i]; 184085d5e707SKishon Vijay Abraham I 184185d5e707SKishon Vijay Abraham I if (!(dep->flags & DWC3_EP_ENABLED)) 184285d5e707SKishon Vijay Abraham I continue; 184385d5e707SKishon Vijay Abraham I 184485d5e707SKishon Vijay Abraham I if (!list_empty(&dep->req_queued)) 184585d5e707SKishon Vijay Abraham I return; 184685d5e707SKishon Vijay Abraham I } 184785d5e707SKishon Vijay Abraham I 184885d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 184985d5e707SKishon Vijay Abraham I reg |= dwc->u1u2; 185085d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 185185d5e707SKishon Vijay Abraham I 185285d5e707SKishon Vijay Abraham I dwc->u1u2 = 0; 185385d5e707SKishon Vijay Abraham I } 185485d5e707SKishon Vijay Abraham I } 185585d5e707SKishon Vijay Abraham I 185685d5e707SKishon Vijay Abraham I static void dwc3_endpoint_interrupt(struct dwc3 *dwc, 185785d5e707SKishon Vijay Abraham I const struct dwc3_event_depevt *event) 185885d5e707SKishon Vijay Abraham I { 185985d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 186085d5e707SKishon Vijay Abraham I u8 epnum = event->endpoint_number; 186185d5e707SKishon Vijay Abraham I 186285d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum]; 186385d5e707SKishon Vijay Abraham I 186485d5e707SKishon Vijay Abraham I if (!(dep->flags & DWC3_EP_ENABLED)) 186585d5e707SKishon Vijay Abraham I return; 186685d5e707SKishon Vijay Abraham I 186785d5e707SKishon Vijay Abraham I if (epnum == 0 || epnum == 1) { 186885d5e707SKishon Vijay Abraham I dwc3_ep0_interrupt(dwc, event); 186985d5e707SKishon Vijay Abraham I return; 187085d5e707SKishon Vijay Abraham I } 187185d5e707SKishon Vijay Abraham I 187285d5e707SKishon Vijay Abraham I switch (event->endpoint_event) { 187385d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERCOMPLETE: 187485d5e707SKishon Vijay Abraham I dep->resource_index = 0; 187585d5e707SKishon Vijay Abraham I 187685d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 187785d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", 187885d5e707SKishon Vijay Abraham I dep->name); 187985d5e707SKishon Vijay Abraham I return; 188085d5e707SKishon Vijay Abraham I } 188185d5e707SKishon Vijay Abraham I 188285d5e707SKishon Vijay Abraham I dwc3_endpoint_transfer_complete(dwc, dep, event); 188385d5e707SKishon Vijay Abraham I break; 188485d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERINPROGRESS: 188585d5e707SKishon Vijay Abraham I dwc3_endpoint_transfer_complete(dwc, dep, event); 188685d5e707SKishon Vijay Abraham I break; 188785d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_XFERNOTREADY: 188885d5e707SKishon Vijay Abraham I if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { 188985d5e707SKishon Vijay Abraham I dwc3_gadget_start_isoc(dwc, dep, event); 189085d5e707SKishon Vijay Abraham I } else { 189185d5e707SKishon Vijay Abraham I int ret; 189285d5e707SKishon Vijay Abraham I 189385d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "%s: reason %s\n", 189485d5e707SKishon Vijay Abraham I dep->name, event->status & 189585d5e707SKishon Vijay Abraham I DEPEVT_STATUS_TRANSFER_ACTIVE 189685d5e707SKishon Vijay Abraham I ? "Transfer Active" 189785d5e707SKishon Vijay Abraham I : "Transfer Not Active"); 189885d5e707SKishon Vijay Abraham I 189985d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_kick_transfer(dep, 0, 1); 190085d5e707SKishon Vijay Abraham I if (!ret || ret == -EBUSY) 190185d5e707SKishon Vijay Abraham I return; 190285d5e707SKishon Vijay Abraham I 190385d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s: failed to kick transfers\n", 190485d5e707SKishon Vijay Abraham I dep->name); 190585d5e707SKishon Vijay Abraham I } 190685d5e707SKishon Vijay Abraham I 190785d5e707SKishon Vijay Abraham I break; 190885d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_STREAMEVT: 190985d5e707SKishon Vijay Abraham I if (!usb_endpoint_xfer_bulk(dep->endpoint.desc)) { 191085d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "Stream event for non-Bulk %s\n", 191185d5e707SKishon Vijay Abraham I dep->name); 191285d5e707SKishon Vijay Abraham I return; 191385d5e707SKishon Vijay Abraham I } 191485d5e707SKishon Vijay Abraham I 191585d5e707SKishon Vijay Abraham I switch (event->status) { 191685d5e707SKishon Vijay Abraham I case DEPEVT_STREAMEVT_FOUND: 191785d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Stream %d found and started\n", 191885d5e707SKishon Vijay Abraham I event->parameters); 191985d5e707SKishon Vijay Abraham I 192085d5e707SKishon Vijay Abraham I break; 192185d5e707SKishon Vijay Abraham I case DEPEVT_STREAMEVT_NOTFOUND: 192285d5e707SKishon Vijay Abraham I /* FALLTHROUGH */ 192385d5e707SKishon Vijay Abraham I default: 192485d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); 192585d5e707SKishon Vijay Abraham I } 192685d5e707SKishon Vijay Abraham I break; 192785d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_RXTXFIFOEVT: 192885d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); 192985d5e707SKishon Vijay Abraham I break; 193085d5e707SKishon Vijay Abraham I case DWC3_DEPEVT_EPCMDCMPLT: 193185d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Endpoint Command Complete\n"); 193285d5e707SKishon Vijay Abraham I break; 193385d5e707SKishon Vijay Abraham I } 193485d5e707SKishon Vijay Abraham I } 193585d5e707SKishon Vijay Abraham I 193685d5e707SKishon Vijay Abraham I static void dwc3_disconnect_gadget(struct dwc3 *dwc) 193785d5e707SKishon Vijay Abraham I { 193885d5e707SKishon Vijay Abraham I if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { 193985d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock); 194085d5e707SKishon Vijay Abraham I dwc->gadget_driver->disconnect(&dwc->gadget); 194185d5e707SKishon Vijay Abraham I spin_lock(&dwc->lock); 194285d5e707SKishon Vijay Abraham I } 194385d5e707SKishon Vijay Abraham I } 194485d5e707SKishon Vijay Abraham I 194585d5e707SKishon Vijay Abraham I static void dwc3_suspend_gadget(struct dwc3 *dwc) 194685d5e707SKishon Vijay Abraham I { 194785d5e707SKishon Vijay Abraham I if (dwc->gadget_driver && dwc->gadget_driver->suspend) { 194885d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock); 194985d5e707SKishon Vijay Abraham I dwc->gadget_driver->suspend(&dwc->gadget); 195085d5e707SKishon Vijay Abraham I spin_lock(&dwc->lock); 195185d5e707SKishon Vijay Abraham I } 195285d5e707SKishon Vijay Abraham I } 195385d5e707SKishon Vijay Abraham I 195485d5e707SKishon Vijay Abraham I static void dwc3_resume_gadget(struct dwc3 *dwc) 195585d5e707SKishon Vijay Abraham I { 195685d5e707SKishon Vijay Abraham I if (dwc->gadget_driver && dwc->gadget_driver->resume) { 195785d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock); 195885d5e707SKishon Vijay Abraham I dwc->gadget_driver->resume(&dwc->gadget); 195985d5e707SKishon Vijay Abraham I } 196085d5e707SKishon Vijay Abraham I } 196185d5e707SKishon Vijay Abraham I 196285d5e707SKishon Vijay Abraham I static void dwc3_reset_gadget(struct dwc3 *dwc) 196385d5e707SKishon Vijay Abraham I { 196485d5e707SKishon Vijay Abraham I if (!dwc->gadget_driver) 196585d5e707SKishon Vijay Abraham I return; 196685d5e707SKishon Vijay Abraham I 196785d5e707SKishon Vijay Abraham I if (dwc->gadget.speed != USB_SPEED_UNKNOWN) { 196885d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock); 196985d5e707SKishon Vijay Abraham I usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver); 197085d5e707SKishon Vijay Abraham I spin_lock(&dwc->lock); 197185d5e707SKishon Vijay Abraham I } 197285d5e707SKishon Vijay Abraham I } 197385d5e707SKishon Vijay Abraham I 197485d5e707SKishon Vijay Abraham I static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) 197585d5e707SKishon Vijay Abraham I { 197685d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 197785d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params; 197885d5e707SKishon Vijay Abraham I u32 cmd; 197985d5e707SKishon Vijay Abraham I int ret; 198085d5e707SKishon Vijay Abraham I 198185d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum]; 198285d5e707SKishon Vijay Abraham I 198385d5e707SKishon Vijay Abraham I if (!dep->resource_index) 198485d5e707SKishon Vijay Abraham I return; 198585d5e707SKishon Vijay Abraham I 198685d5e707SKishon Vijay Abraham I /* 198785d5e707SKishon Vijay Abraham I * NOTICE: We are violating what the Databook says about the 198885d5e707SKishon Vijay Abraham I * EndTransfer command. Ideally we would _always_ wait for the 198985d5e707SKishon Vijay Abraham I * EndTransfer Command Completion IRQ, but that's causing too 199085d5e707SKishon Vijay Abraham I * much trouble synchronizing between us and gadget driver. 199185d5e707SKishon Vijay Abraham I * 199285d5e707SKishon Vijay Abraham I * We have discussed this with the IP Provider and it was 199385d5e707SKishon Vijay Abraham I * suggested to giveback all requests here, but give HW some 199485d5e707SKishon Vijay Abraham I * extra time to synchronize with the interconnect. We're using 199585d5e707SKishon Vijay Abraham I * an arbitraty 100us delay for that. 199685d5e707SKishon Vijay Abraham I * 199785d5e707SKishon Vijay Abraham I * Note also that a similar handling was tested by Synopsys 199885d5e707SKishon Vijay Abraham I * (thanks a lot Paul) and nothing bad has come out of it. 199985d5e707SKishon Vijay Abraham I * In short, what we're doing is: 200085d5e707SKishon Vijay Abraham I * 200185d5e707SKishon Vijay Abraham I * - Issue EndTransfer WITH CMDIOC bit set 200285d5e707SKishon Vijay Abraham I * - Wait 100us 200385d5e707SKishon Vijay Abraham I */ 200485d5e707SKishon Vijay Abraham I 200585d5e707SKishon Vijay Abraham I cmd = DWC3_DEPCMD_ENDTRANSFER; 200685d5e707SKishon Vijay Abraham I cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0; 200785d5e707SKishon Vijay Abraham I cmd |= DWC3_DEPCMD_CMDIOC; 200885d5e707SKishon Vijay Abraham I cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); 200985d5e707SKishon Vijay Abraham I memset(¶ms, 0, sizeof(params)); 201085d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); 201185d5e707SKishon Vijay Abraham I WARN_ON_ONCE(ret); 201285d5e707SKishon Vijay Abraham I dep->resource_index = 0; 201385d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_BUSY; 201485d5e707SKishon Vijay Abraham I udelay(100); 201585d5e707SKishon Vijay Abraham I } 201685d5e707SKishon Vijay Abraham I 201785d5e707SKishon Vijay Abraham I static void dwc3_stop_active_transfers(struct dwc3 *dwc) 201885d5e707SKishon Vijay Abraham I { 201985d5e707SKishon Vijay Abraham I u32 epnum; 202085d5e707SKishon Vijay Abraham I 202185d5e707SKishon Vijay Abraham I for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { 202285d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 202385d5e707SKishon Vijay Abraham I 202485d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum]; 202585d5e707SKishon Vijay Abraham I if (!dep) 202685d5e707SKishon Vijay Abraham I continue; 202785d5e707SKishon Vijay Abraham I 202885d5e707SKishon Vijay Abraham I if (!(dep->flags & DWC3_EP_ENABLED)) 202985d5e707SKishon Vijay Abraham I continue; 203085d5e707SKishon Vijay Abraham I 203185d5e707SKishon Vijay Abraham I dwc3_remove_requests(dwc, dep); 203285d5e707SKishon Vijay Abraham I } 203385d5e707SKishon Vijay Abraham I } 203485d5e707SKishon Vijay Abraham I 203585d5e707SKishon Vijay Abraham I static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) 203685d5e707SKishon Vijay Abraham I { 203785d5e707SKishon Vijay Abraham I u32 epnum; 203885d5e707SKishon Vijay Abraham I 203985d5e707SKishon Vijay Abraham I for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { 204085d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 204185d5e707SKishon Vijay Abraham I struct dwc3_gadget_ep_cmd_params params; 204285d5e707SKishon Vijay Abraham I int ret; 204385d5e707SKishon Vijay Abraham I 204485d5e707SKishon Vijay Abraham I dep = dwc->eps[epnum]; 204585d5e707SKishon Vijay Abraham I if (!dep) 204685d5e707SKishon Vijay Abraham I continue; 204785d5e707SKishon Vijay Abraham I 204885d5e707SKishon Vijay Abraham I if (!(dep->flags & DWC3_EP_STALL)) 204985d5e707SKishon Vijay Abraham I continue; 205085d5e707SKishon Vijay Abraham I 205185d5e707SKishon Vijay Abraham I dep->flags &= ~DWC3_EP_STALL; 205285d5e707SKishon Vijay Abraham I 205385d5e707SKishon Vijay Abraham I memset(¶ms, 0, sizeof(params)); 205485d5e707SKishon Vijay Abraham I ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, 205585d5e707SKishon Vijay Abraham I DWC3_DEPCMD_CLEARSTALL, ¶ms); 205685d5e707SKishon Vijay Abraham I WARN_ON_ONCE(ret); 205785d5e707SKishon Vijay Abraham I } 205885d5e707SKishon Vijay Abraham I } 205985d5e707SKishon Vijay Abraham I 206085d5e707SKishon Vijay Abraham I static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) 206185d5e707SKishon Vijay Abraham I { 206285d5e707SKishon Vijay Abraham I int reg; 206385d5e707SKishon Vijay Abraham I 206485d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 206585d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_INITU1ENA; 206685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 206785d5e707SKishon Vijay Abraham I 206885d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_INITU2ENA; 206985d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 207085d5e707SKishon Vijay Abraham I 207185d5e707SKishon Vijay Abraham I dwc3_disconnect_gadget(dwc); 207285d5e707SKishon Vijay Abraham I dwc->start_config_issued = false; 207385d5e707SKishon Vijay Abraham I 207485d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_UNKNOWN; 207585d5e707SKishon Vijay Abraham I dwc->setup_packet_pending = false; 207685d5e707SKishon Vijay Abraham I usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); 207785d5e707SKishon Vijay Abraham I } 207885d5e707SKishon Vijay Abraham I 207985d5e707SKishon Vijay Abraham I static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) 208085d5e707SKishon Vijay Abraham I { 208185d5e707SKishon Vijay Abraham I u32 reg; 208285d5e707SKishon Vijay Abraham I 208385d5e707SKishon Vijay Abraham I /* 208485d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 revisions <1.88a have an issue which 208585d5e707SKishon Vijay Abraham I * would cause a missing Disconnect Event if there's a 208685d5e707SKishon Vijay Abraham I * pending Setup Packet in the FIFO. 208785d5e707SKishon Vijay Abraham I * 208885d5e707SKishon Vijay Abraham I * There's no suggested workaround on the official Bug 208985d5e707SKishon Vijay Abraham I * report, which states that "unless the driver/application 209085d5e707SKishon Vijay Abraham I * is doing any special handling of a disconnect event, 209185d5e707SKishon Vijay Abraham I * there is no functional issue". 209285d5e707SKishon Vijay Abraham I * 209385d5e707SKishon Vijay Abraham I * Unfortunately, it turns out that we _do_ some special 209485d5e707SKishon Vijay Abraham I * handling of a disconnect event, namely complete all 209585d5e707SKishon Vijay Abraham I * pending transfers, notify gadget driver of the 209685d5e707SKishon Vijay Abraham I * disconnection, and so on. 209785d5e707SKishon Vijay Abraham I * 209885d5e707SKishon Vijay Abraham I * Our suggested workaround is to follow the Disconnect 209985d5e707SKishon Vijay Abraham I * Event steps here, instead, based on a setup_packet_pending 210085d5e707SKishon Vijay Abraham I * flag. Such flag gets set whenever we have a XferNotReady 210185d5e707SKishon Vijay Abraham I * event on EP0 and gets cleared on XferComplete for the 210285d5e707SKishon Vijay Abraham I * same endpoint. 210385d5e707SKishon Vijay Abraham I * 210485d5e707SKishon Vijay Abraham I * Refers to: 210585d5e707SKishon Vijay Abraham I * 210685d5e707SKishon Vijay Abraham I * STAR#9000466709: RTL: Device : Disconnect event not 210785d5e707SKishon Vijay Abraham I * generated if setup packet pending in FIFO 210885d5e707SKishon Vijay Abraham I */ 210985d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_188A) { 211085d5e707SKishon Vijay Abraham I if (dwc->setup_packet_pending) 211185d5e707SKishon Vijay Abraham I dwc3_gadget_disconnect_interrupt(dwc); 211285d5e707SKishon Vijay Abraham I } 211385d5e707SKishon Vijay Abraham I 211485d5e707SKishon Vijay Abraham I dwc3_reset_gadget(dwc); 211585d5e707SKishon Vijay Abraham I 211685d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 211785d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_TSTCTRL_MASK; 211885d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 211985d5e707SKishon Vijay Abraham I dwc->test_mode = false; 212085d5e707SKishon Vijay Abraham I 212185d5e707SKishon Vijay Abraham I dwc3_stop_active_transfers(dwc); 212285d5e707SKishon Vijay Abraham I dwc3_clear_stall_all_ep(dwc); 212385d5e707SKishon Vijay Abraham I dwc->start_config_issued = false; 212485d5e707SKishon Vijay Abraham I 212585d5e707SKishon Vijay Abraham I /* Reset device address to zero */ 212685d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCFG); 212785d5e707SKishon Vijay Abraham I reg &= ~(DWC3_DCFG_DEVADDR_MASK); 212885d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCFG, reg); 212985d5e707SKishon Vijay Abraham I } 213085d5e707SKishon Vijay Abraham I 213185d5e707SKishon Vijay Abraham I static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) 213285d5e707SKishon Vijay Abraham I { 213385d5e707SKishon Vijay Abraham I u32 reg; 213485d5e707SKishon Vijay Abraham I u32 usb30_clock = DWC3_GCTL_CLK_BUS; 213585d5e707SKishon Vijay Abraham I 213685d5e707SKishon Vijay Abraham I /* 213785d5e707SKishon Vijay Abraham I * We change the clock only at SS but I dunno why I would want to do 213885d5e707SKishon Vijay Abraham I * this. Maybe it becomes part of the power saving plan. 213985d5e707SKishon Vijay Abraham I */ 214085d5e707SKishon Vijay Abraham I 214185d5e707SKishon Vijay Abraham I if (speed != DWC3_DSTS_SUPERSPEED) 214285d5e707SKishon Vijay Abraham I return; 214385d5e707SKishon Vijay Abraham I 214485d5e707SKishon Vijay Abraham I /* 214585d5e707SKishon Vijay Abraham I * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed 214685d5e707SKishon Vijay Abraham I * each time on Connect Done. 214785d5e707SKishon Vijay Abraham I */ 214885d5e707SKishon Vijay Abraham I if (!usb30_clock) 214985d5e707SKishon Vijay Abraham I return; 215085d5e707SKishon Vijay Abraham I 215185d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_GCTL); 215285d5e707SKishon Vijay Abraham I reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); 215385d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_GCTL, reg); 215485d5e707SKishon Vijay Abraham I } 215585d5e707SKishon Vijay Abraham I 215685d5e707SKishon Vijay Abraham I static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) 215785d5e707SKishon Vijay Abraham I { 215885d5e707SKishon Vijay Abraham I struct dwc3_ep *dep; 215985d5e707SKishon Vijay Abraham I int ret; 216085d5e707SKishon Vijay Abraham I u32 reg; 216185d5e707SKishon Vijay Abraham I u8 speed; 216285d5e707SKishon Vijay Abraham I 216385d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DSTS); 216485d5e707SKishon Vijay Abraham I speed = reg & DWC3_DSTS_CONNECTSPD; 216585d5e707SKishon Vijay Abraham I dwc->speed = speed; 216685d5e707SKishon Vijay Abraham I 216785d5e707SKishon Vijay Abraham I dwc3_update_ram_clk_sel(dwc, speed); 216885d5e707SKishon Vijay Abraham I 216985d5e707SKishon Vijay Abraham I switch (speed) { 217085d5e707SKishon Vijay Abraham I case DWC3_DCFG_SUPERSPEED: 217185d5e707SKishon Vijay Abraham I /* 217285d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 revisions <1.90a have an issue which 217385d5e707SKishon Vijay Abraham I * would cause a missing USB3 Reset event. 217485d5e707SKishon Vijay Abraham I * 217585d5e707SKishon Vijay Abraham I * In such situations, we should force a USB3 Reset 217685d5e707SKishon Vijay Abraham I * event by calling our dwc3_gadget_reset_interrupt() 217785d5e707SKishon Vijay Abraham I * routine. 217885d5e707SKishon Vijay Abraham I * 217985d5e707SKishon Vijay Abraham I * Refers to: 218085d5e707SKishon Vijay Abraham I * 218185d5e707SKishon Vijay Abraham I * STAR#9000483510: RTL: SS : USB3 reset event may 218285d5e707SKishon Vijay Abraham I * not be generated always when the link enters poll 218385d5e707SKishon Vijay Abraham I */ 218485d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_190A) 218585d5e707SKishon Vijay Abraham I dwc3_gadget_reset_interrupt(dwc); 218685d5e707SKishon Vijay Abraham I 218785d5e707SKishon Vijay Abraham I dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); 218885d5e707SKishon Vijay Abraham I dwc->gadget.ep0->maxpacket = 512; 218985d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_SUPER; 219085d5e707SKishon Vijay Abraham I break; 219185d5e707SKishon Vijay Abraham I case DWC3_DCFG_HIGHSPEED: 219285d5e707SKishon Vijay Abraham I dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); 219385d5e707SKishon Vijay Abraham I dwc->gadget.ep0->maxpacket = 64; 219485d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_HIGH; 219585d5e707SKishon Vijay Abraham I break; 219685d5e707SKishon Vijay Abraham I case DWC3_DCFG_FULLSPEED2: 219785d5e707SKishon Vijay Abraham I case DWC3_DCFG_FULLSPEED1: 219885d5e707SKishon Vijay Abraham I dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); 219985d5e707SKishon Vijay Abraham I dwc->gadget.ep0->maxpacket = 64; 220085d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_FULL; 220185d5e707SKishon Vijay Abraham I break; 220285d5e707SKishon Vijay Abraham I case DWC3_DCFG_LOWSPEED: 220385d5e707SKishon Vijay Abraham I dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); 220485d5e707SKishon Vijay Abraham I dwc->gadget.ep0->maxpacket = 8; 220585d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_LOW; 220685d5e707SKishon Vijay Abraham I break; 220785d5e707SKishon Vijay Abraham I } 220885d5e707SKishon Vijay Abraham I 220985d5e707SKishon Vijay Abraham I /* Enable USB2 LPM Capability */ 221085d5e707SKishon Vijay Abraham I 221185d5e707SKishon Vijay Abraham I if ((dwc->revision > DWC3_REVISION_194A) 221285d5e707SKishon Vijay Abraham I && (speed != DWC3_DCFG_SUPERSPEED)) { 221385d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCFG); 221485d5e707SKishon Vijay Abraham I reg |= DWC3_DCFG_LPM_CAP; 221585d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCFG, reg); 221685d5e707SKishon Vijay Abraham I 221785d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 221885d5e707SKishon Vijay Abraham I reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); 221985d5e707SKishon Vijay Abraham I 222085d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold); 222185d5e707SKishon Vijay Abraham I 222285d5e707SKishon Vijay Abraham I /* 222385d5e707SKishon Vijay Abraham I * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and 222485d5e707SKishon Vijay Abraham I * DCFG.LPMCap is set, core responses with an ACK and the 222585d5e707SKishon Vijay Abraham I * BESL value in the LPM token is less than or equal to LPM 222685d5e707SKishon Vijay Abraham I * NYET threshold. 222785d5e707SKishon Vijay Abraham I */ 2228747a0a5bSKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_240A && dwc->has_lpm_erratum) 2229747a0a5bSKishon Vijay Abraham I WARN(true, "LPM Erratum not available on dwc3 revisisions < 2.40a\n"); 223085d5e707SKishon Vijay Abraham I 223185d5e707SKishon Vijay Abraham I if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) 223285d5e707SKishon Vijay Abraham I reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); 223385d5e707SKishon Vijay Abraham I 223485d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 223585d5e707SKishon Vijay Abraham I } else { 223685d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 223785d5e707SKishon Vijay Abraham I reg &= ~DWC3_DCTL_HIRD_THRES_MASK; 223885d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 223985d5e707SKishon Vijay Abraham I } 224085d5e707SKishon Vijay Abraham I 224185d5e707SKishon Vijay Abraham I dep = dwc->eps[0]; 224285d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, 224385d5e707SKishon Vijay Abraham I false); 224485d5e707SKishon Vijay Abraham I if (ret) { 224585d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to enable %s\n", dep->name); 224685d5e707SKishon Vijay Abraham I return; 224785d5e707SKishon Vijay Abraham I } 224885d5e707SKishon Vijay Abraham I 224985d5e707SKishon Vijay Abraham I dep = dwc->eps[1]; 225085d5e707SKishon Vijay Abraham I ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true, 225185d5e707SKishon Vijay Abraham I false); 225285d5e707SKishon Vijay Abraham I if (ret) { 225385d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to enable %s\n", dep->name); 225485d5e707SKishon Vijay Abraham I return; 225585d5e707SKishon Vijay Abraham I } 225685d5e707SKishon Vijay Abraham I 225785d5e707SKishon Vijay Abraham I /* 225885d5e707SKishon Vijay Abraham I * Configure PHY via GUSB3PIPECTLn if required. 225985d5e707SKishon Vijay Abraham I * 226085d5e707SKishon Vijay Abraham I * Update GTXFIFOSIZn 226185d5e707SKishon Vijay Abraham I * 226285d5e707SKishon Vijay Abraham I * In both cases reset values should be sufficient. 226385d5e707SKishon Vijay Abraham I */ 226485d5e707SKishon Vijay Abraham I } 226585d5e707SKishon Vijay Abraham I 226685d5e707SKishon Vijay Abraham I static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) 226785d5e707SKishon Vijay Abraham I { 226885d5e707SKishon Vijay Abraham I /* 226985d5e707SKishon Vijay Abraham I * TODO take core out of low power mode when that's 227085d5e707SKishon Vijay Abraham I * implemented. 227185d5e707SKishon Vijay Abraham I */ 227285d5e707SKishon Vijay Abraham I 227385d5e707SKishon Vijay Abraham I dwc->gadget_driver->resume(&dwc->gadget); 227485d5e707SKishon Vijay Abraham I } 227585d5e707SKishon Vijay Abraham I 227685d5e707SKishon Vijay Abraham I static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, 227785d5e707SKishon Vijay Abraham I unsigned int evtinfo) 227885d5e707SKishon Vijay Abraham I { 227985d5e707SKishon Vijay Abraham I enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; 228085d5e707SKishon Vijay Abraham I unsigned int pwropt; 228185d5e707SKishon Vijay Abraham I 228285d5e707SKishon Vijay Abraham I /* 228385d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 < 2.50a have an issue when configured without 228485d5e707SKishon Vijay Abraham I * Hibernation mode enabled which would show up when device detects 228585d5e707SKishon Vijay Abraham I * host-initiated U3 exit. 228685d5e707SKishon Vijay Abraham I * 228785d5e707SKishon Vijay Abraham I * In that case, device will generate a Link State Change Interrupt 228885d5e707SKishon Vijay Abraham I * from U3 to RESUME which is only necessary if Hibernation is 228985d5e707SKishon Vijay Abraham I * configured in. 229085d5e707SKishon Vijay Abraham I * 229185d5e707SKishon Vijay Abraham I * There are no functional changes due to such spurious event and we 229285d5e707SKishon Vijay Abraham I * just need to ignore it. 229385d5e707SKishon Vijay Abraham I * 229485d5e707SKishon Vijay Abraham I * Refers to: 229585d5e707SKishon Vijay Abraham I * 229685d5e707SKishon Vijay Abraham I * STAR#9000570034 RTL: SS Resume event generated in non-Hibernation 229785d5e707SKishon Vijay Abraham I * operational mode 229885d5e707SKishon Vijay Abraham I */ 229985d5e707SKishon Vijay Abraham I pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); 230085d5e707SKishon Vijay Abraham I if ((dwc->revision < DWC3_REVISION_250A) && 230185d5e707SKishon Vijay Abraham I (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) { 230285d5e707SKishon Vijay Abraham I if ((dwc->link_state == DWC3_LINK_STATE_U3) && 230385d5e707SKishon Vijay Abraham I (next == DWC3_LINK_STATE_RESUME)) { 230485d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n"); 230585d5e707SKishon Vijay Abraham I return; 230685d5e707SKishon Vijay Abraham I } 230785d5e707SKishon Vijay Abraham I } 230885d5e707SKishon Vijay Abraham I 230985d5e707SKishon Vijay Abraham I /* 231085d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending 231185d5e707SKishon Vijay Abraham I * on the link partner, the USB session might do multiple entry/exit 231285d5e707SKishon Vijay Abraham I * of low power states before a transfer takes place. 231385d5e707SKishon Vijay Abraham I * 231485d5e707SKishon Vijay Abraham I * Due to this problem, we might experience lower throughput. The 231585d5e707SKishon Vijay Abraham I * suggested workaround is to disable DCTL[12:9] bits if we're 231685d5e707SKishon Vijay Abraham I * transitioning from U1/U2 to U0 and enable those bits again 231785d5e707SKishon Vijay Abraham I * after a transfer completes and there are no pending transfers 231885d5e707SKishon Vijay Abraham I * on any of the enabled endpoints. 231985d5e707SKishon Vijay Abraham I * 232085d5e707SKishon Vijay Abraham I * This is the first half of that workaround. 232185d5e707SKishon Vijay Abraham I * 232285d5e707SKishon Vijay Abraham I * Refers to: 232385d5e707SKishon Vijay Abraham I * 232485d5e707SKishon Vijay Abraham I * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us 232585d5e707SKishon Vijay Abraham I * core send LGO_Ux entering U0 232685d5e707SKishon Vijay Abraham I */ 232785d5e707SKishon Vijay Abraham I if (dwc->revision < DWC3_REVISION_183A) { 232885d5e707SKishon Vijay Abraham I if (next == DWC3_LINK_STATE_U0) { 232985d5e707SKishon Vijay Abraham I u32 u1u2; 233085d5e707SKishon Vijay Abraham I u32 reg; 233185d5e707SKishon Vijay Abraham I 233285d5e707SKishon Vijay Abraham I switch (dwc->link_state) { 233385d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U1: 233485d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U2: 233585d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_DCTL); 233685d5e707SKishon Vijay Abraham I u1u2 = reg & (DWC3_DCTL_INITU2ENA 233785d5e707SKishon Vijay Abraham I | DWC3_DCTL_ACCEPTU2ENA 233885d5e707SKishon Vijay Abraham I | DWC3_DCTL_INITU1ENA 233985d5e707SKishon Vijay Abraham I | DWC3_DCTL_ACCEPTU1ENA); 234085d5e707SKishon Vijay Abraham I 234185d5e707SKishon Vijay Abraham I if (!dwc->u1u2) 234285d5e707SKishon Vijay Abraham I dwc->u1u2 = reg & u1u2; 234385d5e707SKishon Vijay Abraham I 234485d5e707SKishon Vijay Abraham I reg &= ~u1u2; 234585d5e707SKishon Vijay Abraham I 234685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_DCTL, reg); 234785d5e707SKishon Vijay Abraham I break; 234885d5e707SKishon Vijay Abraham I default: 234985d5e707SKishon Vijay Abraham I /* do nothing */ 235085d5e707SKishon Vijay Abraham I break; 235185d5e707SKishon Vijay Abraham I } 235285d5e707SKishon Vijay Abraham I } 235385d5e707SKishon Vijay Abraham I } 235485d5e707SKishon Vijay Abraham I 235585d5e707SKishon Vijay Abraham I switch (next) { 235685d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U1: 235785d5e707SKishon Vijay Abraham I if (dwc->speed == USB_SPEED_SUPER) 235885d5e707SKishon Vijay Abraham I dwc3_suspend_gadget(dwc); 235985d5e707SKishon Vijay Abraham I break; 236085d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U2: 236185d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_U3: 236285d5e707SKishon Vijay Abraham I dwc3_suspend_gadget(dwc); 236385d5e707SKishon Vijay Abraham I break; 236485d5e707SKishon Vijay Abraham I case DWC3_LINK_STATE_RESUME: 236585d5e707SKishon Vijay Abraham I dwc3_resume_gadget(dwc); 236685d5e707SKishon Vijay Abraham I break; 236785d5e707SKishon Vijay Abraham I default: 236885d5e707SKishon Vijay Abraham I /* do nothing */ 236985d5e707SKishon Vijay Abraham I break; 237085d5e707SKishon Vijay Abraham I } 237185d5e707SKishon Vijay Abraham I 237285d5e707SKishon Vijay Abraham I dwc->link_state = next; 237385d5e707SKishon Vijay Abraham I } 237485d5e707SKishon Vijay Abraham I 237585d5e707SKishon Vijay Abraham I static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, 237685d5e707SKishon Vijay Abraham I unsigned int evtinfo) 237785d5e707SKishon Vijay Abraham I { 23782252d150SLukasz Majewski unsigned int is_ss = evtinfo & (1UL << 4); 237985d5e707SKishon Vijay Abraham I 238085d5e707SKishon Vijay Abraham I /** 238185d5e707SKishon Vijay Abraham I * WORKAROUND: DWC3 revison 2.20a with hibernation support 238285d5e707SKishon Vijay Abraham I * have a known issue which can cause USB CV TD.9.23 to fail 238385d5e707SKishon Vijay Abraham I * randomly. 238485d5e707SKishon Vijay Abraham I * 238585d5e707SKishon Vijay Abraham I * Because of this issue, core could generate bogus hibernation 238685d5e707SKishon Vijay Abraham I * events which SW needs to ignore. 238785d5e707SKishon Vijay Abraham I * 238885d5e707SKishon Vijay Abraham I * Refers to: 238985d5e707SKishon Vijay Abraham I * 239085d5e707SKishon Vijay Abraham I * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0 239185d5e707SKishon Vijay Abraham I * Device Fallback from SuperSpeed 239285d5e707SKishon Vijay Abraham I */ 239385d5e707SKishon Vijay Abraham I if (is_ss ^ (dwc->speed == USB_SPEED_SUPER)) 239485d5e707SKishon Vijay Abraham I return; 239585d5e707SKishon Vijay Abraham I 239685d5e707SKishon Vijay Abraham I /* enter hibernation here */ 239785d5e707SKishon Vijay Abraham I } 239885d5e707SKishon Vijay Abraham I 239985d5e707SKishon Vijay Abraham I static void dwc3_gadget_interrupt(struct dwc3 *dwc, 240085d5e707SKishon Vijay Abraham I const struct dwc3_event_devt *event) 240185d5e707SKishon Vijay Abraham I { 240285d5e707SKishon Vijay Abraham I switch (event->type) { 240385d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_DISCONNECT: 240485d5e707SKishon Vijay Abraham I dwc3_gadget_disconnect_interrupt(dwc); 240585d5e707SKishon Vijay Abraham I break; 240685d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_RESET: 240785d5e707SKishon Vijay Abraham I dwc3_gadget_reset_interrupt(dwc); 240885d5e707SKishon Vijay Abraham I break; 240985d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_CONNECT_DONE: 241085d5e707SKishon Vijay Abraham I dwc3_gadget_conndone_interrupt(dwc); 241185d5e707SKishon Vijay Abraham I break; 241285d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_WAKEUP: 241385d5e707SKishon Vijay Abraham I dwc3_gadget_wakeup_interrupt(dwc); 241485d5e707SKishon Vijay Abraham I break; 241585d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_HIBER_REQ: 2416747a0a5bSKishon Vijay Abraham I if (!dwc->has_hibernation) { 2417747a0a5bSKishon Vijay Abraham I WARN(1 ,"unexpected hibernation event\n"); 241885d5e707SKishon Vijay Abraham I break; 2419747a0a5bSKishon Vijay Abraham I } 242085d5e707SKishon Vijay Abraham I dwc3_gadget_hibernation_interrupt(dwc, event->event_info); 242185d5e707SKishon Vijay Abraham I break; 242285d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: 242385d5e707SKishon Vijay Abraham I dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); 242485d5e707SKishon Vijay Abraham I break; 242585d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_EOPF: 242685d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "End of Periodic Frame\n"); 242785d5e707SKishon Vijay Abraham I break; 242885d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_SOF: 242985d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); 243085d5e707SKishon Vijay Abraham I break; 243185d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_ERRATIC_ERROR: 243285d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Erratic Error\n"); 243385d5e707SKishon Vijay Abraham I break; 243485d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_CMD_CMPL: 243585d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Command Complete\n"); 243685d5e707SKishon Vijay Abraham I break; 243785d5e707SKishon Vijay Abraham I case DWC3_DEVICE_EVENT_OVERFLOW: 243885d5e707SKishon Vijay Abraham I dev_vdbg(dwc->dev, "Overflow\n"); 243985d5e707SKishon Vijay Abraham I break; 244085d5e707SKishon Vijay Abraham I default: 244185d5e707SKishon Vijay Abraham I dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); 244285d5e707SKishon Vijay Abraham I } 244385d5e707SKishon Vijay Abraham I } 244485d5e707SKishon Vijay Abraham I 244585d5e707SKishon Vijay Abraham I static void dwc3_process_event_entry(struct dwc3 *dwc, 244685d5e707SKishon Vijay Abraham I const union dwc3_event *event) 244785d5e707SKishon Vijay Abraham I { 244885d5e707SKishon Vijay Abraham I /* Endpoint IRQ, handle it and return early */ 244985d5e707SKishon Vijay Abraham I if (event->type.is_devspec == 0) { 245085d5e707SKishon Vijay Abraham I /* depevt */ 245185d5e707SKishon Vijay Abraham I return dwc3_endpoint_interrupt(dwc, &event->depevt); 245285d5e707SKishon Vijay Abraham I } 245385d5e707SKishon Vijay Abraham I 245485d5e707SKishon Vijay Abraham I switch (event->type.type) { 245585d5e707SKishon Vijay Abraham I case DWC3_EVENT_TYPE_DEV: 245685d5e707SKishon Vijay Abraham I dwc3_gadget_interrupt(dwc, &event->devt); 245785d5e707SKishon Vijay Abraham I break; 245885d5e707SKishon Vijay Abraham I /* REVISIT what to do with Carkit and I2C events ? */ 245985d5e707SKishon Vijay Abraham I default: 246085d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); 246185d5e707SKishon Vijay Abraham I } 246285d5e707SKishon Vijay Abraham I } 246385d5e707SKishon Vijay Abraham I 246485d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) 246585d5e707SKishon Vijay Abraham I { 246685d5e707SKishon Vijay Abraham I struct dwc3_event_buffer *evt; 246785d5e707SKishon Vijay Abraham I irqreturn_t ret = IRQ_NONE; 246885d5e707SKishon Vijay Abraham I int left; 246985d5e707SKishon Vijay Abraham I u32 reg; 247085d5e707SKishon Vijay Abraham I 247185d5e707SKishon Vijay Abraham I evt = dwc->ev_buffs[buf]; 247285d5e707SKishon Vijay Abraham I left = evt->count; 247385d5e707SKishon Vijay Abraham I 247485d5e707SKishon Vijay Abraham I if (!(evt->flags & DWC3_EVENT_PENDING)) 247585d5e707SKishon Vijay Abraham I return IRQ_NONE; 247685d5e707SKishon Vijay Abraham I 247785d5e707SKishon Vijay Abraham I while (left > 0) { 247885d5e707SKishon Vijay Abraham I union dwc3_event event; 247985d5e707SKishon Vijay Abraham I 248085d5e707SKishon Vijay Abraham I event.raw = *(u32 *) (evt->buf + evt->lpos); 248185d5e707SKishon Vijay Abraham I 248285d5e707SKishon Vijay Abraham I dwc3_process_event_entry(dwc, &event); 248385d5e707SKishon Vijay Abraham I 248485d5e707SKishon Vijay Abraham I /* 248585d5e707SKishon Vijay Abraham I * FIXME we wrap around correctly to the next entry as 248685d5e707SKishon Vijay Abraham I * almost all entries are 4 bytes in size. There is one 248785d5e707SKishon Vijay Abraham I * entry which has 12 bytes which is a regular entry 248885d5e707SKishon Vijay Abraham I * followed by 8 bytes data. ATM I don't know how 248985d5e707SKishon Vijay Abraham I * things are organized if we get next to the a 249085d5e707SKishon Vijay Abraham I * boundary so I worry about that once we try to handle 249185d5e707SKishon Vijay Abraham I * that. 249285d5e707SKishon Vijay Abraham I */ 249385d5e707SKishon Vijay Abraham I evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; 249485d5e707SKishon Vijay Abraham I left -= 4; 249585d5e707SKishon Vijay Abraham I 249685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); 249785d5e707SKishon Vijay Abraham I } 249885d5e707SKishon Vijay Abraham I 249985d5e707SKishon Vijay Abraham I evt->count = 0; 250085d5e707SKishon Vijay Abraham I evt->flags &= ~DWC3_EVENT_PENDING; 250185d5e707SKishon Vijay Abraham I ret = IRQ_HANDLED; 250285d5e707SKishon Vijay Abraham I 250385d5e707SKishon Vijay Abraham I /* Unmask interrupt */ 250485d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); 250585d5e707SKishon Vijay Abraham I reg &= ~DWC3_GEVNTSIZ_INTMASK; 250685d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); 250785d5e707SKishon Vijay Abraham I 250885d5e707SKishon Vijay Abraham I return ret; 250985d5e707SKishon Vijay Abraham I } 251085d5e707SKishon Vijay Abraham I 251185d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc) 251285d5e707SKishon Vijay Abraham I { 251385d5e707SKishon Vijay Abraham I struct dwc3 *dwc = _dwc; 251485d5e707SKishon Vijay Abraham I unsigned long flags; 251585d5e707SKishon Vijay Abraham I irqreturn_t ret = IRQ_NONE; 251685d5e707SKishon Vijay Abraham I int i; 251785d5e707SKishon Vijay Abraham I 251885d5e707SKishon Vijay Abraham I spin_lock_irqsave(&dwc->lock, flags); 251985d5e707SKishon Vijay Abraham I 252085d5e707SKishon Vijay Abraham I for (i = 0; i < dwc->num_event_buffers; i++) 252185d5e707SKishon Vijay Abraham I ret |= dwc3_process_event_buf(dwc, i); 252285d5e707SKishon Vijay Abraham I 252385d5e707SKishon Vijay Abraham I spin_unlock_irqrestore(&dwc->lock, flags); 252485d5e707SKishon Vijay Abraham I 252585d5e707SKishon Vijay Abraham I return ret; 252685d5e707SKishon Vijay Abraham I } 252785d5e707SKishon Vijay Abraham I 252885d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf) 252985d5e707SKishon Vijay Abraham I { 253085d5e707SKishon Vijay Abraham I struct dwc3_event_buffer *evt; 253185d5e707SKishon Vijay Abraham I u32 count; 253285d5e707SKishon Vijay Abraham I u32 reg; 253385d5e707SKishon Vijay Abraham I 253485d5e707SKishon Vijay Abraham I evt = dwc->ev_buffs[buf]; 253585d5e707SKishon Vijay Abraham I 253685d5e707SKishon Vijay Abraham I count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); 253785d5e707SKishon Vijay Abraham I count &= DWC3_GEVNTCOUNT_MASK; 253885d5e707SKishon Vijay Abraham I if (!count) 253985d5e707SKishon Vijay Abraham I return IRQ_NONE; 254085d5e707SKishon Vijay Abraham I 254185d5e707SKishon Vijay Abraham I evt->count = count; 254285d5e707SKishon Vijay Abraham I evt->flags |= DWC3_EVENT_PENDING; 254385d5e707SKishon Vijay Abraham I 254485d5e707SKishon Vijay Abraham I /* Mask interrupt */ 254585d5e707SKishon Vijay Abraham I reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(buf)); 254685d5e707SKishon Vijay Abraham I reg |= DWC3_GEVNTSIZ_INTMASK; 254785d5e707SKishon Vijay Abraham I dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); 254885d5e707SKishon Vijay Abraham I 254985d5e707SKishon Vijay Abraham I return IRQ_WAKE_THREAD; 255085d5e707SKishon Vijay Abraham I } 255185d5e707SKishon Vijay Abraham I 255285d5e707SKishon Vijay Abraham I static irqreturn_t dwc3_interrupt(int irq, void *_dwc) 255385d5e707SKishon Vijay Abraham I { 255485d5e707SKishon Vijay Abraham I struct dwc3 *dwc = _dwc; 255585d5e707SKishon Vijay Abraham I int i; 255685d5e707SKishon Vijay Abraham I irqreturn_t ret = IRQ_NONE; 255785d5e707SKishon Vijay Abraham I 255885d5e707SKishon Vijay Abraham I spin_lock(&dwc->lock); 255985d5e707SKishon Vijay Abraham I 256085d5e707SKishon Vijay Abraham I for (i = 0; i < dwc->num_event_buffers; i++) { 256185d5e707SKishon Vijay Abraham I irqreturn_t status; 256285d5e707SKishon Vijay Abraham I 256385d5e707SKishon Vijay Abraham I status = dwc3_check_event_buf(dwc, i); 256485d5e707SKishon Vijay Abraham I if (status == IRQ_WAKE_THREAD) 256585d5e707SKishon Vijay Abraham I ret = status; 256685d5e707SKishon Vijay Abraham I } 256785d5e707SKishon Vijay Abraham I 256885d5e707SKishon Vijay Abraham I spin_unlock(&dwc->lock); 256985d5e707SKishon Vijay Abraham I 257085d5e707SKishon Vijay Abraham I return ret; 257185d5e707SKishon Vijay Abraham I } 257285d5e707SKishon Vijay Abraham I 257385d5e707SKishon Vijay Abraham I /** 257485d5e707SKishon Vijay Abraham I * dwc3_gadget_init - Initializes gadget related registers 257585d5e707SKishon Vijay Abraham I * @dwc: pointer to our controller context structure 257685d5e707SKishon Vijay Abraham I * 257785d5e707SKishon Vijay Abraham I * Returns 0 on success otherwise negative errno. 257885d5e707SKishon Vijay Abraham I */ 257985d5e707SKishon Vijay Abraham I int dwc3_gadget_init(struct dwc3 *dwc) 258085d5e707SKishon Vijay Abraham I { 258185d5e707SKishon Vijay Abraham I int ret; 258285d5e707SKishon Vijay Abraham I 2583747a0a5bSKishon Vijay Abraham I dwc->ctrl_req = dma_alloc_coherent(sizeof(*dwc->ctrl_req), 2584747a0a5bSKishon Vijay Abraham I (unsigned long *)&dwc->ctrl_req_addr); 258585d5e707SKishon Vijay Abraham I if (!dwc->ctrl_req) { 258685d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to allocate ctrl request\n"); 258785d5e707SKishon Vijay Abraham I ret = -ENOMEM; 258885d5e707SKishon Vijay Abraham I goto err0; 258985d5e707SKishon Vijay Abraham I } 259085d5e707SKishon Vijay Abraham I 25918d488f3eSKishon Vijay Abraham I dwc->ep0_trb = dma_alloc_coherent(sizeof(*dwc->ep0_trb) * 2, 2592747a0a5bSKishon Vijay Abraham I (unsigned long *)&dwc->ep0_trb_addr); 259385d5e707SKishon Vijay Abraham I if (!dwc->ep0_trb) { 259485d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to allocate ep0 trb\n"); 259585d5e707SKishon Vijay Abraham I ret = -ENOMEM; 259685d5e707SKishon Vijay Abraham I goto err1; 259785d5e707SKishon Vijay Abraham I } 259885d5e707SKishon Vijay Abraham I 2599526a50f8SKishon Vijay Abraham I dwc->setup_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, 2600526a50f8SKishon Vijay Abraham I DWC3_EP0_BOUNCE_SIZE); 260185d5e707SKishon Vijay Abraham I if (!dwc->setup_buf) { 260285d5e707SKishon Vijay Abraham I ret = -ENOMEM; 260385d5e707SKishon Vijay Abraham I goto err2; 260485d5e707SKishon Vijay Abraham I } 260585d5e707SKishon Vijay Abraham I 2606747a0a5bSKishon Vijay Abraham I dwc->ep0_bounce = dma_alloc_coherent(DWC3_EP0_BOUNCE_SIZE, 2607747a0a5bSKishon Vijay Abraham I (unsigned long *)&dwc->ep0_bounce_addr); 260885d5e707SKishon Vijay Abraham I if (!dwc->ep0_bounce) { 260985d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); 261085d5e707SKishon Vijay Abraham I ret = -ENOMEM; 261185d5e707SKishon Vijay Abraham I goto err3; 261285d5e707SKishon Vijay Abraham I } 261385d5e707SKishon Vijay Abraham I 261485d5e707SKishon Vijay Abraham I dwc->gadget.ops = &dwc3_gadget_ops; 261585d5e707SKishon Vijay Abraham I dwc->gadget.max_speed = USB_SPEED_SUPER; 261685d5e707SKishon Vijay Abraham I dwc->gadget.speed = USB_SPEED_UNKNOWN; 261785d5e707SKishon Vijay Abraham I dwc->gadget.name = "dwc3-gadget"; 261885d5e707SKishon Vijay Abraham I 261985d5e707SKishon Vijay Abraham I /* 262085d5e707SKishon Vijay Abraham I * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize 262185d5e707SKishon Vijay Abraham I * on ep out. 262285d5e707SKishon Vijay Abraham I */ 262385d5e707SKishon Vijay Abraham I dwc->gadget.quirk_ep_out_aligned_size = true; 262485d5e707SKishon Vijay Abraham I 262585d5e707SKishon Vijay Abraham I /* 262685d5e707SKishon Vijay Abraham I * REVISIT: Here we should clear all pending IRQs to be 262785d5e707SKishon Vijay Abraham I * sure we're starting from a well known location. 262885d5e707SKishon Vijay Abraham I */ 262985d5e707SKishon Vijay Abraham I 263085d5e707SKishon Vijay Abraham I ret = dwc3_gadget_init_endpoints(dwc); 263185d5e707SKishon Vijay Abraham I if (ret) 263285d5e707SKishon Vijay Abraham I goto err4; 263385d5e707SKishon Vijay Abraham I 263485d5e707SKishon Vijay Abraham I ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); 263585d5e707SKishon Vijay Abraham I if (ret) { 263685d5e707SKishon Vijay Abraham I dev_err(dwc->dev, "failed to register udc\n"); 263785d5e707SKishon Vijay Abraham I goto err4; 263885d5e707SKishon Vijay Abraham I } 263985d5e707SKishon Vijay Abraham I 264085d5e707SKishon Vijay Abraham I return 0; 264185d5e707SKishon Vijay Abraham I 264285d5e707SKishon Vijay Abraham I err4: 264385d5e707SKishon Vijay Abraham I dwc3_gadget_free_endpoints(dwc); 2644747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ep0_bounce); 264585d5e707SKishon Vijay Abraham I 264685d5e707SKishon Vijay Abraham I err3: 264785d5e707SKishon Vijay Abraham I kfree(dwc->setup_buf); 264885d5e707SKishon Vijay Abraham I 264985d5e707SKishon Vijay Abraham I err2: 2650747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ep0_trb); 265185d5e707SKishon Vijay Abraham I 265285d5e707SKishon Vijay Abraham I err1: 2653747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ctrl_req); 265485d5e707SKishon Vijay Abraham I 265585d5e707SKishon Vijay Abraham I err0: 265685d5e707SKishon Vijay Abraham I return ret; 265785d5e707SKishon Vijay Abraham I } 265885d5e707SKishon Vijay Abraham I 265985d5e707SKishon Vijay Abraham I /* -------------------------------------------------------------------------- */ 266085d5e707SKishon Vijay Abraham I 266185d5e707SKishon Vijay Abraham I void dwc3_gadget_exit(struct dwc3 *dwc) 266285d5e707SKishon Vijay Abraham I { 266385d5e707SKishon Vijay Abraham I usb_del_gadget_udc(&dwc->gadget); 266485d5e707SKishon Vijay Abraham I 266585d5e707SKishon Vijay Abraham I dwc3_gadget_free_endpoints(dwc); 266685d5e707SKishon Vijay Abraham I 2667747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ep0_bounce); 266885d5e707SKishon Vijay Abraham I 266985d5e707SKishon Vijay Abraham I kfree(dwc->setup_buf); 267085d5e707SKishon Vijay Abraham I 2671747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ep0_trb); 267285d5e707SKishon Vijay Abraham I 2673747a0a5bSKishon Vijay Abraham I dma_free_coherent(dwc->ctrl_req); 2674747a0a5bSKishon Vijay Abraham I } 2675747a0a5bSKishon Vijay Abraham I 2676747a0a5bSKishon Vijay Abraham I /** 2677747a0a5bSKishon Vijay Abraham I * dwc3_gadget_uboot_handle_interrupt - handle dwc3 gadget interrupt 2678747a0a5bSKishon Vijay Abraham I * @dwc: struct dwce * 2679747a0a5bSKishon Vijay Abraham I * 2680747a0a5bSKishon Vijay Abraham I * Handles ep0 and gadget interrupt 2681747a0a5bSKishon Vijay Abraham I * 2682747a0a5bSKishon Vijay Abraham I * Should be called from dwc3 core. 2683747a0a5bSKishon Vijay Abraham I */ 2684747a0a5bSKishon Vijay Abraham I void dwc3_gadget_uboot_handle_interrupt(struct dwc3 *dwc) 2685747a0a5bSKishon Vijay Abraham I { 2686*137f7c59SMarek Szyprowski int ret = dwc3_interrupt(0, dwc); 2687*137f7c59SMarek Szyprowski 2688*137f7c59SMarek Szyprowski if (ret == IRQ_WAKE_THREAD) { 2689*137f7c59SMarek Szyprowski int i; 2690*137f7c59SMarek Szyprowski struct dwc3_event_buffer *evt; 2691*137f7c59SMarek Szyprowski 2692*137f7c59SMarek Szyprowski for (i = 0; i < dwc->num_event_buffers; i++) { 2693*137f7c59SMarek Szyprowski evt = dwc->ev_buffs[i]; 2694*137f7c59SMarek Szyprowski dwc3_flush_cache((int)evt->buf, evt->length); 2695*137f7c59SMarek Szyprowski } 2696*137f7c59SMarek Szyprowski 2697747a0a5bSKishon Vijay Abraham I dwc3_thread_interrupt(0, dwc); 269885d5e707SKishon Vijay Abraham I } 2699*137f7c59SMarek Szyprowski } 2700