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