--- /dev/null
+From f88359e1588b85cf0e8209ab7d6620085f3441d9 Mon Sep 17 00:00:00 2001
+From: Yu Chen <chenyu56@huawei.com>
+Date: Thu, 15 Apr 2021 15:20:30 -0700
+Subject: usb: dwc3: core: Do core softreset when switch mode
+
+From: Yu Chen <chenyu56@huawei.com>
+
+commit f88359e1588b85cf0e8209ab7d6620085f3441d9 upstream.
+
+From: John Stultz <john.stultz@linaro.org>
+
+According to the programming guide, to switch mode for DRD controller,
+the driver needs to do the following.
+
+To switch from device to host:
+1. Reset controller with GCTL.CoreSoftReset
+2. Set GCTL.PrtCapDir(host mode)
+3. Reset the host with USBCMD.HCRESET
+4. Then follow up with the initializing host registers sequence
+
+To switch from host to device:
+1. Reset controller with GCTL.CoreSoftReset
+2. Set GCTL.PrtCapDir(device mode)
+3. Reset the device with DCTL.CSftRst
+4. Then follow up with the initializing registers sequence
+
+Currently we're missing step 1) to do GCTL.CoreSoftReset and step 3) of
+switching from host to device. John Stult reported a lockup issue seen
+with HiKey960 platform without these steps[1]. Similar issue is observed
+with Ferry's testing platform[2].
+
+So, apply the required steps along with some fixes to Yu Chen's and John
+Stultz's version. The main fixes to their versions are the missing wait
+for clocks synchronization before clearing GCTL.CoreSoftReset and only
+apply DCTL.CSftRst when switching from host to device.
+
+[1] https://lore.kernel.org/linux-usb/20210108015115.27920-1-john.stultz@linaro.org/
+[2] https://lore.kernel.org/linux-usb/0ba7a6ba-e6a7-9cd4-0695-64fc927e01f1@gmail.com/
+
+Fixes: 41ce1456e1db ("usb: dwc3: core: make dwc3_set_mode() work properly")
+Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
+Cc: Ferry Toth <fntoth@gmail.com>
+Cc: Wesley Cheng <wcheng@codeaurora.org>
+Cc: <stable@vger.kernel.org>
+Tested-by: John Stultz <john.stultz@linaro.org>
+Tested-by: Wesley Cheng <wcheng@codeaurora.org>
+Signed-off-by: Yu Chen <chenyu56@huawei.com>
+Signed-off-by: John Stultz <john.stultz@linaro.org>
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/374440f8dcd4f06c02c2caf4b1efde86774e02d9.1618521663.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Hardik Gohil <hgohil@mvista.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/dwc3/core.c | 25 +++++++++++++++++++++++++
+ drivers/usb/dwc3/core.h | 5 +++++
+ 2 files changed, 30 insertions(+)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -122,6 +122,8 @@ static void __dwc3_set_mode(struct work_
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
++ mutex_lock(&dwc->mutex);
++
+ pm_runtime_get_sync(dwc->dev);
+
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+@@ -155,6 +157,25 @@ static void __dwc3_set_mode(struct work_
+ break;
+ }
+
++ /* For DRD host or device mode only */
++ if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) {
++ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
++ reg |= DWC3_GCTL_CORESOFTRESET;
++ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
++
++ /*
++ * Wait for internal clocks to synchronized. DWC_usb31 and
++ * DWC_usb32 may need at least 50ms (less for DWC_usb3). To
++ * keep it consistent across different IPs, let's wait up to
++ * 100ms before clearing GCTL.CORESOFTRESET.
++ */
++ msleep(100);
++
++ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
++ reg &= ~DWC3_GCTL_CORESOFTRESET;
++ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
++ }
++
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc3_set_prtcap(dwc, dwc->desired_dr_role);
+@@ -179,6 +200,8 @@ static void __dwc3_set_mode(struct work_
+ }
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
++ dwc3_core_soft_reset(dwc);
++
+ dwc3_event_buffers_setup(dwc);
+
+ if (dwc->usb2_phy)
+@@ -201,6 +224,7 @@ static void __dwc3_set_mode(struct work_
+ out:
+ pm_runtime_mark_last_busy(dwc->dev);
+ pm_runtime_put_autosuspend(dwc->dev);
++ mutex_unlock(&dwc->mutex);
+ }
+
+ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+@@ -1511,6 +1535,7 @@ static int dwc3_probe(struct platform_de
+ dwc3_cache_hwparams(dwc);
+
+ spin_lock_init(&dwc->lock);
++ mutex_init(&dwc->mutex);
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -13,6 +13,7 @@
+
+ #include <linux/device.h>
+ #include <linux/spinlock.h>
++#include <linux/mutex.h>
+ #include <linux/ioport.h>
+ #include <linux/list.h>
+ #include <linux/bitops.h>
+@@ -929,6 +930,7 @@ struct dwc3_scratchpad_array {
+ * @scratch_addr: dma address of scratchbuf
+ * @ep0_in_setup: one control transfer is completed and enter setup phase
+ * @lock: for synchronizing
++ * @mutex: for mode switching
+ * @dev: pointer to our struct device
+ * @sysdev: pointer to the DMA-capable device
+ * @xhci: pointer to our xHCI child
+@@ -1061,6 +1063,9 @@ struct dwc3 {
+ /* device lock */
+ spinlock_t lock;
+
++ /* mode switching lock */
++ struct mutex mutex;
++
+ struct device *dev;
+ struct device *sysdev;
+
--- /dev/null
+From c2cd3452d5f8b66d49a73138fba5baadd5b489bd Mon Sep 17 00:00:00 2001
+From: Martin Kepplinger <martin.kepplinger@puri.sm>
+Date: Thu, 19 Mar 2020 11:02:07 +0100
+Subject: usb: dwc3: support continuous runtime PM with dual role
+
+From: Martin Kepplinger <martin.kepplinger@puri.sm>
+
+commit c2cd3452d5f8b66d49a73138fba5baadd5b489bd upstream.
+
+The DRD module calls dwc3_set_mode() on role switches, i.e. when a device is
+being plugged in. In order to support continuous runtime power management when
+plugging in / unplugging a cable, we need to call pm_runtime_get_sync() in
+this path.
+
+Signed-off-by: Martin Kepplinger <martin.kepplinger@puri.sm>
+Signed-off-by: Felipe Balbi <balbi@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/dwc3/core.c | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -122,17 +122,19 @@ static void __dwc3_set_mode(struct work_
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
++ pm_runtime_get_sync(dwc->dev);
++
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
+ if (!dwc->desired_dr_role)
+- return;
++ goto out;
+
+ if (dwc->desired_dr_role == dwc->current_dr_role)
+- return;
++ goto out;
+
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
+- return;
++ goto out;
+
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+@@ -196,6 +198,9 @@ static void __dwc3_set_mode(struct work_
+ break;
+ }
+
++out:
++ pm_runtime_mark_last_busy(dwc->dev);
++ pm_runtime_put_autosuspend(dwc->dev);
+ }
+
+ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)