]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/scsi/ufs/ufshcd.c
scsi: remove scsi_show_result()
[mirror_ubuntu-artful-kernel.git] / drivers / scsi / ufs / ufshcd.c
index 6f1ea5192db66291f1101afa7365478d1f6ea3a9..9da319130da5837ce47ac9b6bda93c5534252bfa 100644 (file)
@@ -38,6 +38,7 @@
  */
 
 #include <linux/async.h>
+#include <linux/devfreq.h>
 
 #include "ufshcd.h"
 #include "unipro.h"
@@ -545,6 +546,8 @@ 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);
        scsi_unblock_requests(hba->host);
 }
 
@@ -561,10 +564,10 @@ int ufshcd_hold(struct ufs_hba *hba, bool async)
 
        if (!ufshcd_is_clkgating_allowed(hba))
                goto out;
-start:
        spin_lock_irqsave(hba->host->host_lock, flags);
        hba->clk_gating.active_reqs++;
 
+start:
        switch (hba->clk_gating.state) {
        case CLKS_ON:
                break;
@@ -596,6 +599,7 @@ start:
                spin_unlock_irqrestore(hba->host->host_lock, flags);
                flush_work(&hba->clk_gating.ungate_work);
                /* Make sure state is CLKS_ON before returning */
+               spin_lock_irqsave(hba->host->host_lock, flags);
                goto start;
        default:
                dev_err(hba->dev, "%s: clk gating is in invalid state %d\n",
@@ -636,6 +640,11 @@ 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;
+       }
+
        if (!ufshcd_is_link_active(hba))
                ufshcd_setup_clocks(hba, false);
        else
@@ -737,6 +746,32 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
        device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
 }
 
+/* Must be called with host lock acquired */
+static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
+{
+       if (!ufshcd_is_clkscaling_enabled(hba))
+               return;
+
+       if (!hba->clk_scaling.is_busy_started) {
+               hba->clk_scaling.busy_start_t = ktime_get();
+               hba->clk_scaling.is_busy_started = true;
+       }
+}
+
+static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
+{
+       struct ufs_clk_scaling *scaling = &hba->clk_scaling;
+
+       if (!ufshcd_is_clkscaling_enabled(hba))
+               return;
+
+       if (!hba->outstanding_reqs && scaling->is_busy_started) {
+               scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(),
+                                       scaling->busy_start_t));
+               scaling->busy_start_t = ktime_set(0, 0);
+               scaling->is_busy_started = false;
+       }
+}
 /**
  * ufshcd_send_command - Send SCSI or device management commands
  * @hba: per adapter instance
@@ -745,6 +780,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
 static inline
 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);
 }
@@ -3027,6 +3063,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
        /* clear corresponding bits of completed commands */
        hba->outstanding_reqs ^= completed_reqs;
 
+       ufshcd_clk_scaling_update_busy(hba);
+
        /* we might have free'd some tags above */
        wake_up(&hba->dev_cmd.tag_wq);
 }
@@ -4151,6 +4189,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
        if (!hba->is_init_prefetch)
                hba->is_init_prefetch = true;
 
+       /* Resume devfreq after UFS device is detected */
+       if (ufshcd_is_clkscaling_enabled(hba))
+               devfreq_resume_device(hba->devfreq);
+
 out:
        /*
         * If we failed to initialize the device or the device is not
@@ -4472,6 +4514,7 @@ static int ufshcd_init_clocks(struct ufs_hba *hba)
                                        clki->max_freq, ret);
                                goto out;
                        }
+                       clki->curr_freq = clki->max_freq;
                }
                dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
                                clki->name, clk_get_rate(clki->clk));
@@ -4664,11 +4707,11 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
                                     START_STOP_TIMEOUT, 0, NULL, REQ_PM);
        if (ret) {
                sdev_printk(KERN_WARNING, sdp,
-                         "START_STOP failed for power mode: %d\n", pwr_mode);
-               scsi_show_result(ret);
+                           "START_STOP failed for power mode: %d, result %x\n",
+                           pwr_mode, ret);
                if (driver_byte(ret) & DRIVER_SENSE) {
-                       scsi_show_sense_hdr(&sshdr);
-                       scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
+                       scsi_show_sense_hdr(sdp, NULL, &sshdr);
+                       scsi_show_extd_sense(sdp, NULL, sshdr.asc, sshdr.ascq);
                }
        }
 
@@ -4841,13 +4884,19 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
        }
 
        if (ufshcd_is_runtime_pm(pm_op)) {
-               /*
-                * The device is idle with no requests in the queue,
-                * allow background operations if needed.
-                */
-               ret = ufshcd_bkops_ctrl(hba, BKOPS_STATUS_NON_CRITICAL);
-               if (ret)
-                       goto enable_gating;
+               if (ufshcd_can_autobkops_during_suspend(hba)) {
+                       /*
+                        * The device is idle with no requests in the queue,
+                        * allow background operations if bkops status shows
+                        * that performance might be impacted.
+                        */
+                       ret = ufshcd_urgent_bkops(hba);
+                       if (ret)
+                               goto enable_gating;
+               } else {
+                       /* make sure that auto bkops is disabled */
+                       ufshcd_disable_auto_bkops(hba);
+               }
        }
 
        if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) &&
@@ -4867,6 +4916,15 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
        ufshcd_vreg_set_lpm(hba);
 
 disable_clks:
+       /*
+        * The clock scaling needs access to controller registers. Hence, Wait
+        * 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;
+       }
        /*
         * Call vendor specific suspend callback. As these callbacks may access
         * vendor specific host controller register space call them before the
@@ -4986,9 +5044,16 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
                        goto set_old_link_state;
        }
 
-       ufshcd_disable_auto_bkops(hba);
+       /*
+        * 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 (ufshcd_is_clkscaling_enabled(hba))
+               devfreq_resume_device(hba->devfreq);
+
        /* Schedule clock gating in case of no access to UFS device yet */
        ufshcd_release(hba);
        goto out;
@@ -5049,6 +5114,8 @@ int ufshcd_system_suspend(struct ufs_hba *hba)
 
        ret = ufshcd_suspend(hba, UFS_SYSTEM_PM);
 out:
+       if (!ret)
+               hba->is_sys_suspended = true;
        return ret;
 }
 EXPORT_SYMBOL(ufshcd_system_suspend);
@@ -5172,6 +5239,8 @@ void ufshcd_remove(struct ufs_hba *hba)
        scsi_host_put(hba->host);
 
        ufshcd_exit_clk_gating(hba);
+       if (ufshcd_is_clkscaling_enabled(hba))
+               devfreq_remove_device(hba->devfreq);
        ufshcd_hba_exit(hba);
 }
 EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -5228,6 +5297,112 @@ out_error:
 }
 EXPORT_SYMBOL(ufshcd_alloc_host);
 
+static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
+{
+       int ret = 0;
+       struct ufs_clk_info *clki;
+       struct list_head *head = &hba->clk_list_head;
+
+       if (!head || list_empty(head))
+               goto out;
+
+       list_for_each_entry(clki, head, list) {
+               if (!IS_ERR_OR_NULL(clki->clk)) {
+                       if (scale_up && clki->max_freq) {
+                               if (clki->curr_freq == clki->max_freq)
+                                       continue;
+                               ret = clk_set_rate(clki->clk, clki->max_freq);
+                               if (ret) {
+                                       dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
+                                               __func__, clki->name,
+                                               clki->max_freq, ret);
+                                       break;
+                               }
+                               clki->curr_freq = clki->max_freq;
+
+                       } else if (!scale_up && clki->min_freq) {
+                               if (clki->curr_freq == clki->min_freq)
+                                       continue;
+                               ret = clk_set_rate(clki->clk, clki->min_freq);
+                               if (ret) {
+                                       dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
+                                               __func__, clki->name,
+                                               clki->min_freq, ret);
+                                       break;
+                               }
+                               clki->curr_freq = clki->min_freq;
+                       }
+               }
+               dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__,
+                               clki->name, clk_get_rate(clki->clk));
+       }
+       if (hba->vops->clk_scale_notify)
+               hba->vops->clk_scale_notify(hba);
+out:
+       return ret;
+}
+
+static int ufshcd_devfreq_target(struct device *dev,
+                               unsigned long *freq, u32 flags)
+{
+       int err = 0;
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+
+       if (!ufshcd_is_clkscaling_enabled(hba))
+               return -EINVAL;
+
+       if (*freq == UINT_MAX)
+               err = ufshcd_scale_clks(hba, true);
+       else if (*freq == 0)
+               err = ufshcd_scale_clks(hba, false);
+
+       return err;
+}
+
+static int ufshcd_devfreq_get_dev_status(struct device *dev,
+               struct devfreq_dev_status *stat)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct ufs_clk_scaling *scaling = &hba->clk_scaling;
+       unsigned long flags;
+
+       if (!ufshcd_is_clkscaling_enabled(hba))
+               return -EINVAL;
+
+       memset(stat, 0, sizeof(*stat));
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       if (!scaling->window_start_t)
+               goto start_window;
+
+       if (scaling->is_busy_started)
+               scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(),
+                                       scaling->busy_start_t));
+
+       stat->total_time = jiffies_to_usecs((long)jiffies -
+                               (long)scaling->window_start_t);
+       stat->busy_time = scaling->tot_busy_t;
+start_window:
+       scaling->window_start_t = jiffies;
+       scaling->tot_busy_t = 0;
+
+       if (hba->outstanding_reqs) {
+               scaling->busy_start_t = ktime_get();
+               scaling->is_busy_started = true;
+       } else {
+               scaling->busy_start_t = ktime_set(0, 0);
+               scaling->is_busy_started = false;
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+       return 0;
+}
+
+static struct devfreq_dev_profile ufs_devfreq_profile = {
+       .polling_ms     = 100,
+       .target         = ufshcd_devfreq_target,
+       .get_dev_status = ufshcd_devfreq_get_dev_status,
+};
+
 /**
  * ufshcd_init - Driver initialization routine
  * @hba: per-adapter instance
@@ -5337,6 +5512,19 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
                goto out_remove_scsi_host;
        }
 
+       if (ufshcd_is_clkscaling_enabled(hba)) {
+               hba->devfreq = devfreq_add_device(dev, &ufs_devfreq_profile,
+                                                  "simple_ondemand", NULL);
+               if (IS_ERR(hba->devfreq)) {
+                       dev_err(hba->dev, "Unable to register with devfreq %ld\n",
+                                       PTR_ERR(hba->devfreq));
+                       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;
+       }
+
        /* Hold auto suspend until async scan completes */
        pm_runtime_get_sync(dev);