summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2/hcd.c
diff options
context:
space:
mode:
authorDouglas Anderson <dianders@chromium.org>2019-04-17 00:53:49 +0300
committerFelipe Balbi <felipe.balbi@linux.intel.com>2019-05-03 09:13:47 +0300
commitc40cf7705e13d288d900e044c0a2f756e9e4909a (patch)
tree31ab9133f03c69cda3587744766a8ce59a72eddb /drivers/usb/dwc2/hcd.c
parentcc389eaabd7082a14e46aaa5a02f87c9eef37d7f (diff)
downloadlinux-c40cf7705e13d288d900e044c0a2f756e9e4909a.tar.xz
usb: dwc2: optionally assert phy reset when waking up
On the rk3288 USB host-only port (the one that's not the OTG-enabled port) the PHY can get into a bad state when a wakeup is asserted (not just a wakeup from full system suspend but also a wakeup from autosuspend). We can get the PHY out of its bad state by asserting its "port reset", but unfortunately that seems to assert a reset onto the USB bus so it could confuse things if we don't actually deenumerate / reenumerate the device. We can also get the PHY out of its bad state by fully resetting it using the reset from the CRU (clock reset unit), which does a more full reset. The CRU-based reset appears to actually cause devices on the bus to be removed and reinserted, which fixes the problem (albeit in a hacky way). It's unfortunate that we need to do a full re-enumeration of devices at wakeup time, but this is better than alternative of letting the bus get wedged. Signed-off-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Yunzhi Li <lyz@rock-chips.com> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Diffstat (limited to 'drivers/usb/dwc2/hcd.c')
-rw-r--r--drivers/usb/dwc2/hcd.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8667ddf3ca74..978232a9e4a8 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -4376,6 +4376,17 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
spin_unlock_irqrestore(&hsotg->lock, flags);
}
+static void dwc2_hcd_phy_reset_func(struct work_struct *work)
+{
+ struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
+ phy_reset_work);
+ int ret;
+
+ ret = phy_reset(hsotg->phy);
+ if (ret)
+ dev_warn(hsotg->dev, "PHY reset failed\n");
+}
+
/*
* =========================================================================
* Linux HC Driver Functions
@@ -5152,6 +5163,8 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
destroy_workqueue(hsotg->wq_otg);
}
+ cancel_work_sync(&hsotg->phy_reset_work);
+
del_timer(&hsotg->wkp_timer);
}
@@ -5293,11 +5306,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
hsotg->hc_ptr_array[i] = channel;
}
- /* Initialize hsotg start work */
+ /* Initialize work */
INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
-
- /* Initialize port reset work */
INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func);
+ INIT_WORK(&hsotg->phy_reset_work, dwc2_hcd_phy_reset_func);
/*
* Allocate space for storing data on status transactions. Normally no