static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
 static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
 static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
+static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba);
 static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
 static irqreturn_t ufshcd_intr(int irq, void *__hba);
 static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
 {
        int ret = 0;
 
+       /* let's not get into low power until clock scaling is completed */
+       ufshcd_hold(hba, false);
+
        ret = ufshcd_clock_scaling_prepare(hba);
        if (ret)
                return ret;
 
 out:
        ufshcd_clock_scaling_unprepare(hba);
+       ufshcd_release(hba);
        return ret;
 }
 
+static void ufshcd_clk_scaling_suspend_work(struct work_struct *work)
+{
+       struct ufs_hba *hba = container_of(work, struct ufs_hba,
+                                          clk_scaling.suspend_work);
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(hba->host->host_lock, irq_flags);
+       if (hba->clk_scaling.active_reqs || hba->clk_scaling.is_suspended) {
+               spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+               return;
+       }
+       hba->clk_scaling.is_suspended = true;
+       spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+
+       __ufshcd_suspend_clkscaling(hba);
+}
+
+static void ufshcd_clk_scaling_resume_work(struct work_struct *work)
+{
+       struct ufs_hba *hba = container_of(work, struct ufs_hba,
+                                          clk_scaling.resume_work);
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(hba->host->host_lock, irq_flags);
+       if (!hba->clk_scaling.is_suspended) {
+               spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+               return;
+       }
+       hba->clk_scaling.is_suspended = false;
+       spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+
+       devfreq_resume_device(hba->devfreq);
+}
+
 static int ufshcd_devfreq_target(struct device *dev,
                                unsigned long *freq, u32 flags)
 {
        int ret = 0;
        struct ufs_hba *hba = dev_get_drvdata(dev);
        ktime_t start;
-       bool scale_up, release_clk_hold = false;
+       bool scale_up, sched_clk_scaling_suspend_work = false;
        unsigned long irq_flags;
 
        if (!ufshcd_is_clkscaling_supported(hba))
                return -EINVAL;
        }
 
-       scale_up = (*freq == UINT_MAX) ? true : false;
-       if (!ufshcd_is_devfreq_scaling_required(hba, scale_up))
-               return 0; /* no state change required */
-
        spin_lock_irqsave(hba->host->host_lock, irq_flags);
        if (ufshcd_eh_in_progress(hba)) {
                spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
                return 0;
        }
 
-       if (ufshcd_is_clkgating_allowed(hba)) {
-               if (cancel_delayed_work(&hba->clk_gating.gate_work) ||
-                   (hba->clk_gating.state == CLKS_ON)) {
-                       /* hold the vote until the scaling work is completed */
-                       hba->clk_gating.active_reqs++;
-                       release_clk_hold = true;
-                       if (hba->clk_gating.state != CLKS_ON) {
-                               hba->clk_gating.state = CLKS_ON;
-                               trace_ufshcd_clk_gating(dev_name(hba->dev),
-                                               hba->clk_gating.state);
-                       }
-               } else {
-                       /*
-                        * Clock gating work seems to be running in parallel
-                        * hence skip scaling work to avoid deadlock between
-                        * current scaling work and gating work.
-                        */
-                       spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
-                       return 0;
-               }
+       if (!hba->clk_scaling.active_reqs)
+               sched_clk_scaling_suspend_work = true;
+
+       scale_up = (*freq == UINT_MAX) ? true : false;
+       if (!ufshcd_is_devfreq_scaling_required(hba, scale_up)) {
+               spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
+               ret = 0;
+               goto out; /* no state change required */
        }
        spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
 
        start = ktime_get();
-
        ret = ufshcd_devfreq_scale(hba, scale_up);
 
-       if (release_clk_hold)
-               ufshcd_release(hba);
-
        trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
                (scale_up ? "up" : "down"),
                ktime_to_us(ktime_sub(ktime_get(), start)), ret);
 
+out:
+       if (sched_clk_scaling_suspend_work)
+               queue_work(hba->clk_scaling.workq,
+                          &hba->clk_scaling.suspend_work);
+
        return ret;
 }
 
        .get_dev_status = ufshcd_devfreq_get_dev_status,
 };
 
+static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba)
+{
+       unsigned long flags;
+
+       devfreq_suspend_device(hba->devfreq);
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       hba->clk_scaling.window_start_t = 0;
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
 
 static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
 {
+       unsigned long flags;
+       bool suspend = false;
+
        if (!ufshcd_is_clkscaling_supported(hba))
                return;
 
-       devfreq_suspend_device(hba->devfreq);
-       hba->clk_scaling.window_start_t = 0;
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       if (!hba->clk_scaling.is_suspended) {
+               suspend = true;
+               hba->clk_scaling.is_suspended = true;
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       if (suspend)
+               __ufshcd_suspend_clkscaling(hba);
 }
 
 static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
 {
-       devfreq_resume_device(hba->devfreq);
+       unsigned long flags;
+       bool resume = false;
+
+       if (!ufshcd_is_clkscaling_supported(hba))
+               return;
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       if (hba->clk_scaling.is_suspended) {
+               resume = true;
+               hba->clk_scaling.is_suspended = false;
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       if (resume)
+               devfreq_resume_device(hba->devfreq);
 }
 
 static ssize_t ufshcd_clkscale_enable_show(struct device *dev,
        pm_runtime_get_sync(hba->dev);
        ufshcd_hold(hba, false);
 
+       cancel_work_sync(&hba->clk_scaling.suspend_work);
+       cancel_work_sync(&hba->clk_scaling.resume_work);
+
+       hba->clk_scaling.is_allowed = value;
+
        if (value) {
                ufshcd_resume_clkscaling(hba);
        } else {
                        dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
                                        __func__, err);
        }
-       hba->clk_scaling.is_allowed = value;
 
        ufshcd_release(hba);
        pm_runtime_put_sync(hba->dev);
                hba->clk_gating.is_suspended = false;
        }
 unblock_reqs:
-       if (hba->clk_scaling.is_allowed)
-               ufshcd_resume_clkscaling(hba);
        scsi_unblock_requests(hba->host);
 }
 
                ufshcd_set_link_hibern8(hba);
        }
 
-       ufshcd_suspend_clkscaling(hba);
-
        if (!ufshcd_is_link_active(hba))
                ufshcd_setup_clocks(hba, false);
        else
 /* Must be called with host lock acquired */
 static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
 {
+       bool queue_resume_work = false;
+
        if (!ufshcd_is_clkscaling_supported(hba))
                return;
 
+       if (!hba->clk_scaling.active_reqs++)
+               queue_resume_work = true;
+
+       if (!hba->clk_scaling.is_allowed || hba->pm_op_in_progress)
+               return;
+
+       if (queue_resume_work)
+               queue_work(hba->clk_scaling.workq,
+                          &hba->clk_scaling.resume_work);
+
+       if (!hba->clk_scaling.window_start_t) {
+               hba->clk_scaling.window_start_t = jiffies;
+               hba->clk_scaling.tot_busy_t = 0;
+               hba->clk_scaling.is_busy_started = false;
+       }
+
        if (!hba->clk_scaling.is_busy_started) {
                hba->clk_scaling.busy_start_t = ktime_get();
                hba->clk_scaling.is_busy_started = true;
                                complete(hba->dev_cmd.complete);
                        }
                }
+               if (ufshcd_is_clkscaling_supported(hba))
+                       hba->clk_scaling.active_reqs--;
+               if (ufshcd_is_clkscaling_supported(hba))
+                       hba->clk_scaling.active_reqs--;
        }
 
        /* clear corresponding bits of completed commands */
                ufshcd_variant_hba_exit(hba);
                ufshcd_setup_vreg(hba, false);
                ufshcd_suspend_clkscaling(hba);
+               if (ufshcd_is_clkscaling_supported(hba)) {
+                       ufshcd_suspend_clkscaling(hba);
+                       destroy_workqueue(hba->clk_scaling.workq);
+               }
                ufshcd_setup_clocks(hba, false);
                ufshcd_setup_hba_vreg(hba, false);
                hba->is_powered = false;
        ufshcd_hold(hba, false);
        hba->clk_gating.is_suspended = true;
 
-       ufshcd_suspend_clkscaling(hba);
+       if (hba->clk_scaling.is_allowed) {
+               cancel_work_sync(&hba->clk_scaling.suspend_work);
+               cancel_work_sync(&hba->clk_scaling.resume_work);
+               ufshcd_suspend_clkscaling(hba);
+       }
 
        if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE &&
                        req_link_state == UIC_LINK_ACTIVE_STATE) {
        goto out;
 
 set_link_active:
-       ufshcd_resume_clkscaling(hba);
+       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);
        if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
                ufshcd_disable_auto_bkops(hba);
 enable_gating:
-       ufshcd_resume_clkscaling(hba);
+       if (hba->clk_scaling.is_allowed)
+               ufshcd_resume_clkscaling(hba);
        hba->clk_gating.is_suspended = false;
        ufshcd_release(hba);
 out:
        ufshcd_vreg_set_lpm(hba);
 disable_irq_and_vops_clks:
        ufshcd_disable_irq(hba);
-       ufshcd_suspend_clkscaling(hba);
+       if (hba->clk_scaling.is_allowed)
+               ufshcd_suspend_clkscaling(hba);
        ufshcd_setup_clocks(hba, false);
 out:
        hba->pm_op_in_progress = 0;
        }
 
        if (ufshcd_is_clkscaling_supported(hba)) {
+               char wq_name[sizeof("ufs_clkscaling_00")];
+
                hba->devfreq = devm_devfreq_add_device(dev, &ufs_devfreq_profile,
                                                   "simple_ondemand", NULL);
                if (IS_ERR(hba->devfreq)) {
                        err = PTR_ERR(hba->devfreq);
                        goto out_remove_scsi_host;
                }
+               hba->clk_scaling.is_suspended = false;
+
+               INIT_WORK(&hba->clk_scaling.suspend_work,
+                         ufshcd_clk_scaling_suspend_work);
+               INIT_WORK(&hba->clk_scaling.resume_work,
+                         ufshcd_clk_scaling_resume_work);
+
+               snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clkscaling_%d",
+                        host->host_no);
+               hba->clk_scaling.workq = create_singlethread_workqueue(wq_name);
+
                /* Suspend devfreq until the UFS device is detected */
                ufshcd_suspend_clkscaling(hba);
                ufshcd_clkscaling_init_sysfs(hba);