diff options
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 124 |
1 files changed, 86 insertions, 38 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1e8bdf817811..e24a01cc98df 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -948,7 +948,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name); return -EBUSY; } - dep->flags &= ~DWC3_EP_PENDING_REQUEST; /* * If we are getting here after a short-out-packet we don't enqueue any @@ -1050,6 +1049,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) req->direction = dep->direction; req->epnum = dep->number; + trace_dwc3_ep_queue(req); + /* * We only add to our list of requests now and * start consuming the list once we get XferNotReady @@ -1070,6 +1071,19 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->request_list); /* + * If there are no pending requests and the endpoint isn't already + * busy, we will just start the request straight away. + * + * This will save one IRQ (XFER_NOT_READY) and possibly make it a + * little bit faster. + */ + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && + !(dep->flags & DWC3_EP_BUSY)) { + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + goto out; + } + + /* * There are a few special cases: * * 1. XferNotReady with empty list of requests. We need to kick the @@ -1096,10 +1110,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } ret = __dwc3_gadget_kick_transfer(dep, 0, true); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - return ret; + if (!ret) + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + goto out; } /* @@ -1113,10 +1127,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) WARN_ON_ONCE(!dep->resource_index); ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, false); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - return ret; + goto out; } /* @@ -1124,14 +1135,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * right away, otherwise host will not know we have streams to be * handled. */ - if (dep->stream_capable) { + if (dep->stream_capable) ret = __dwc3_gadget_kick_transfer(dep, 0, true); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - } - return 0; +out: + if (ret && ret != -EBUSY) + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + if (ret == -EBUSY) + ret = 0; + + return ret; } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, @@ -1159,8 +1173,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - trace_dwc3_ep_queue(req); - ret = __dwc3_gadget_ep_queue(dep, req); out: @@ -1872,27 +1884,32 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int i; int ret; - req = next_request(&dep->req_queued); - if (!req) { - WARN_ON_ONCE(1); - return 1; - } - i = 0; do { - slot = req->start_slot + i; - if ((slot == DWC3_TRB_NUM - 1) && + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + i = 0; + do { + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); + if (ret) + break; + } while (++i < req->request.num_mapped_sgs); + + dwc3_gadget_giveback(dep, req, status); - ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, - event, status); if (ret) break; - } while (++i < req->request.num_mapped_sgs); - - dwc3_gadget_giveback(dep, req, status); + } while (1); if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->req_queued)) { @@ -1955,6 +1972,14 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, dwc->u1u2 = 0; } + + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + int ret; + + ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete); + if (!ret || ret == -EBUSY) + return; + } } static void dwc3_endpoint_interrupt(struct dwc3 *dwc, @@ -1992,15 +2017,16 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dwc3_gadget_start_isoc(dwc, dep, event); } else { + int active; int ret; + active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE; + dwc3_trace(trace_dwc3_gadget, "%s: reason %s", - dep->name, event->status & - DEPEVT_STATUS_TRANSFER_ACTIVE - ? "Transfer Active" + dep->name, active ? "Transfer Active" : "Transfer Not Active"); - ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + ret = __dwc3_gadget_kick_transfer(dep, 0, !active); if (!ret || ret == -EBUSY) return; @@ -2718,12 +2744,34 @@ int dwc3_gadget_init(struct dwc3 *dwc) } dwc->gadget.ops = &dwc3_gadget_ops; - dwc->gadget.max_speed = USB_SPEED_SUPER; dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->gadget.sg_supported = true; dwc->gadget.name = "dwc3-gadget"; /* + * FIXME We might be setting max_speed to <SUPER, however versions + * <2.20a of dwc3 have an issue with metastability (documented + * elsewhere in this driver) which tells us we can't set max speed to + * anything lower than SUPER. + * + * Because gadget.max_speed is only used by composite.c and function + * drivers (i.e. it won't go into dwc3's registers) we are allowing this + * to happen so we avoid sending SuperSpeed Capability descriptor + * together with our BOS descriptor as that could confuse host into + * thinking we can handle super speed. + * + * Note that, in fact, we won't even support GetBOS requests when speed + * is less than super speed because we don't have means, yet, to tell + * composite.c that we are USB 2.0 + LPM ECN. + */ + if (dwc->revision < DWC3_REVISION_220A) + dwc3_trace(trace_dwc3_gadget, + "Changing max_speed on rev %08x\n", + dwc->revision); + + dwc->gadget.max_speed = dwc->maximum_speed; + + /* * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize * on ep out. */ |