Merge tag 'f2fs-for-v5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
[linux-2.6-microblaze.git] / drivers / usb / dwc2 / core.c
index 55d5ae2..8b499d6 100644 (file)
@@ -1020,6 +1020,205 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask,
        return -ETIMEDOUT;
 }
 
+/*
+ * Initializes the FSLSPClkSel field of the HCFG register depending on the
+ * PHY type
+ */
+void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
+{
+       u32 hcfg, val;
+
+       if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
+            hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
+            hsotg->params.ulpi_fs_ls) ||
+           hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+               /* Full speed PHY */
+               val = HCFG_FSLSPCLKSEL_48_MHZ;
+       } else {
+               /* High speed PHY running at full speed or high speed */
+               val = HCFG_FSLSPCLKSEL_30_60_MHZ;
+       }
+
+       dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val);
+       hcfg = dwc2_readl(hsotg, HCFG);
+       hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
+       hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT;
+       dwc2_writel(hsotg, hcfg, HCFG);
+}
+
+static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
+{
+       u32 usbcfg, ggpio, i2cctl;
+       int retval = 0;
+
+       /*
+        * core_init() is now called on every switch so only call the
+        * following for the first time through
+        */
+       if (select_phy) {
+               dev_dbg(hsotg->dev, "FS PHY selected\n");
+
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               if (!(usbcfg & GUSBCFG_PHYSEL)) {
+                       usbcfg |= GUSBCFG_PHYSEL;
+                       dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
+                       /* Reset after a PHY select */
+                       retval = dwc2_core_reset(hsotg, false);
+
+                       if (retval) {
+                               dev_err(hsotg->dev,
+                                       "%s: Reset failed, aborting", __func__);
+                               return retval;
+                       }
+               }
+
+               if (hsotg->params.activate_stm_fs_transceiver) {
+                       ggpio = dwc2_readl(hsotg, GGPIO);
+                       if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
+                               dev_dbg(hsotg->dev, "Activating transceiver\n");
+                               /*
+                                * STM32F4x9 uses the GGPIO register as general
+                                * core configuration register.
+                                */
+                               ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
+                               dwc2_writel(hsotg, ggpio, GGPIO);
+                       }
+               }
+       }
+
+       /*
+        * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
+        * do this on HNP Dev/Host mode switches (done in dev_init and
+        * host_init).
+        */
+       if (dwc2_is_host_mode(hsotg))
+               dwc2_init_fs_ls_pclk_sel(hsotg);
+
+       if (hsotg->params.i2c_enable) {
+               dev_dbg(hsotg->dev, "FS PHY enabling I2C\n");
+
+               /* Program GUSBCFG.OtgUtmiFsSel to I2C */
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
+               /* Program GI2CCTL.I2CEn */
+               i2cctl = dwc2_readl(hsotg, GI2CCTL);
+               i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK;
+               i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT;
+               i2cctl &= ~GI2CCTL_I2CEN;
+               dwc2_writel(hsotg, i2cctl, GI2CCTL);
+               i2cctl |= GI2CCTL_I2CEN;
+               dwc2_writel(hsotg, i2cctl, GI2CCTL);
+       }
+
+       return retval;
+}
+
+static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
+{
+       u32 usbcfg, usbcfg_old;
+       int retval = 0;
+
+       if (!select_phy)
+               return 0;
+
+       usbcfg = dwc2_readl(hsotg, GUSBCFG);
+       usbcfg_old = usbcfg;
+
+       /*
+        * HS PHY parameters. These parameters are preserved during soft reset
+        * so only program the first time. Do a soft reset immediately after
+        * setting phyif.
+        */
+       switch (hsotg->params.phy_type) {
+       case DWC2_PHY_TYPE_PARAM_ULPI:
+               /* ULPI interface */
+               dev_dbg(hsotg->dev, "HS ULPI PHY selected\n");
+               usbcfg |= GUSBCFG_ULPI_UTMI_SEL;
+               usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL);
+               if (hsotg->params.phy_ulpi_ddr)
+                       usbcfg |= GUSBCFG_DDRSEL;
+
+               /* Set external VBUS indicator as needed. */
+               if (hsotg->params.oc_disable)
+                       usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND |
+                                  GUSBCFG_INDICATORPASSTHROUGH);
+               break;
+       case DWC2_PHY_TYPE_PARAM_UTMI:
+               /* UTMI+ interface */
+               dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n");
+               usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16);
+               if (hsotg->params.phy_utmi_width == 16)
+                       usbcfg |= GUSBCFG_PHYIF16;
+
+               /* Set turnaround time */
+               if (dwc2_is_device_mode(hsotg)) {
+                       usbcfg &= ~GUSBCFG_USBTRDTIM_MASK;
+                       if (hsotg->params.phy_utmi_width == 16)
+                               usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT;
+                       else
+                               usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT;
+               }
+               break;
+       default:
+               dev_err(hsotg->dev, "FS PHY selected at HS!\n");
+               break;
+       }
+
+       if (usbcfg != usbcfg_old) {
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+
+               /* Reset after setting the PHY parameters */
+               retval = dwc2_core_reset(hsotg, false);
+               if (retval) {
+                       dev_err(hsotg->dev,
+                               "%s: Reset failed, aborting", __func__);
+                       return retval;
+               }
+       }
+
+       return retval;
+}
+
+int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
+{
+       u32 usbcfg;
+       int retval = 0;
+
+       if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
+            hsotg->params.speed == DWC2_SPEED_PARAM_LOW) &&
+           hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) {
+               /* If FS/LS mode with FS/LS PHY */
+               retval = dwc2_fs_phy_init(hsotg, select_phy);
+               if (retval)
+                       return retval;
+       } else {
+               /* High speed PHY */
+               retval = dwc2_hs_phy_init(hsotg, select_phy);
+               if (retval)
+                       return retval;
+       }
+
+       if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI &&
+           hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED &&
+           hsotg->params.ulpi_fs_ls) {
+               dev_dbg(hsotg->dev, "Setting ULPI FSLS\n");
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg |= GUSBCFG_ULPI_FS_LS;
+               usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+       } else {
+               usbcfg = dwc2_readl(hsotg, GUSBCFG);
+               usbcfg &= ~GUSBCFG_ULPI_FS_LS;
+               usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M;
+               dwc2_writel(hsotg, usbcfg, GUSBCFG);
+       }
+
+       return retval;
+}
+
 MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
 MODULE_AUTHOR("Synopsys, Inc.");
 MODULE_LICENSE("Dual BSD/GPL");