diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-07 10:09:55 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-07 10:09:55 -0700 |
commit | 23063b378de734383c9f42de770b01cd661cd9b4 (patch) | |
tree | e02c091743d8880e2bc8f37044b280a61714065d /drivers/usb/dwc3/gadget.c | |
parent | a5c708e84140bb19553303531316c540396a3a33 (diff) | |
parent | 3ef35fafdc87b72851f7785dc5331a0f4eb1b3ce (diff) |
Merge tag 'dwc3-for-v3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
usb: dwc3: patches for v3.5 merge window
This pull request contains one workaround for a Silicon
Issue found on all RTL releases prior to 2.20a, which
would cause a metastability state on Run/Stop bit.
We also have some patches implementing a few extra Standard
requests introduced by USB3 spec (Set SEL and Set Isoch Delay),
as well as one patch, which has been pending for a long time,
implementing LPM support.
Last, but not least, we are splitting the host address space
out of the dwc3 core driver otherwise xHCI won't be able to
request_mem_region() its own address space. This patch is
only needed because we are (as we should) re-using the xHCI
driver, which is a completely separate module.
Together with these three big changes, come a few extra preparatory
patches which most move code around, define macros and so on, as
well as a fix for Isochronous transfers which hasn't been triggered
before.
[ resolved conflicts and build error in drivers/usb/dwc3/gadget.c - gregkh]
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 114 |
1 files changed, 89 insertions, 25 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 561625a1284..3df1a1973b0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -276,6 +276,33 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd) } } +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) +{ + u32 timeout = 500; + u32 reg; + + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); + dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + if (!(reg & DWC3_DGCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DGCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it's also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + udelay(1); + } while (1); +} + int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) { @@ -929,10 +956,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, } dep->flags |= DWC3_EP_BUSY; - dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, - dep->number); - WARN_ON_ONCE(!dep->res_trans_idx); + if (start_new) { + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + WARN_ON_ONCE(!dep->res_trans_idx); + } return 0; } @@ -966,28 +995,37 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->request_list); + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY)) + dep->flags |= DWC3_EP_PENDING_REQUEST; + /* - * There is one special case: XferNotReady with - * empty list of requests. We need to kick the - * transfer here in that situation, otherwise - * we will be NAKing forever. + * There are two special cases: + * + * 1. XferNotReady with empty list of requests. We need to kick the + * transfer here in that situation, otherwise we will be NAKing + * forever. If we get XferNotReady before gadget driver has a + * chance to queue a request, we will ACK the IRQ but won't be + * able to receive the data until the next request is queued. + * The following code is handling exactly that. * - * If we get XferNotReady before gadget driver - * has a chance to queue a request, we will ACK - * the IRQ but won't be able to receive the data - * until the next request is queued. The following - * code is handling exactly that. + * 2. XferInProgress on Isoc EP with an active transfer. We need to + * kick the transfer here after queuing a request, otherwise the + * core may not see the modified TRB(s). */ if (dep->flags & DWC3_EP_PENDING_REQUEST) { - int ret; - int start_trans; + int ret; + int start_trans = 1; + u8 trans_idx = dep->res_trans_idx; - start_trans = 1; if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && - (dep->flags & DWC3_EP_BUSY)) + (dep->flags & DWC3_EP_BUSY)) { start_trans = 0; + WARN_ON_ONCE(!trans_idx); + } else { + trans_idx = 0; + } - ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans); + ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans); if (ret && ret != -EBUSY) { struct dwc3 *dwc = dep->dwc; @@ -1355,7 +1393,24 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); - reg |= dwc->maximum_speed; + + /** + * WORKAROUND: DWC3 revision < 2.20a have an issue + * which would cause metastability state on Run/Stop + * bit if we try to force the IP to USB2-only mode. + * + * Because of that, we cannot configure the IP to any + * speed other than the SuperSpeed + * + * Refers to: + * + * STAR#9000525659: Clock Domain Crossing on DCTL in + * USB 2.0 Mode + */ + if (dwc->revision < DWC3_REVISION_220A) + reg |= DWC3_DCFG_SUPERSPEED; + else + reg |= dwc->maximum_speed; dwc3_writel(dwc->regs, DWC3_DCFG, reg); dwc->start_config_issued = false; @@ -1915,6 +1970,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; + reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA); dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->test_mode = false; @@ -2262,8 +2318,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) goto err1; } - dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2, - GFP_KERNEL); + dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { dev_err(dwc->dev, "failed to allocate setup buffer\n"); ret = -ENOMEM; @@ -2271,7 +2326,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) } dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, - 512, &dwc->ep0_bounce_addr, GFP_KERNEL); + DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr, + GFP_KERNEL); if (!dwc->ep0_bounce) { dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); ret = -ENOMEM; @@ -2312,6 +2368,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) goto err5; } + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg |= DWC3_DCFG_LPM_CAP; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + /* Enable all but Start and End of Frame IRQs */ reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | DWC3_DEVTEN_EVNTOVERFLOWEN | @@ -2350,8 +2414,8 @@ err5: dwc3_gadget_free_endpoints(dwc); err4: - dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, - dwc->ep0_bounce_addr); + dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE, + dwc->ep0_bounce, dwc->ep0_bounce_addr); err3: kfree(dwc->setup_buf); @@ -2380,8 +2444,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc3_gadget_free_endpoints(dwc); - dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, - dwc->ep0_bounce_addr); + dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE, + dwc->ep0_bounce, dwc->ep0_bounce_addr); kfree(dwc->setup_buf); |