usb: mtu3: fix race condition about delayed_status
[linux-2.6-microblaze.git] / drivers / usb / mtu3 / mtu3_gadget_ep0.c
index 4da216c..2be182b 100644 (file)
@@ -153,6 +153,15 @@ static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
                set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
 }
 
+static void ep0_do_status_stage(struct mtu3 *mtu)
+{
+       void __iomem *mbase = mtu->mac_base;
+       u32 value;
+
+       value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+       mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
+}
+
 static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
 
 static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
@@ -297,8 +306,7 @@ static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
                ep0_load_test_packet(mtu);
 
        /* send status before entering test mode. */
-       value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
-       mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
+       ep0_do_status_stage(mtu);
 
        /* wait for ACK status sent by host */
        readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value,
@@ -632,7 +640,6 @@ __acquires(mtu->lock)
 {
        struct usb_ctrlrequest setup;
        struct mtu3_request *mreq;
-       void __iomem *mbase = mtu->mac_base;
        int handled = 0;
 
        ep0_read_setup(mtu, &setup);
@@ -664,14 +671,19 @@ finish:
        if (mtu->test_mode) {
                ;       /* nothing to do */
        } else if (handled == USB_GADGET_DELAYED_STATUS) {
-               /* handle the delay STATUS phase till receive ep_queue on ep0 */
-               mtu->delayed_status = true;
-       } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
 
-               mtu3_writel(mbase, U3D_EP0CSR,
-                       (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
-                       | EP0_SETUPPKTRDY | EP0_DATAEND);
+               mreq = next_ep0_request(mtu);
+               if (mreq) {
+                       /* already asked us to continue delayed status */
+                       ep0_do_status_stage(mtu);
+                       ep0_req_giveback(mtu, &mreq->request);
+               } else {
+                       /* do delayed STATUS stage till receive ep0_queue */
+                       mtu->delayed_status = true;
+               }
+       } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
 
+               ep0_do_status_stage(mtu);
                /* complete zlp request directly */
                mreq = next_ep0_request(mtu);
                if (mreq && !mreq->request.length)
@@ -802,12 +814,9 @@ static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
        }
 
        if (mtu->delayed_status) {
-               u32 csr;
 
                mtu->delayed_status = false;
-               csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
-               csr |= EP0_SETUPPKTRDY | EP0_DATAEND;
-               mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+               ep0_do_status_stage(mtu);
                /* needn't giveback the request for handling delay STATUS */
                return 0;
        }