diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-12 15:19:40 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-12 15:19:53 -0800 |
commit | 007d00d4c11b30b8fd7ff23b9d5aed3743e41f74 (patch) | |
tree | d5c307ba2ee0e2f56fbc284db21b8bccf95d8a40 /drivers/usb/dwc3/gadget.c | |
parent | c91043adaf50ef13609003120f3471783460fb71 (diff) | |
parent | 68d3e668d245bb8300c7c6ddbc8508ddfe352e0f (diff) |
Merge branch 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
* 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (392 commits)
usb: dwc3: ep0: fix for possible early delayed_status
usb: dwc3: gadget: fix stream enable bit
usb: dwc3: ep0: fix GetStatus handling (again)
usb: dwc3: ep0: use dwc3_request for ep0 requsts instead of usb_request
usb: dwc3: use correct hwparam register for power mgm check
usb: dwc3: omap: move to module_platform_driver
usb: dwc3: workaround: missing disconnect event
usb: dwc3: workaround: missing USB3 Reset event
usb: dwc3: workaround: U1/U2 -> U0 transiton
usb: dwc3: gadget: return early in dwc3_cleanup_done_reqs()
usb: dwc3: ep0: handle delayed_status again
usb: dwc3: ep0: push ep0state into xfernotready processing
usb: dwc3: fix sparse errors
usb: dwc3: fix few coding style problems
usb: dwc3: move generic dwc3 code from gadget into core
usb: dwc3: use a helper function for operation mode setting
usb: dwc3: ep0: don't use ep0in for transfers
usb: dwc3: ep0: use proper endianess in SetFeature for wIndex
usb: dwc3: core: drop DWC3_EVENT_BUFFERS_MAX
usb: dwc3: omap: add multiple instances support to OMAP
...
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 197 |
1 files changed, 147 insertions, 50 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 25dbd8614e7..026c53cf164 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -251,7 +251,8 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) } static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc) { struct dwc3_gadget_ep_cmd_params params; @@ -264,7 +265,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN; - if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { + if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes) + && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; @@ -317,7 +319,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) * Caller should take care of locking */ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, - const struct usb_endpoint_descriptor *desc) + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -329,7 +332,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc); + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc); if (ret) return ret; @@ -343,6 +346,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; dep->desc = desc; + dep->comp_desc = comp_desc; dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; @@ -405,6 +409,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->stream_capable = false; dep->desc = NULL; + dep->comp_desc = NULL; dep->type = 0; dep->flags = 0; @@ -473,7 +478,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_enable(dep, desc); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -745,8 +750,9 @@ 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); - if (!dep->res_trans_idx) - printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__); + + WARN_ON_ONCE(!dep->res_trans_idx); + return 0; } @@ -1155,35 +1161,9 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->gadget_driver = driver; dwc->gadget.dev.driver = &driver->driver; - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - - reg &= ~DWC3_GCTL_SCALEDOWN(3); - reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG); - reg &= ~DWC3_GCTL_DISSCRAMBLE; - reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); - - switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { - case DWC3_GHWPARAMS1_EN_PWROPT_CLK: - reg &= ~DWC3_GCTL_DSBLCLKGTNG; - break; - default: - dev_dbg(dwc->dev, "No power optimization available\n"); - } - - /* - * WORKAROUND: DWC3 revisions <1.90a have a bug - * when The device fails to connect at SuperSpeed - * and falls back to high-speed mode which causes - * the device to enter in a Connect/Disconnect loop - */ - if (dwc->revision < DWC3_REVISION_190A) - reg |= DWC3_GCTL_U2RSTECN; - - dwc3_writel(dwc->regs, DWC3_GCTL, reg); - reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); - reg |= DWC3_DCFG_SUPERSPEED; + reg |= dwc->maximum_speed; dwc3_writel(dwc->regs, DWC3_DCFG, reg); dwc->start_config_issued = false; @@ -1192,14 +1172,14 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err0; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err1; @@ -1290,11 +1270,10 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) &dwc->gadget.ep_list); ret = dwc3_alloc_trb_pool(dep); - if (ret) { - dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name); + if (ret) return ret; - } } + INIT_LIST_HEAD(&dep->request_list); INIT_LIST_HEAD(&dep->req_queued); } @@ -1334,8 +1313,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, do { req = next_request(&dep->req_queued); - if (!req) - break; + if (!req) { + WARN_ON_ONCE(1); + return 1; + } dwc3_trb_to_nat(req->trb, &trb); @@ -1400,6 +1381,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_BUSY; dep->res_trans_idx = 0; } + + /* + * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. + * See dwc3_gadget_linksts_change_interrupt() for 1st half. + */ + if (dwc->revision < DWC3_REVISION_183A) { + u32 reg; + int i; + + for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { + struct dwc3_ep *dep = dwc->eps[i]; + + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + if (!list_empty(&dep->req_queued)) + return; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= dwc->u1u2; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc->u1u2 = 0; + } } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1639,6 +1645,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->start_config_issued = false; dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->setup_packet_pending = false; } static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) @@ -1675,6 +1682,37 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dev_vdbg(dwc->dev, "%s\n", __func__); + /* + * WORKAROUND: DWC3 revisions <1.88a have an issue which + * would cause a missing Disconnect Event if there's a + * pending Setup Packet in the FIFO. + * + * There's no suggested workaround on the official Bug + * report, which states that "unless the driver/application + * is doing any special handling of a disconnect event, + * there is no functional issue". + * + * Unfortunately, it turns out that we _do_ some special + * handling of a disconnect event, namely complete all + * pending transfers, notify gadget driver of the + * disconnection, and so on. + * + * Our suggested workaround is to follow the Disconnect + * Event steps here, instead, based on a setup_packet_pending + * flag. Such flag gets set whenever we have a XferNotReady + * event on EP0 and gets cleared on XferComplete for the + * same endpoint. + * + * Refers to: + * + * STAR#9000466709: RTL: Device : Disconnect event not + * generated if setup packet pending in FIFO + */ + if (dwc->revision < DWC3_REVISION_188A) { + if (dwc->setup_packet_pending) + dwc3_gadget_disconnect_interrupt(dwc); + } + /* Enable PHYs */ dwc3_gadget_usb2_phy_power(dwc, true); dwc3_gadget_usb3_phy_power(dwc, true); @@ -1755,6 +1793,22 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) switch (speed) { case DWC3_DCFG_SUPERSPEED: + /* + * WORKAROUND: DWC3 revisions <1.90a have an issue which + * would cause a missing USB3 Reset event. + * + * In such situations, we should force a USB3 Reset + * event by calling our dwc3_gadget_reset_interrupt() + * routine. + * + * Refers to: + * + * STAR#9000483510: RTL: SS : USB3 reset event may + * not be generated always when the link enters poll + */ + if (dwc->revision < DWC3_REVISION_190A) + dwc3_gadget_reset_interrupt(dwc); + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dwc->gadget.ep0->maxpacket = 512; dwc->gadget.speed = USB_SPEED_SUPER; @@ -1781,14 +1835,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; @@ -1818,8 +1872,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { - /* The fith bit says SuperSpeed yes or no. */ - dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + + /* + * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending + * on the link partner, the USB session might do multiple entry/exit + * of low power states before a transfer takes place. + * + * Due to this problem, we might experience lower throughput. The + * suggested workaround is to disable DCTL[12:9] bits if we're + * transitioning from U1/U2 to U0 and enable those bits again + * after a transfer completes and there are no pending transfers + * on any of the enabled endpoints. + * + * This is the first half of that workaround. + * + * Refers to: + * + * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us + * core send LGO_Ux entering U0 + */ + if (dwc->revision < DWC3_REVISION_183A) { + if (next == DWC3_LINK_STATE_U0) { + u32 u1u2; + u32 reg; + + switch (dwc->link_state) { + case DWC3_LINK_STATE_U1: + case DWC3_LINK_STATE_U2: + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + u1u2 = reg & (DWC3_DCTL_INITU2ENA + | DWC3_DCTL_ACCEPTU2ENA + | DWC3_DCTL_INITU1ENA + | DWC3_DCTL_ACCEPTU1ENA); + + if (!dwc->u1u2) + dwc->u1u2 = reg & u1u2; + + reg &= ~u1u2; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + /* do nothing */ + break; + } + } + } + + dwc->link_state = next; dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } @@ -1925,7 +2026,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) spin_lock(&dwc->lock); - for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + for (i = 0; i < dwc->num_event_buffers; i++) { irqreturn_t status; status = dwc3_process_event_buf(dwc, i); @@ -2076,7 +2177,6 @@ err0: void dwc3_gadget_exit(struct dwc3 *dwc) { int irq; - int i; usb_del_gadget_udc(&dwc->gadget); irq = platform_get_irq(to_platform_device(dwc->dev), 0); @@ -2084,9 +2184,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); free_irq(irq, dwc); - for (i = 0; i < ARRAY_SIZE(dwc->eps); i++) - __dwc3_gadget_ep_disable(dwc->eps[i]); - dwc3_gadget_free_endpoints(dwc); dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, |