Merge tag 'dwc3-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
[cascardo/linux.git] / drivers / usb / dwc3 / gadget.c
index 4f3e881..58fdfad 100644 (file)
@@ -100,6 +100,23 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
        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;
 
@@ -107,7 +124,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
        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) {
                reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 
@@ -265,8 +290,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
                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:
@@ -530,9 +555,37 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
 
-       if (!list_empty(&dep->req_queued))
+       if (!list_empty(&dep->req_queued)) {
                dwc3_stop_active_transfer(dwc, dep->number);
 
+               /*
+                * NOTICE: We are violating what the Databook says about the
+                * EndTransfer command. Ideally we would _always_ wait for the
+                * EndTransfer Command Completion IRQ, but that's causing too
+                * much trouble synchronizing between us and gadget driver.
+                *
+                * We have discussed this with the IP Provider and it was
+                * suggested to giveback all requests here, but give HW some
+                * extra time to synchronize with the interconnect. We're using
+                * an arbitraty 100us delay for that.
+                *
+                * Note also that a similar handling was tested by Synopsys
+                * (thanks a lot Paul) and nothing bad has come out of it.
+                * In short, what we're doing is:
+                *
+                * - Issue EndTransfer WITH CMDIOC bit set
+                * - Wait 100us
+                * - giveback all requests to gadget driver
+                */
+               udelay(100);
+
+               while (!list_empty(&dep->req_queued)) {
+                       req = next_request(&dep->req_queued);
+
+                       dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+               }
+       }
+
        while (!list_empty(&dep->request_list)) {
                req = next_request(&dep->request_list);
 
@@ -741,8 +794,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        case USB_ENDPOINT_XFER_ISOC:
                trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
 
-               /* IOC every DWC3_TRB_NUM / 4 so we can refill */
-               if (!(cur_slot % (DWC3_TRB_NUM / 4)))
+               if (!req->request.no_interrupt)
                        trb->ctrl |= DWC3_TRB_CTRL_IOC;
                break;
 
@@ -958,14 +1010,42 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
        dep->flags |= DWC3_EP_BUSY;
 
        if (start_new) {
-               dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
+               dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
                                dep->number);
-               WARN_ON_ONCE(!dep->res_trans_idx);
+               WARN_ON_ONCE(!dep->resource_index);
        }
 
        return 0;
 }
 
+static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
+               struct dwc3_ep *dep, u32 cur_uf)
+{
+       u32 uf;
+
+       if (list_empty(&dep->request_list)) {
+               dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
+                       dep->name);
+               return;
+       }
+
+       /* 4 micro frames in the future */
+       uf = cur_uf + dep->interval * 4;
+
+       __dwc3_gadget_kick_transfer(dep, uf, 1);
+}
+
+static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
+               struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
+{
+       u32 cur_uf, mask;
+
+       mask = ~(dep->interval - 1);
+       cur_uf = event->parameters & mask;
+
+       __dwc3_gadget_start_isoc(dwc, dep, cur_uf);
+}
+
 static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 {
        struct dwc3             *dwc = dep->dwc;
@@ -995,11 +1075,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 
        list_add_tail(&req->list, &dep->request_list);
 
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
-               dep->flags |= DWC3_EP_PENDING_REQUEST;
-
        /*
-        * There are two special cases:
+        * There are a few special cases:
         *
         * 1. XferNotReady with empty list of requests. We need to kick the
         *    transfer here in that situation, otherwise we will be NAKing
@@ -1008,31 +1085,46 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         *    able to receive the data until the next request is queued.
         *    The following code is handling exactly that.
         *
-        * 2. XferInProgress on Isoc EP with an active transfer. We need to
-        *    kick the transfer here after queuing a request, otherwise the
-        *    core may not see the modified TRB(s).
         */
        if (dep->flags & DWC3_EP_PENDING_REQUEST) {
                int     ret;
-               int     start_trans = 1;
-               u8      trans_idx = dep->res_trans_idx;
 
-               if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
-                               (dep->flags & DWC3_EP_BUSY)) {
-                       start_trans = 0;
-                       WARN_ON_ONCE(!trans_idx);
-               } else {
-                       trans_idx = 0;
+               ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+               if (ret && ret != -EBUSY) {
+                       struct dwc3     *dwc = dep->dwc;
+
+                       dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+                                       dep->name);
                }
+       }
 
-               ret = __dwc3_gadget_kick_transfer(dep, trans_idx, start_trans);
+       /*
+        * 2. XferInProgress on Isoc EP with an active transfer. We need to
+        *    kick the transfer here after queuing a request, otherwise the
+        *    core may not see the modified TRB(s).
+        */
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+                       (dep->flags & DWC3_EP_BUSY)) {
+               WARN_ON_ONCE(!dep->resource_index);
+               ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
+                               false);
                if (ret && ret != -EBUSY) {
                        struct dwc3     *dwc = dep->dwc;
 
                        dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
                                        dep->name);
                }
-       };
+       }
+
+       /*
+        * 3. Missed ISOC Handling. We need to start isoc transfer on the saved
+        * uframe number.
+        */
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+               (dep->flags & DWC3_EP_MISSED_ISOC)) {
+                       __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
+                       dep->flags &= ~DWC3_EP_MISSED_ISOC;
+       }
 
        return 0;
 }
@@ -1118,15 +1210,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
        memset(&params, 0x00, sizeof(params));
 
        if (value) {
-               if (dep->number == 0 || dep->number == 1) {
-                       /*
-                        * Whenever EP0 is stalled, we will restart
-                        * the state machine, thus moving back to
-                        * Setup Phase
-                        */
-                       dwc->ep0state = EP0_SETUP_PHASE;
-               }
-
                ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_SETSTALL, &params);
                if (ret)
@@ -1186,7 +1269,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
        dep->flags |= DWC3_EP_WEDGE;
        spin_unlock_irqrestore(&dwc->lock, flags);
 
-       return dwc3_gadget_ep_set_halt(ep, 1);
+       if (dep->number == 0 || dep->number == 1)
+               return dwc3_gadget_ep0_set_halt(ep, 1);
+       else
+               return dwc3_gadget_ep_set_halt(ep, 1);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -1204,7 +1290,7 @@ static const struct usb_ep_ops dwc3_gadget_ep0_ops = {
        .free_request   = dwc3_gadget_ep_free_request,
        .queue          = dwc3_gadget_ep0_queue,
        .dequeue        = dwc3_gadget_ep_dequeue,
-       .set_halt       = dwc3_gadget_ep_set_halt,
+       .set_halt       = dwc3_gadget_ep0_set_halt,
        .set_wedge      = dwc3_gadget_ep_set_wedge,
 };
 
@@ -1280,9 +1366,13 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
                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_readl(dwc->regs, DWC3_DCTL);
+               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);
@@ -1319,16 +1409,21 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
        return 0;
 }
 
-static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
 {
        u32                     reg;
        u32                     timeout = 500;
 
        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;
        }
@@ -1346,7 +1441,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
                }
                timeout--;
                if (!timeout)
-                       break;
+                       return -ETIMEDOUT;
                udelay(1);
        } while (1);
 
@@ -1354,20 +1449,23 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
                        dwc->gadget_driver
                        ? dwc->gadget_driver->function : "no-function",
                        is_on ? "connect" : "disconnect");
+
+       return 0;
 }
 
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
        unsigned long           flags;
+       int                     ret;
 
        is_on = !!is_on;
 
        spin_lock_irqsave(&dwc->lock, flags);
-       dwc3_gadget_run_stop(dwc, is_on);
+       ret = dwc3_gadget_run_stop(dwc, is_on);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int dwc3_gadget_start(struct usb_gadget *g,
@@ -1468,6 +1566,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
 
        return 0;
 }
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
        .get_frame              = dwc3_gadget_get_frame,
        .wakeup                 = dwc3_gadget_wakeup,
@@ -1558,6 +1657,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
        struct dwc3_trb         *trb;
        unsigned int            count;
        unsigned int            s_pkt = 0;
+       unsigned int            trb_status;
 
        do {
                req = next_request(&dep->req_queued);
@@ -1583,9 +1683,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 
                if (dep->direction) {
                        if (count) {
-                               dev_err(dwc->dev, "incomplete IN transfer %s\n",
-                                               dep->name);
-                               status = -ECONNRESET;
+                               trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+                               if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
+                                       dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+                                                       dep->name);
+                                       dep->current_uf = event->parameters &
+                                               ~(dep->interval - 1);
+                                       dep->flags |= DWC3_EP_MISSED_ISOC;
+                               } else {
+                                       dev_err(dwc->dev, "incomplete IN transfer %s\n",
+                                                       dep->name);
+                                       status = -ECONNRESET;
+                               }
                        }
                } else {
                        if (count && (event->status & DEPEVT_STATUS_SHORT))
@@ -1604,7 +1713,8 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
                if (s_pkt)
                        break;
                if ((event->status & DEPEVT_STATUS_LST) &&
-                               (trb->ctrl & DWC3_TRB_CTRL_LST))
+                               (trb->ctrl & (DWC3_TRB_CTRL_LST |
+                                               DWC3_TRB_CTRL_HWO)))
                        break;
                if ((event->status & DEPEVT_STATUS_IOC) &&
                                (trb->ctrl & DWC3_TRB_CTRL_IOC))
@@ -1657,65 +1767,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
        }
 }
 
-static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
-               struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
-{
-       u32 uf, mask;
-
-       if (list_empty(&dep->request_list)) {
-               dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
-                       dep->name);
-               return;
-       }
-
-       mask = ~(dep->interval - 1);
-       uf = event->parameters & mask;
-       /* 4 micro frames in the future */
-       uf += dep->interval * 4;
-
-       __dwc3_gadget_kick_transfer(dep, uf, 1);
-}
-
-static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
-               const struct dwc3_event_depevt *event)
-{
-       struct dwc3 *dwc = dep->dwc;
-       struct dwc3_event_depevt mod_ev = *event;
-
-       /*
-        * We were asked to remove one request. It is possible that this
-        * request and a few others were started together and have the same
-        * transfer index. Since we stopped the complete endpoint we don't
-        * know how many requests were already completed (and not yet)
-        * reported and how could be done (later). We purge them all until
-        * the end of the list.
-        */
-       mod_ev.status = DEPEVT_STATUS_LST;
-       dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
-       dep->flags &= ~DWC3_EP_BUSY;
-       /* pending requests are ignored and are queued on XferNotReady */
-}
-
-static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
-               const struct dwc3_event_depevt *event)
-{
-       u32 param = event->parameters;
-       u32 cmd_type = (param >> 8) & ((1 << 5) - 1);
-
-       switch (cmd_type) {
-       case DWC3_DEPCMD_ENDTRANSFER:
-               dwc3_process_ep_cmd_complete(dep, event);
-               break;
-       case DWC3_DEPCMD_STARTTRANSFER:
-               dep->res_trans_idx = param & 0x7f;
-               break;
-       default:
-               printk(KERN_ERR "%s() unknown /unexpected type: %d\n",
-                               __func__, cmd_type);
-               break;
-       };
-}
-
 static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
@@ -1724,6 +1775,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
        dep = dwc->eps[epnum];
 
+       if (!(dep->flags & DWC3_EP_ENABLED))
+               return;
+
        dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
                        dwc3_ep_event_string(event->endpoint_event));
 
@@ -1734,7 +1788,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
        switch (event->endpoint_event) {
        case DWC3_DEPEVT_XFERCOMPLETE:
-               dep->res_trans_idx = 0;
+               dep->resource_index = 0;
 
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                        dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
@@ -1797,7 +1851,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
                break;
        case DWC3_DEPEVT_EPCMDCMPLT:
-               dwc3_ep_cmd_compl(dep, event);
+               dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
                break;
        }
 }
@@ -1820,16 +1874,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
 
        dep = dwc->eps[epnum];
 
-       WARN_ON(!dep->res_trans_idx);
-       if (dep->res_trans_idx) {
-               cmd = DWC3_DEPCMD_ENDTRANSFER;
-               cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
-               cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx);
-               memset(&params, 0, sizeof(params));
-               ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
-               WARN_ON_ONCE(ret);
-               dep->res_trans_idx = 0;
-       }
+       if (!dep->resource_index)
+               return;
+
+       cmd = DWC3_DEPCMD_ENDTRANSFER;
+       cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+       cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+       memset(&params, 0, sizeof(params));
+       ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+       WARN_ON_ONCE(ret);
+       dep->resource_index = 0;
 }
 
 static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -1872,11 +1926,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
 
 static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 {
+       int                     reg;
+
        dev_vdbg(dwc->dev, "%s\n", __func__);
-#if 0
-       XXX
-       U1/U2 is powersave optimization. Skip it for now. Anyway we need to
-       enable it before we can disable it.
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_INITU1ENA;
@@ -1884,9 +1936,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 
        reg &= ~DWC3_DCTL_INITU2ENA;
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-#endif
 
-       dwc3_stop_active_transfers(dwc);
        dwc3_disconnect_gadget(dwc);
        dwc->start_config_issued = false;
 
@@ -1894,30 +1944,30 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
        dwc->setup_packet_pending = false;
 }
 
-static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
 {
        u32                     reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
 
-       if (on)
-               reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
-       else
+       if (suspend)
                reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+       else
+               reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
        dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 }
 
-static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on)
+static void dwc3_gadget_usb2_phy_suspend(struct dwc3 *dwc, int suspend)
 {
        u32                     reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
 
-       if (on)
-               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-       else
+       if (suspend)
                reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+       else
+               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
 
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }
@@ -1962,16 +2012,18 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
        /* after reset -> Default State */
        dwc->dev_state = DWC3_DEFAULT_STATE;
 
-       /* Enable PHYs */
-       dwc3_gadget_usb2_phy_power(dwc, true);
-       dwc3_gadget_usb3_phy_power(dwc, true);
+       /* 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);
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_TSTCTRL_MASK;
-       reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
        dwc->test_mode = false;
 
@@ -2010,16 +2062,16 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
-static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed)
+static void dwc3_gadget_phy_suspend(struct dwc3 *dwc, u8 speed)
 {
        switch (speed) {
        case USB_SPEED_SUPER:
-               dwc3_gadget_usb2_phy_power(dwc, false);
+               dwc3_gadget_usb2_phy_suspend(dwc, true);
                break;
        case USB_SPEED_HIGH:
        case USB_SPEED_FULL:
        case USB_SPEED_LOW:
-               dwc3_gadget_usb3_phy_power(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, true);
                break;
        }
 }
@@ -2082,8 +2134,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                break;
        }
 
-       /* Disable unneded PHY */
-       dwc3_gadget_disable_phy(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);
@@ -2373,10 +2428,6 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
        reg |= DWC3_DCFG_LPM_CAP;
        dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 
-       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
-       reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
-       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
        /* Enable all but Start and End of Frame IRQs */
        reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
                        DWC3_DEVTEN_EVNTOVERFLOWEN |
@@ -2389,6 +2440,24 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
                        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(28);
+
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+               dwc3_gadget_usb2_phy_suspend(dwc, false);
+               dwc3_gadget_usb3_phy_suspend(dwc, false);
+       }
+
        ret = device_register(&dwc->gadget.dev);
        if (ret) {
                dev_err(dwc->dev, "failed to register gadget device\n");