From 5801290e03061e72e5f31b22d3452ab776ae150a Mon Sep 17 00:00:00 2001 From: hgchu Date: Fri, 12 Jan 2018 11:26:28 +0900 Subject: [PATCH] scsi: ufs: allow fake clock gating Change-Id: Iff1f258be4ec2375e32bbea85f0fb49d97c87d18 Signed-off-by: hgchu --- drivers/scsi/ufs/ufshcd.c | 76 ++++++++++++++++++++++++++------------- drivers/scsi/ufs/ufshcd.h | 11 +++++- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 515ee921002d..04c0e869f920 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1416,29 +1416,37 @@ static void ufshcd_ungate_work(struct work_struct *work) unsigned long flags; struct ufs_hba *hba = container_of(work, struct ufs_hba, clk_gating.ungate_work); + bool gating_allowed = !ufshcd_can_fake_clkgating(hba); cancel_delayed_work_sync(&hba->clk_gating.gate_work); spin_lock_irqsave(hba->host->host_lock, flags); - if (hba->clk_gating.state == CLKS_ON) { + if (hba->clk_gating.state == CLKS_ON && gating_allowed) { spin_unlock_irqrestore(hba->host->host_lock, flags); goto unblock_reqs; } spin_unlock_irqrestore(hba->host->host_lock, flags); - ufshcd_setup_clocks(hba, true); + if (gating_allowed) { + ufshcd_setup_clocks(hba, true); + } else { + hba->clk_gating.state = CLKS_ON; + } /* Exit from hibern8 */ if (ufshcd_can_hibern8_during_gating(hba)) { /* Prevent gating in this path */ hba->clk_gating.is_suspended = true; if (ufshcd_is_link_hibern8(hba)) { + ufshcd_set_link_trans_active(hba); ret = ufshcd_link_hibern8_ctrl(hba, false); - if (ret) + if (ret) { + ufshcd_set_link_off(hba); dev_err(hba->dev, "%s: hibern8 exit failed %d\n", __func__, ret); - else + } else { ufshcd_set_link_active(hba); + } } hba->clk_gating.is_suspended = false; } @@ -1535,6 +1543,7 @@ static void ufshcd_gate_work(struct work_struct *work) { struct ufs_hba *hba = container_of(work, struct ufs_hba, clk_gating.gate_work.work); + bool gating_allowed = !ufshcd_can_fake_clkgating(hba); unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); @@ -1563,6 +1572,7 @@ static void ufshcd_gate_work(struct work_struct *work) /* put the link into hibern8 mode before turning off clocks */ if (ufshcd_can_hibern8_during_gating(hba)) { + ufshcd_set_link_trans_hibern8(hba); if (ufshcd_link_hibern8_ctrl(hba, true)) { hba->clk_gating.state = CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), @@ -1572,11 +1582,13 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } - if (!ufshcd_is_link_active(hba)) - ufshcd_setup_clocks(hba, false); - else - /* If link is active, device ref_clk can't be switched off */ - __ufshcd_setup_clocks(hba, false, true); + if (gating_allowed) { + if (!ufshcd_is_link_active(hba)) + ufshcd_setup_clocks(hba, false); + else + /* If link is active, device ref_clk can't be switched off */ + __ufshcd_setup_clocks(hba, false, true); + } /* * In case you are here to cancel this work the gating state @@ -4264,9 +4276,11 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) int ret; ufshcd_hold(hba, false); + hba->ufshcd_state = UFSHCD_STATE_RESET; if (hba->vops && hba->vops->host_reset) hba->vops->host_reset(hba); if (hba->quirks & UFSHCD_QUIRK_USE_OF_HCE) { + ufshcd_set_link_off(hba); /* enable UIC related interrupts */ ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); @@ -7286,10 +7300,11 @@ static int ufshcd_link_state_transition(struct ufs_hba *hba, return 0; if (req_link_state == UIC_LINK_HIBERN8_STATE) { + ufshcd_set_link_trans_hibern8(hba); ret = ufshcd_link_hibern8_ctrl(hba, true); if (!ret) ufshcd_set_link_hibern8(hba); - else + else { goto out; } /* @@ -7423,6 +7438,7 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) enum ufs_pm_level pm_lvl; enum ufs_dev_pwr_mode req_dev_pwr_mode; enum uic_link_state req_link_state; + bool gating_allowed = !ufshcd_can_fake_clkgating(hba); hba->pm_op_in_progress = 1; if (!ufshcd_is_shutdown_pm(pm_op)) { @@ -7508,11 +7524,13 @@ disable_clks: ufshcd_disable_irq(hba); - if (!ufshcd_is_link_active(hba)) - ufshcd_setup_clocks(hba, false); - else - /* If link is active, device ref_clk can't be switched off */ - __ufshcd_setup_clocks(hba, false, true); + if (gating_allowed) { + if (!ufshcd_is_link_active(hba)) + ufshcd_setup_clocks(hba, false); + else + /* If link is active, device ref_clk can't be switched off */ + __ufshcd_setup_clocks(hba, false, true); + } hba->clk_gating.state = CLKS_OFF; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -7533,10 +7551,13 @@ disable_clks: set_link_active: if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); - ufshcd_vreg_set_hpm(hba); - if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) - ufshcd_set_link_active(hba); - else if (ufshcd_is_link_off(hba)) + if (ufshcd_is_link_hibern8(hba)) { + ufshcd_set_link_trans_active(hba); + if (!ufshcd_link_hibern8_ctrl(hba, false)) + ufshcd_set_link_active(hba); + else + ufshcd_set_link_off(hba); + } else if (ufshcd_is_link_off(hba)) ufshcd_host_reset_and_restore(hba); set_dev_active: if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE)) @@ -7565,15 +7586,13 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { int ret; enum uic_link_state old_link_state; + bool gating_allowed = !ufshcd_can_fake_clkgating(hba); hba->pm_op_in_progress = 1; old_link_state = hba->uic_link_state; ufshcd_hba_vreg_set_hpm(hba); - /* Make sure clocks are enabled before accessing controller */ - ret = ufshcd_setup_clocks(hba, true); - if (ret) - goto out; + /* enable the host irq as host controller would be active soon */ ret = ufshcd_enable_irq(hba); @@ -7593,12 +7612,21 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto disable_vreg; + if (gating_allowed) { + /* Make sure clocks are enabled before accessing controller */ + ret = ufshcd_setup_clocks(hba, true); + if (ret) + goto disable_vreg; + } if (ufshcd_is_link_hibern8(hba)) { + ufshcd_set_link_trans_active(hba); ret = ufshcd_link_hibern8_ctrl(hba, false); if (!ret) ufshcd_set_link_active(hba); - else + else { + ufshcd_set_link_off(hba); goto vendor_suspend; + } } else if (ufshcd_is_link_off(hba)) { ret = ufshcd_host_reset_and_restore(hba); /* diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index b2669823fa0c..abe402788a43 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -115,6 +115,8 @@ enum uic_link_state { UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */ UIC_LINK_ACTIVE_STATE = 1, /* Link is in Fast/Slow/Sleep state */ UIC_LINK_HIBERN8_STATE = 2, /* Link is in Hibernate state */ + UIC_LINK_TRANS_ACTIVE_STATE = 3, + UIC_LINK_TRANS_HIBERN8_STATE = 4, }; #define ufshcd_is_link_off(hba) ((hba)->uic_link_state == UIC_LINK_OFF_STATE) @@ -127,7 +129,10 @@ enum uic_link_state { UIC_LINK_ACTIVE_STATE) #define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \ UIC_LINK_HIBERN8_STATE) - +#define ufshcd_set_link_trans_active(hba) ((hba)->uic_link_state = \ + UIC_LINK_TRANS_ACTIVE_STATE) +#define ufshcd_set_link_trans_hibern8(hba) ((hba)->uic_link_state = \ + UIC_LINK_TRANS_HIBERN8_STATE) /* * UFS Power management levels. * Each level is in increasing order of power savings. @@ -725,6 +730,10 @@ static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba) return true; #endif } +static inline bool ufshcd_can_fake_clkgating(struct ufs_hba *hba) +{ + return hba->caps & UFSHCD_CAP_FAKE_CLK_GATING; +} #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) -- 2.20.1