[RFC,v2] ufs: introduce async ufs interface initialization
diff mbox series

Message ID 1593754241-194331-1-git-send-email-kwmad.kim@samsung.com
State Superseded
Headers show
Series
  • [RFC,v2] ufs: introduce async ufs interface initialization
Related show

Commit Message

Kiwoong Kim July 3, 2020, 5:30 a.m. UTC
v1 -> v2
fix warning message

When you set uic_link_state during sleep statae to
UIC_LINK_OFF_STATE, UFS driver does interface initialization
that is a series of some steps including fDeviceInit and thus,
You might feel that its latency is a little bit longer.

This patch is run it asynchronously to reduce system wake-up time.

Signed-off-by: Kiwoong Kim <kwmad.kim@samsung.com>
---
 drivers/scsi/ufs/Kconfig  |  10 ++++
 drivers/scsi/ufs/ufshcd.c | 120 ++++++++++++++++++++++++++++++++++------------
 2 files changed, 100 insertions(+), 30 deletions(-)

Patch
diff mbox series

diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 8cd9026..9ef1727 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -172,3 +172,13 @@  config SCSI_UFS_EXYNOS
 
 	  Select this if you have UFS host controller on EXYNOS chipset.
 	  If unsure, say N.
+
+config SCSI_UFSHCD_ASYNC_INIT
+	bool "Asynchronous UFS interface initialization support"
+	depends on SCSI_UFSHCD
+	default n
+	help
+	This selects the support of doing UFS interface initialization
+	asynchronously when you set link state to link off,
+	i.e. UIC_LINK_OFF_STATE, to reduce system wake-up time.
+	Select this if you have UFS Host Controller.
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6c08ed2..3d2cead 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8370,6 +8370,80 @@  static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 	return ret;
 }
 
+static int ufshcd_post_resume(struct ufs_hba *hba)
+{
+	int ret = 0;
+
+	if (!ufshcd_is_ufs_dev_active(hba)) {
+		ret = ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE);
+		if (ret)
+			goto out;
+	}
+
+	if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
+		ufshcd_enable_auto_bkops(hba);
+	else
+		/*
+		 * If BKOPs operations are urgently needed at this moment then
+		 * keep auto-bkops enabled or else disable it.
+		 */
+		ufshcd_urgent_bkops(hba);
+
+	hba->clk_gating.is_suspended = false;
+
+	if (hba->clk_scaling.is_allowed)
+		ufshcd_resume_clkscaling(hba);
+
+	/* Enable Auto-Hibernate if configured */
+	ufshcd_auto_hibern8_enable(hba);
+
+	if (hba->dev_info.b_rpm_dev_flush_capable) {
+		hba->dev_info.b_rpm_dev_flush_capable = false;
+		cancel_delayed_work(&hba->rpm_dev_flush_recheck_work);
+	}
+
+	/* Schedule clock gating in case of no access to UFS device yet */
+	ufshcd_release(hba);
+out:
+	return ret;
+}
+
+#if defined(SCSI_UFSHCD_ASYNC_INIT)
+static void ufshcd_async_resume(void *data, async_cookie_t cookie)
+{
+	struct ufs_hba *hba = (struct ufs_hba *)data;
+	unsigned long flags;
+	int ret = 0;
+	int retries = 2;
+
+	/* transition to block requests */
+	spin_lock_irqsave(hba->host->host_lock, flags);
+	hba->ufshcd_state = UFSHCD_STATE_RESET;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+	/* initialize, instead of set_old_link_state ?? */
+	do {
+		ret = ufshcd_reset_and_restore(hba);
+		if (ret) {
+			dev_err(hba->dev, "%s: reset and restore failed\n",
+					__func__);
+			spin_lock_irqsave(hba->host->host_lock, flags);
+			hba->ufshcd_state = UFSHCD_STATE_ERROR;
+			hba->pm_op_in_progress = 0;
+			spin_unlock_irqrestore(hba->host->host_lock, flags);
+			return;
+		}
+		ret = ufshcd_post_resume(hba);
+	} while (ret && --retries);
+	if (ret)
+		goto reset;
+
+	hba->pm_op_in_progress = 0;
+	if (ret)
+		ufshcd_update_reg_hist(&hba->ufs_stats.resume_err, (u32)ret);
+}
+#endif
+
 /**
  * ufshcd_resume - helper function for resume operations
  * @hba: per adapter instance
@@ -8421,6 +8495,14 @@  static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 		 * A full initialization of the host and the device is
 		 * required since the link was put to off during suspend.
 		 */
+#if defined(SCSI_UFSHCD_ASYNC_INIT)
+		/*
+		 * Assuems error free since ufshcd_probe_hba failure is
+		 * uncorrectable.
+		 */
+		ufshcd_async_schedule(ufshcd_async_resume, hba);
+		goto out_new;
+#else
 		ret = ufshcd_reset_and_restore(hba);
 		/*
 		 * ufshcd_reset_and_restore() should have already
@@ -8428,38 +8510,12 @@  static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 		 */
 		if (ret || !ufshcd_is_link_active(hba))
 			goto vendor_suspend;
+#endif
 	}
 
-	if (!ufshcd_is_ufs_dev_active(hba)) {
-		ret = ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE);
-		if (ret)
-			goto set_old_link_state;
-	}
-
-	if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
-		ufshcd_enable_auto_bkops(hba);
-	else
-		/*
-		 * If BKOPs operations are urgently needed at this moment then
-		 * keep auto-bkops enabled or else disable it.
-		 */
-		ufshcd_urgent_bkops(hba);
-
-	hba->clk_gating.is_suspended = false;
-
-	if (hba->clk_scaling.is_allowed)
-		ufshcd_resume_clkscaling(hba);
-
-	/* Enable Auto-Hibernate if configured */
-	ufshcd_auto_hibern8_enable(hba);
-
-	if (hba->dev_info.b_rpm_dev_flush_capable) {
-		hba->dev_info.b_rpm_dev_flush_capable = false;
-		cancel_delayed_work(&hba->rpm_dev_flush_recheck_work);
-	}
-
-	/* Schedule clock gating in case of no access to UFS device yet */
-	ufshcd_release(hba);
+	ret = ufshcd_post_resume(hba);
+	if (ret)
+		goto set_old_link_state;
 
 	goto out;
 
@@ -8478,6 +8534,10 @@  static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 	hba->pm_op_in_progress = 0;
 	if (ret)
 		ufshcd_update_reg_hist(&hba->ufs_stats.resume_err, (u32)ret);
+	/* For async init, pm_op_in_progress still needs to be one */
+#if defined(SCSI_UFSHCD_ASYNC_INIT)
+out_new:
+#endif
 	return ret;
 }