]> Pileus Git - ~andy/linux/commitdiff
Merge tag 'dwc3-for-v3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jul 2012 22:15:38 +0000 (15:15 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jul 2012 22:15:38 +0000 (15:15 -0700)
usb: dwc3: patches for v3.6 merge window

lots of changes for the dwc3 driver which will make
the driver a lot more stable and some changes which
are just cosmetic.

The bulk of changes is related to mis-defined macros
which were never used before, some fixes to error path
which e.g. prevent the driver from initiating two
transfer on ep0out in case of stall, some fixes to
request dequeueing and the End Transfer Command.

We have some changes to U1/U2 handling where we were
enabling those transtions at the wrong spot (though
we haven't seen a problem from that as of today).

A few patches will make it easier to add support for
newer releases of the core by adding definitions for
new registers and changing the code to act accordingly
when certain revisions are detected.

There's also the usual comestic changes to make the
driver easier to maintain and make it easier for new-
comers to understand the driver. Also one patch fixing
a double inclusion of a header.

1  2 
drivers/usb/dwc3/gadget.c

index 4f3e8811daefd9956558caa00f80f241151cd3ab,aedc1c5cfac100d05c16b2128d82804063b998ba..58fdfad96b4d61b2cc86de9763a135dc81fcd908
@@@ -100,6 -100,23 +100,23 @@@ int dwc3_gadget_set_link_state(struct d
        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) {
                reg = dwc3_readl(dwc->regs, DWC3_DSTS);
  
@@@ -265,8 -290,8 +290,8 @@@ static const char *dwc3_gadget_ep_cmd_s
                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:
@@@ -414,7 -439,7 +439,7 @@@ static int dwc3_gadget_set_ep_config(st
  
        params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
                | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
 -              | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst);
 +              | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
  
        params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
                | DWC3_DEPCFG_XFER_NOT_READY_EN;
@@@ -530,9 -555,37 +555,37 @@@ static void dwc3_remove_requests(struc
  {
        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 +794,7 @@@ static void dwc3_prepare_one_trb(struc
        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 +1010,42 @@@ static int __dwc3_gadget_kick_transfer(
        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;
  
        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
         *    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;
  }
@@@ -1091,7 -1183,7 +1183,7 @@@ static int dwc3_gadget_ep_dequeue(struc
                if (r == req) {
                        /* wait until it is processed */
                        dwc3_stop_active_transfer(dwc, dep->number);
 -                      goto out0;
 +                      goto out1;
                }
                dev_err(dwc->dev, "request %p was not queued to %s\n",
                                request, ep->name);
                goto out0;
        }
  
 +out1:
        /* giveback the request */
        dwc3_gadget_giveback(dep, req, -ECONNRESET);
  
@@@ -1118,15 -1209,6 +1210,6 @@@ int __dwc3_gadget_ep_set_halt(struct dw
        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 -1268,10 +1269,10 @@@ static int dwc3_gadget_ep_set_wedge(str
        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 -1289,7 +1290,7 @@@ static const struct usb_ep_ops dwc3_gad
        .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 -1365,13 +1366,13 @@@ static int dwc3_gadget_wakeup(struct us
                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 -1408,21 +1409,21 @@@ static int dwc3_gadget_set_selfpowered(
        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;
        }
                }
                timeout--;
                if (!timeout)
-                       break;
+                       return -ETIMEDOUT;
                udelay(1);
        } while (1);
  
                        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 -1565,7 +1566,7 @@@ static int dwc3_gadget_stop(struct usb_
  
        return 0;
  }
  static const struct usb_gadget_ops dwc3_gadget_ops = {
        .get_frame              = dwc3_gadget_get_frame,
        .wakeup                 = dwc3_gadget_wakeup,
@@@ -1558,6 -1656,7 +1657,7 @@@ static int dwc3_cleanup_done_reqs(struc
        struct dwc3_trb         *trb;
        unsigned int            count;
        unsigned int            s_pkt = 0;
+       unsigned int            trb_status;
  
        do {
                req = next_request(&dep->req_queued);
  
                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))
                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 -1766,6 +1767,6 @@@ static void dwc3_endpoint_transfer_comp
        }
  }
  
- 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)
  {
  
        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));
  
  
        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",
                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 -1873,16 +1874,16 @@@ static void dwc3_stop_active_transfer(s
  
        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 -1925,9 +1926,9 @@@ static void dwc3_clear_stall_all_ep(str
  
  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;
  
        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;
  
        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 -2011,18 +2012,18 @@@ static void dwc3_gadget_reset_interrupt
        /* 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 -2061,16 +2062,16 @@@ static void dwc3_update_ram_clk_sel(str
        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 -2133,11 +2134,11 @@@ static void dwc3_gadget_conndone_interr
                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 -2427,6 +2428,6 @@@ int __devinit dwc3_gadget_init(struct d
        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 |
                        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");