]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/usb/host/dwc2.c
usb: dwc2: add support for external vbus supply
[people/ms/u-boot.git] / drivers / usb / host / dwc2.c
index 9e95ecb42a5b98533bb34662c62e08ba5c39462d..ae7d8fb1da7153d4994b08f31deaa051f91a5eea 100644 (file)
 #include <usbroothubdes.h>
 #include <wait_bit.h>
 #include <asm/io.h>
+#include <power/regulator.h>
 
 #include "dwc2.h"
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /* Use only HC channel 0. */
 #define DWC2_HC_CHANNEL                        0
 
@@ -35,9 +38,12 @@ struct dwc2_priv {
        uint8_t *aligned_buffer;
        uint8_t *status_buffer;
 #endif
-       int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
+       u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
+       u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
        struct dwc2_core_regs *regs;
        int root_hub_devnum;
+       bool ext_vbus;
+       bool oc_disable;
 };
 
 #ifndef CONFIG_DM_USB
@@ -154,6 +160,33 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs)
        mdelay(100);
 }
 
+#if defined(CONFIG_DM_USB) && defined(CONFIG_DM_REGULATOR)
+static int dwc_vbus_supply_init(struct udevice *dev)
+{
+       struct udevice *vbus_supply;
+       int ret;
+
+       ret = device_get_supply_regulator(dev, "vbus-supply", &vbus_supply);
+       if (ret) {
+               debug("%s: No vbus supply\n", dev->name);
+               return 0;
+       }
+
+       ret = regulator_set_enable(vbus_supply, true);
+       if (ret) {
+               error("Error enabling vbus supply\n");
+               return ret;
+       }
+
+       return 0;
+}
+#else
+static int dwc_vbus_supply_init(struct udevice *dev)
+{
+       return 0;
+}
+#endif
+
 /*
  * This function initializes the DWC_otg controller registers for
  * host mode.
@@ -162,10 +195,12 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs)
  * request queues. Host channels are reset to ensure that they are ready for
  * performing transfers.
  *
+ * @param dev USB Device (NULL if driver model is not being used)
  * @param regs Programming view of DWC_otg controller
  *
  */
-static void dwc_otg_core_host_init(struct dwc2_core_regs *regs)
+static void dwc_otg_core_host_init(struct udevice *dev,
+                                  struct dwc2_core_regs *regs)
 {
        uint32_t nptxfifosize = 0;
        uint32_t ptxfifosize = 0;
@@ -243,6 +278,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs)
                        writel(hprt0, &regs->hprt0);
                }
        }
+
+       if (dev)
+               dwc_vbus_supply_init(dev);
 }
 
 /*
@@ -251,8 +289,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs)
  *
  * @param regs Programming view of the DWC_otg controller
  */
-static void dwc_otg_core_init(struct dwc2_core_regs *regs)
+static void dwc_otg_core_init(struct dwc2_priv *priv)
 {
+       struct dwc2_core_regs *regs = priv->regs;
        uint32_t ahbcfg = 0;
        uint32_t usbcfg = 0;
        uint8_t brst_sz = CONFIG_DWC2_DMA_BURST_SIZE;
@@ -261,11 +300,15 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs)
        usbcfg = readl(&regs->gusbcfg);
 
        /* Program the ULPI External VBUS bit if needed */
-#ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS
-       usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
-#else
-       usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
-#endif
+       if (priv->ext_vbus) {
+               usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
+               if (!priv->oc_disable) {
+                       usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR |
+                                 DWC2_GUSBCFG_INDICATOR_PASSTHROUGH;
+               }
+       } else {
+               usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV;
+       }
 
        /* Set external TS Dline pulsing */
 #ifdef CONFIG_DWC2_TS_DLINE
@@ -410,19 +453,29 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
        if (dev->speed == USB_SPEED_LOW)
                hcchar |= DWC2_HCCHAR_LSPDDEV;
 
-       /* Clear old interrupt conditions for this host channel. */
-       writel(0x3fff, &hc_regs->hcint);
-
        /*
         * Program the HCCHARn register with the endpoint characteristics
         * for the current transfer.
         */
        writel(hcchar, &hc_regs->hcchar);
 
-       /* Program the HCSPLIT register for SPLITs */
+       /* Program the HCSPLIT register, default to no SPLIT */
        writel(0, &hc_regs->hcsplt);
 }
 
+static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs,
+                                 uint8_t hub_devnum, uint8_t hub_port)
+{
+       uint32_t hcsplt = 0;
+
+       hcsplt = DWC2_HCSPLT_SPLTENA;
+       hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET;
+       hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET;
+
+       /* Program the HCSPLIT register for SPLITs */
+       writel(hcsplt, &hc_regs->hcsplt);
+}
+
 /*
  * DWC2 to USB API interface
  */
@@ -713,11 +766,8 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
        return stat;
 }
 
-int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle,
-                   bool ignore_ack)
+int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle)
 {
-       uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD;
-       struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
        int ret;
        uint32_t hcint, hctsiz;
 
@@ -727,25 +777,22 @@ int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle,
                return ret;
 
        hcint = readl(&hc_regs->hcint);
-       if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN))
-               return -EAGAIN;
-       if (ignore_ack)
-               hcint &= ~DWC2_HCINT_ACK;
-       else
-               hcint_comp_hlt_ack |= DWC2_HCINT_ACK;
-       if (hcint != hcint_comp_hlt_ack) {
-               debug("%s: Error (HCINT=%08x)\n", __func__, hcint);
-               return -EINVAL;
-       }
-
        hctsiz = readl(&hc_regs->hctsiz);
        *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >>
                DWC2_HCTSIZ_XFERSIZE_OFFSET;
        *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET;
 
-       debug("%s: sub=%u toggle=%d\n", __func__, *sub, *toggle);
+       debug("%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub,
+             *toggle);
 
-       return 0;
+       if (hcint & DWC2_HCINT_XFERCOMP)
+               return 0;
+
+       if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN))
+               return -EAGAIN;
+
+       debug("%s: Error (HCINT=%08x)\n", __func__, hcint);
+       return -EINVAL;
 }
 
 static int dwc2_eptype[] = {
@@ -755,96 +802,176 @@ static int dwc2_eptype[] = {
        DWC2_HCCHAR_EPTYPE_BULK,
 };
 
+static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
+                         u8 *pid, int in, void *buffer, int num_packets,
+                         int xfer_len, int *actual_len, int odd_frame)
+{
+       int ret = 0;
+       uint32_t sub;
+
+       debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
+             *pid, xfer_len, num_packets);
+
+       writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
+              (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
+              (*pid << DWC2_HCTSIZ_PID_OFFSET),
+              &hc_regs->hctsiz);
+
+       if (!in && xfer_len) {
+               memcpy(aligned_buffer, buffer, xfer_len);
+
+               flush_dcache_range((unsigned long)aligned_buffer,
+                                  (unsigned long)aligned_buffer +
+                                  roundup(xfer_len, ARCH_DMA_MINALIGN));
+       }
+
+       writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma);
+
+       /* Clear old interrupt conditions for this host channel. */
+       writel(0x3fff, &hc_regs->hcint);
+
+       /* Set host channel enable after all other setup is complete. */
+       clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
+                       DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS |
+                       DWC2_HCCHAR_ODDFRM,
+                       (1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
+                       (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) |
+                       DWC2_HCCHAR_CHEN);
+
+       ret = wait_for_chhltd(hc_regs, &sub, pid);
+       if (ret < 0)
+               return ret;
+
+       if (in) {
+               xfer_len -= sub;
+
+               invalidate_dcache_range((unsigned long)aligned_buffer,
+                                       (unsigned long)aligned_buffer +
+                                       roundup(xfer_len, ARCH_DMA_MINALIGN));
+
+               memcpy(buffer, aligned_buffer, xfer_len);
+       }
+       *actual_len = xfer_len;
+
+       return ret;
+}
+
 int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
-             unsigned long pipe, int *pid, int in, void *buffer, int len,
-             bool ignore_ack)
+             unsigned long pipe, u8 *pid, int in, void *buffer, int len)
 {
        struct dwc2_core_regs *regs = priv->regs;
        struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
+       struct dwc2_host_regs *host_regs = &regs->host_regs;
        int devnum = usb_pipedevice(pipe);
        int ep = usb_pipeendpoint(pipe);
        int max = usb_maxpacket(dev, pipe);
        int eptype = dwc2_eptype[usb_pipetype(pipe)];
        int done = 0;
        int ret = 0;
-       uint32_t sub;
+       int do_split = 0;
+       int complete_split = 0;
        uint32_t xfer_len;
        uint32_t num_packets;
        int stop_transfer = 0;
+       uint32_t max_xfer_len;
+       int ssplit_frame_num = 0;
 
        debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
              in, len);
 
-       do {
-               /* Initialize channel */
-               dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
-                               eptype, max);
+       max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max;
+       if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE)
+               max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE;
+       if (max_xfer_len > DWC2_DATA_BUF_SIZE)
+               max_xfer_len = DWC2_DATA_BUF_SIZE;
+
+       /* Make sure that max_xfer_len is a multiple of max packet size. */
+       num_packets = max_xfer_len / max;
+       max_xfer_len = num_packets * max;
+
+       /* Initialize channel */
+       dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
+                       eptype, max);
+
+       /* Check if the target is a FS/LS device behind a HS hub */
+       if (dev->speed != USB_SPEED_HIGH) {
+               uint8_t hub_addr;
+               uint8_t hub_port;
+               uint32_t hprt0 = readl(&regs->hprt0);
+               if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) ==
+                    DWC2_HPRT0_PRTSPD_HIGH) {
+                       usb_find_usb2_hub_address_port(dev, &hub_addr,
+                                                      &hub_port);
+                       dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port);
+
+                       do_split = 1;
+                       num_packets = 1;
+                       max_xfer_len = max;
+               }
+       }
 
+       do {
+               int actual_len = 0;
+               uint32_t hcint;
+               int odd_frame = 0;
                xfer_len = len - done;
-               if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE)
-                       xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1;
-               if (xfer_len > DWC2_DATA_BUF_SIZE)
-                       xfer_len = DWC2_DATA_BUF_SIZE - max + 1;
 
-               /* Make sure that xfer_len is a multiple of max packet size. */
-               if (xfer_len > 0) {
+               if (xfer_len > max_xfer_len)
+                       xfer_len = max_xfer_len;
+               else if (xfer_len > max)
                        num_packets = (xfer_len + max - 1) / max;
-                       if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) {
-                               num_packets = CONFIG_DWC2_MAX_PACKET_COUNT;
-                               xfer_len = num_packets * max;
-                       }
-               } else {
+               else
                        num_packets = 1;
-               }
-
-               if (in)
-                       xfer_len = num_packets * max;
 
-               debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
-                     *pid, xfer_len, num_packets);
+               if (complete_split)
+                       setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
+               else if (do_split)
+                       clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
 
-               writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
-                      (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
-                      (*pid << DWC2_HCTSIZ_PID_OFFSET),
-                      &hc_regs->hctsiz);
-
-               if (!in && xfer_len) {
-                       memcpy(priv->aligned_buffer, (char *)buffer + done,
-                              xfer_len);
-
-                       flush_dcache_range((unsigned long)priv->aligned_buffer,
-                               (unsigned long)((void *)priv->aligned_buffer +
-                               roundup(xfer_len, ARCH_DMA_MINALIGN)));
+               if (eptype == DWC2_HCCHAR_EPTYPE_INTR) {
+                       int uframe_num = readl(&host_regs->hfnum);
+                       if (!(uframe_num & 0x1))
+                               odd_frame = 1;
                }
 
-               writel(phys_to_bus((unsigned long)priv->aligned_buffer),
-                      &hc_regs->hcdma);
-
-               /* Set host channel enable after all other setup is complete. */
-               clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
-                               DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
-                               (1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
-                               DWC2_HCCHAR_CHEN);
+               ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
+                                    in, (char *)buffer + done, num_packets,
+                                    xfer_len, &actual_len, odd_frame);
+
+               hcint = readl(&hc_regs->hcint);
+               if (complete_split) {
+                       stop_transfer = 0;
+                       if (hcint & DWC2_HCINT_NYET) {
+                               ret = 0;
+                               int frame_num = DWC2_HFNUM_MAX_FRNUM &
+                                               readl(&host_regs->hfnum);
+                               if (((frame_num - ssplit_frame_num) &
+                                   DWC2_HFNUM_MAX_FRNUM) > 4)
+                                       ret = -EAGAIN;
+                       } else
+                               complete_split = 0;
+               } else if (do_split) {
+                       if (hcint & DWC2_HCINT_ACK) {
+                               ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM &
+                                                  readl(&host_regs->hfnum);
+                               ret = 0;
+                               complete_split = 1;
+                       }
+               }
 
-               ret = wait_for_chhltd(regs, &sub, pid, ignore_ack);
                if (ret)
                        break;
 
-               if (in) {
-                       xfer_len -= sub;
+               if (actual_len < xfer_len)
+                       stop_transfer = 1;
 
-                       invalidate_dcache_range((unsigned long)priv->aligned_buffer,
-                               (unsigned long)((void *)priv->aligned_buffer +
-                               roundup(xfer_len, ARCH_DMA_MINALIGN)));
-
-                       memcpy(buffer + done, priv->aligned_buffer, xfer_len);
-                       if (sub)
-                               stop_transfer = 1;
-               }
+               done += actual_len;
 
-               done += xfer_len;
-
-       } while ((done < len) && !stop_transfer);
+       /* Transactions are done when when either all data is transferred or
+        * there is a short transfer. In case of a SPLIT make sure the CSPLIT
+        * is executed.
+        */
+       } while (((done < len) && !stop_transfer) || complete_split);
 
        writel(0, &hc_regs->hcintmsk);
        writel(0xFFFFFFFF, &hc_regs->hcint);
@@ -861,14 +988,19 @@ int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 {
        int devnum = usb_pipedevice(pipe);
        int ep = usb_pipeendpoint(pipe);
+       u8* pid;
 
-       if (devnum == priv->root_hub_devnum) {
+       if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) {
                dev->status = 0;
                return -EINVAL;
        }
 
-       return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep],
-                        usb_pipein(pipe), buffer, len, true);
+       if (usb_pipein(pipe))
+               pid = &priv->in_data_toggle[devnum][ep];
+       else
+               pid = &priv->out_data_toggle[devnum][ep];
+
+       return chunk_msg(priv, dev, pipe, pid, usb_pipein(pipe), buffer, len);
 }
 
 static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
@@ -876,7 +1008,8 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
                               struct devrequest *setup)
 {
        int devnum = usb_pipedevice(pipe);
-       int pid, ret, act_len;
+       int ret, act_len;
+       u8 pid;
        /* For CONTROL endpoint pid should start with DATA1 */
        int status_direction;
 
@@ -887,31 +1020,39 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
                                             setup);
        }
 
+       /* SETUP stage */
        pid = DWC2_HC_PID_SETUP;
-       ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true);
+       do {
+               ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
+       } while (ret == -EAGAIN);
        if (ret)
                return ret;
 
+       /* DATA stage */
+       act_len = 0;
        if (buffer) {
                pid = DWC2_HC_PID_DATA1;
-               ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer,
-                               len, false);
+               do {
+                       ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe),
+                                       buffer, len);
+                       act_len += dev->act_len;
+                       buffer += dev->act_len;
+                       len -= dev->act_len;
+               } while (ret == -EAGAIN);
                if (ret)
                        return ret;
-               act_len = dev->act_len;
-       } /* End of DATA stage */
-       else
-               act_len = 0;
-
-       /* STATUS stage */
-       if ((len == 0) || usb_pipeout(pipe))
+               status_direction = usb_pipeout(pipe);
+       } else {
+               /* No-data CONTROL always ends with an IN transaction */
                status_direction = 1;
-       else
-               status_direction = 0;
+       }
 
+       /* STATUS stage */
        pid = DWC2_HC_PID_DATA1;
-       ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
-                       priv->status_buffer, 0, false);
+       do {
+               ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
+                               priv->status_buffer, 0);
+       } while (ret == -EAGAIN);
        if (ret)
                return ret;
 
@@ -940,7 +1081,7 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev,
        }
 }
 
-static int dwc2_init_common(struct dwc2_priv *priv)
+static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv)
 {
        struct dwc2_core_regs *regs = priv->regs;
        uint32_t snpsid;
@@ -955,8 +1096,14 @@ static int dwc2_init_common(struct dwc2_priv *priv)
                return -ENODEV;
        }
 
-       dwc_otg_core_init(regs);
-       dwc_otg_core_host_init(regs);
+#ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS
+       priv->ext_vbus = 1;
+#else
+       priv->ext_vbus = 0;
+#endif
+
+       dwc_otg_core_init(priv);
+       dwc_otg_core_host_init(dev, regs);
 
        clrsetbits_le32(&regs->hprt0, DWC2_HPRT0_PRTENA |
                        DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG |
@@ -968,10 +1115,21 @@ static int dwc2_init_common(struct dwc2_priv *priv)
                     DWC2_HPRT0_PRTRST);
 
        for (i = 0; i < MAX_DEVICE; i++) {
-               for (j = 0; j < MAX_ENDPOINT; j++)
-                       priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+               for (j = 0; j < MAX_ENDPOINT; j++) {
+                       priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+                       priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0;
+               }
        }
 
+       /*
+        * Add a 1 second delay here. This gives the host controller
+        * a bit time before the comminucation with the USB devices
+        * is started (the bus is scanned) and  fixes the USB detection
+        * problems with some problematic USB keys.
+        */
+       if (readl(&regs->gintsts) & DWC2_GINTSTS_CURMODE_HOST)
+               mdelay(1000);
+
        return 0;
 }
 
@@ -1018,7 +1176,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
        if (board_usb_init(index, USB_INIT_HOST))
                return -1;
 
-       return dwc2_init_common(priv);
+       return dwc2_init_common(NULL, priv);
 }
 
 int usb_lowlevel_stop(int index)
@@ -1066,6 +1224,7 @@ static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev,
 static int dwc2_usb_ofdata_to_platdata(struct udevice *dev)
 {
        struct dwc2_priv *priv = dev_get_priv(dev);
+       const void *prop;
        fdt_addr_t addr;
 
        addr = dev_get_addr(dev);
@@ -1073,14 +1232,22 @@ static int dwc2_usb_ofdata_to_platdata(struct udevice *dev)
                return -EINVAL;
        priv->regs = (struct dwc2_core_regs *)addr;
 
+       prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+                          "disable-over-current", NULL);
+       if (prop)
+               priv->oc_disable = true;
+
        return 0;
 }
 
 static int dwc2_usb_probe(struct udevice *dev)
 {
        struct dwc2_priv *priv = dev_get_priv(dev);
+       struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev);
+
+       bus_priv->desc_before_addr = true;
 
-       return dwc2_init_common(priv);
+       return dwc2_init_common(dev, priv);
 }
 
 static int dwc2_usb_remove(struct udevice *dev)