]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: ufs: core: Fix bRefClkFreq write failure in HS-LSS mode
authorWang Shuaiwei <wangshuaiwei1@xiaomi.com>
Tue, 14 Apr 2026 03:37:18 +0000 (11:37 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 22 Apr 2026 00:58:06 +0000 (20:58 -0400)
According to the UFS spec, the bRefClkFreq attribute can only be written
when both sub-links are in LS-MODE. However, in HS LSS mode with
resetmode = HS_MODE, if the UFS device's default bRefClkFreq value
differs from the host controller's dev_ref_clk_freq setting, the write
operation will fail.

To fix this issue, introduce ufshcd_get_op_mode() function to detect the
current link operational mode. Call ufshcd_set_dev_ref_clk() only when
both sub-links are in LS-MODE to ensure the attribute can be written
successfully.

Signed-off-by: Wang Shuaiwei <wangshuaiwei1@xiaomi.com>
Link: https://patch.msgid.link/20260414033718.1459540-1-wangshuaiwei1@xiaomi.com
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/ufs/core/ufshcd.c
include/ufs/unipro.h

index 4805e40ed4d78cd4b0c07cda0df6bb0f7e172cb1..c3f08957d179ac4d94f57dc83ac9266a43eee18e 100644 (file)
@@ -9259,6 +9259,30 @@ static void ufshcd_config_mcq(struct ufs_hba *hba)
                 hba->nutrs);
 }
 
+/**
+ * ufshcd_get_op_mode - get UFS operating mode.
+ * @hba: per-adapter instance
+ *
+ * Use the PA_PWRMODE value to represent the operating mode of UFS.
+ *
+ */
+static enum ufs_op_mode ufshcd_get_op_mode(struct ufs_hba *hba)
+{
+       u32 mode;
+       u8 rx_mode;
+       u8 tx_mode;
+
+       ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &mode);
+       rx_mode = (mode >> PWRMODE_RX_OFFSET) & PWRMODE_MASK;
+       tx_mode = mode & PWRMODE_MASK;
+
+       if ((rx_mode == SLOW_MODE || rx_mode == SLOWAUTO_MODE) &&
+           (tx_mode == SLOW_MODE || tx_mode == SLOWAUTO_MODE))
+               return LS_MODE;
+
+       return HS_MODE;
+}
+
 static int ufshcd_post_device_init(struct ufs_hba *hba)
 {
        int ret;
@@ -9281,11 +9305,13 @@ static int ufshcd_post_device_init(struct ufs_hba *hba)
                return 0;
 
        /*
-        * Set the right value to bRefClkFreq before attempting to
+        * Set the right value to bRefClkFreq in LS_MODE before attempting to
         * switch to HS gears.
         */
-       if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL)
+       if (ufshcd_get_op_mode(hba) == LS_MODE &&
+           hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL)
                ufshcd_set_dev_ref_clk(hba);
+
        /* Gear up to HS gear. */
        ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info,
                                     UFSHCD_PMC_POLICY_DONT_FORCE);
index f849a2a101ae295c6ece83a763e3a8dd3c838744..9c168703b104801c87f24aae6d5c7cf42fb8263f 100644 (file)
@@ -333,6 +333,11 @@ enum ufs_eom_eye_mask {
 #define DME_LocalTC0ReplayTimeOutVal           0xD042
 #define DME_LocalAFC0ReqTimeOutVal             0xD043
 
+enum ufs_op_mode {
+       LS_MODE = 1,
+       HS_MODE = 2,
+};
+
 /* PA power modes */
 enum ufs_pa_pwr_mode {
        FAST_MODE       = 1,