scsi: ufs: ufs-mediatek: Support clk-scaling to optimize power consumption
Provide clk-scaling feature in MediaTek UFS platforms. MediaTek platform supports clk-scaling by switching parent clock mux of UFSHCI main clocks: ufs_sel. The driver needs to prevent changing the rate of ufs_sel because its parent PLL clock may be shared between multiple IPs. In order to achieve this goal, the maximum and minimum clock rates of ufs_sel defined in dts should match the rate of "ufs_sel_max_src" and "ufs_sel_min_src" respectively. Link: https://lore.kernel.org/r/20220802235437.4547-6-stanley.chu@mediatek.com Reviewed-by: Stanley Chu <stanley.chu@mediatek.com> Signed-off-by: Po-Wen Kao <powen.kao@mediatek.com> Signed-off-by: Stanley Chu <stanley.chu@mediatek.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Родитель
2873e0453b
Коммит
b7dbc686f6
|
@ -24,9 +24,32 @@ TRACE_EVENT(ufs_mtk_event,
|
||||||
__entry->data = data;
|
__entry->data = data;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("ufs:event=%u data=%u",
|
TP_printk("ufs: event=%u data=%u",
|
||||||
__entry->type, __entry->data)
|
__entry->type, __entry->data)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(ufs_mtk_clk_scale,
|
||||||
|
TP_PROTO(const char *name, bool scale_up, unsigned long clk_rate),
|
||||||
|
TP_ARGS(name, scale_up, clk_rate),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(const char*, name)
|
||||||
|
__field(bool, scale_up)
|
||||||
|
__field(unsigned long, clk_rate)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->name = name;
|
||||||
|
__entry->scale_up = scale_up;
|
||||||
|
__entry->clk_rate = clk_rate;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("ufs: clk (%s) scaled %s @ %lu",
|
||||||
|
__entry->name,
|
||||||
|
__entry->scale_up ? "up" : "down",
|
||||||
|
__entry->clk_rate)
|
||||||
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef TRACE_INCLUDE_PATH
|
#undef TRACE_INCLUDE_PATH
|
||||||
|
|
|
@ -738,6 +738,46 @@ static u32 ufs_mtk_get_ufs_hci_version(struct ufs_hba *hba)
|
||||||
return hba->ufs_version;
|
return hba->ufs_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ufs_mtk_init_clocks - Init mtk driver private clocks
|
||||||
|
*
|
||||||
|
* @hba: per adapter instance
|
||||||
|
*/
|
||||||
|
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_mtk_clk *mclk = &host->mclk;
|
||||||
|
struct ufs_clk_info *clki, *clki_tmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find private clocks and store them in struct ufs_mtk_clk.
|
||||||
|
* Remove "ufs_sel_min_src" and "ufs_sel_min_src" from list to avoid
|
||||||
|
* being switched on/off in clock gating.
|
||||||
|
*/
|
||||||
|
list_for_each_entry_safe(clki, clki_tmp, head, list) {
|
||||||
|
if (!strcmp(clki->name, "ufs_sel")) {
|
||||||
|
host->mclk.ufs_sel_clki = clki;
|
||||||
|
} else if (!strcmp(clki->name, "ufs_sel_max_src")) {
|
||||||
|
host->mclk.ufs_sel_max_clki = clki;
|
||||||
|
clk_disable_unprepare(clki->clk);
|
||||||
|
list_del(&clki->list);
|
||||||
|
} else if (!strcmp(clki->name, "ufs_sel_min_src")) {
|
||||||
|
host->mclk.ufs_sel_min_clki = clki;
|
||||||
|
clk_disable_unprepare(clki->clk);
|
||||||
|
list_del(&clki->list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mclk->ufs_sel_clki || !mclk->ufs_sel_max_clki ||
|
||||||
|
!mclk->ufs_sel_min_clki) {
|
||||||
|
hba->caps &= ~UFSHCD_CAP_CLK_SCALING;
|
||||||
|
dev_info(hba->dev,
|
||||||
|
"%s: Clk-scaling not ready. Feature disabled.",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_VCC_NAME 30
|
#define MAX_VCC_NAME 30
|
||||||
static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba)
|
static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
|
@ -858,12 +898,18 @@ static int ufs_mtk_init(struct ufs_hba *hba)
|
||||||
|
|
||||||
/* Enable WriteBooster */
|
/* Enable WriteBooster */
|
||||||
hba->caps |= UFSHCD_CAP_WB_EN;
|
hba->caps |= UFSHCD_CAP_WB_EN;
|
||||||
|
|
||||||
|
/* Enable clk scaling*/
|
||||||
|
hba->caps |= UFSHCD_CAP_CLK_SCALING;
|
||||||
|
|
||||||
hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL;
|
hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL;
|
||||||
hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80);
|
hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80);
|
||||||
|
|
||||||
if (host->caps & UFS_MTK_CAP_DISABLE_AH8)
|
if (host->caps & UFS_MTK_CAP_DISABLE_AH8)
|
||||||
hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
|
hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
|
||||||
|
|
||||||
|
ufs_mtk_init_clocks(hba);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ufshcd_vops_init() is invoked after
|
* ufshcd_vops_init() is invoked after
|
||||||
* ufshcd_setup_clock(true) in ufshcd_hba_init() thus
|
* ufshcd_setup_clock(true) in ufshcd_hba_init() thus
|
||||||
|
@ -1384,6 +1430,79 @@ static void ufs_mtk_event_notify(struct ufs_hba *hba,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ufs_mtk_config_scaling_param(struct ufs_hba *hba,
|
||||||
|
struct devfreq_dev_profile *profile,
|
||||||
|
struct devfreq_simple_ondemand_data *data)
|
||||||
|
{
|
||||||
|
/* Customize min gear in clk scaling */
|
||||||
|
hba->clk_scaling.min_gear = UFS_HS_G4;
|
||||||
|
|
||||||
|
hba->vps->devfreq_profile.polling_ms = 200;
|
||||||
|
hba->vps->ondemand_data.upthreshold = 50;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 (scale_up) {
|
||||||
|
ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk);
|
||||||
|
clki->curr_freq = clki->max_freq;
|
||||||
|
} 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 ufs_sel_clki, ret: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_disable_unprepare(clki->clk);
|
||||||
|
|
||||||
|
trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up,
|
||||||
|
enum ufs_notify_change_status status)
|
||||||
|
{
|
||||||
|
if (!ufshcd_is_clkscaling_supported(hba))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (status == PRE_CHANGE) {
|
||||||
|
/* Switch parent before clk_set_rate() */
|
||||||
|
ufs_mtk_clk_scale(hba, scale_up);
|
||||||
|
} else {
|
||||||
|
/* Request interrupt latency QoS accordingly */
|
||||||
|
ufs_mtk_scale_perf(hba, scale_up);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct ufs_hba_mtk_vops - UFS MTK specific variant operations
|
* struct ufs_hba_mtk_vops - UFS MTK specific variant operations
|
||||||
*
|
*
|
||||||
|
@ -1405,6 +1524,8 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
|
||||||
.dbg_register_dump = ufs_mtk_dbg_register_dump,
|
.dbg_register_dump = ufs_mtk_dbg_register_dump,
|
||||||
.device_reset = ufs_mtk_device_reset,
|
.device_reset = ufs_mtk_device_reset,
|
||||||
.event_notify = ufs_mtk_event_notify,
|
.event_notify = ufs_mtk_event_notify,
|
||||||
|
.config_scaling_param = ufs_mtk_config_scaling_param,
|
||||||
|
.clk_scale_notify = ufs_mtk_clk_scale_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -124,6 +124,12 @@ struct ufs_mtk_crypt_cfg {
|
||||||
int vcore_volt;
|
int vcore_volt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 ufs_mtk_hw_ver {
|
struct ufs_mtk_hw_ver {
|
||||||
u8 step;
|
u8 step;
|
||||||
u8 minor;
|
u8 minor;
|
||||||
|
@ -139,6 +145,7 @@ struct ufs_mtk_host {
|
||||||
struct reset_control *crypto_reset;
|
struct reset_control *crypto_reset;
|
||||||
struct ufs_hba *hba;
|
struct ufs_hba *hba;
|
||||||
struct ufs_mtk_crypt_cfg *crypt;
|
struct ufs_mtk_crypt_cfg *crypt;
|
||||||
|
struct ufs_mtk_clk mclk;
|
||||||
struct ufs_mtk_hw_ver hw_ver;
|
struct ufs_mtk_hw_ver hw_ver;
|
||||||
enum ufs_mtk_host_caps caps;
|
enum ufs_mtk_host_caps caps;
|
||||||
bool mphy_powered_on;
|
bool mphy_powered_on;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче