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

Commit 5e3029e3 authored by Bao D. Nguyen's avatar Bao D. Nguyen
Browse files

scsi: ufs: Add load voting for UFS's VCCQ parent regulator



The UFS clock gating events can trigger CX Power Collapse followed by
SoC system sleep events, independent of the Linux Power Management's
runtime/system suspend events. When this happens, the VCCQ's
parent regulator source may limit the amount of current the UFS VCCQ
regulator can draw while the SoC is in sleep mode but the UFS device
is in active full power mode. The UFS3.0 and later devices with
Turbo Write feature support may consume high power during the
cache flush and bkops operations which may trigger over current
detection event. To prevent the VCCQ's parent regulator source
from limiting the VCCQ's regulator power consumption while the
UFS device is in active mode, add a high load voting on
the VCCQ parent regulator. Set the low load voting on this
regulator when the UFS enters Linux's runtime/system suspend.
In addition, vote the UFS regulator sources in fully active mode when
the UFS bkops is enabled during runtime suspend.

Change-Id: I25406a067dc9702a31d7a5ad2fa65e3891d95185
Signed-off-by: default avatarBao D. Nguyen <nguyenb@codeaurora.org>
parent 3d18c71f
Loading
Loading
Loading
Loading
+36 −9
Original line number Diff line number Diff line
@@ -772,15 +772,16 @@ static int ufs_qcom_config_vreg(struct device *dev,
		ret = regulator_set_load(vreg->reg, uA_load);
		if (ret)
			goto out;

		if (vreg->min_uV && vreg->max_uV) {
			min_uV = on ? vreg->min_uV : 0;
			ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
			if (ret) {
			dev_err(dev, "%s: %s set voltage failed, err=%d\n",
				dev_err(dev, "%s: %s failed, err=%d\n",
					__func__, vreg->name, ret);
				goto out;
			}
		}
	}
out:
	return ret;
}
@@ -845,6 +846,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
		if (host->vddp_ref_clk && ufs_qcom_is_link_off(hba))
			ret = ufs_qcom_disable_vreg(hba->dev,
					host->vddp_ref_clk);
		if (host->vccq_parent && !hba->auto_bkops_enabled)
			ufs_qcom_config_vreg(hba->dev,
					host->vccq_parent, false);

		ufs_qcom_ice_suspend(host);
		if (ufs_qcom_is_link_off(hba)) {
			/* Assert PHY soft reset */
@@ -878,6 +883,8 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
				   hba->spm_lvl > UFS_PM_LVL_3))
		ufs_qcom_enable_vreg(hba->dev,
				      host->vddp_ref_clk);
	if (host->vccq_parent)
		ufs_qcom_config_vreg(hba->dev, host->vccq_parent, true);

	err = ufs_qcom_enable_lane_clks(host);
	if (err)
@@ -2117,7 +2124,10 @@ static int ufs_qcom_parse_reg_info(struct ufs_qcom_host *host, char *name,
	if (ret) {
		dev_dbg(dev, "%s: unable to find %s err %d, using default\n",
			__func__, prop_name, ret);
		if (!strcmp(name, "qcom,vddp-ref-clk"))
			vreg->min_uV = VDDP_REF_CLK_MIN_UV;
		else if (!strcmp(name, "qcom,vccq-parent"))
			vreg->min_uV = 0;
		ret = 0;
	}

@@ -2126,7 +2136,10 @@ static int ufs_qcom_parse_reg_info(struct ufs_qcom_host *host, char *name,
	if (ret) {
		dev_dbg(dev, "%s: unable to find %s err %d, using default\n",
			__func__, prop_name, ret);
		if (!strcmp(name, "qcom,vddp-ref-clk"))
			vreg->max_uV = VDDP_REF_CLK_MAX_UV;
		else if (!strcmp(name, "qcom,vccq-parent"))
			vreg->max_uV = 0;
		ret = 0;
	}

@@ -2284,9 +2297,20 @@ static int ufs_qcom_init(struct ufs_hba *hba)
		}
	}

	err = ufs_qcom_parse_reg_info(host, "qcom,vccq-parent",
				      &host->vccq_parent);
	if (host->vccq_parent) {
		err = ufs_qcom_config_vreg(hba->dev, host->vccq_parent, true);
		if (err) {
			dev_err(dev, "%s: failed vccq-parent set load: %d\n",
				__func__, err);
			goto out_disable_vddp;
		}
	}

	err = ufs_qcom_init_lane_clks(host);
	if (err)
		goto out_disable_vddp;
		goto out_set_load_vccq_parent;

	ufs_qcom_parse_lpm(host);
	if (host->disable_lpm)
@@ -2311,6 +2335,9 @@ static int ufs_qcom_init(struct ufs_hba *hba)

	goto out;

out_set_load_vccq_parent:
	if (host->vccq_parent)
		ufs_qcom_config_vreg(hba->dev, host->vccq_parent, false);
out_disable_vddp:
	if (host->vddp_ref_clk)
		ufs_qcom_disable_vreg(dev, host->vddp_ref_clk);
+1 −0
Original line number Diff line number Diff line
@@ -386,6 +386,7 @@ struct ufs_qcom_host {
	bool is_ice_cfg_work_set;
	struct request *req_pending;
	struct ufs_vreg *vddp_ref_clk;
	struct ufs_vreg *vccq_parent;
	bool work_pending;
	bool is_phy_pwr_on;
};
+2 −2
Original line number Diff line number Diff line
@@ -10309,8 +10309,8 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
		hba->hibern8_on_idle.state = HIBERN8_ENTERED;

set_vreg_lpm:
	if (!hba->auto_bkops_enabled)
		ufshcd_vreg_set_lpm(hba);

disable_clks:
	/*
	 * Call vendor specific suspend callback. As these callbacks may access