usb: msm_otg: Fix timeout during usb disconnect with hvdcp charger

Once DCP is detected, ext_chg_active is set to true and DCP uevent
reached to user space. The hvdcp main thread mark dcp_connected to
true and wakesup the detector thread to do hvdcp detection. But before
the detector thread gets a chance to run, DCP is removed. The disconnect
uevent reached the user space and the hvdcp main thread marks the
dcp_connected to false and it will not call any ioctl call. As
ext_chg_active is still set to true, it is waiting for 3 seconds while
processing usb disconnect.

Also it is possible that DCP uevent is reached to the user space much
before ext_chg_active flag is set to true in otg state machine work which
later leads to timeout of 3 seconds later while processing usb disconnect.

Fix these issues by maintaining different states for ext_chg_active. Upon
DCP disconnect, if the ext_chg_active value is still default, mark it to
inactive. This avoid the 3 seconds wait timeout while processing the usb
disconnect.

CRs-Fixed: 671181
Change-Id: Ic4aa6745e994a007bd6bd2e75d4f44fb8f7ac9c1
Signed-off-by: Saket Saurabh <ssaurabh@codeaurora.org>
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index c4e5962..ea724d6 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -2438,6 +2438,11 @@
 		 * Notify the charger type to power supply
 		 * owner as soon as we determine the charger.
 		 */
+		if (motg->chg_type == USB_DCP_CHARGER &&
+			motg->ext_chg_opened) {
+				init_completion(&motg->ext_chg_wait);
+				motg->ext_chg_active = DEFAULT;
+		}
 		msm_otg_notify_chg_type(motg);
 		msm_chg_block_off(motg);
 		msm_chg_enable_aca_det(motg);
@@ -2541,7 +2546,7 @@
 	 * detection is completed.
 	 */
 
-	if (motg->ext_chg_active) {
+	if (motg->ext_chg_active == ACTIVE) {
 
 do_wait:
 		pr_debug("before msm_otg ext chg wait\n");
@@ -2550,7 +2555,7 @@
 				msecs_to_jiffies(3000));
 		if (!t)
 			pr_err("msm_otg ext chg wait timeout\n");
-		else if (motg->ext_chg_active)
+		else if (motg->ext_chg_active == ACTIVE)
 			goto do_wait;
 		else
 			pr_debug("msm_otg ext chg wait done\n");
@@ -2627,11 +2632,6 @@
 				case USB_DCP_CHARGER:
 					/* Enable VDP_SRC */
 					ulpi_write(otg->phy, 0x2, 0x85);
-					if (motg->ext_chg_opened) {
-						init_completion(
-							&motg->ext_chg_wait);
-						motg->ext_chg_active = true;
-					}
 					/* fall through */
 				case USB_PROPRIETARY_CHARGER:
 					msm_otg_notify_charger(motg,
@@ -2701,6 +2701,8 @@
 			motg->chg_type = USB_INVALID_CHARGER;
 			msm_otg_notify_charger(motg, 0);
 			if (dcp) {
+				if (motg->ext_chg_active == DEFAULT)
+					motg->ext_chg_active = INACTIVE;
 				msm_otg_wait_for_ext_chg_done(motg);
 				/* Turn off VDP_SRC */
 				ulpi_write(otg->phy, 0x2, 0x86);
@@ -4054,7 +4056,7 @@
 		pr_debug("%s: LPM block request %d\n", __func__, val);
 		if (val) { /* block LPM */
 			if (motg->chg_type == USB_DCP_CHARGER) {
-				motg->ext_chg_active = true;
+				motg->ext_chg_active = ACTIVE;
 				/*
 				 * If device is already suspended, resume it.
 				 * The PM usage counter is incremented in
@@ -4067,12 +4069,12 @@
 				else
 					pm_runtime_get_sync(motg->phy.dev);
 			} else {
-				motg->ext_chg_active = false;
+				motg->ext_chg_active = INACTIVE;
 				complete(&motg->ext_chg_wait);
 				ret = -ENODEV;
 			}
 		} else {
-			motg->ext_chg_active = false;
+			motg->ext_chg_active = INACTIVE;
 			complete(&motg->ext_chg_wait);
 			/*
 			 * If usb cable is disconnected and then userspace
@@ -4910,7 +4912,7 @@
 	if (phy->state == OTG_STATE_UNDEFINED)
 		return -EAGAIN;
 
-	if (motg->ext_chg_active) {
+	if (motg->ext_chg_active == DEFAULT) {
 		dev_dbg(dev, "Deferring LPM\n");
 		/*
 		 * Charger detection may happen in user space.
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 4ecacc7..44fe134 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -191,6 +191,20 @@
 };
 
 /**
+ * Maintain state for hvdcp external charger status
+ * DEFAULT	This is used when DCP is detected
+ * ACTIVE	This is used when ioctl is called to block LPM
+ * INACTIVE	This is used when ioctl is called to unblock LPM
+ */
+
+enum usb_ext_chg_status {
+	DEFAULT = 1,
+	ACTIVE,
+	INACTIVE,
+};
+
+
+/**
  * struct msm_otg_platform_data - platform device data
  *              for msm_otg driver.
  * @phy_init_seq: PHY configuration sequence. val, reg pairs
@@ -463,7 +477,7 @@
 	struct class *ext_chg_class;
 	struct device *ext_chg_device;
 	bool ext_chg_opened;
-	bool ext_chg_active;
+	enum usb_ext_chg_status ext_chg_active;
 	struct completion ext_chg_wait;
 	int ui_enabled;
 	bool pm_done;