int             retries = 10000;
        u32             reg;
 
+       /*
+        * Wait until device controller is ready. Only applies to 1.94a and
+        * later RTL.
+        */
+       if (dwc->revision >= DWC3_REVISION_194A) {
+               while (--retries) {
+                       reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+                       if (reg & DWC3_DSTS_DCNRD)
+                               udelay(5);
+                       else
+                               break;
+               }
+
+               if (retries <= 0)
+                       return -ETIMEDOUT;
+       }
+
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
 
        reg |= DWC3_DCTL_ULSTCHNGREQ(state);
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 
+       /*
+        * The following code is racy when called from dwc3_gadget_wakeup,
+        * and is not needed, at least on newer versions
+        */
+       if (dwc->revision >= DWC3_REVISION_194A)
+               return 0;
+
        /* wait for a change in DSTS */
        retries = 10000;
        while (--retries) {
                return "Clear Stall";
        case DWC3_DEPCMD_SETSTALL:
                return "Set Stall";
-       case DWC3_DEPCMD_GETSEQNUMBER:
-               return "Get Data Sequence Number";
+       case DWC3_DEPCMD_GETEPSTATE:
+               return "Get Endpoint State";
        case DWC3_DEPCMD_SETTRANSFRESOURCE:
                return "Set Endpoint Transfer Resource";
        case DWC3_DEPCMD_SETEPCONFIG:
                goto out;
        }
 
-       /* write zeroes to Link Change Request */
-       reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
-       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       /* Recent versions do this automatically */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* write zeroes to Link Change Request */
+               reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       }
 
        /* poll until Link State changes to ON */
        timeout = jiffies + msecs_to_jiffies(100);
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (is_on) {
-               reg &= ~DWC3_DCTL_TRGTULST_MASK;
-               reg |= (DWC3_DCTL_RUN_STOP
-                               | DWC3_DCTL_TRGTULST_RX_DET);
+               if (dwc->revision <= DWC3_REVISION_187A) {
+                       reg &= ~DWC3_DCTL_TRGTULST_MASK;
+                       reg |= DWC3_DCTL_TRGTULST_RX_DET;
+               }
+
+               if (dwc->revision >= DWC3_REVISION_194A)
+                       reg &= ~DWC3_DCTL_KEEP_CONNECT;
+               reg |= DWC3_DCTL_RUN_STOP;
        } else {
                reg &= ~DWC3_DCTL_RUN_STOP;
        }
 
        return 0;
 }
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
        .get_frame              = dwc3_gadget_get_frame,
        .wakeup                 = dwc3_gadget_wakeup,
        /* after reset -> Default State */
        dwc->dev_state = DWC3_DEFAULT_STATE;
 
-       /* Resume PHYs */
-       dwc3_gadget_usb2_phy_suspend(dwc, false);
-       dwc3_gadget_usb3_phy_suspend(dwc, false);
+       /* Recent versions support automatic phy suspend and don't need this */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* Resume PHYs */
+               dwc3_gadget_usb2_phy_suspend(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, false);
+       }
 
        if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
                dwc3_disconnect_gadget(dwc);
                break;
        }
 
-       /* Suspend unneded PHY */
-       dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+       /* Recent versions support automatic phy suspend and don't need this */
+       if (dwc->revision < DWC3_REVISION_194A) {
+               /* Suspend unneeded PHY */
+               dwc3_gadget_phy_suspend(dwc, dwc->gadget.speed);
+       }
 
        dep = dwc->eps[0];
        ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
                        DWC3_DEVTEN_DISCONNEVTEN);
        dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
 
+       /* Enable USB2 LPM and automatic phy suspend only on recent versions */
+       if (dwc->revision >= DWC3_REVISION_194A) {
+               reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+               reg |= DWC3_DCFG_LPM_CAP;
+               dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+
+               /* TODO: This should be configurable */
+               reg |= DWC3_DCTL_HIRD_THRES(31);
+
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+               dwc3_gadget_usb2_phy_suspend(dwc, true);
+               dwc3_gadget_usb3_phy_suspend(dwc, true);
+       }
+
        ret = device_register(&dwc->gadget.dev);
        if (ret) {
                dev_err(dwc->dev, "failed to register gadget device\n");