ehci_msm2: Fix enumeration during PHY suspend failure

PHY suspend fails as part of USB LPM at times. Currently, we are resetting
the PHY and Link during this failure case, but the EHCI stack is not reset
due to which we are seeing device enumeration failure cases. Fix this issue
by removing hcd and adding hcd back during PHY suspend failure.

CRs-Fixed: 382279
Change-Id: I2d69e75409ec2b783dbdd06de4f34fe0356c1c45
Signed-off-by: Chiranjeevi Velempati <cvelempa@codeaurora.org>
(cherry picked from commit ce16958d07cedf2c46c97a9b0088a410c1a464a2)
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index c612cb9..34d90fb 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -59,6 +59,7 @@
 	uint32_t				pmic_gpio_int_cnt;
 	atomic_t				pm_usage_cnt;
 	struct wake_lock			wlock;
+	struct work_struct			phy_susp_fail_work;
 };
 
 static inline struct msm_hcd *hcd_to_mhcd(struct usb_hcd *hcd)
@@ -539,6 +540,19 @@
 	return 0;
 }
 
+static void msm_ehci_phy_susp_fail_work(struct work_struct *w)
+{
+	struct msm_hcd *mhcd = container_of(w, struct msm_hcd,
+					phy_susp_fail_work);
+	struct usb_hcd *hcd = mhcd_to_hcd(mhcd);
+
+	msm_ehci_vbus_power(mhcd, 0);
+	usb_remove_hcd(hcd);
+	msm_hsusb_reset(mhcd);
+	usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	msm_ehci_vbus_power(mhcd, 1);
+}
+
 #define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
 #define PHY_RESUME_TIMEOUT_USEC		(100 * 1000)
 
@@ -571,8 +585,8 @@
 		while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) {
 			if (time_after(jiffies, timeout)) {
 				dev_err(mhcd->dev, "Unable to suspend PHY\n");
-				msm_hsusb_reset(mhcd);
-				break;
+				schedule_work(&mhcd->phy_susp_fail_work);
+				return -ETIMEDOUT;
 			}
 			udelay(1);
 		}
@@ -992,6 +1006,7 @@
 	device_init_wakeup(&pdev->dev, 1);
 	wake_lock_init(&mhcd->wlock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
 	wake_lock(&mhcd->wlock);
+	INIT_WORK(&mhcd->phy_susp_fail_work, msm_ehci_phy_susp_fail_work);
 	/*
 	 * This pdev->dev is assigned parent of root-hub by USB core,
 	 * hence, runtime framework automatically calls this driver's