aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/gadget.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r--drivers/usb/dwc3/gadget.c120
1 files changed, 71 insertions, 49 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 58fdfad96b4d..c9e729a4bf65 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -263,8 +263,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ if (dwc->ep0_bounced && dep->number == 0)
+ dwc->ep0_bounced = false;
+ else
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -431,15 +434,25 @@ 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_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3_gadget_ep_cmd_params params;
memset(&params, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
- | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
- | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
+ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
+
+ /* Burst size is only needed in SuperSpeed mode */
+ if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ u32 burst = dep->endpoint.maxburst - 1;
+
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ }
+
+ if (ignore)
+ params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -498,7 +511,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
@@ -510,7 +524,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
if (ret)
return ret;
@@ -558,27 +572,7 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
if (!list_empty(&dep->req_queued)) {
dwc3_stop_active_transfer(dwc, dep->number);
- /*
- * NOTICE: We are violating what the Databook says about the
- * EndTransfer command. Ideally we would _always_ wait for the
- * EndTransfer Command Completion IRQ, but that's causing too
- * much trouble synchronizing between us and gadget driver.
- *
- * We have discussed this with the IP Provider and it was
- * suggested to giveback all requests here, but give HW some
- * extra time to synchronize with the interconnect. We're using
- * an arbitraty 100us delay for that.
- *
- * Note also that a similar handling was tested by Synopsys
- * (thanks a lot Paul) and nothing bad has come out of it.
- * In short, what we're doing is:
- *
- * - Issue EndTransfer WITH CMDIOC bit set
- * - Wait 100us
- * - giveback all requests to gadget driver
- */
- udelay(100);
-
+ /* - giveback all requests to gadget driver */
while (!list_empty(&dep->req_queued)) {
req = next_request(&dep->req_queued);
@@ -657,6 +651,12 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
+ if (dep->flags & DWC3_EP_ENABLED) {
+ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
+ dep->name);
+ return 0;
+ }
+
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
strlcat(dep->name, "-control", sizeof(dep->name));
@@ -674,16 +674,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dev_err(dwc->dev, "invalid endpoint transfer type\n");
}
- if (dep->flags & DWC3_EP_ENABLED) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
- dep->name);
- return 0;
- }
-
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
+ ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1026,6 +1020,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
dep->name);
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
@@ -1089,13 +1084,21 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
int ret;
- ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
+ /*
+ * If xfernotready is already elapsed and it is a case
+ * of isoc transfer, then issue END TRANSFER, so that
+ * you can receive xfernotready again and can have
+ * notion of current microframe.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dwc3_stop_active_transfer(dwc, dep->number);
+ return 0;
+ }
+ ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
/*
@@ -1104,16 +1107,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* core may not see the modified TRB(s).
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_BUSY)) {
+ (dep->flags & DWC3_EP_BUSY) &&
+ !(dep->flags & DWC3_EP_MISSED_ISOC)) {
WARN_ON_ONCE(!dep->resource_index);
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
false);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
/*
@@ -1518,14 +1519,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, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
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, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -1750,7 +1751,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
int i;
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
- struct dwc3_ep *dep = dwc->eps[i];
+ dep = dwc->eps[i];
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -1877,6 +1878,25 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
if (!dep->resource_index)
return;
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+ * EndTransfer Command Completion IRQ, but that's causing too
+ * much trouble synchronizing between us and gadget driver.
+ *
+ * We have discussed this with the IP Provider and it was
+ * suggested to giveback all requests here, but give HW some
+ * extra time to synchronize with the interconnect. We're using
+ * an arbitraty 100us delay for that.
+ *
+ * Note also that a similar handling was tested by Synopsys
+ * (thanks a lot Paul) and nothing bad has come out of it.
+ * In short, what we're doing is:
+ *
+ * - Issue EndTransfer WITH CMDIOC bit set
+ * - Wait 100us
+ */
+
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
@@ -1884,6 +1904,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
+
+ udelay(100);
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -2141,14 +2163,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
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, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;