scsi: ufs: allow fake clock gating
authorhgchu <hg.chu@samsung.com>
Fri, 12 Jan 2018 02:26:28 +0000 (11:26 +0900)
committerJaeHun Jung <jh0801.jung@samsung.com>
Tue, 8 May 2018 08:20:19 +0000 (17:20 +0900)
Change-Id: Iff1f258be4ec2375e32bbea85f0fb49d97c87d18
Signed-off-by: hgchu <hg.chu@samsung.com>
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h

index 515ee921002d163fcbb80730509562566487aff7..04c0e869f9203e9a460c85b9b76bf4989e10ecc0 100644 (file)
@@ -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);
                /*
index b2669823fa0c602722cb437bcaef336ec217739d..abe402788a4360972c7d66b213029d30be680a31 100644 (file)
@@ -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))