USB: OTG: msm: Simplify Runtime PM usage

In current design, PM usage counter operations are scattered across
charging detection work, state machine work, interrupt handler and
resume work.  This did not give any problems for typical device mode
or host mode use cases.  But with ACA implementation, there will be
many corner cases where PM usage operations can go wrong and device
stays out of low power mode (LPM).  Hence leave PM usage counter
operations to state machine work, which knows best about the device
state.

Don't create device and host controller platform devices as children
of OTG device.  This helps OTG state machine to know about host bus
suspend and can take a decision whether low power mode is possible or
not.

Change-Id: I00474e396546d2567d09409e70591a6c9d325c06
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index 7ee30ef..bce026d 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -701,7 +701,6 @@
 	pm8018_platform_data.num_regulators = msm_pm8018_regulator_pdata_len;
 
 	msm_device_otg.dev.platform_data = &msm_otg_pdata;
-	msm_device_gadget_peripheral.dev.parent = &msm_device_otg.dev;
 	platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
 
 	msm_clock_init(&msm9615_clock_init_data);
diff --git a/arch/arm/mach-msm/board-apq8064.c b/arch/arm/mach-msm/board-apq8064.c
index 5ae388f..bc06870 100644
--- a/arch/arm/mach-msm/board-apq8064.c
+++ b/arch/arm/mach-msm/board-apq8064.c
@@ -760,7 +760,6 @@
 	apq8064_device_ssbi_pmic2.dev.platform_data =
 				&apq8064_ssbi_pm8821_pdata;
 	apq8064_device_otg.dev.platform_data = &msm_otg_pdata;
-	apq8064_device_gadget_peripheral.dev.parent = &apq8064_device_otg.dev;
 	apq8064_pm8921_platform_data.num_regulators =
 					msm8064_pm8921_regulator_pdata_len;
 	platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index cf74c66..7403da9 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -4739,8 +4739,6 @@
 	pm8921_platform_data.keypad_pdata = &keypad_data_sim;
 
 	msm8960_device_otg.dev.platform_data = &msm_otg_pdata;
-	msm8960_device_gadget_peripheral.dev.parent = &msm8960_device_otg.dev;
-	msm_device_hsusb_host.dev.parent = &msm8960_device_otg.dev;
 	gpiomux_init();
 	msm8960_i2c_init();
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
@@ -4817,8 +4815,6 @@
 	if (machine_is_msm8960_liquid())
 		msm_otg_pdata.mhl_enable = true;
 	msm8960_device_otg.dev.platform_data = &msm_otg_pdata;
-	msm8960_device_gadget_peripheral.dev.parent = &msm8960_device_otg.dev;
-	msm_device_hsusb_host.dev.parent = &msm8960_device_otg.dev;
 #ifdef CONFIG_USB_EHCI_MSM_HSIC
 	if (machine_is_msm8960_liquid()) {
 		if (SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2)
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 411fa97..f45f257 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -177,11 +177,6 @@
 	}
 
 	device_init_wakeup(&pdev->dev, 1);
-	/*
-	 * OTG device parent of HCD takes care of putting
-	 * hardware into low power mode.
-	 */
-	pm_runtime_no_callbacks(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
 	return 0;
@@ -212,7 +207,31 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int ehci_msm_runtime_idle(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime idle\n");
+	return 0;
+}
+
+static int ehci_msm_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime suspend\n");
+	/*
+	 * Notify OTG about suspend.  It takes care of
+	 * putting the hardware in LPM.
+	 */
+	return otg_set_suspend(otg, 1);
+}
+
+static int ehci_msm_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "ehci runtime resume\n");
+	return otg_set_suspend(otg, 0);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
 static int ehci_msm_pm_suspend(struct device *dev)
 {
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
@@ -236,7 +255,7 @@
 				wakeup);
 	}
 
-	return 0;
+	return otg_set_suspend(otg, 1);
 }
 
 static int ehci_msm_pm_resume(struct device *dev)
@@ -250,16 +269,14 @@
 
 	ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
 
-	return 0;
+	return otg_set_suspend(otg, 0);
 }
-#else
-#define ehci_msm_pm_suspend	NULL
-#define ehci_msm_pm_resume	NULL
 #endif
 
 static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
-	.suspend         = ehci_msm_pm_suspend,
-	.resume          = ehci_msm_pm_resume,
+	SET_SYSTEM_SLEEP_PM_OPS(ehci_msm_pm_suspend, ehci_msm_pm_resume)
+	SET_RUNTIME_PM_OPS(ehci_msm_runtime_suspend, ehci_msm_runtime_resume,
+				ehci_msm_runtime_idle)
 };
 
 static struct platform_driver ehci_msm_driver = {
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index d16fcda..04c1449 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -535,6 +535,24 @@
 	return 0;
 }
 
+static int msm_otg_set_suspend(struct otg_transceiver *otg, int suspend)
+{
+	struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
+
+	/*
+	 * Allow bus suspend only for host mode.  Device mode bus suspend
+	 * is not implemented yet.
+	 */
+	if (!test_bit(ID, &motg->inputs) || test_bit(ID_A, &motg->inputs)) {
+		if (suspend)
+			pm_runtime_put(otg->dev);
+		else
+			pm_runtime_resume(otg->dev);
+	}
+
+	return 0;
+}
+
 #define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
 #define PHY_RESUME_TIMEOUT_USEC	(100 * 1000)
 
@@ -745,7 +763,6 @@
 
 	if (motg->async_int) {
 		motg->async_int = 0;
-		pm_runtime_put(otg->dev);
 		enable_irq(motg->irq);
 	}
 
@@ -1329,7 +1346,6 @@
 	dev_dbg(otg->dev, "chg detection work\n");
 	switch (motg->chg_state) {
 	case USB_CHG_STATE_UNDEFINED:
-		pm_runtime_get_sync(otg->dev);
 		msm_chg_block_on(motg);
 		msm_chg_enable_dcd(motg);
 		msm_chg_enable_aca_det(motg);
@@ -1462,6 +1478,7 @@
 	struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
 	struct otg_transceiver *otg = &motg->otg;
 
+	pm_runtime_resume(otg->dev);
 	switch (otg->state) {
 	case OTG_STATE_UNDEFINED:
 		dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n");
@@ -1494,6 +1511,8 @@
 				case USB_ACA_B_CHARGER:
 					msm_otg_notify_charger(motg,
 							IDEV_CHG_MAX);
+					pm_runtime_put_noidle(otg->dev);
+					pm_runtime_suspend(otg->dev);
 					break;
 				case USB_CDP_CHARGER:
 				case USB_ACA_C_CHARGER:
@@ -1515,20 +1534,14 @@
 				break;
 			}
 		} else {
-			/*
-			 * If charger detection work is pending, decrement
-			 * the pm usage counter to balance with the one that
-			 * is incremented in charger detection work.
-			 */
-			if (cancel_delayed_work_sync(&motg->chg_work)) {
-				pm_runtime_put_sync(otg->dev);
+			if (cancel_delayed_work_sync(&motg->chg_work))
 				msm_otg_reset(otg);
-			}
 			msm_otg_notify_charger(motg, 0);
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
+			pm_runtime_put_noidle(otg->dev);
+			pm_runtime_suspend(otg->dev);
 		}
-		pm_runtime_put_sync(otg->dev);
 		break;
 	case OTG_STATE_B_PERIPHERAL:
 		dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n");
@@ -1547,7 +1560,6 @@
 			schedule_work(w);
 		} else if (test_bit(ID_C, &motg->inputs)) {
 			msm_otg_notify_charger(motg, IDEV_CHG_MAX);
-			pm_runtime_put_sync(otg->dev);
 		}
 		break;
 	case OTG_STATE_A_HOST:
@@ -1569,7 +1581,6 @@
 				motg->pdata->vbus_power(0);
 			msm_otg_notify_charger(motg,
 					IDEV_CHG_MIN - motg->mA_port);
-			pm_runtime_put_sync(otg->dev);
 		} else if (!test_bit(ID, &motg->inputs)) {
 			motg->chg_state = USB_CHG_STATE_UNDEFINED;
 			motg->chg_type = USB_INVALID_CHARGER;
@@ -1577,7 +1588,6 @@
 			writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
 			if (motg->pdata->vbus_power)
 				motg->pdata->vbus_power(1);
-			pm_runtime_put_sync(otg->dev);
 		}
 		break;
 	default:
@@ -1594,17 +1604,15 @@
 	if (atomic_read(&motg->in_lpm)) {
 		disable_irq_nosync(irq);
 		motg->async_int = 1;
-		pm_runtime_get(otg->dev);
+		pm_request_resume(otg->dev);
 		return IRQ_HANDLED;
 	}
 
 	usbsts = readl(USB_USBSTS);
 	if ((usbsts & PHY_ALT_INT)) {
 		writel(PHY_ALT_INT, USB_USBSTS);
-		if (msm_chg_check_aca_intr(motg)) {
-			pm_runtime_get_noresume(otg->dev);
+		if (msm_chg_check_aca_intr(motg))
 			schedule_work(&motg->sm_work);
-		}
 		return IRQ_HANDLED;
 	}
 
@@ -1619,7 +1627,6 @@
 			clear_bit(ID, &motg->inputs);
 		dev_dbg(otg->dev, "ID set/clear\n");
 		schedule_work(&motg->sm_work);
-		pm_runtime_get_noresume(otg->dev);
 	} else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) {
 		if (otgsc & OTGSC_BSV)
 			set_bit(B_SESS_VLD, &motg->inputs);
@@ -1627,7 +1634,6 @@
 			clear_bit(B_SESS_VLD, &motg->inputs);
 		dev_dbg(otg->dev, "BSV set/clear\n");
 		schedule_work(&motg->sm_work);
-		pm_runtime_get_noresume(otg->dev);
 	}
 
 	writel(otgsc, USB_OTGSC);
@@ -1749,7 +1755,7 @@
 		goto out;
 	}
 
-	pm_runtime_get_sync(otg->dev);
+	pm_runtime_resume(otg->dev);
 	schedule_work(&motg->sm_work);
 out:
 	return status;
@@ -1973,6 +1979,7 @@
 	otg->set_host = msm_otg_set_host;
 	otg->set_peripheral = msm_otg_set_peripheral;
 	otg->set_power = msm_otg_set_power;
+	otg->set_suspend = msm_otg_set_suspend;
 
 	otg->io_ops = &msm_otg_io_ops;
 
@@ -2145,16 +2152,10 @@
 
 	dev_dbg(dev, "OTG runtime idle\n");
 
-	/*
-	 * It is observed some times that a spurious interrupt
-	 * comes when PHY is put into LPM immediately after PHY reset.
-	 * This 1 sec delay also prevents entering into LPM immediately
-	 * after asynchronous interrupt.
-	 */
-	if (otg->state != OTG_STATE_UNDEFINED)
-		pm_schedule_suspend(dev, 1000);
-
-	return -EAGAIN;
+	if (otg->state == OTG_STATE_UNDEFINED)
+		return -EAGAIN;
+	else
+		return 0;
 }
 
 static int msm_otg_runtime_suspend(struct device *dev)
@@ -2170,6 +2171,7 @@
 	struct msm_otg *motg = dev_get_drvdata(dev);
 
 	dev_dbg(dev, "OTG runtime resume\n");
+	pm_runtime_get_noresume(dev);
 	return msm_otg_resume(motg);
 }
 #endif
@@ -2177,10 +2179,18 @@
 #ifdef CONFIG_PM_SLEEP
 static int msm_otg_pm_suspend(struct device *dev)
 {
-	struct msm_otg *motg = dev_get_drvdata(dev);
+	int ret;
 
 	dev_dbg(dev, "OTG PM suspend\n");
-	return msm_otg_suspend(motg);
+
+#ifdef CONFIG_PM_RUNTIME
+	ret = pm_runtime_suspend(dev);
+	if (ret > 0)
+		ret = 0;
+#else
+	ret =  msm_otg_suspend(dev_get_drvdata(dev));
+#endif
+	return ret;
 }
 
 static int msm_otg_pm_resume(struct device *dev)