scsi: ufs: host: mediatek: Support clock scaling with Vcore binding

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>
This commit is contained in:
Peter Wang 2025-07-22 11:07:23 +08:00 committed by Martin K. Petersen
parent ff40f31216
commit 31a20e9f7c
2 changed files with 112 additions and 20 deletions

View 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,6 +1753,69 @@ static void ufs_mtk_config_scaling_param(struct ufs_hba *hba,
hba->vps->ondemand_data.downdifferential = 20;
}
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;
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) {
dev_info(hba->dev,
"clk_prepare_enable() fail, ret: %d\n", ret);
return;
}
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);
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);
if (ret) {
dev_info(hba->dev, "Failed to set clk mux, ret = %d\n",
ret);
goto out;
}
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
*
@ -1737,30 +1833,23 @@ 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;
ret = clk_prepare_enable(clki->clk);
if (ret) {
dev_info(hba->dev,
"clk_prepare_enable() fail, ret: %d\n", ret);
return;
}
if (host->clk_scale_up == scale_up)
goto out;
if (scale_up) {
ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk);
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 {
ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk);
else
clki->curr_freq = clki->min_freq;
}
if (ret) {
dev_info(hba->dev,
"Failed to set ufs_sel_clki, ret: %d\n", ret);
}
clk_disable_unprepare(clki->clk);
out:
trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk));
}

View 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;