usb: dwc3: introduce a connection flag for rockchip platform
authorWu Liang feng <wulf@rock-chips.com>
Sat, 10 Sep 2016 04:34:14 +0000 (12:34 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 12 Sep 2016 03:30:02 +0000 (11:30 +0800)
We used dwc->connected flag for rockchip platform before, but this
flag can be modified both in dwc3_gadget_disconnect_interrupt() and
dwc3_rockchip_otg_extcon_evt_work(), this result in race condition
may casue USB hot plug failed.

A typical error case is:
Set DWC3 works as peripheral device, connect to PC, after
PC identifies the USB device, and then disconnet USB cable,
if dwc3_gadget_disconnect_interrupt() is called prior to
dwc3_rockchip_otg_extcon_evt_work(), it will cause extcon
work unable to do disconnect operation, and let DWC3 core
device stay in runtime active status. Then if we plug in
USB cable again, we'll fail to do runtime resume DWC3 core
and reinit it.

Change-Id: I1c06fa55c10b3dfb749b689a116ab939a6e13f97
Signed-off-by: Wu Liang feng <wulf@rock-chips.com>
drivers/usb/dwc3/dwc3-rockchip.c

index 744e0e1712735e927ca82786266b4d6cc0ff3eb7..3c77bd3fed70cc6602540e07b7f076c35fc024b8 100644 (file)
@@ -37,6 +37,7 @@
 
 struct dwc3_rockchip {
        int                     num_clocks;
+       bool                    connected;
        struct device           *dev;
        struct clk              **clks;
        struct dwc3             *dwc;
@@ -84,7 +85,7 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                return;
 
        if (extcon_get_cable_state_(edev, EXTCON_USB) > 0) {
-               if (dwc->connected)
+               if (rockchip->connected)
                        return;
 
                /*
@@ -97,13 +98,13 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                pm_runtime_get_sync(dwc->dev);
 
                spin_lock_irqsave(&dwc->lock, flags);
-               dwc->connected = true;
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
                spin_unlock_irqrestore(&dwc->lock, flags);
 
+               rockchip->connected = true;
                dev_info(rockchip->dev, "USB peripheral connected\n");
        } else if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) > 0) {
-               if (dwc->connected)
+               if (rockchip->connected)
                        return;
 
                /*
@@ -131,7 +132,6 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
 
                spin_lock_irqsave(&dwc->lock, flags);
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
-               dwc->connected = true;
                spin_unlock_irqrestore(&dwc->lock, flags);
 
                if (hcd->state == HC_STATE_HALT) {
@@ -139,9 +139,10 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                        usb_add_hcd(hcd->shared_hcd, hcd->irq, IRQF_SHARED);
                }
 
+               rockchip->connected = true;
                dev_info(rockchip->dev, "USB HOST connected\n");
        } else {
-               if (!dwc->connected)
+               if (!rockchip->connected)
                        return;
 
                reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -160,12 +161,9 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
 
                }
 
-               spin_lock_irqsave(&dwc->lock, flags);
-               dwc->connected = false;
-               spin_unlock_irqrestore(&dwc->lock, flags);
-
                pm_runtime_put_sync(dwc->dev);
 
+               rockchip->connected = false;
                dev_info(rockchip->dev, "USB unconnected\n");
        }
 }