]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: ufs: host: mediatek: Support clock scaling with Vcore binding
authorPeter Wang <peter.wang@mediatek.com>
Tue, 22 Jul 2025 03:07:23 +0000 (11:07 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 25 Jul 2025 02:20:10 +0000 (22:20 -0400)
Add support for clock scaling with Vcore binding:

 1. Parse the DTS setting for Vcore voltage.

 2. Set the Vcore voltage to the DTS-specified value before scaling up.

 3. Reset the Vcore voltage to the default setting after scaling down.

These changes ensure that the Vcore voltage is appropriately managed
during clock scaling operations to maintain system stability and
performance.

Signed-off-by: Peter Wang <peter.wang@mediatek.com>
Link: https://lore.kernel.org/r/20250722030841.1998783-9-peter.wang@mediatek.com
Reviewed-by: Chun-Hung Wu <chun-hung.wu@mediatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/ufs/host/ufs-mediatek.c
drivers/ufs/host/ufs-mediatek.h

index 0b3cce8d97876d875ac62ca8d7be5aa664fe869c..a0c53d796a60267ad85175d0d471adbd8ef41208 100644 (file)
@@ -933,6 +933,9 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba)
        struct ufs_mtk_host *host = ufshcd_get_variant(hba);
        struct list_head *head = &hba->clk_list_head;
        struct ufs_clk_info *clki, *clki_tmp;
+       struct device *dev = hba->dev;
+       struct regulator *reg;
+       u32 volt;
 
        /*
         * Find private clocks and store them in struct ufs_mtk_clk.
@@ -958,6 +961,35 @@ static void ufs_mtk_init_clocks(struct ufs_hba *hba)
                dev_info(hba->dev,
                         "%s: Clk-scaling not ready. Feature disabled.",
                         __func__);
+               return;
+       }
+
+       /*
+        * Default get vcore if dts have these settings.
+        * No matter clock scaling support or not. (may disable by customer)
+        */
+       reg = devm_regulator_get_optional(dev, "dvfsrc-vcore");
+       if (IS_ERR(reg)) {
+               dev_info(dev, "failed to get dvfsrc-vcore: %ld",
+                        PTR_ERR(reg));
+               return;
+       }
+
+       if (of_property_read_u32(dev->of_node, "clk-scale-up-vcore-min",
+                                &volt)) {
+               dev_info(dev, "failed to get clk-scale-up-vcore-min");
+               return;
+       }
+
+       host->mclk.reg_vcore = reg;
+       host->mclk.vcore_volt = volt;
+
+       /* If default boot is max gear, request vcore */
+       if (reg && volt && host->clk_scale_up) {
+               if (regulator_set_voltage(reg, volt, INT_MAX)) {
+                       dev_info(hba->dev,
+                               "Failed to set vcore to %d\n", volt);
+               }
        }
 }
 
@@ -1126,6 +1158,7 @@ static int ufs_mtk_init(struct ufs_hba *hba)
 
        /* Enable clk scaling*/
        hba->caps |= UFSHCD_CAP_CLK_SCALING;
+       host->clk_scale_up = true; /* default is max freq */
 
        /* Set runtime pm delay to replace default */
        shost->rpm_autosuspend_delay = MTK_RPM_AUTOSUSPEND_DELAY_MS;
@@ -1720,24 +1753,25 @@ static void ufs_mtk_config_scaling_param(struct ufs_hba *hba,
        hba->vps->ondemand_data.downdifferential = 20;
 }
 
-/**
- * ufs_mtk_clk_scale - Internal clk scaling operation
- *
- * MTK platform supports clk scaling by switching parent of ufs_sel(mux).
- * The ufs_sel downstream to ufs_ck which feeds directly to UFS hardware.
- * Max and min clocks rate of ufs_sel defined in dts should match rate of
- * "ufs_sel_max_src" and "ufs_sel_min_src" respectively.
- * This prevent changing rate of pll clock that is shared between modules.
- *
- * @hba: per adapter instance
- * @scale_up: True for scaling up and false for scaling down
- */
-static void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up)
+static void _ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up)
 {
        struct ufs_mtk_host *host = ufshcd_get_variant(hba);
        struct ufs_mtk_clk *mclk = &host->mclk;
        struct ufs_clk_info *clki = mclk->ufs_sel_clki;
-       int ret = 0;
+       struct regulator *reg;
+       int volt, ret = 0;
+       bool clk_bind_vcore = false;
+
+       if (!hba->clk_scaling.is_initialized)
+               return;
+
+       if (!clki)
+               return;
+
+       reg = host->mclk.reg_vcore;
+       volt = host->mclk.vcore_volt;
+       if (reg && volt != 0)
+               clk_bind_vcore = true;
 
        ret = clk_prepare_enable(clki->clk);
        if (ret) {
@@ -1747,20 +1781,75 @@ static void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up)
        }
 
        if (scale_up) {
+               if (clk_bind_vcore) {
+                       ret = regulator_set_voltage(reg, volt, INT_MAX);
+                       if (ret) {
+                               dev_info(hba->dev,
+                                       "Failed to set vcore to %d\n", volt);
+                               goto out;
+                       }
+               }
+
                ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk);
-               clki->curr_freq = clki->max_freq;
+               if (ret) {
+                       dev_info(hba->dev, "Failed to set clk mux, ret = %d\n",
+                               ret);
+               }
        } else {
                ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk);
-               clki->curr_freq = clki->min_freq;
-       }
+               if (ret) {
+                       dev_info(hba->dev, "Failed to set clk mux, ret = %d\n",
+                               ret);
+                       goto out;
+               }
 
-       if (ret) {
-               dev_info(hba->dev,
-                        "Failed to set ufs_sel_clki, ret: %d\n", ret);
+               if (clk_bind_vcore) {
+                       ret = regulator_set_voltage(reg, 0, INT_MAX);
+                       if (ret) {
+                               dev_info(hba->dev,
+                                       "failed to set vcore to MIN\n");
+                       }
+               }
        }
 
+out:
        clk_disable_unprepare(clki->clk);
+}
+
+/**
+ * ufs_mtk_clk_scale - Internal clk scaling operation
+ *
+ * MTK platform supports clk scaling by switching parent of ufs_sel(mux).
+ * The ufs_sel downstream to ufs_ck which feeds directly to UFS hardware.
+ * Max and min clocks rate of ufs_sel defined in dts should match rate of
+ * "ufs_sel_max_src" and "ufs_sel_min_src" respectively.
+ * This prevent changing rate of pll clock that is shared between modules.
+ *
+ * @hba: per adapter instance
+ * @scale_up: True for scaling up and false for scaling down
+ */
+static void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up)
+{
+       struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+       struct ufs_mtk_clk *mclk = &host->mclk;
+       struct ufs_clk_info *clki = mclk->ufs_sel_clki;
+
+       if (host->clk_scale_up == scale_up)
+               goto out;
+
+       if (scale_up)
+               _ufs_mtk_clk_scale(hba, true);
+       else
+               _ufs_mtk_clk_scale(hba, false);
 
+       host->clk_scale_up = scale_up;
+
+       /* Must always set before clk_set_rate() */
+       if (scale_up)
+               clki->curr_freq = clki->max_freq;
+       else
+               clki->curr_freq = clki->min_freq;
+out:
        trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk));
 }
 
index fd229514384e802373a7f23966e3e560ef851790..0b25ce5aa8368c09c28dfefbddca3304ff5aa37f 100644 (file)
@@ -149,6 +149,8 @@ struct ufs_mtk_clk {
        struct ufs_clk_info *ufs_sel_clki; /* Mux */
        struct ufs_clk_info *ufs_sel_max_clki; /* Max src */
        struct ufs_clk_info *ufs_sel_min_clki; /* Min src */
+       struct regulator *reg_vcore;
+       int vcore_volt;
 };
 
 struct ufs_mtk_hw_ver {
@@ -178,6 +180,7 @@ struct ufs_mtk_host {
        bool mphy_powered_on;
        bool unipro_lpm;
        bool ref_clk_enabled;
+       bool clk_scale_up;
        u16 ref_clk_ungating_wait_us;
        u16 ref_clk_gating_wait_us;
        u32 ip_ver;