]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
scsi: ufs: core: Enable multi-level gear scaling
authorCan Guo <quic_cang@quicinc.com>
Thu, 13 Feb 2025 08:00:05 +0000 (16:00 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 21 Feb 2025 02:53:49 +0000 (21:53 -0500)
With OPP V2 enabled, devfreq can scale clocks amongst multiple frequency
plans. However, the gear speed is only toggled between min and max during
clock scaling. Enable multi-level gear scaling by mapping clock frequencies
to gear speeds, so that when devfreq scales clock frequencies we can put
the UFS link at the appropriate gear speeds accordingly.

Signed-off-by: Can Guo <quic_cang@quicinc.com>
Co-developed-by: Ziqi Chen <quic_ziqichen@quicinc.com>
Signed-off-by: Ziqi Chen <quic_ziqichen@quicinc.com>
Link: https://lore.kernel.org/r/20250213080008.2984807-6-quic_ziqichen@quicinc.com
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org>
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/ufs/core/ufshcd.c

index 329cae156f68e15a6a6bb98c8199794dc29b98c6..637901d09054bc481fde5ff0f75e7de417a96482 100644 (file)
@@ -1313,16 +1313,26 @@ out:
 /**
  * ufshcd_scale_gear - scale up/down UFS gear
  * @hba: per adapter instance
+ * @target_gear: target gear to scale to
  * @scale_up: True for scaling up gear and false for scaling down
  *
  * Return: 0 for success; -EBUSY if scaling can't happen at this time;
  * non-zero for any other errors.
  */
-static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
+static int ufshcd_scale_gear(struct ufs_hba *hba, u32 target_gear, bool scale_up)
 {
        int ret = 0;
        struct ufs_pa_layer_attr new_pwr_info;
 
+       if (target_gear) {
+               new_pwr_info = hba->pwr_info;
+               new_pwr_info.gear_tx = target_gear;
+               new_pwr_info.gear_rx = target_gear;
+
+               goto config_pwr_mode;
+       }
+
+       /* Legacy gear scaling, in case vops_freq_to_gear_speed() is not implemented */
        if (scale_up) {
                memcpy(&new_pwr_info, &hba->clk_scaling.saved_pwr_info,
                       sizeof(struct ufs_pa_layer_attr));
@@ -1343,6 +1353,7 @@ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
                }
        }
 
+config_pwr_mode:
        /* check if the power mode needs to be changed or not? */
        ret = ufshcd_config_pwr_mode(hba, &new_pwr_info);
        if (ret)
@@ -1413,15 +1424,19 @@ static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err, bool sc
 static int ufshcd_devfreq_scale(struct ufs_hba *hba, unsigned long freq,
                                bool scale_up)
 {
+       u32 old_gear = hba->pwr_info.gear_rx;
+       u32 new_gear = 0;
        int ret = 0;
 
+       new_gear = ufshcd_vops_freq_to_gear_speed(hba, freq);
+
        ret = ufshcd_clock_scaling_prepare(hba, 1 * USEC_PER_SEC);
        if (ret)
                return ret;
 
        /* scale down the gear before scaling down clocks */
        if (!scale_up) {
-               ret = ufshcd_scale_gear(hba, false);
+               ret = ufshcd_scale_gear(hba, new_gear, false);
                if (ret)
                        goto out_unprepare;
        }
@@ -1429,13 +1444,13 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, unsigned long freq,
        ret = ufshcd_scale_clks(hba, freq, scale_up);
        if (ret) {
                if (!scale_up)
-                       ufshcd_scale_gear(hba, true);
+                       ufshcd_scale_gear(hba, old_gear, true);
                goto out_unprepare;
        }
 
        /* scale up the gear after scaling up clocks */
        if (scale_up) {
-               ret = ufshcd_scale_gear(hba, true);
+               ret = ufshcd_scale_gear(hba, new_gear, true);
                if (ret) {
                        ufshcd_scale_clks(hba, hba->devfreq->previous_freq,
                                          false);
@@ -1720,6 +1735,8 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct ufs_clk_info *clki;
+       unsigned long freq;
        u32 value;
        int err = 0;
 
@@ -1743,14 +1760,21 @@ static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
 
        if (value) {
                ufshcd_resume_clkscaling(hba);
-       } else {
-               ufshcd_suspend_clkscaling(hba);
-               err = ufshcd_devfreq_scale(hba, ULONG_MAX, true);
-               if (err)
-                       dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
-                                       __func__, err);
+               goto out_rel;
        }
 
+       clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info, list);
+       freq = clki->max_freq;
+
+       ufshcd_suspend_clkscaling(hba);
+       err = ufshcd_devfreq_scale(hba, freq, true);
+       if (err)
+               dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
+                               __func__, err);
+       else
+               hba->clk_scaling.target_freq = freq;
+
+out_rel:
        ufshcd_release(hba);
        ufshcd_rpm_put_sync(hba);
 out: