scsi: ufs: suspend clock scaling for failed runtime_resume
[linux-2.6-block.git] / drivers / scsi / ufs / ufshcd.c
index 05c745663c103a7ddb70d4e6845702a6c08ebd08..93e2fe82822fd16310c97e6ab0febd027be49b4c 100644 (file)
@@ -45,6 +45,8 @@
 #include "ufs_quirks.h"
 #include "unipro.h"
 
+#define UFSHCD_REQ_SENSE_SIZE  18
+
 #define UFSHCD_ENABLE_INTRS    (UTP_TRANSFER_REQ_COMPL |\
                                 UTP_TASK_REQ_COMPL |\
                                 UFSHCD_ERROR_MASK)
@@ -598,6 +600,20 @@ static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
                return false;
 }
 
+static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
+{
+       if (ufshcd_is_clkscaling_enabled(hba)) {
+               devfreq_suspend_device(hba->devfreq);
+               hba->clk_scaling.window_start_t = 0;
+       }
+}
+
+static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
+{
+       if (ufshcd_is_clkscaling_enabled(hba))
+               devfreq_resume_device(hba->devfreq);
+}
+
 static void ufshcd_ungate_work(struct work_struct *work)
 {
        int ret;
@@ -631,8 +647,7 @@ static void ufshcd_ungate_work(struct work_struct *work)
                hba->clk_gating.is_suspended = false;
        }
 unblock_reqs:
-       if (ufshcd_is_clkscaling_enabled(hba))
-               devfreq_resume_device(hba->devfreq);
+       ufshcd_resume_clkscaling(hba);
        scsi_unblock_requests(hba->host);
 }
 
@@ -731,10 +746,7 @@ static void ufshcd_gate_work(struct work_struct *work)
                ufshcd_set_link_hibern8(hba);
        }
 
-       if (ufshcd_is_clkscaling_enabled(hba)) {
-               devfreq_suspend_device(hba->devfreq);
-               hba->clk_scaling.window_start_t = 0;
-       }
+       ufshcd_suspend_clkscaling(hba);
 
        if (!ufshcd_is_link_active(hba))
                ufshcd_setup_clocks(hba, false);
@@ -878,6 +890,8 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
        ufshcd_clk_scaling_start_busy(hba);
        __set_bit(task_tag, &hba->outstanding_reqs);
        ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+       /* Make sure that doorbell is committed immediately */
+       wmb();
 }
 
 /**
@@ -889,10 +903,14 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
        int len;
        if (lrbp->sense_buffer &&
            ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
+               int len_to_copy;
+
                len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
+               len_to_copy = min_t(int, RESPONSE_UPIU_SENSE_DATA_LENGTH, len);
+
                memcpy(lrbp->sense_buffer,
                        lrbp->ucd_rsp_ptr->sr.sense_data,
-                       min_t(int, len, SCSI_SENSE_BUFFERSIZE));
+                       min_t(int, len_to_copy, UFSHCD_REQ_SENSE_SIZE));
        }
 }
 
@@ -1457,7 +1475,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
 
        WARN_ON(lrbp->cmd);
        lrbp->cmd = cmd;
-       lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
+       lrbp->sense_bufflen = UFSHCD_REQ_SENSE_SIZE;
        lrbp->sense_buffer = cmd->sense_buffer;
        lrbp->task_tag = tag;
        lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
@@ -1471,6 +1489,8 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
                clear_bit_unlock(tag, &hba->lrb_in_use);
                goto out;
        }
+       /* Make sure descriptors are ready before ringing the doorbell */
+       wmb();
 
        /* issue command to the controller */
        spin_lock_irqsave(hba->host->host_lock, flags);
@@ -1581,6 +1601,8 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
        time_left = wait_for_completion_timeout(hba->dev_cmd.complete,
                        msecs_to_jiffies(max_timeout));
 
+       /* Make sure descriptors are ready before ringing the doorbell */
+       wmb();
        spin_lock_irqsave(hba->host->host_lock, flags);
        hba->dev_cmd.complete = NULL;
        if (likely(time_left)) {
@@ -4318,6 +4340,8 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
        wmb();
 
        ufshcd_writel(hba, 1 << free_slot, REG_UTP_TASK_REQ_DOOR_BELL);
+       /* Make sure that doorbell is committed immediately */
+       wmb();
 
        spin_unlock_irqrestore(host->host_lock, flags);
 
@@ -5062,8 +5086,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
                hba->is_init_prefetch = true;
 
        /* Resume devfreq after UFS device is detected */
-       if (ufshcd_is_clkscaling_enabled(hba))
-               devfreq_resume_device(hba->devfreq);
+       ufshcd_resume_clkscaling(hba);
 
 out:
        /*
@@ -5389,6 +5412,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
        if (!head || list_empty(head))
                goto out;
 
+       ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE);
+       if (ret)
+               return ret;
+
        list_for_each_entry(clki, head, list) {
                if (!IS_ERR_OR_NULL(clki->clk)) {
                        if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
@@ -5410,7 +5437,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
                }
        }
 
-       ret = ufshcd_vops_setup_clocks(hba, on);
+       ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE);
+       if (ret)
+               return ret;
+
 out:
        if (ret) {
                list_for_each_entry(clki, head, list) {
@@ -5500,8 +5530,6 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba)
        if (!hba->vops)
                return;
 
-       ufshcd_vops_setup_clocks(hba, false);
-
        ufshcd_vops_setup_regulators(hba, false);
 
        ufshcd_vops_exit(hba);
@@ -5564,6 +5592,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba)
        if (hba->is_powered) {
                ufshcd_variant_hba_exit(hba);
                ufshcd_setup_vreg(hba, false);
+               ufshcd_suspend_clkscaling(hba);
                ufshcd_setup_clocks(hba, false);
                ufshcd_setup_hba_vreg(hba, false);
                hba->is_powered = false;
@@ -5577,19 +5606,19 @@ ufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp)
                                0,
                                0,
                                0,
-                               SCSI_SENSE_BUFFERSIZE,
+                               UFSHCD_REQ_SENSE_SIZE,
                                0};
        char *buffer;
        int ret;
 
-       buffer = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
+       buffer = kzalloc(UFSHCD_REQ_SENSE_SIZE, GFP_KERNEL);
        if (!buffer) {
                ret = -ENOMEM;
                goto out;
        }
 
        ret = scsi_execute_req_flags(sdp, cmd, DMA_FROM_DEVICE, buffer,
-                               SCSI_SENSE_BUFFERSIZE, NULL,
+                               UFSHCD_REQ_SENSE_SIZE, NULL,
                                msecs_to_jiffies(1000), 3, NULL, REQ_PM);
        if (ret)
                pr_err("%s: failed with err %d\n", __func__, ret);
@@ -5892,10 +5921,8 @@ disable_clks:
         * for pending clock scaling work to be done before clocks are
         * turned off.
         */
-       if (ufshcd_is_clkscaling_enabled(hba)) {
-               devfreq_suspend_device(hba->devfreq);
-               hba->clk_scaling.window_start_t = 0;
-       }
+       ufshcd_suspend_clkscaling(hba);
+
        /*
         * Call vendor specific suspend callback. As these callbacks may access
         * vendor specific host controller register space call them before the
@@ -5905,10 +5932,6 @@ disable_clks:
        if (ret)
                goto set_link_active;
 
-       ret = ufshcd_vops_setup_clocks(hba, false);
-       if (ret)
-               goto vops_resume;
-
        if (!ufshcd_is_link_active(hba))
                ufshcd_setup_clocks(hba, false);
        else
@@ -5925,9 +5948,8 @@ disable_clks:
        ufshcd_hba_vreg_set_lpm(hba);
        goto out;
 
-vops_resume:
-       ufshcd_vops_resume(hba, pm_op);
 set_link_active:
+       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);
@@ -6015,8 +6037,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
        ufshcd_urgent_bkops(hba);
        hba->clk_gating.is_suspended = false;
 
-       if (ufshcd_is_clkscaling_enabled(hba))
-               devfreq_resume_device(hba->devfreq);
+       ufshcd_resume_clkscaling(hba);
 
        /* Schedule clock gating in case of no access to UFS device yet */
        ufshcd_release(hba);
@@ -6030,6 +6051,7 @@ disable_vreg:
        ufshcd_vreg_set_lpm(hba);
 disable_irq_and_vops_clks:
        ufshcd_disable_irq(hba);
+       ufshcd_suspend_clkscaling(hba);
        ufshcd_setup_clocks(hba, false);
 out:
        hba->pm_op_in_progress = 0;
@@ -6092,7 +6114,10 @@ EXPORT_SYMBOL(ufshcd_system_suspend);
 
 int ufshcd_system_resume(struct ufs_hba *hba)
 {
-       if (!hba || !hba->is_powered || pm_runtime_suspended(hba->dev))
+       if (!hba)
+               return -EINVAL;
+
+       if (!hba->is_powered || pm_runtime_suspended(hba->dev))
                /*
                 * Let the runtime resume take care of resuming
                 * if runtime suspended.
@@ -6113,7 +6138,10 @@ EXPORT_SYMBOL(ufshcd_system_resume);
  */
 int ufshcd_runtime_suspend(struct ufs_hba *hba)
 {
-       if (!hba || !hba->is_powered)
+       if (!hba)
+               return -EINVAL;
+
+       if (!hba->is_powered)
                return 0;
 
        return ufshcd_suspend(hba, UFS_RUNTIME_PM);
@@ -6143,10 +6171,13 @@ EXPORT_SYMBOL(ufshcd_runtime_suspend);
  */
 int ufshcd_runtime_resume(struct ufs_hba *hba)
 {
-       if (!hba || !hba->is_powered)
+       if (!hba)
+               return -EINVAL;
+
+       if (!hba->is_powered)
                return 0;
-       else
-               return ufshcd_resume(hba, UFS_RUNTIME_PM);
+
+       return ufshcd_resume(hba, UFS_RUNTIME_PM);
 }
 EXPORT_SYMBOL(ufshcd_runtime_resume);
 
@@ -6507,8 +6538,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
                        goto out_remove_scsi_host;
                }
                /* Suspend devfreq until the UFS device is detected */
-               devfreq_suspend_device(hba->devfreq);
-               hba->clk_scaling.window_start_t = 0;
+               ufshcd_suspend_clkscaling(hba);
        }
 
        /* Hold auto suspend until async scan completes */