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

Commit 7310964f authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 47cab2c4 e377a1de
Loading
Loading
Loading
Loading
+163 −38
Original line number Diff line number Diff line
@@ -129,7 +129,10 @@ struct qusb_phy {
	struct clk		*ref_clk;
	struct clk		*cfg_ahb_clk;
	struct clk		*phy_reset;
	struct clk		*iface_clk;
	struct clk		*core_clk;

	struct regulator	*gdsc;
	struct regulator	*vdd;
	struct regulator	*vdda33;
	struct regulator	*vdda18;
@@ -142,6 +145,7 @@ struct qusb_phy {
	int			tune2_efuse_num_of_bits;
	int			tune2_efuse_correction;

	bool			vdd_enabled;
	bool			power_enabled;
	bool			clocks_enabled;
	bool			cable_connected;
@@ -160,6 +164,7 @@ struct qusb_phy {
	int			*emu_dcm_reset_seq;
	int			emu_dcm_reset_seq_len;
	spinlock_t		pulse_lock;
	bool			put_into_high_z_state;
};

static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
@@ -170,14 +175,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;
	}

@@ -185,6 +198,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;
@@ -202,6 +241,47 @@ static int qusb_phy_config_vdd(struct qusb_phy *qphy, int high)
	return ret;
}

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

	if (!qphy->vdd_enabled && on) {
		dev_dbg(qphy->phy.dev, "TURNING ON VDD\n");
		ret = qusb_phy_config_vdd(qphy, true);
		if (ret) {
			dev_err(qphy->phy.dev, "Unable to config VDD:%d\n",
								ret);
			goto err;
		}

		ret = regulator_enable(qphy->vdd);
		if (ret) {
			dev_err(qphy->phy.dev, "Unable to enable VDD\n");
			goto err;
		}
		qphy->vdd_enabled = true;
	}

	if (qphy->vdd_enabled && !on) {
		dev_dbg(qphy->phy.dev, "TURNING OFF VDD\n");
		ret = regulator_disable(qphy->vdd);
		if (ret) {
			dev_err(qphy->phy.dev, "Unable to disable vdd:%d\n",
									ret);
			goto err;
		}

		ret = qusb_phy_config_vdd(qphy, false);
		if (ret) {
			dev_err(qphy->phy.dev, "Unable unconfig VDD:%d\n", ret);
			goto err;
		}
		qphy->vdd_enabled = false;
	}
err:
	return ret;
}

static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)
{
	int ret = 0;
@@ -217,18 +297,9 @@ static int qusb_phy_enable_power(struct qusb_phy *qphy, bool on)
	if (!on)
		goto disable_vdda33;

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

	ret = regulator_enable(qphy->vdd);
	if (ret) {
		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) {
@@ -308,16 +379,7 @@ put_vdda18_lpm:
		dev_err(qphy->phy.dev, "Unable to set LPM of vdda18\n");

disable_vdd:
	ret = regulator_disable(qphy->vdd);
	if (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);
	ret = qusb_phy_vdd(qphy, false);
err_vdd:
	qphy->power_enabled = false;
	dev_dbg(qphy->phy.dev, "QUSB PHY's regulators are turned OFF.\n");
@@ -340,12 +402,55 @@ static int qusb_phy_update_dpdm(struct usb_phy *phy, int value)
	case POWER_SUPPLY_DP_DM_DPF_DMF:
		dev_dbg(phy->dev, "POWER_SUPPLY_DP_DM_DPF_DMF\n");
		if (!qphy->rm_pulldown) {

			if (qphy->put_into_high_z_state) {

				/* Bring up DVDD */
				ret = qusb_phy_vdd(qphy, true);
				if (ret < 0)
					goto clk_error;
				qusb_phy_gdsc(qphy, true);
				qusb_phy_enable_clocks(qphy, true);

				dev_dbg(phy->dev, "RESET QUSB PHY\n");
				clk_reset(qphy->phy_reset, CLK_RESET_ASSERT);
				usleep_range(100, 150);
				clk_reset(qphy->phy_reset, CLK_RESET_DEASSERT);

				/*
				 * 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();
			}

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

			if (qphy->put_into_high_z_state) {
				qusb_phy_enable_clocks(qphy, false);
				qusb_phy_gdsc(qphy, false);
			}
		}

		/* Clear QC1 and QC2 registers when rm_pulldown = 1 */
@@ -892,28 +997,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);

			/* 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_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 {
@@ -1101,6 +1198,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");