Hi, > > 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. Can you share your initial testing findings? How much time does it save? > > Signed-off-by: Kiwoong Kim <kwmad.kim@xxxxxxxxxxx> > --- > drivers/scsi/ufs/Kconfig | 10 ++++ > drivers/scsi/ufs/ufshcd.c | 120 ++++++++++++++++++++++++++++++++++--------- > --- > 2 files changed, 100 insertions(+), 30 deletions(-) > > diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig > index 8cd9026..723e7cb 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. Maybe replace this config switch with platform capability? So each platform vendor can choose if he is using a sync vs async init? > diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c > index 52abe82..b65d38c 100644 > --- a/drivers/scsi/ufs/ufshcd.c > +++ b/drivers/scsi/ufs/ufshcd.c > @@ -8319,6 +8319,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) Why do you need to move this code around? If its async - then there is no shared code - you go through the full init flow, and just goto out? > +{ > + int ret; > + > + 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 > @@ -8370,6 +8444,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 > @@ -8377,38 +8459,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; > > @@ -8427,6 +8483,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; > } Thanks, Avri > > -- > 2.7.4