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

Commit 284fa080 authored by Can Guo's avatar Can Guo
Browse files

scsi: ufs: fix broken hibern8_on_idle sysfs nodes



Setting 0 to auto hibern8 delay means turning auto hibern8 off, then the
link shall be kept active even when clock gating kicks in. To keep link
active, both device ref clock and UNIPRO clock should be kept ON when
disable clocks during clock gating.

Change-Id: If2c70eff7cf99928aabe46f311e7413c57af0ba7
Signed-off-by: default avatarCan Guo <cang@codeaurora.org>
parent 24af5c26
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1230,7 +1230,8 @@ static int ufsdbg_config_pwr_mode(struct ufs_hba *hba,
		 * hibern8 manually, this is to avoid auto hibern8
		 * racing during clock frequency scaling sequence.
		 */
		if (ufshcd_is_auto_hibern8_supported(hba)) {
		if (ufshcd_is_auto_hibern8_supported(hba) &&
		    hba->hibern8_on_idle.is_enabled) {
			ret = ufshcd_uic_hibern8_enter(hba);
			if (ret)
				goto out;
@@ -1240,7 +1241,8 @@ static int ufsdbg_config_pwr_mode(struct ufs_hba *hba,
		if (ret)
			goto out;

		if (ufshcd_is_auto_hibern8_supported(hba))
		if (ufshcd_is_auto_hibern8_supported(hba) &&
		    hba->hibern8_on_idle.is_enabled)
			ret = ufshcd_uic_hibern8_exit(hba);

		if (scale_up) {
+2 −1
Original line number Diff line number Diff line
@@ -1621,7 +1621,8 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
		 * If auto hibern8 is supported then the link will already
		 * be in hibern8 state and the ref clock can be gated.
		 */
		if (ufshcd_is_auto_hibern8_supported(hba) ||
		if ((ufshcd_is_auto_hibern8_supported(hba) &&
		     hba->hibern8_on_idle.is_enabled) ||
		    !ufs_qcom_is_link_active(hba)) {
			/* disable device ref_clk */
			ufs_qcom_dev_ref_clk_ctrl(host, false);
+31 −16
Original line number Diff line number Diff line
@@ -445,7 +445,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba);
static int ufshcd_enable_clocks(struct ufs_hba *hba);
static int ufshcd_disable_clocks(struct ufs_hba *hba,
				 bool is_gating_context);
static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba,
static int ufshcd_disable_clocks_keep_link_active(struct ufs_hba *hba,
					      bool is_gating_context);
static void ufshcd_hold_all(struct ufs_hba *hba);
static void ufshcd_release_all(struct ufs_hba *hba);
@@ -1791,7 +1791,8 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
	 * hibern8 manually, this is to avoid auto hibern8
	 * racing during clock frequency scaling sequence.
	 */
	if (ufshcd_is_auto_hibern8_supported(hba)) {
	if (ufshcd_is_auto_hibern8_supported(hba) &&
	    hba->hibern8_on_idle.is_enabled) {
		ret = ufshcd_uic_hibern8_enter(hba);
		if (ret)
			/* link will be bad state so no need to scale_up_gear */
@@ -1804,7 +1805,8 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
		goto scale_up_gear;
	ufshcd_custom_cmd_log(hba, "Clk-freq-switched");

	if (ufshcd_is_auto_hibern8_supported(hba)) {
	if (ufshcd_is_auto_hibern8_supported(hba) &&
	    hba->hibern8_on_idle.is_enabled) {
		ret = ufshcd_uic_hibern8_exit(hba);
		if (ret)
			/* link will be bad state so no need to scale_up_gear */
@@ -2267,15 +2269,16 @@ static void ufshcd_gate_work(struct work_struct *work)
	}

	/*
	 * If auto hibern8 is supported then the link will already
	 * If auto hibern8 is supported and enabled then the link will already
	 * be in hibern8 state and the ref clock can be gated.
	 */
	if ((ufshcd_is_auto_hibern8_supported(hba) ||
	if ((((ufshcd_is_auto_hibern8_supported(hba) &&
	       hba->hibern8_on_idle.is_enabled)) ||
	     !ufshcd_is_link_active(hba)) && !hba->no_ref_clk_gating)
		ufshcd_disable_clocks(hba, true);
	else
		/* If link is active, device ref_clk can't be switched off */
		ufshcd_disable_clocks_skip_ref_clk(hba, true);
		ufshcd_disable_clocks_keep_link_active(hba, true);

	/* Put the host controller in low power mode if possible */
	ufshcd_hba_vreg_set_lpm(hba);
@@ -2757,6 +2760,7 @@ static void __ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba,
	/* wait for all the outstanding requests to finish */
	ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
	ufshcd_set_auto_hibern8_timer(hba, delay_ms);
	hba->hibern8_on_idle.is_enabled = !!delay_ms;
	up_write(&hba->lock);
	ufshcd_scsi_unblock_requests(hba);
	ufshcd_release_all(hba);
@@ -2871,7 +2875,7 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
	if (ufshcd_is_auto_hibern8_supported(hba)) {
		__ufshcd_set_auto_hibern8_timer(hba,
			value ? hba->hibern8_on_idle.delay_ms : value);
		goto update;
		goto out;
	}

	if (value) {
@@ -2887,7 +2891,6 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
		spin_unlock_irqrestore(hba->host->host_lock, flags);
	}

update:
	hba->hibern8_on_idle.is_enabled = value;
out:
	return count;
@@ -6282,7 +6285,8 @@ static irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
		if (hba->uic_async_done) {
			complete(hba->uic_async_done);
			retval = IRQ_HANDLED;
		} else if (ufshcd_is_auto_hibern8_supported(hba)) {
		} else if (ufshcd_is_auto_hibern8_supported(hba) &&
			   hba->hibern8_on_idle.is_enabled) {
			/*
			 * If uic_async_done flag is not set then this
			 * is an Auto hibern8 err interrupt.
@@ -6952,7 +6956,8 @@ static void ufshcd_err_handler(struct work_struct *work)
	 * process of gating when the err handler runs.
	 */
	if (unlikely((hba->clk_gating.state != CLKS_ON) &&
	    ufshcd_is_auto_hibern8_supported(hba))) {
	    ufshcd_is_auto_hibern8_supported(hba) &&
	    hba->hibern8_on_idle.is_enabled)) {
		spin_unlock_irqrestore(hba->host->host_lock, flags);
		hba->ufs_stats.clk_hold.ctx = ERR_HNDLR_WORK;
		ufshcd_hold(hba, false);
@@ -8708,7 +8713,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
	 * Enable auto hibern8 if supported, after full host and
	 * device initialization.
	 */
	if (ufshcd_is_auto_hibern8_supported(hba))
	if (ufshcd_is_auto_hibern8_supported(hba) &&
	    hba->hibern8_on_idle.is_enabled)
		ufshcd_set_auto_hibern8_timer(hba,
				      hba->hibern8_on_idle.delay_ms);
out:
@@ -9422,7 +9428,7 @@ static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused)
}

static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
			       bool skip_ref_clk, bool is_gating_context)
			       bool keep_link_active, bool is_gating_context)
{
	int ret = 0;
	struct ufs_clk_info *clki;
@@ -9454,7 +9460,13 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on,

	list_for_each_entry(clki, head, list) {
		if (!IS_ERR_OR_NULL(clki->clk)) {
			if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
			/*
			 * To keep link active, both device ref clock and unipro
			 * clock should be kept ON.
			 */
			if (keep_link_active &&
			    (!strcmp(clki->name, "ref_clk") ||
			     !strcmp(clki->name, "core_clk_unipro")))
				continue;

			clk_state_changed = on ^ clki->enabled;
@@ -9529,7 +9541,7 @@ static int ufshcd_disable_clocks(struct ufs_hba *hba,
	return  ufshcd_setup_clocks(hba, false, false, is_gating_context);
}

static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba,
static int ufshcd_disable_clocks_keep_link_active(struct ufs_hba *hba,
					      bool is_gating_context)
{
	return  ufshcd_setup_clocks(hba, false, true, is_gating_context);
@@ -10012,8 +10024,11 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	if (!ufshcd_is_link_active(hba))
		ret = ufshcd_disable_clocks(hba, false);
	else
		/* If link is active, device ref_clk can't be switched off */
		ret = ufshcd_disable_clocks_skip_ref_clk(hba, false);
		/*
		 * If link is active, device ref_clk and unipro clock can't be
		 * switched off.
		 */
		ret = ufshcd_disable_clocks_keep_link_active(hba, false);
	if (ret)
		goto set_link_active;