#define DRIVER_NAME    "msm_otg"
 
 #define ULPI_IO_TIMEOUT_USEC   (10 * 1000)
+#define LINK_RESET_TIMEOUT_USEC        (250 * 1000)
 
 #define USB_PHY_3P3_VOL_MIN    3050000 /* uV */
 #define USB_PHY_3P3_VOL_MAX    3300000 /* uV */
        return ret;
 }
 
-static int msm_otg_phy_reset(struct msm_otg *motg)
+static int msm_link_reset(struct msm_otg *motg)
 {
        u32 val;
        int ret;
-       int retries;
 
        ret = msm_otg_link_clk_reset(motg, 1);
-       if (ret)
-               return ret;
-       ret = msm_otg_phy_clk_reset(motg);
-       if (ret)
-               return ret;
-       ret = msm_otg_link_clk_reset(motg, 0);
        if (ret)
                return ret;
 
-       val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
-       writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
-
-       for (retries = 3; retries > 0; retries--) {
-               ret = ulpi_write(&motg->phy, ULPI_FUNC_CTRL_SUSPENDM,
-                               ULPI_CLR(ULPI_FUNC_CTRL));
-               if (!ret)
-                       break;
-               ret = msm_otg_phy_clk_reset(motg);
-               if (ret)
-                       return ret;
-       }
-       if (!retries)
-               return -ETIMEDOUT;
+       /* wait for 1ms delay as suggested in HPG. */
+       usleep_range(1000, 1200);
 
-       /* This reset calibrates the phy, if the above write succeeded */
-       ret = msm_otg_phy_clk_reset(motg);
+       ret = msm_otg_link_clk_reset(motg, 0);
        if (ret)
                return ret;
 
-       for (retries = 3; retries > 0; retries--) {
-               ret = ulpi_read(&motg->phy, ULPI_DEBUG);
-               if (ret != -ETIMEDOUT)
-                       break;
-               ret = msm_otg_phy_clk_reset(motg);
-               if (ret)
-                       return ret;
-       }
-       if (!retries)
-               return -ETIMEDOUT;
-
        if (motg->phy_number)
                writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
 
-       dev_info(motg->phy.dev, "phy_reset: success\n");
+       val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK;
+       writel(val | PORTSC_PTS_ULPI, USB_PORTSC);
+
        return 0;
 }
 
-#define LINK_RESET_TIMEOUT_USEC                (250 * 1000)
 static int msm_otg_reset(struct usb_phy *phy)
 {
        struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
-       struct msm_otg_platform_data *pdata = motg->pdata;
        int cnt = 0;
-       int ret;
-       u32 val = 0;
-       u32 ulpi_val = 0;
-
-       ret = msm_otg_phy_reset(motg);
-       if (ret) {
-               dev_err(phy->dev, "phy_reset failed\n");
-               return ret;
-       }
-
-       ulpi_init(motg);
 
        writel(USBCMD_RESET, USB_USBCMD);
        while (cnt < LINK_RESET_TIMEOUT_USEC) {
 
        /* select ULPI phy */
        writel(0x80000000, USB_PORTSC);
+       writel(0x0, USB_AHBBURST);
+       writel(0x08, USB_AHBMODE);
+
+       if (motg->phy_number)
+               writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2);
+       return 0;
+}
+
+static void msm_phy_reset(struct msm_otg *motg)
+{
+       void __iomem *addr;
+
+       if (motg->pdata->phy_type != SNPS_28NM_INTEGRATED_PHY) {
+               msm_otg_phy_clk_reset(motg);
+               return;
+       }
+
+       addr = USB_PHY_CTRL;
+       if (motg->phy_number)
+               addr = USB_PHY_CTRL2;
+
+       /* Assert USB PHY_POR */
+       writel(readl(addr) | PHY_POR_ASSERT, addr);
+
+       /*
+        * wait for minimum 10 microseconds as suggested in HPG.
+        * Use a slightly larger value since the exact value didn't
+        * work 100% of the time.
+        */
+       udelay(12);
+
+       /* Deassert USB PHY_POR */
+       writel(readl(addr) & ~PHY_POR_ASSERT, addr);
+}
+
+static int msm_usb_reset(struct usb_phy *phy)
+{
+       struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+       int ret;
+
+       if (!IS_ERR(motg->core_clk))
+               clk_prepare_enable(motg->core_clk);
+
+       ret = msm_link_reset(motg);
+       if (ret) {
+               dev_err(phy->dev, "phy_reset failed\n");
+               return ret;
+       }
+
+       ret = msm_otg_reset(&motg->phy);
+       if (ret) {
+               dev_err(phy->dev, "link reset failed\n");
+               return ret;
+       }
 
        msleep(100);
 
-       writel(0x0, USB_AHBBURST);
-       writel(0x00, USB_AHBMODE);
+       /* Reset USB PHY after performing USB Link RESET */
+       msm_phy_reset(motg);
+
+       if (!IS_ERR(motg->core_clk))
+               clk_disable_unprepare(motg->core_clk);
+
+       return 0;
+}
+
+static int msm_phy_init(struct usb_phy *phy)
+{
+       struct msm_otg *motg = container_of(phy, struct msm_otg, phy);
+       struct msm_otg_platform_data *pdata = motg->pdata;
+       u32 val, ulpi_val = 0;
+
+       /* Program USB PHY Override registers. */
+       ulpi_init(motg);
+
+       /*
+        * It is recommended in HPG to reset USB PHY after programming
+        * USB PHY Override registers.
+        */
+       msm_phy_reset(motg);
 
        if (pdata->otg_control == OTG_PHY_CONTROL) {
                val = readl(USB_OTGSC);
                goto disable_ldo;
        }
 
-       phy->init = msm_otg_reset;
+       phy->init = msm_phy_init;
        phy->set_power = msm_otg_set_power;
 
        phy->io_ops = &msm_otg_io_ops;
        phy->otg->set_host = msm_otg_set_host;
        phy->otg->set_peripheral = msm_otg_set_peripheral;
 
+       msm_usb_reset(phy);
+
        ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2);
        if (ret) {
                dev_err(&pdev->dev, "usb_add_phy failed\n");