aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/ci13xxx_udc.c
diff options
context:
space:
mode:
authorArtem Leonenko <tikkeri@gmail.com>2010-12-15 02:45:50 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-12-16 16:32:37 -0500
commitd9bb9c1820cb2a7aeb5e42a5470cf208002d9aa8 (patch)
tree758428f68ad6290ffc04c287f7bfd6cc2df48df8 /drivers/usb/gadget/ci13xxx_udc.c
parent6549e8b7f34b456d5689b98c2c0cf38c98414e47 (diff)
USB: gadget: update ci13xxx to work with g_ether
There is one nasty scenario when CI13xxx driver fails: a) two or more rx requests are queued (g_ether does that) b) rx request completed, interrupt fires and ci13xxx dequeues rq c) request complete() callback gets called and in turn it calls ep_queue() c1) in ep_queue() request gets added to the TAIL of the rx queue list d) ep gets primed with rq from (b) e) interrupt fires f) request gets popped from queue head for hw dequeue G) requets from queue head wasn't enqueued g1) isr_tr_complete_low() doesn't enqueue more requests and it doesn't prime EP, rx traffic stalls Solution: a) enque queued requests ASAP, i.e. before calling complete() callback. b) don't HW enqueue and prime endpoint with recently added request and use the oldest request in the queue. Fixed issues: a) ep_queue() may return an error code despite request was successfully added to the queue (if _hardware_enqueue() fails) b) Added requests are always processed in LIFO order, even if they are added in complete() callback c) Finally more than two and more queued requests are processed consistently, even if they were added in complete() callback The fix was successfully tested on MIPS based SoC with 4KEc CPU core and CI13612 USB core. Board successfully boots with NFS root using g_ether on ci13xxx udc. Signed-off-by: Artem Leonenko <tikkeri@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/ci13xxx_udc.c')
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c20
1 files changed, 12 insertions, 8 deletions
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 03505cbae873..f20e861deebf 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -1794,18 +1794,20 @@ __acquires(mEp->lock)
1794 1794
1795 dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); 1795 dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
1796 1796
1797 if (!list_empty(&mEp->qh[mEp->dir].queue)) {
1798 struct ci13xxx_req* mReqEnq;
1799
1800 mReqEnq = list_entry(mEp->qh[mEp->dir].queue.next,
1801 struct ci13xxx_req, queue);
1802 _hardware_enqueue(mEp, mReqEnq);
1803 }
1804
1797 if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { 1805 if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
1798 spin_unlock(mEp->lock); 1806 spin_unlock(mEp->lock);
1799 mReq->req.complete(&mEp->ep, &mReq->req); 1807 mReq->req.complete(&mEp->ep, &mReq->req);
1800 spin_lock(mEp->lock); 1808 spin_lock(mEp->lock);
1801 } 1809 }
1802 1810
1803 if (!list_empty(&mEp->qh[mEp->dir].queue)) {
1804 mReq = list_entry(mEp->qh[mEp->dir].queue.next,
1805 struct ci13xxx_req, queue);
1806 _hardware_enqueue(mEp, mReq);
1807 }
1808
1809 done: 1811 done:
1810 return retval; 1812 return retval;
1811} 1813}
@@ -2170,8 +2172,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
2170 mReq->req.actual = 0; 2172 mReq->req.actual = 0;
2171 list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); 2173 list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue);
2172 2174
2173 retval = _hardware_enqueue(mEp, mReq); 2175 if (list_is_singular(&mEp->qh[mEp->dir].queue))
2174 if (retval == -EALREADY || retval == -EBUSY) { 2176 retval = _hardware_enqueue(mEp, mReq);
2177
2178 if (retval == -EALREADY) {
2175 dbg_event(_usb_addr(mEp), "QUEUE", retval); 2179 dbg_event(_usb_addr(mEp), "QUEUE", retval);
2176 retval = 0; 2180 retval = 0;
2177 } 2181 }