Commit 1de2ebfb authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by Felipe Balbi

usb: gadget: net2280: fix infinite loop in irq handler

With SuperSpeed CDC NCM gadget, net2280 would get stuck in
'handle_ep_small' function. Triggering issue requires large
TCP transfer from host to USB3380.

Patch adds check for stuck condition and prevents hard lockup.
Signed-off-by: default avatarJussi Kivilinna <jussi.kivilinna@haltian.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 16501138
...@@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, ...@@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status); 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, /* only look at descriptors that were "naturally" retired,
* so fifo and list head state won't matter * so fifo and list head state won't matter
*/ */
...@@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep) ...@@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep)
break; break;
/* single transfer mode */ /* single transfer mode */
dma_done(ep, req, tmp, 0); dma_done(ep, req, tmp, 0);
num_completed++;
break; break;
} else if (!ep->is_in && } else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) && (req->req.length % ep->ep.maxpacket) &&
...@@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep) ...@@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
} }
} }
dma_done(ep, req, tmp, 0); dma_done(ep, req, tmp, 0);
num_completed++;
} }
return num_completed;
} }
static void restart_dma(struct net2280_ep *ep) static void restart_dma(struct net2280_ep *ep)
...@@ -2547,8 +2553,11 @@ static void handle_ep_small(struct net2280_ep *ep) ...@@ -2547,8 +2553,11 @@ static void handle_ep_small(struct net2280_ep *ep)
/* manual DMA queue advance after short OUT */ /* manual DMA queue advance after short OUT */
if (likely(ep->dma)) { if (likely(ep->dma)) {
if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
u32 count; struct net2280_request *stuck_req = NULL;
int stopped = ep->stopped; int stopped = ep->stopped;
int num_completed;
int stuck = 0;
u32 count;
/* TRANSFERRED works around OUT_DONE erratum 0112. /* TRANSFERRED works around OUT_DONE erratum 0112.
* we expect (N <= maxpacket) bytes; host wrote M. * we expect (N <= maxpacket) bytes; host wrote M.
...@@ -2560,7 +2569,7 @@ static void handle_ep_small(struct net2280_ep *ep) ...@@ -2560,7 +2569,7 @@ static void handle_ep_small(struct net2280_ep *ep)
/* any preceding dma transfers must finish. /* any preceding dma transfers must finish.
* dma handles (M >= N), may empty the queue * dma handles (M >= N), may empty the queue
*/ */
scan_dma_completions(ep); num_completed = scan_dma_completions(ep);
if (unlikely(list_empty(&ep->queue) || if (unlikely(list_empty(&ep->queue) ||
ep->out_overflow)) { ep->out_overflow)) {
req = NULL; req = NULL;
...@@ -2580,6 +2589,31 @@ static void handle_ep_small(struct net2280_ep *ep) ...@@ -2580,6 +2589,31 @@ static void handle_ep_small(struct net2280_ep *ep)
req = NULL; req = NULL;
break; 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); udelay(1);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment