CHROMIUM: usb: dwc3: rockchip: fix otg reset problem
authorWilliam wu <wulf@rock-chips.com>
Sun, 9 Oct 2016 16:26:20 +0000 (00:26 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 13 Oct 2016 01:51:27 +0000 (09:51 +0800)
We need to ensure the dwc controller stay in P2 state prior
to phy init. In order to set dwc controller in P2 state,
there're two methods:

1. Hold dwc controller in reset while initialize phy.
2. Do OTG reset before phy init, one thing to note here is
   that we can't reinit dwc controller again prior to phy init.

We choose the second mothod now. Because asserting the OTG
reset may affect dwc chip operation. The reset will clear all
of the dwc controller registers, and there are no synchronization
primitives, meaning the dwc3 core code could at least in theory
access chip registers while the reset is asserted, with unknown
impact. So we need to deassert the OTG reset as soon as possible.
Since phy init may take a long time, we can't hold the reset while
initialize phy.

Also, we add otg reset if dwc controller works as peripheral mode.

Change-Id: I54fec922308f62bfc7ebdde3e07ede9347e8f70a
Signed-off-by: William wu <wulf@rock-chips.com>
drivers/usb/dwc3/dwc3-rockchip.c

index 656a00c72ab1ba34890fa51d124ae3912e4b6ab2..7b2abf063ae557067e543304e1d1c02832bf2859 100644 (file)
@@ -76,7 +76,7 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                container_of(work, struct dwc3_rockchip, otg_work);
        struct dwc3             *dwc = rockchip->dwc;
        struct extcon_dev       *edev = rockchip->edev;
-       struct usb_hcd          *hcd;
+       struct usb_hcd          *hcd = dev_get_drvdata(&dwc->xhci->dev);
        unsigned long           flags;
        int                     ret;
        u32                     reg;
@@ -95,6 +95,21 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                if (WARN_ON(dwc->dr_mode == USB_DR_MODE_HOST))
                        return;
 
+               /*
+                * Assert otg reset can put the dwc in P2 state, it's
+                * necessary operation prior to phy power on. However,
+                * asserting the otg reset may affect dwc chip operation.
+                * The reset will clear all of the dwc controller registers.
+                * So we need to reinit the dwc controller after deassert
+                * the reset. We use pm runtime to initialize dwc controller.
+                * Also, there are no synchronization primitives, meaning
+                * the dwc3 core code could at least in theory access chip
+                * registers while the reset is asserted, with unknown impact.
+                */
+               reset_control_assert(rockchip->otg_rst);
+               usleep_range(1000, 1200);
+               reset_control_deassert(rockchip->otg_rst);
+
                pm_runtime_get_sync(dwc->dev);
 
                spin_lock_irqsave(&dwc->lock, flags);
@@ -114,31 +129,49 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
                if (WARN_ON(dwc->dr_mode == USB_DR_MODE_PERIPHERAL))
                        return;
 
+               /*
+                * Assert otg reset can put the dwc in P2 state, it's
+                * necessary operation prior to phy power on. However,
+                * asserting the otg reset may affect dwc chip operation.
+                * The reset will clear all of the dwc controller registers.
+                * So we need to reinit the dwc controller after deassert
+                * the reset. We use pm runtime to initialize dwc controller.
+                * Also, there are no synchronization primitives, meaning
+                * the dwc3 core code could at least in theory access chip
+                * registers while the reset is asserted, with unknown impact.
+                */
                reset_control_assert(rockchip->otg_rst);
+               usleep_range(1000, 1200);
+               reset_control_deassert(rockchip->otg_rst);
 
+               /*
+                * Don't abort on errors. If powering on a phy fails,
+                * we still need to init dwc controller and add the
+                * HCDs to avoid a crash when unloading the driver.
+                */
                ret = phy_power_on(dwc->usb2_generic_phy);
-               if (ret < 0) {
-                       reset_control_deassert(rockchip->otg_rst);
-                       return;
-               }
+               if (ret < 0)
+                       dev_err(dwc->dev, "Failed to power on usb2 phy\n");
 
                ret = phy_power_on(dwc->usb3_generic_phy);
                if (ret < 0) {
                        phy_power_off(dwc->usb2_generic_phy);
-                       reset_control_deassert(rockchip->otg_rst);
-                       return;
+                       dev_err(dwc->dev, "Failed to power on usb3 phy\n");
                }
 
-               reset_control_deassert(rockchip->otg_rst);
-
                pm_runtime_get_sync(dwc->dev);
 
-               hcd = dev_get_drvdata(&dwc->xhci->dev);
-
                spin_lock_irqsave(&dwc->lock, flags);
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
                spin_unlock_irqrestore(&dwc->lock, flags);
 
+               /*
+                * The following sleep helps to ensure that inserted USB3
+                * Ethernet devices are discovered if already inserted
+                * when booting.
+                */
+               usleep_range(10000, 11000);
+
                if (hcd->state == HC_STATE_HALT) {
                        usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
                        usb_add_hcd(hcd->shared_hcd, hcd->irq, IRQF_SHARED);
@@ -163,7 +196,6 @@ static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
 
                        phy_power_off(dwc->usb2_generic_phy);
                        phy_power_off(dwc->usb3_generic_phy);
-
                }
 
                pm_runtime_put_sync(dwc->dev);