Commit b7dbc686 authored by Po-Wen Kao's avatar Po-Wen Kao Committed by Martin K. Petersen
Browse files

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: default avatarStanley Chu <stanley.chu@mediatek.com>
Signed-off-by: default avatarPo-Wen Kao <powen.kao@mediatek.com>
Signed-off-by: default avatarStanley Chu <stanley.chu@mediatek.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 2873e045
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -27,6 +27,29 @@ TRACE_EVENT(ufs_mtk_event,
	TP_printk("ufs: event=%u data=%u",
		  __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

#undef TRACE_INCLUDE_PATH
+121 −0
Original line number Diff line number Diff line
@@ -738,6 +738,46 @@ static u32 ufs_mtk_get_ufs_hci_version(struct ufs_hba *hba)
	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
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 */
	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->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80);

	if (host->caps & UFS_MTK_CAP_DISABLE_AH8)
		hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;

	ufs_mtk_init_clocks(hba);

	/*
	 * ufshcd_vops_init() is invoked after
	 * 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
 *
@@ -1405,6 +1524,8 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
	.dbg_register_dump   = ufs_mtk_dbg_register_dump,
	.device_reset        = ufs_mtk_device_reset,
	.event_notify        = ufs_mtk_event_notify,
	.config_scaling_param = ufs_mtk_config_scaling_param,
	.clk_scale_notify    = ufs_mtk_clk_scale_notify,
};

/**
+7 −0
Original line number Diff line number Diff line
@@ -124,6 +124,12 @@ struct ufs_mtk_crypt_cfg {
	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 {
	u8 step;
	u8 minor;
@@ -139,6 +145,7 @@ struct ufs_mtk_host {
	struct reset_control *crypto_reset;
	struct ufs_hba *hba;
	struct ufs_mtk_crypt_cfg *crypt;
	struct ufs_mtk_clk mclk;
	struct ufs_mtk_hw_ver hw_ver;
	enum ufs_mtk_host_caps caps;
	bool mphy_powered_on;