usb: dwc3-msm: Add support for controller power collapse

Starting from msm USB core version 1.2.0, if usb cable is
disconnected controller power collapse is allowed. Hence
add support to vote for Global Distributed Switch
Controller (GDSC) when usb cable is connected and remove
the vote when cable is disconnected to shut down the
controller power.

CRs-Fixed: 498960
Change-Id: Ia1d8bb1b8fb5370a335d28fade72485def68f80c
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index 282257c..ff7b03d 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -47,6 +47,8 @@
 		bits 20-25 PARAMETER_OVERRIDE_D
 - qcom,skip-charger-detection: If present then charger detection using BC1.2
   is not supported and attached host should always be assumed as SDP.
+- USB3_GDSC-supply : phandle to the globally distributed switch controller
+  regulator node to the USB controller.
 
 Sub nodes:
 - Sub node for "DWC3- USB3 controller".
@@ -66,6 +68,7 @@
 		HSUSB_1p8-supply = <&pm8941_l6>;
 		HSUSB_3p3-supply = <&pm8941_l24>;
 		vbus_dwc3-supply = <&pm8941_mvs1>;
+		USB3_GDSC-supply = <&gdsc_usb30>;
 		qcom,dwc-usb3-msm-dbm-eps = <4>
 		qcom,vdd-voltage-level = <1 5 7>;
 		qcom,dwc-hsphy-init = <0x00D195A4>;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index a27eb09..bae514f 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -179,6 +179,7 @@
 	struct regulator	*hsusb_vddcx;
 	struct regulator	*ssusb_1p8;
 	struct regulator	*ssusb_vddcx;
+	struct regulator	*dwc3_gdsc;
 
 	/* VBUS regulator if no OTG and running in host only mode */
 	struct regulator	*vbus_otg;
@@ -1234,6 +1235,37 @@
 	return rc < 0 ? rc : 0;
 }
 
+/*
+ * Config Global Distributed Switch Controller (GDSC)
+ * to support controller power collapse
+ */
+static int dwc3_msm_config_gdsc(struct dwc3_msm *msm, int on)
+{
+	int ret = 0;
+
+	if (IS_ERR(msm->dwc3_gdsc))
+		return 0;
+
+	if (!msm->dwc3_gdsc) {
+		msm->dwc3_gdsc = devm_regulator_get(msm->dev,
+			"USB3_GDSC");
+		if (IS_ERR(msm->dwc3_gdsc))
+			return 0;
+	}
+
+	if (on) {
+		ret = regulator_enable(msm->dwc3_gdsc);
+		if (ret) {
+			dev_err(msm->dev, "unable to enable usb3 gdsc\n");
+			return ret;
+		}
+	} else {
+		regulator_disable(msm->dwc3_gdsc);
+	}
+
+	return 0;
+}
+
 static int dwc3_msm_link_clk_reset(bool assert)
 {
 	int ret = 0;
@@ -1678,6 +1710,11 @@
 
 	/* make sure above writes are completed before turning off clocks */
 	wmb();
+
+	/* remove vote for controller power collapse */
+	if (!host_bus_suspend)
+		dwc3_msm_config_gdsc(mdwc, 0);
+
 	if (!host_bus_suspend || !host_ss_active) {
 		clk_disable_unprepare(mdwc->core_clk);
 		mdwc->lpm_flags |= MDWC3_CORECLK_OFF;
@@ -1758,6 +1795,10 @@
 		mdwc->lpm_flags &= ~MDWC3_TCXO_SHUTDOWN;
 	}
 
+	/* add vote for controller power collapse */
+	if (!host_bus_suspend)
+		dwc3_msm_config_gdsc(mdwc, 1);
+
 	if (!host_bus_suspend)
 		clk_prepare_enable(mdwc->utmi_clk);
 
@@ -2289,11 +2330,18 @@
 	INIT_WORK(&msm->id_work, dwc3_id_work);
 	INIT_DELAYED_WORK(&msm->init_adc_work, dwc3_init_adc_work);
 
+	ret = dwc3_msm_config_gdsc(msm, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to configure usb3 gdsc\n");
+		return ret;
+	}
+
 	msm->xo_clk = clk_get(&pdev->dev, "xo");
 	if (IS_ERR(msm->xo_clk)) {
 		dev_err(&pdev->dev, "%s unable to get TCXO buffer handle\n",
 								__func__);
-		return PTR_ERR(msm->xo_clk);
+		ret = PTR_ERR(msm->xo_clk);
+		goto disable_dwc3_gdsc;
 	}
 
 	ret = clk_prepare_enable(msm->xo_clk);
@@ -2695,6 +2743,8 @@
 	clk_disable_unprepare(msm->xo_clk);
 put_xo:
 	clk_put(msm->xo_clk);
+disable_dwc3_gdsc:
+	dwc3_msm_config_gdsc(msm, 0);
 
 	return ret;
 }
@@ -2735,6 +2785,8 @@
 	clk_disable_unprepare(msm->xo_clk);
 	clk_put(msm->xo_clk);
 
+	dwc3_msm_config_gdsc(msm, 0);
+
 	return 0;
 }