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

Commit 9e999ccd authored by Chandana Kishori Chiluveru's avatar Chandana Kishori Chiluveru Committed by Gerrit - the friendly Code Review server
Browse files

usb: phy: qusb: Make sure QUSB PHY is into proper state



On some platforms, QUSB PHY's DVDD related power supply (LDO) is
not always ON. Hence when this power supply is switched off, QUSB
PHY's register configuration is not retained. QUSB PHY state
is unknown when required LDOs are turned ON with USB cable connect
case and may interfere charger detection. Hence use suggested sequence
which involves resetting QUSB PHY and performing few set of QUSB PHY
register configuration to bring QUSB PHY into non-driving mode.

Change-Id: I30b8488a1c19815601e6a1c5bcbdeed53715f8fa
Signed-off-by: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
Signed-off-by: default avatarChandana Kishori Chiluveru <cchiluve@codeaurora.org>
parent d5d55ba0
Loading
Loading
Loading
Loading
+117 −14
Original line number Diff line number Diff line
@@ -112,7 +112,10 @@ struct qusb_phy {
	struct clk		*ref_clk;
	struct clk		*cfg_ahb_clk;
	struct reset_control	*phy_reset;
	struct clk		*iface_clk;
	struct clk		*core_clk;

	struct regulator	*gdsc;
	struct regulator	*vdd;
	struct regulator	*vdda33;
	struct regulator	*vdda18;
@@ -145,6 +148,7 @@ struct qusb_phy {
	int			phy_pll_reset_seq_len;
	int			*emu_dcm_reset_seq;
	int			emu_dcm_reset_seq_len;
	bool			put_into_high_z_state;
};

static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
@@ -155,14 +159,22 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
	if (!qphy->clocks_enabled && on) {
		clk_prepare_enable(qphy->ref_clk_src);
		clk_prepare_enable(qphy->ref_clk);
		clk_prepare_enable(qphy->iface_clk);
		clk_prepare_enable(qphy->core_clk);
		clk_prepare_enable(qphy->cfg_ahb_clk);
		qphy->clocks_enabled = true;
	}

	if (qphy->clocks_enabled && !on) {
		clk_disable_unprepare(qphy->cfg_ahb_clk);
		/*
		 * FSM depedency beween iface_clk and core_clk.
		 * Hence turned off core_clk before iface_clk.
		 */
		clk_disable_unprepare(qphy->core_clk);
		clk_disable_unprepare(qphy->iface_clk);
		clk_disable_unprepare(qphy->ref_clk);
		clk_disable_unprepare(qphy->ref_clk_src);
		clk_disable_unprepare(qphy->cfg_ahb_clk);
		qphy->clocks_enabled = false;
	}

@@ -170,6 +182,32 @@ static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
						qphy->clocks_enabled);
}

static int qusb_phy_gdsc(struct qusb_phy *qphy, bool on)
{
	int ret;

	if (IS_ERR_OR_NULL(qphy->gdsc))
		return -EPERM;

	if (on) {
		dev_dbg(qphy->phy.dev, "TURNING ON GDSC\n");
		ret = regulator_enable(qphy->gdsc);
		if (ret) {
			dev_err(qphy->phy.dev, "unable to enable gdsc\n");
			return ret;
		}
	} else {
		dev_dbg(qphy->phy.dev, "TURNING OFF GDSC\n");
		ret = regulator_disable(qphy->gdsc);
		if (ret) {
			dev_err(qphy->phy.dev, "unable to disable gdsc\n");
			return ret;
		}
	}

	return ret;
}

static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high)
{
	int min, ret;
@@ -601,22 +639,20 @@ static int qusb_phy_set_suspend(struct usb_phy *phy, int suspend)
			/* Disable all interrupts */
			writel_relaxed(0x00,
				qphy->base + QUSB2PHY_PORT_INTR_CTRL);
			/*
			 * Phy in non-driving mode leaves Dp and Dm lines in
			 * high-Z state. Controller power collapse is not
			 * switching phy to non-driving mode causing charger
			 * detection failure. Bring phy to non-driving mode by
			 * overriding controller output via UTMI interface.
			 */
			writel_relaxed(TERM_SELECT | XCVR_SELECT_FS |
				OP_MODE_NON_DRIVE,
				qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);
			writel_relaxed(UTMI_ULPI_SEL | UTMI_TEST_MUX_SEL,
				qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);

			/* Make sure that above write is completed */
			wmb();

			qusb_phy_enable_clocks(qphy, false);
			qusb_phy_enable_power(qphy, false);

			/*
			 * Set put_into_high_z_state to true so next USB
			 * cable connect, DPF_DMF request performs PHY
			 * reset and put it into high-z state. For bootup
			 * with or without USB cable, it doesn't require
			 * to put QUSB PHY into high-z state.
			 */
			qphy->put_into_high_z_state = true;
		}
		qphy->suspended = true;
	} else {
@@ -677,6 +713,45 @@ static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
			return ret;
		}
		qphy->dpdm_enable = true;
		if (qphy->put_into_high_z_state) {
			qusb_phy_gdsc(qphy, true);
			qusb_phy_enable_clocks(qphy, true);

			dev_dbg(qphy->phy.dev, "RESET QUSB PHY\n");
			ret = reset_control_assert(qphy->phy_reset);
			if (ret)
				dev_err(qphy->phy.dev, "phyassert failed\n");
			usleep_range(100, 150);
			ret = reset_control_deassert(qphy->phy_reset);
			if (ret)
				dev_err(qphy->phy.dev, "deassert failed\n");

			/*
			 * Phy in non-driving mode leaves Dp and Dm
			 * lines in high-Z state. Controller power
			 * collapse is not switching phy to non-driving
			 * mode causing charger detection failure. Bring
			 * phy to non-driving mode by overriding
			 * controller output via UTMI interface.
			 */
			writel_relaxed(TERM_SELECT | XCVR_SELECT_FS |
				OP_MODE_NON_DRIVE,
				qphy->base + QUSB2PHY_PORT_UTMI_CTRL1);
			writel_relaxed(UTMI_ULPI_SEL |
				UTMI_TEST_MUX_SEL,
				qphy->base + QUSB2PHY_PORT_UTMI_CTRL2);


			/* Disable PHY */
			writel_relaxed(CLAMP_N_EN | FREEZIO_N |
					POWER_DOWN,
					qphy->base + QUSB2PHY_PORT_POWERDOWN);
			/* Make sure that above write is completed */
			wmb();

			qusb_phy_enable_clocks(qphy, false);
			qusb_phy_gdsc(qphy, false);
		}
	}

	return ret;
@@ -847,6 +922,34 @@ static int qusb_phy_probe(struct platform_device *pdev)
	if (IS_ERR(qphy->phy_reset))
		return PTR_ERR(qphy->phy_reset);

	if (of_property_match_string(dev->of_node,
		"clock-names", "iface_clk") >= 0) {
		qphy->iface_clk = devm_clk_get(dev, "iface_clk");
		if (IS_ERR(qphy->iface_clk)) {
			ret = PTR_ERR(qphy->iface_clk);
			qphy->iface_clk = NULL;
		if (ret == -EPROBE_DEFER)
			return ret;
			dev_err(dev, "couldn't get iface_clk(%d)\n", ret);
		}
	}

	if (of_property_match_string(dev->of_node,
		"clock-names", "core_clk") >= 0) {
		qphy->core_clk = devm_clk_get(dev, "core_clk");
		if (IS_ERR(qphy->core_clk)) {
			ret = PTR_ERR(qphy->core_clk);
			qphy->core_clk = NULL;
			if (ret == -EPROBE_DEFER)
				return ret;
			dev_err(dev, "couldn't get core_clk(%d)\n", ret);
		}
	}

	qphy->gdsc = devm_regulator_get(dev, "USB3_GDSC");
	if (IS_ERR(qphy->gdsc))
		qphy->gdsc = NULL;

	qphy->emulation = of_property_read_bool(dev->of_node,
					"qcom,emulation");