[PATCH v2 2/2] ufs: Add bRefClkFreq attribute setting

Jared McArthur j-mcarthur at ti.com
Fri Oct 10 21:55:56 CEST 2025


A UFS device needs its bRefClkFreq attribute set to the correct value
before switching to high speed. If bRefClkFreq is set to the wrong
value, all transactions after the power mode change will fail.

The bRefClkFreq depends on the host controller and the device.
Query the device's current bRefClkFreq and compare with the ref_clk
specified in the device-tree. If the two differ, set the bRefClkFreq
to the device-tree's ref_clk frequency.

Taken from Linux kernel v6.17 (drivers/ufs/core/ufshcd.c and
include/ufs/ufs.h) and ported to U-Boot.

Signed-off-by: Jared McArthur <j-mcarthur at ti.com>
Reviewed-by: Bryan Brattlof <bb at ti.com>
Reviewed-by: Udit Kumar <u-kumar1 at ti.com>
Reviewed-by: Neha Malcom Francis <n-francis at ti.com>
---

Changes in v2:
- drop the dev_ref_clk_freq field from the ufs_hba struct
- ufshcd_parse_dev_ref_clk_freq returns a enum ufs_ref_clk_freq instead
  of setting the field in the ufs_hba struct (and returning void) 

drivers/ufs/ufs.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/ufs/ufs.h |  9 +++++
 2 files changed, 94 insertions(+)

diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c
index 005c212f..58884f63 100644
--- a/drivers/ufs/ufs.c
+++ b/drivers/ufs/ufs.c
@@ -10,6 +10,7 @@
 
 #include <bouncebuf.h>
 #include <charset.h>
+#include <clk.h>
 #include <dm.h>
 #include <log.h>
 #include <dm/device_compat.h>
@@ -1777,6 +1778,88 @@ out:
 	return err;
 }
 
+struct ufs_ref_clk {
+	unsigned long freq_hz;
+	enum ufs_ref_clk_freq val;
+};
+
+static const struct ufs_ref_clk ufs_ref_clk_freqs[] = {
+	{19200000, REF_CLK_FREQ_19_2_MHZ},
+	{26000000, REF_CLK_FREQ_26_MHZ},
+	{38400000, REF_CLK_FREQ_38_4_MHZ},
+	{52000000, REF_CLK_FREQ_52_MHZ},
+	{0, REF_CLK_FREQ_INVAL},
+};
+
+static enum ufs_ref_clk_freq
+ufs_get_bref_clk_from_hz(unsigned long freq)
+{
+	int i;
+
+	for (i = 0; ufs_ref_clk_freqs[i].freq_hz; i++)
+		if (ufs_ref_clk_freqs[i].freq_hz == freq)
+			return ufs_ref_clk_freqs[i].val;
+
+	return REF_CLK_FREQ_INVAL;
+}
+
+enum ufs_ref_clk_freq ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk)
+{
+	unsigned long freq;
+
+	freq = clk_get_rate(refclk);
+	return ufs_get_bref_clk_from_hz(freq);
+}
+
+static int ufshcd_set_dev_ref_clk(struct ufs_hba *hba)
+{
+	int err;
+	struct clk *ref_clk;
+	u32 host_ref_clk_freq;
+	u32 dev_ref_clk_freq;
+
+	/* get ref_clk */
+	ref_clk = devm_clk_get(hba->dev, "ref_clk");
+	if (IS_ERR((const void *)ref_clk)) {
+		err = PTR_ERR(ref_clk);
+		goto out;
+	}
+
+	host_ref_clk_freq = ufshcd_parse_dev_ref_clk_freq(hba, ref_clk);
+	if (host_ref_clk_freq == REF_CLK_FREQ_INVAL)
+		dev_err(hba->dev,
+			"invalid ref_clk setting = %ld\n", clk_get_rate(ref_clk));
+
+	if (host_ref_clk_freq == REF_CLK_FREQ_INVAL)
+		goto out;
+
+	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+				      QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &dev_ref_clk_freq);
+
+	if (err) {
+		dev_err(hba->dev, "failed reading bRefClkFreq. err = %d\n", err);
+		goto out;
+	}
+
+	if (dev_ref_clk_freq == host_ref_clk_freq)
+		goto out; /* nothing to update */
+
+	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+				      QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &host_ref_clk_freq);
+
+	if (err) {
+		dev_err(hba->dev, "bRefClkFreq setting to %lu Hz failed\n",
+			ufs_ref_clk_freqs[host_ref_clk_freq].freq_hz);
+		goto out;
+	}
+
+	dev_dbg(hba->dev, "bRefClkFreq setting to %lu Hz succeeded\n",
+		ufs_ref_clk_freqs[host_ref_clk_freq].freq_hz);
+
+out:
+	return err;
+}
+
 /**
  * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device
  */
@@ -2004,6 +2087,8 @@ int ufs_start(struct ufs_hba *hba)
 		return ret;
 	}
 
+	ufshcd_set_dev_ref_clk(hba);
+
 	if (ufshcd_get_max_pwr_mode(hba)) {
 		dev_err(hba->dev,
 			"%s: Failed getting max supported power mode\n",
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 00ecca35..549a2457 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -214,6 +214,15 @@ enum query_opcode {
 	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
 };
 
+/* bRefClkFreq attribute values */
+enum ufs_ref_clk_freq {
+	REF_CLK_FREQ_19_2_MHZ	= 0,
+	REF_CLK_FREQ_26_MHZ	= 1,
+	REF_CLK_FREQ_38_4_MHZ	= 2,
+	REF_CLK_FREQ_52_MHZ	= 3,
+	REF_CLK_FREQ_INVAL	= -1,
+};
+
 /* Query response result code */
 enum {
 	QUERY_RESULT_SUCCESS                    = 0x00,
-- 
2.34.1



More information about the U-Boot mailing list