Merge git://git.denx.de/u-boot-fsl-qoriq
[oweals/u-boot.git] / drivers / usb / host / dwc2.c
index 637b6c0297256997196200fbdd01306703f63340..1293e18f75e74da33ccdd3dea7bc8c779af02d21 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
 
@@ -39,6 +42,13 @@ struct dwc2_priv {
        u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT];
        struct dwc2_core_regs *regs;
        int root_hub_devnum;
+       bool ext_vbus;
+       /*
+        * The hnp/srp capability must be disabled if the platform
+        * does't support hnp/srp. Otherwise the force mode can't work.
+        */
+       bool hnp_srp_disable;
+       bool oc_disable;
 };
 
 #ifndef CONFIG_DM_USB
@@ -155,6 +165,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) {
+               pr_err("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.
@@ -163,10 +200,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;
@@ -244,6 +283,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs)
                        writel(hprt0, &regs->hprt0);
                }
        }
+
+       if (dev)
+               dwc_vbus_supply_init(dev);
 }
 
 /*
@@ -263,13 +305,15 @@ static void dwc_otg_core_init(struct dwc2_priv *priv)
        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 |
-                  DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR |
-                  DWC2_GUSBCFG_INDICATOR_PASSTHROUGH);
-#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
@@ -355,6 +399,9 @@ static void dwc_otg_core_init(struct dwc2_priv *priv)
                usbcfg |= DWC2_GUSBCFG_ULPI_CLK_SUS_M;
        }
 #endif
+       if (priv->hnp_srp_disable)
+               usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE;
+
        writel(usbcfg, &regs->gusbcfg);
 
        /* Program the GAHBCFG Register. */
@@ -383,12 +430,16 @@ static void dwc_otg_core_init(struct dwc2_priv *priv)
 
        writel(ahbcfg, &regs->gahbcfg);
 
-       /* Program the GUSBCFG register for HNP/SRP. */
-       setbits_le32(&regs->gusbcfg, DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP);
+       /* Program the capabilities in GUSBCFG Register */
+       usbcfg = 0;
 
+       if (!priv->hnp_srp_disable)
+               usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP;
 #ifdef CONFIG_DWC2_IC_USB_CAP
-       setbits_le32(&regs->gusbcfg, DWC2_GUSBCFG_IC_USB_CAP);
+       usbcfg |= DWC2_GUSBCFG_IC_USB_CAP;
 #endif
+
+       setbits_le32(&regs->gusbcfg, usbcfg);
 }
 
 /*
@@ -778,12 +829,19 @@ static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
               (*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));
+       if (xfer_len) {
+               if (in) {
+                       invalidate_dcache_range(
+                                       (uintptr_t)aligned_buffer,
+                                       (uintptr_t)aligned_buffer +
+                                       roundup(xfer_len, ARCH_DMA_MINALIGN));
+               } else {
+                       memcpy(aligned_buffer, buffer, xfer_len);
+                       flush_dcache_range(
+                                       (uintptr_t)aligned_buffer,
+                                       (uintptr_t)aligned_buffer +
+                                       roundup(xfer_len, ARCH_DMA_MINALIGN));
+               }
        }
 
        writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma);
@@ -1042,7 +1100,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;
@@ -1057,8 +1115,14 @@ static int dwc2_init_common(struct dwc2_priv *priv)
                return -ENODEV;
        }
 
+#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(regs);
+       dwc_otg_core_host_init(dev, regs);
 
        clrsetbits_le32(&regs->hprt0, DWC2_HPRT0_PRTENA |
                        DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG |
@@ -1076,6 +1140,15 @@ static int dwc2_init_common(struct dwc2_priv *priv)
                }
        }
 
+       /*
+        * 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;
 }
 
@@ -1122,7 +1195,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)
@@ -1172,19 +1245,25 @@ static int dwc2_usb_ofdata_to_platdata(struct udevice *dev)
        struct dwc2_priv *priv = dev_get_priv(dev);
        fdt_addr_t addr;
 
-       addr = dev_get_addr(dev);
+       addr = dev_read_addr(dev);
        if (addr == FDT_ADDR_T_NONE)
                return -EINVAL;
        priv->regs = (struct dwc2_core_regs *)addr;
 
+       priv->oc_disable = dev_read_bool(dev, "disable-over-current");
+       priv->hnp_srp_disable = dev_read_bool(dev, "hnp-srp-disable");
+
        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)