[PATCH 2/2] ufs: Add bRefClkFreq attribute setting
Jared McArthur
j-mcarthur at ti.com
Fri Oct 10 18:57:13 CEST 2025
On 10/9/25 10:57, Kumar, Udit wrote:
>
> 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"
>
> Since this is same series, you can drop above from commit message
>
Will do.
>
>> 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);
>
> I understand this is backport but I don't see real use of dev_ref_clk_freq variable ,
>
> I suggest to drop dev_ref_clk_freq varaible and return freq index from this function instead of void.
>
Noted.
>
>> +
>> + 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;
>
> host_ref_clk_freq = ufshcd_parse_dev_ref_clk_freq(hba, ref_clk);
>
> and check for error code here
>
> If you agree with above change, then please use
>
> Reviewed-by: Udit Kumar <u-kumar1 at ti.com> in next version
>
>
I think the two edits (one change) are reasonable.
>> +
>> + 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 */
--
Best,
Jared McArthur
More information about the U-Boot
mailing list