dwc->current_dr_role = mode;
 }
 
+static int dwc3_core_soft_reset(struct dwc3 *dwc);
+
 static void __dwc3_set_mode(struct work_struct *work)
 {
        struct dwc3 *dwc = work_to_dwc(work);
        int ret;
        u32 reg;
 
+       mutex_lock(&dwc->mutex);
+
        pm_runtime_get_sync(dwc->dev);
 
        if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
                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);
                }
                break;
        case DWC3_GCTL_PRTCAP_DEVICE:
+               dwc3_core_soft_reset(dwc);
+
                dwc3_event_buffers_setup(dwc);
 
                if (dwc->usb2_phy)
 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)
        dwc3_cache_hwparams(dwc);
 
        spin_lock_init(&dwc->lock);
+       mutex_init(&dwc->mutex);
 
        pm_runtime_set_active(dev);
        pm_runtime_use_autosuspend(dev);
 
 
 #include <linux/device.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/ioport.h>
 #include <linux/list.h>
 #include <linux/bitops.h>
  * @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
        /* device lock */
        spinlock_t              lock;
 
+       /* mode switching lock */
+       struct mutex            mutex;
+
        struct device           *dev;
        struct device           *sysdev;