Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 55b93e25 authored by Mayank Rana's avatar Mayank Rana
Browse files

USB: Add support to invoke DP and DM sourcing and pulsing



This change adds support for sourcing and pulsig with DP/DM
using QUSB PHY. This change uses POWER_SUPPLY_PROP_DP_DM power
supply property with different values to achieve the same.

1. With value: POWER_SUPPLY_DP0P6_DMF_MODE
- Set DP = 0.6V, DM= High Z

2. With value: POWER_SUPPLY_DP0P6_DM3P3_MODE
- Set DP = 0.6V and set DM to 3.075V

3. With value: POWER_SUPPLY_DP_PULSE
- Increment DP voltage to Vdp_up=3.075V
- Sleep for 2ms - 3ms
- Pull DP back to Vdp_src=0.6V
- Sleep for 2ms - 3ms

4. With value: POWER_SUPPLY_DM_PULSE
- Decrement DM voltage to Vdm_src=0.6V
- Sleep for 2ms - 3ms
- Pull DM back to Vdm_up=3.075V
- Sleep for 2ms - 3ms

5. With value: POWER_SUPPLY_DP_DM_DPF_DMF:
- Turned ON QUSB PHY LDOs to keep DP and DM to High Z state

6. With value: POWER_SUPPLY_DP_DM_DPR_DMR:
- Turned OFF QUSB PHY, and keep DP and DM to floating state

It removes USB controller's based QUSB PHY operations and reuses
already available functionality as part of QUSB PHY driver.

CRs-Fixed: 853930
Change-Id: Ibefb13c6ccf883f6e961f5e6ec726173863ce1de
Signed-off-by: default avatarMayank Rana <mrana@codeaurora.org>
parent d3846f3c
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -15,10 +15,6 @@ Required properties :
	"vbus_dwc3" : vbus supply for host mode when DWC3 operating as DRD.
	This can be left as optional if "host-only-mode" is selected in the
	'dwc3' sub node for "DWC3-USB3 Core device".
	"vdda33" : 3.3V analog voltage supply is applied to remove Dp & Dm pull
	downs for proper USB charger detection by charger driver.
	"vdda18" : 1.8V analog voltage supply always required to be turned on
	anytime the 3.3V supply is on.

Optional properties :
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+0 −2
Original line number Diff line number Diff line
@@ -2028,8 +2028,6 @@
		interrupt-names = "hs_phy_irq", "pwr_event_irq";

		USB3_GDSC-supply = <&gdsc_usb30>;
		vdda33-supply = <&pm8994_l24>;
		vdda18-supply = <&pm8994_l12>;
		vbus_dwc3-supply = <&smbcharger_charger_otg>;
		qcom,usb-dbm = <&dbm_1p5>;
		qcom,msm-bus,name = "usb3";
+1 −142
Original line number Diff line number Diff line
@@ -103,13 +103,6 @@ MODULE_PARM_DESC(dcp_max_current, "max current drawn for DCP charger");
#define PIPE3_PHYSTATUS_SW	BIT(3)
#define PIPE_UTMI_CLK_DIS	BIT(8)

#define DWC3_3P3_VOL_MIN		3075000 /* uV */
#define DWC3_3P3_VOL_MAX		3200000 /* uV */
#define DWC3_3P3_HPM_LOAD		30000	/* uA */
#define DWC3_1P8_VOL_MIN		1800000 /* uV */
#define DWC3_1P8_VOL_MAX		1800000 /* uV */
#define DWC3_1P8_HPM_LOAD		30000   /* uA */

/* TZ SCM parameters */
#define DWC3_MSM_RESTORE_SCM_CFG_CMD 0x2
struct dwc3_msm_scm_cmd_buf {
@@ -183,8 +176,6 @@ struct dwc3_msm {

	struct dbm		*dbm;

	struct regulator	*vdda33;
	struct regulator	*vdda18;
	/* VBUS regulator for host mode */
	struct regulator	*vbus_reg;
	int			vbus_retry_count;
@@ -220,7 +211,6 @@ struct dwc3_msm {
	bool			vbus_active;
	bool			suspend;
	bool			ext_inuse;
	bool			rm_pulldown;
	enum dwc3_id_state	id_state;
	unsigned long		lpm_flags;
#define MDWC3_SS_PHY_SUSPEND		BIT(0)
@@ -1865,120 +1855,6 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data)
	return IRQ_HANDLED;
}

static int dwc3_msm_remove_pulldown(struct dwc3_msm *mdwc, bool rm_pulldown)
{
	int ret = 0;

	if (!rm_pulldown)
		goto disable_vdda33;

	ret = regulator_set_optimum_mode(mdwc->vdda18, DWC3_1P8_HPM_LOAD);
	if (ret < 0) {
		dev_err(mdwc->dev, "Unable to set HPM of vdda18:%d\n", ret);
		return ret;
	}

	ret = regulator_set_voltage(mdwc->vdda18, DWC3_1P8_VOL_MIN,
						DWC3_1P8_VOL_MAX);
	if (ret) {
		dev_err(mdwc->dev,
				"Unable to set voltage for vdda18:%d\n", ret);
		goto put_vdda18_lpm;
	}

	ret = regulator_enable(mdwc->vdda18);
	if (ret) {
		dev_err(mdwc->dev, "Unable to enable vdda18:%d\n", ret);
		goto unset_vdda18;
	}

	ret = regulator_set_optimum_mode(mdwc->vdda33, DWC3_3P3_HPM_LOAD);
	if (ret < 0) {
		dev_err(mdwc->dev, "Unable to set HPM of vdda33:%d\n", ret);
		goto disable_vdda18;
	}

	ret = regulator_set_voltage(mdwc->vdda33, DWC3_3P3_VOL_MIN,
						DWC3_3P3_VOL_MAX);
	if (ret) {
		dev_err(mdwc->dev,
				"Unable to set voltage for vdda33:%d\n", ret);
		goto put_vdda33_lpm;
	}

	ret = regulator_enable(mdwc->vdda33);
	if (ret) {
		dev_err(mdwc->dev, "Unable to enable vdda33:%d\n", ret);
		goto unset_vdd33;
	}

	return 0;

disable_vdda33:
	ret = regulator_disable(mdwc->vdda33);
	if (ret)
		dev_err(mdwc->dev, "Unable to disable vdda33:%d\n", ret);

unset_vdd33:
	ret = regulator_set_voltage(mdwc->vdda33, 0, DWC3_3P3_VOL_MAX);
	if (ret)
		dev_err(mdwc->dev,
			"Unable to set (0) voltage for vdda33:%d\n", ret);

put_vdda33_lpm:
	ret = regulator_set_optimum_mode(mdwc->vdda33, 0);
	if (ret < 0)
		dev_err(mdwc->dev, "Unable to set (0) HPM of vdda33\n");

disable_vdda18:
	ret = regulator_disable(mdwc->vdda18);
	if (ret)
		dev_err(mdwc->dev, "Unable to disable vdda18:%d\n", ret);

unset_vdda18:
	ret = regulator_set_voltage(mdwc->vdda18, 0, DWC3_1P8_VOL_MAX);
	if (ret)
		dev_err(mdwc->dev,
			"Unable to set (0) voltage for vdda18:%d\n", ret);

put_vdda18_lpm:
	ret = regulator_set_optimum_mode(mdwc->vdda18, 0);
	if (ret < 0)
		dev_err(mdwc->dev, "Unable to set LPM of vdda18\n");

	return ret;
}

static int dwc3_msm_pmic_dp_dm(struct dwc3_msm *mdwc, int value)
{
	int ret = 0;

	switch (value) {
	case POWER_SUPPLY_DP_DM_DPF_DMF:
		if (!mdwc->rm_pulldown) {
			ret = dwc3_msm_remove_pulldown(mdwc, true);
			if (!ret) {
				mdwc->rm_pulldown = true;
				dbg_event(0xFF, "RM PuDwn", mdwc->rm_pulldown);
			}
		}
		break;
	case POWER_SUPPLY_DP_DM_DPR_DMR:
		if (mdwc->rm_pulldown) {
			ret = dwc3_msm_remove_pulldown(mdwc, false);
			if (!ret) {
				mdwc->rm_pulldown = false;
				dbg_event(0xFF, "RM PuDwn", mdwc->rm_pulldown);
			}
		}
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

static int dwc3_msm_power_get_property_usb(struct power_supply *psy,
				  enum power_supply_property psp,
				  union power_supply_propval *val)
@@ -2031,7 +1907,7 @@ static int dwc3_msm_power_set_property_usb(struct power_supply *psy,
		break;
	/* PMIC notification for DP_DM state */
	case POWER_SUPPLY_PROP_DP_DM:
		dwc3_msm_pmic_dp_dm(mdwc, val->intval);
		usb_phy_change_dpdm(mdwc->hs_phy, val->intval);
		break;
	/* Process PMIC notification in PRESENT prop */
	case POWER_SUPPLY_PROP_PRESENT:
@@ -2285,23 +2161,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
	init_completion(&mdwc->dwc3_xcvr_vbus_init);
	INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work);


	/*
	 * This regulator needs to be turned on to remove pull down on Dp and
	 * Dm lines to detect charger type properly.
	 */
	mdwc->vdda33 = devm_regulator_get(dev, "vdda33");
	if (IS_ERR(mdwc->vdda33)) {
		dev_err(&pdev->dev, "unable to get vdda33 supply\n");
		return PTR_ERR(mdwc->vdda33);
	}

	mdwc->vdda18 = devm_regulator_get(dev, "vdda18");
	if (IS_ERR(mdwc->vdda18)) {
		dev_err(&pdev->dev, "unable to get vdda18 supply\n");
		return PTR_ERR(mdwc->vdda18);
	}

	ret = dwc3_msm_config_gdsc(mdwc, 1);
	if (ret) {
		dev_err(&pdev->dev, "unable to configure usb3 gdsc\n");
+167 −22
Original line number Diff line number Diff line
@@ -25,6 +25,16 @@
#include <linux/usb/phy.h>
#include <linux/usb/msm_hsusb.h>

#define QUSB2PHY_PORT_QC1	0x70
#define VDM_SRC_EN		BIT(4)
#define VDP_SRC_EN		BIT(2)

#define QUSB2PHY_PORT_QC2	0x74
#define RDM_UP_EN		BIT(1)
#define RDP_UP_EN		BIT(3)
#define RPUM_LOW_EN		BIT(4)
#define RPUP_LOW_EN		BIT(5)

#define QUSB2PHY_PORT_POWERDOWN		0xB4
#define CLAMP_N_EN			BIT(5)
#define FREEZIO_N			BIT(1)
@@ -111,6 +121,7 @@ struct qusb_phy {
	bool			suspended;
	bool			ulpi_mode;
	bool			emulation;
	bool			rm_pulldown;
};

static int qusb_phy_reset(struct usb_phy *phy)
@@ -143,7 +154,8 @@ static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high)
	return ret;
}

static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)
static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on,
						bool toggle_vdd)
{
	int ret = 0;

@@ -158,9 +170,11 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)
	if (!on)
		goto disable_vdda33;

	if (toggle_vdd) {
		ret = qusb_phy_config_vdd(qphy, true);
		if (ret) {
		dev_err(qphy->phy.dev, "Unable to config VDD:%d\n", ret);
			dev_err(qphy->phy.dev, "Unable to config VDD:%d\n",
								ret);
			goto err_vdd;
		}

@@ -169,6 +183,7 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)
			dev_err(qphy->phy.dev, "Unable to enable VDD\n");
			goto unconfig_vdd;
		}
	}

	ret = regulator_set_optimum_mode(qphy->vdda18, QUSB2PHY_1P8_HPM_LOAD);
	if (ret < 0) {
@@ -212,7 +227,7 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)

	qphy->power_enabled = true;
	pr_debug("%s(): QUSB PHY's regulators are turned ON.\n", __func__);
	return 0;
	return ret;

disable_vdda33:
	ret = regulator_disable(qphy->vdda33);
@@ -247,21 +262,150 @@ put_vdda18_lpm:
		dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n");

disable_vdd:
	if (toggle_vdd) {
		ret = regulator_disable(qphy->vdd);
		if (ret)
		dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n", ret);
			dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n",
								ret);

unconfig_vdd:
		ret = qusb_phy_config_vdd(qphy, false);
		if (ret)
		dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret);

			dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n",
								ret);
	}
err_vdd:
	qphy->power_enabled = false;
	dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n");
	return ret;
}

static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
{
	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
	int ret = 0;

	dev_dbg(phy->dev, "%s value:%d\n", __func__, value);

	switch (value) {
	case POWER_SUPPLY_DP_DM_DPF_DMF:
		if (!qphy->rm_pulldown) {
			ret = qusb_phy_enable_power(qphy, true, false);
			if (ret >= 0) {
				qphy->rm_pulldown = true;
				dev_dbg(phy->dev, "DP_DM_F: rm_pulldown:%d\n",
						qphy->rm_pulldown);
			}

			/* Clear qc1 and qc2 registers */
			writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1);
			writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2);
			/* to make sure above write goes through */
			mb();
		}
		break;

	case POWER_SUPPLY_DP_DM_DPR_DMR:
		if (qphy->rm_pulldown) {
			/* Clear qc1 and qc2 registers */
			writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC1);
			writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2);
			/* to make sure above write goes through */
			mb();
			ret = qusb_phy_enable_power(qphy, false, false);
			if (ret >= 0) {
				qphy->rm_pulldown = false;
				dev_dbg(phy->dev, "DP_DM_R: rm_pulldown:%d\n",
						qphy->rm_pulldown);
			}
		}
		break;

	case POWER_SUPPLY_DP_DM_DP0P6_DMF:
		dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0P6_DMF\n");
		/* Set DP to 0.6v and DM to High Z state */
		writel_relaxed(VDP_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1);
		/* complete above write */
		mb();
		break;

	case POWER_SUPPLY_DP_DM_DP0P6_DM3P3:
		dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP0P6_DM3P3\n");
		/* Set DP to 0.6v and DM to 3.075v */
		writel_relaxed(VDM_SRC_EN | VDP_SRC_EN,
				qphy->base + QUSB2PHY_PORT_QC1);
		writel_relaxed(RPUP_LOW_EN | RDM_UP_EN,
				qphy->base + QUSB2PHY_PORT_QC2);
		/* complete above write */
		mb();
		break;

	case POWER_SUPPLY_DP_DM_DP_PULSE:
		dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DP_PULSE\n");
		/*
		 * Set DP to 3.075v, sleep 2-3ms,
		 * Set DP to 0.6v, sleep 2-3ms
		 */
		writel_relaxed(VDM_SRC_EN, qphy->base + QUSB2PHY_PORT_QC1);
		writel_relaxed(RDP_UP_EN | RDM_UP_EN |
				RPUM_LOW_EN | RPUP_LOW_EN,
				qphy->base + QUSB2PHY_PORT_QC2);
		/* complete above write */
		mb();

		/*
		 * It is recommended to wait here to get voltage change on
		 * DP/DM line.
		 */
		usleep_range(2000, 3000);
		writel_relaxed(RPUP_LOW_EN | RDM_UP_EN,
				qphy->base + QUSB2PHY_PORT_QC2);
		writel_relaxed(VDM_SRC_EN | VDP_SRC_EN,
				qphy->base + QUSB2PHY_PORT_QC1);
		/* complete above write */
		mb();
		/*
		 * It is recommended to wait here to get voltage change on
		 * DP/DM line.
		 */
		usleep_range(2000, 3000);
		break;

	case POWER_SUPPLY_DP_DM_DM_PULSE:
		dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DM_PULSE\n");
		/* Set DM to 0.6v, sleep 3ms, DM to 3.075v, sleep 2-3ms */
		writel_relaxed(0x00, qphy->base + QUSB2PHY_PORT_QC2);
		writel_relaxed(VDM_SRC_EN | VDP_SRC_EN,
				qphy->base + QUSB2PHY_PORT_QC1);
		/* complete above write */
		mb();

		/*
		 * It is recommended to wait here to get voltage change on
		 * DP/DM line.
		 */
		usleep_range(2000, 3000);

		writel_relaxed(RPUP_LOW_EN | RDM_UP_EN,
				qphy->base + QUSB2PHY_PORT_QC2);
		/* complete above write */
		mb();

		/*
		 * It is recommended to wait here to get voltage change on
		 * DP/DM line.
		 */
		usleep_range(2000, 3000);
		break;
	default:
		ret = -EINVAL;
		dev_err(phy->dev, "Invalid power supply property(%d)\n", value);
		break;
	}

	return ret;
}

static void qusb_phy_get_tune2_param(struct qusb_phy *qphy)
{
	u8 num_of_bits;
@@ -305,7 +449,7 @@ static int qusb_phy_init(struct usb_phy *phy)

	dev_dbg(phy->dev, "%s\n", __func__);

	ret = qusb_phy_enable_power(qphy, true);
	ret = qusb_phy_enable_power(qphy, true, true);
	if (ret)
		return ret;

@@ -485,7 +629,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
			clk_disable_unprepare(qphy->cfg_ahb_clk);
			clk_disable_unprepare(qphy->ref_clk);
			clk_disable_unprepare(qphy->ref_clk_src);
			qusb_phy_enable_power(qphy, false);
			qusb_phy_enable_power(qphy, false, true);
		}
		qphy->suspended = true;
	} else {
@@ -499,7 +643,7 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
			writel_relaxed(0x00,
				qphy->base + QUSB2PHY_PORT_INTR_CTRL);
		} else {
			qusb_phy_enable_power(qphy, true);
			qusb_phy_enable_power(qphy, true, true);
			clk_prepare_enable(qphy->ref_clk_src);
			clk_prepare_enable(qphy->ref_clk);
			clk_prepare_enable(qphy->cfg_ahb_clk);
@@ -699,6 +843,7 @@ static int qusb_phy_probe(struct platform_device *pdev)
	qphy->phy.set_suspend           = qusb_phy_set_suspend;
	qphy->phy.shutdown		= qusb_phy_shutdown;
	qphy->phy.reset			= qusb_phy_reset;
	qphy->phy.change_dpdm		= qusb_phy_update_dpdm;
	qphy->phy.type			= USB_PHY_TYPE_USB2;

	if (qphy->qscratch_base) {
@@ -731,7 +876,7 @@ static int qusb_phy_remove(struct platform_device *pdev)
		qphy->clocks_enabled = false;
	}

	qusb_phy_enable_power(qphy, false);
	qusb_phy_enable_power(qphy, false, true);

	return 0;
}
+10 −0
Original line number Diff line number Diff line
@@ -132,6 +132,8 @@ struct usb_phy {
	/* for notification of usb_phy_dbg_events */
	void    (*dbg_event)(struct usb_phy *x,
			char *event, int msg1, int msg2);
	/* update DP/DM state */
	int	(*change_dpdm)(struct usb_phy *x, int dpdm);
};

/**
@@ -279,6 +281,14 @@ usb_phy_set_power(struct usb_phy *x, unsigned mA)
	return 0;
}

static inline int
usb_phy_change_dpdm(struct usb_phy *x, int dpdm)
{
	if (x && x->change_dpdm)
		return x->change_dpdm(x, dpdm);
	return 0;
}

/* Context: can sleep */
static inline int
usb_phy_set_suspend(struct usb_phy *x, int suspend)