usb: dwc2: controller must update lx_state before releasing lock
authorGregory Herrero <gregory.herrero@intel.com>
Wed, 29 Apr 2015 20:09:03 +0000 (22:09 +0200)
committerFelipe Balbi <balbi@ti.com>
Wed, 29 Apr 2015 20:18:49 +0000 (15:18 -0500)
During suspend, there could a race condition between ep_queue and
suspend interrupt if lx_state is updated after releasing spinlock in
call_gadget(hsotg, suspend).

Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Gregory Herrero <gregory.herrero@intel.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc2/core_intr.c

index 6ffb5a9c385eaa65d10b61c915b858faa0fe3625..0b7f2b2e580e04a4b33bbfce3369fcbf57f1b094 100644 (file)
@@ -439,6 +439,12 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
                        if (!IS_ERR_OR_NULL(hsotg->uphy))
                                usb_phy_set_suspend(hsotg->uphy, true);
 skip_power_saving:
+                       /*
+                        * Change to L2 (suspend) state before releasing
+                        * spinlock
+                        */
+                       hsotg->lx_state = DWC2_L2;
+
                        /* Call gadget suspend callback */
                        call_gadget(hsotg, suspend);
                }
@@ -446,6 +452,8 @@ skip_power_saving:
                if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
                        dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
 
+                       /* Change to L2 (suspend) state */
+                       hsotg->lx_state = DWC2_L2;
                        /* Clear the a_peripheral flag, back to a_host */
                        spin_unlock(&hsotg->lock);
                        dwc2_hcd_start(hsotg);
@@ -454,9 +462,6 @@ skip_power_saving:
                }
        }
 
-       /* Change to L2 (suspend) state */
-       hsotg->lx_state = DWC2_L2;
-
 clear_int:
        /* Clear interrupt */
        writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);