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

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

scsi: ufs: Add support for turning off UFS power supplies



UFS3.0 and newer devices use Vcc and Vccq(1.2V) power supplies
while UFS2.1 devices use Vcc and Vccq2(1.8V) power supplies.
If the system allows turning off these regulators during
power collapse events or system suspend events, then turn off
the regulators to save power. If the UFS device power supplies
are turned off, the UFS host will perform full initialization
sequence to the UFS device during system resume events.

Change-Id: I652702b0e72d37c76f0551688d050b88b7f13984
Signed-off-by: default avatarBao D. Nguyen <nguyenb@codeaurora.org>
parent 3b501c04
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -535,6 +535,7 @@ struct ufs_vreg {
	int max_uV;
	bool low_voltage_sup;
	bool low_voltage_active;
	bool sys_suspend_pwr_off;
	int min_uA;
	int max_uA;
};
+15 −0
Original line number Diff line number Diff line
@@ -217,9 +217,24 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name,
	} else if (!strcmp(name, "vccq")) {
		vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
		vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
		/**
		 * Only if the SoC supports turning off VCCQ or VCCQ2 power
		 * supply source during power collapse, set a flag to turn off
		 * the specified power supply to reduce the system power
		 * consumption during system suspend events. The tradeoffs are:
		 *   - System resume time will increase due
		 *     to UFS device full re-initialization time.
		 *   - UFS device life may be affected due to multiple
		 *     UFS power on/off events.
		 * The benefits vs tradeoff should be considered carefully.
		 */
		if (of_property_read_bool(np, "vccq-pwr-collapse-sup"))
			vreg->sys_suspend_pwr_off = true;
	} else if (!strcmp(name, "vccq2")) {
		vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
		vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
		if (of_property_read_bool(np, "vccq2-pwr-collapse-sup"))
			vreg->sys_suspend_pwr_off = true;
	}

	goto out;
+55 −9
Original line number Diff line number Diff line
@@ -8825,6 +8825,22 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
			goto out;
	}

	/**
	 * UFS3.0 and newer devices use Vcc and Vccq(1.2V)
	 * while UFS2.1 devices use Vcc and Vccq2(1.8V) power
	 * supplies. If the system allows turning off the regulators
	 * during power collapse event, turn off the regulators
	 * during system suspend events. This will cause the UFS
	 * device to re-initialize upon system resume events.
	 */
	if ((hba->dev_info.w_spec_version >= 0x300 &&
		hba->vreg_info.vccq->sys_suspend_pwr_off) ||
		(hba->dev_info.w_spec_version < 0x300 &&
		hba->vreg_info.vccq2->sys_suspend_pwr_off))
		hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
				UFS_POWERDOWN_PWR_MODE,
				UIC_LINK_OFF_STATE);

	/* UFS device is also active now */
	ufshcd_set_ufs_dev_active(hba);
	ufshcd_force_reset_auto_bkops(hba);
@@ -10070,7 +10086,20 @@ static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
	 */
	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
	    !hba->dev_info.is_lu_power_on_wp) {
		ufshcd_setup_vreg(hba, false);
		ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
		if (hba->dev_info.w_spec_version >= 0x300 &&
			hba->vreg_info.vccq->sys_suspend_pwr_off)
			ufshcd_toggle_vreg(hba->dev,
				hba->vreg_info.vccq, false);
		else
			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);

		if (hba->dev_info.w_spec_version < 0x300 &&
			hba->vreg_info.vccq2->sys_suspend_pwr_off)
			ufshcd_toggle_vreg(hba->dev,
				hba->vreg_info.vccq2, false);
		else
			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
	} else if (!ufshcd_is_ufs_dev_active(hba)) {
		ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
		if (!ufshcd_is_link_active(hba)) {
@@ -10086,22 +10115,39 @@ static int ufshcd_vreg_set_hpm(struct ufs_hba *hba)

	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
		!hba->dev_info.is_lu_power_on_wp) {
		ret = ufshcd_setup_vreg(hba, true);
	} else if (!ufshcd_is_ufs_dev_active(hba)) {
		if (!ret && !ufshcd_is_link_active(hba)) {
			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
		if (hba->dev_info.w_spec_version < 0x300 &&
			hba->vreg_info.vccq2->sys_suspend_pwr_off)
			ret = ufshcd_toggle_vreg(hba->dev,
				hba->vreg_info.vccq2, true);
		else
			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
		if (ret)
			goto vcc_disable;

		if (hba->dev_info.w_spec_version >= 0x300 &&
			hba->vreg_info.vccq->sys_suspend_pwr_off)
			ret = ufshcd_toggle_vreg(hba->dev,
				hba->vreg_info.vccq, true);
		else
			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
		if (ret)
			goto vccq2_lpm;
		ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
	} else if (!ufshcd_is_ufs_dev_active(hba)) {
		if (!ufshcd_is_link_active(hba)) {
			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
			if (ret)
				goto vccq_lpm;
				goto vcc_disable;
			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
			if (ret)
				goto vccq2_lpm;
		}
		ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
	}
	goto out;

vccq_lpm:
	ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
vccq2_lpm:
	ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
vcc_disable:
	ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
out: