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

Commit 33714450 authored by Can Guo's avatar Can Guo
Browse files

scsi: ufs-qcom: Refactor phy_power_on/off calls



Commit 052553af ("ufs/phy: qcom: Refactor to use phy_init call") puts
enabling regulators & clks, calibrating UFS PHY, starting serdes and
polling PCS ready status into phy_power_on.

Due to UFS PHY's registers are retained after power collapse, so
calibrating UFS PHY, starting serdes and polling PCS ready status need to
be done only when start hba, but not needed every time when phy_power_on
is called during resume. Keep the codes which enables PHY's regulators &
clks in phy_power_on but move the rest steps into PHY's general calibration
func, namely phy_calibrate.

Commit 3f6d1767 ("phy: ufs-qcom: Refactor all init steps into
phy_poweron") removes the phy_power_on/off from ufs_qcom_setup_clocks.

To have a better power saving, remove the phy_power_on/off calls from
resume/suspend path and put them back to ufs_qcom_setup_clocks, so that
PHY's regulators & clks can be turned on/off along with UFS's clocks.

At last, add a mutex lock to protect the usage of is_phy_pwr_on against
possible racing.

Change-Id: I1ed5d5d6968c3f16018e9723eab010ea7ed798b2
Signed-off-by: default avatarCan Guo <cang@codeaurora.org>
parent 55af5bb5
Loading
Loading
Loading
Loading
+78 −42
Original line number Original line Diff line number Diff line
@@ -450,6 +450,46 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba)
	return ret;
	return ret;
}
}


static int ufs_qcom_phy_power_on(struct ufs_hba *hba)
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct phy *phy = host->generic_phy;
	int ret = 0;

	mutex_lock(&host->phy_mutex);
	if (!host->is_phy_pwr_on) {
		ret = phy_power_on(phy);
		if (ret) {
			mutex_unlock(&host->phy_mutex);
			return ret;
		}
		host->is_phy_pwr_on = true;
	}
	mutex_unlock(&host->phy_mutex);

	return ret;
}

static int ufs_qcom_phy_power_off(struct ufs_hba *hba)
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct phy *phy = host->generic_phy;
	int ret = 0;

	mutex_lock(&host->phy_mutex);
	if (host->is_phy_pwr_on) {
		ret = phy_power_off(phy);
		if (ret) {
			mutex_unlock(&host->phy_mutex);
			return ret;
		}
		host->is_phy_pwr_on = false;
	}
	mutex_unlock(&host->phy_mutex);

	return ret;
}

static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
{
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
@@ -469,28 +509,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
		submode = UFS_QCOM_PHY_SUBMODE_NON_G4;
		submode = UFS_QCOM_PHY_SUBMODE_NON_G4;
	phy_set_mode_ext(phy, mode, submode);
	phy_set_mode_ext(phy, mode, submode);


	/* phy initialization - calibrate the phy */
	ret = ufs_qcom_phy_power_on(hba);
	ret = phy_init(phy);
	if (ret) {
	if (ret) {
		dev_err(hba->dev, "%s: phy init failed, ret = %d\n",
		dev_err(hba->dev, "%s: phy power on failed, ret = %d\n",
				 __func__, ret);
				 __func__, ret);
		goto out;
		goto out;
	}
	}


	/* power on phy - start serdes and phy's power and clocks */
	ret = phy_calibrate(phy);
	ret = phy_power_on(phy);
	if (ret) {
	if (ret) {
		dev_err(hba->dev, "%s: phy power on failed, ret = %d\n",
		dev_err(hba->dev, "%s: Failed to calibrate PHY %d\n",
				  __func__, ret);
				  __func__, ret);
		goto out_disable_phy;
		goto out;
	}
	}


	ufs_qcom_select_unipro_mode(host);
	ufs_qcom_select_unipro_mode(host);


	return 0;

out_disable_phy:
	phy_exit(phy);
out:
out:
	return ret;
	return ret;
}
}
@@ -899,8 +933,7 @@ static int ufs_qcom_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct phy *phy = host->generic_phy;
	int err = 0;
	int ret = 0;


	/*
	/*
	 * If UniPro link is not active or OFF, PHY ref_clk, main PHY analog
	 * If UniPro link is not active or OFF, PHY ref_clk, main PHY analog
@@ -909,38 +942,22 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	 */
	 */
	if (!ufs_qcom_is_link_active(hba)) {
	if (!ufs_qcom_is_link_active(hba)) {
		ufs_qcom_disable_lane_clks(host);
		ufs_qcom_disable_lane_clks(host);
		if (host->is_phy_pwr_on) {
			phy_power_off(phy);
			host->is_phy_pwr_on = false;
		}
		if (host->vddp_ref_clk && ufs_qcom_is_link_off(hba))
		if (host->vddp_ref_clk && ufs_qcom_is_link_off(hba))
			ret = ufs_qcom_disable_vreg(hba->dev,
			err = ufs_qcom_disable_vreg(hba->dev,
					host->vddp_ref_clk);
					host->vddp_ref_clk);
		if (host->vccq_parent && !hba->auto_bkops_enabled)
		if (host->vccq_parent && !hba->auto_bkops_enabled)
			ufs_qcom_config_vreg(hba->dev,
			ufs_qcom_config_vreg(hba->dev,
					host->vccq_parent, false);
					host->vccq_parent, false);

	}
	}


	return ret;
	return err;
}
}


static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
{
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct phy *phy = host->generic_phy;
	int err;
	int err;


	if (!host->is_phy_pwr_on) {
		err = phy_power_on(phy);
		if (err) {
			dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
				__func__, err);
			return 0;
		}
		host->is_phy_pwr_on = true;
	}

	if (host->vddp_ref_clk && (hba->rpm_lvl > UFS_PM_LVL_3 ||
	if (host->vddp_ref_clk && (hba->rpm_lvl > UFS_PM_LVL_3 ||
				   hba->spm_lvl > UFS_PM_LVL_3))
				   hba->spm_lvl > UFS_PM_LVL_3))
		ufs_qcom_enable_vreg(hba->dev,
		ufs_qcom_enable_vreg(hba->dev,
@@ -952,7 +969,6 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	if (err)
	if (err)
		return err;
		return err;


	hba->is_sys_suspended = false;
	return 0;
	return 0;
}
}


@@ -1596,11 +1612,26 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
			if (!ufs_qcom_is_link_active(hba)) {
			if (!ufs_qcom_is_link_active(hba)) {
				/* disable device ref_clk */
				/* disable device ref_clk */
				ufs_qcom_dev_ref_clk_ctrl(host, false);
				ufs_qcom_dev_ref_clk_ctrl(host, false);

				/* power off PHY during aggressive clk gating */
				err = ufs_qcom_phy_power_off(hba);
				if (err) {
					dev_err(hba->dev, "%s: phy power off failed, ret = %d\n",
							 __func__, err);
					return err;
				}
			}
			}
		}
		}
		break;
		break;
	case POST_CHANGE:
	case POST_CHANGE:
		if (on) {
		if (on) {
			err = ufs_qcom_phy_power_on(hba);
			if (err) {
				dev_err(hba->dev, "%s: phy power on failed, ret = %d\n",
						 __func__, err);
				return err;
			}

			/* enable the device ref clock for HS mode*/
			/* enable the device ref clock for HS mode*/
			if (ufshcd_is_hs_mode(&hba->pwr_info))
			if (ufshcd_is_hs_mode(&hba->pwr_info))
				ufs_qcom_dev_ref_clk_ctrl(host, true);
				ufs_qcom_dev_ref_clk_ctrl(host, true);
@@ -2144,14 +2175,20 @@ static int ufs_qcom_init(struct ufs_hba *hba)
	err = ufs_qcom_parse_reg_info(host, "qcom,vddp-ref-clk",
	err = ufs_qcom_parse_reg_info(host, "qcom,vddp-ref-clk",
				      &host->vddp_ref_clk);
				      &host->vddp_ref_clk);


	phy_init(host->generic_phy);
	err = phy_init(host->generic_phy);
	if (err) {
		dev_err(hba->dev, "%s: phy init failed, err %d\n",
				__func__, err);
		goto out_variant_clear;
	}
	mutex_init(&host->phy_mutex);


	if (host->vddp_ref_clk) {
	if (host->vddp_ref_clk) {
		err = ufs_qcom_enable_vreg(dev, host->vddp_ref_clk);
		err = ufs_qcom_enable_vreg(dev, host->vddp_ref_clk);
		if (err) {
		if (err) {
			dev_err(dev, "%s: failed enabling ref clk supply: %d\n",
			dev_err(dev, "%s: failed enabling ref clk supply: %d\n",
				__func__, err);
				__func__, err);
			goto out_unregister_bus;
			goto out_phy_exit;
		}
		}
	}
	}


@@ -2180,7 +2217,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)
	ufs_qcom_advertise_quirks(hba);
	ufs_qcom_advertise_quirks(hba);


	ufs_qcom_set_bus_vote(hba, true);
	ufs_qcom_set_bus_vote(hba, true);
	ufs_qcom_setup_clocks(hba, true, POST_CHANGE);
	/* enable the device ref clock for HS mode*/
	if (ufshcd_is_hs_mode(&hba->pwr_info))
		ufs_qcom_dev_ref_clk_ctrl(host, true);


	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
	if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
		ufs_qcom_hosts[hba->dev->id] = host;
		ufs_qcom_hosts[hba->dev->id] = host;
@@ -2213,7 +2252,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
out_disable_vddp:
out_disable_vddp:
	if (host->vddp_ref_clk)
	if (host->vddp_ref_clk)
		ufs_qcom_disable_vreg(dev, host->vddp_ref_clk);
		ufs_qcom_disable_vreg(dev, host->vddp_ref_clk);
out_unregister_bus:
out_phy_exit:
	phy_exit(host->generic_phy);
	phy_exit(host->generic_phy);
out_variant_clear:
out_variant_clear:
	devm_kfree(dev, host);
	devm_kfree(dev, host);
@@ -2227,10 +2266,7 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
	struct ufs_qcom_host *host = ufshcd_get_variant(hba);


	ufs_qcom_disable_lane_clks(host);
	ufs_qcom_disable_lane_clks(host);
	if (host->is_phy_pwr_on) {
	ufs_qcom_phy_power_off(hba);
		phy_power_off(host->generic_phy);
		host->is_phy_pwr_on = false;
	}
	phy_exit(host->generic_phy);
	phy_exit(host->generic_phy);
}
}


+2 −0
Original line number Original line Diff line number Diff line
@@ -331,6 +331,8 @@ struct ufs_qcom_host {
	struct ufs_vreg *vccq_parent;
	struct ufs_vreg *vccq_parent;
	bool work_pending;
	bool work_pending;
	bool is_phy_pwr_on;
	bool is_phy_pwr_on;
	/* Protect the usage of is_phy_pwr_on against racing */
	struct mutex phy_mutex;
};
};


static inline u32
static inline u32