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

Francis, Neha n-francis at ti.com
Thu Oct 9 18:58:22 CEST 2025


On 8/28/2025 12:20 AM, Jared McArthur wrote:
> 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.
> 
> This patch depends on the previous patch: "ufs: Add support for
> sending UFS attribute requests"
> 
> Signed-off-by: Jared McArthur <j-mcarthur at ti.com>
> ---
>  drivers/ufs/ufs.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/ufs/ufs.h | 10 ++++++
>  2 files changed, 99 insertions(+)
> 
> diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c
> index 9e67dd86232..4bffbf87749 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>
> @@ -1773,6 +1774,92 @@ 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;
> +}
> +
> +void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk)
> +{
> +	unsigned long freq;
> +
> +	freq = clk_get_rate(refclk);
> +
> +	hba->dev_ref_clk_freq =
> +		ufs_get_bref_clk_from_hz(freq);
> +
> +	if (hba->dev_ref_clk_freq == REF_CLK_FREQ_INVAL)
> +		dev_err(hba->dev,
> +			"invalid ref_clk setting = %ld\n", 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;
> +	}
> +
> +	ufshcd_parse_dev_ref_clk_freq(hba, ref_clk);
> +	host_ref_clk_freq = hba->dev_ref_clk_freq;
> +
> +	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
>   */
> @@ -2000,6 +2087,8 @@ static 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 53137fae3a8..def39d4ad24 100644
> --- a/drivers/ufs/ufs.h
> +++ b/drivers/ufs/ufs.h
> @@ -172,6 +172,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,
> @@ -684,6 +693,7 @@ struct ufs_hba {
>  	u32			capabilities;
>  	u32			version;
>  	u32			intr_mask;
> +	enum ufs_ref_clk_freq dev_ref_clk_freq;
>  	enum ufshcd_quirks	quirks;
>  
>  	/* Virtual memory reference */

Reviewed-by: Neha Malcom Francis <n-francis at ti.com>

-- 
Thanking You
Neha Malcom Francis



More information about the U-Boot mailing list