aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/udc/net2280.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/udc/net2280.c')
-rw-r--r--drivers/usb/gadget/udc/net2280.c80
1 files changed, 76 insertions, 4 deletions
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 614ab951a4ae..61c938c36d88 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -589,7 +589,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !_req) {
- dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
+ dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n",
__func__, _ep, _req);
return;
}
@@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status);
}
-static void scan_dma_completions(struct net2280_ep *ep)
+static int scan_dma_completions(struct net2280_ep *ep)
{
+ int num_completed = 0;
+
/* only look at descriptors that were "naturally" retired,
* so fifo and list head state won't matter
*/
@@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep)
break;
/* single transfer mode */
dma_done(ep, req, tmp, 0);
+ num_completed++;
break;
} else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) &&
@@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
}
}
dma_done(ep, req, tmp, 0);
+ num_completed++;
}
+
+ return num_completed;
}
static void restart_dma(struct net2280_ep *ep)
@@ -1567,6 +1573,44 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
return ep;
}
+ /* USB3380: Only first four endpoints have DMA channels. Allocate
+ * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc
+ * endpoints use DMA hw endpoints.
+ */
+ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep2in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep4in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ !usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep1out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep3out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+ usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep1in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep3in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+ !usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep2out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep4out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ }
+
/* USB3380: use same address for usb and hardware endpoints */
snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
usb_endpoint_dir_in(desc) ? "in" : "out");
@@ -2547,8 +2591,11 @@ static void handle_ep_small(struct net2280_ep *ep)
/* manual DMA queue advance after short OUT */
if (likely(ep->dma)) {
if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
- u32 count;
+ struct net2280_request *stuck_req = NULL;
int stopped = ep->stopped;
+ int num_completed;
+ int stuck = 0;
+ u32 count;
/* TRANSFERRED works around OUT_DONE erratum 0112.
* we expect (N <= maxpacket) bytes; host wrote M.
@@ -2560,7 +2607,7 @@ static void handle_ep_small(struct net2280_ep *ep)
/* any preceding dma transfers must finish.
* dma handles (M >= N), may empty the queue
*/
- scan_dma_completions(ep);
+ num_completed = scan_dma_completions(ep);
if (unlikely(list_empty(&ep->queue) ||
ep->out_overflow)) {
req = NULL;
@@ -2580,6 +2627,31 @@ static void handle_ep_small(struct net2280_ep *ep)
req = NULL;
break;
}
+
+ /* Escape loop if no dma transfers completed
+ * after few retries.
+ */
+ if (num_completed == 0) {
+ if (stuck_req == req &&
+ readl(&ep->dma->dmadesc) !=
+ req->td_dma && stuck++ > 5) {
+ count = readl(
+ &ep->dma->dmacount);
+ count &= DMA_BYTE_COUNT_MASK;
+ req = NULL;
+ ep_dbg(ep->dev, "%s escape stuck %d, count %u\n",
+ ep->ep.name, stuck,
+ count);
+ break;
+ } else if (stuck_req != req) {
+ stuck_req = req;
+ stuck = 0;
+ }
+ } else {
+ stuck_req = NULL;
+ stuck = 0;
+ }
+
udelay(1);
}