From 890b10e76e6cca5a3b4fa48a994eb3cdc566dec3 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Thu, 25 Jun 2026 05:13:03 -0700 Subject: [PATCH 1/3] scsi: ufs: ufs-qcom: Restore TX Equalization settings on FOM failure ufs_qcom_get_rx_fom() applies temporary device TX Equalization values before forcing HS mode and running the EOM-based SW FOM scan. When one of these steps fails, the function can bypass the shared cleanup path and leave temporary TX Equalization settings programmed. Route those failures through the cleanup label so the original TX EQ settings are restored and link recovery runs before exit. This path also reuses ret for cleanup, so it may overwrite the original error. Keep that on purpose: if cleanup succeeds, the caller can proceed with the FOM result for the current iteration. Signed-off-by: Can Guo Reviewed-by: Bean Huo Reviewed-by: Ziqi Chen Link: https://patch.msgid.link/20260625121306.1655467-2-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/host/ufs-qcom.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 291c43448764..0500320545c4 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -2794,7 +2794,7 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, if (ret) { dev_err(hba->dev, "%s: Failed to apply TX EQ settings for HS-G%u: %d\n", __func__, gear, ret); - return ret; + goto link_recover_and_restore; } /* Force PMC to target HS Gear to use new TX Equalization settings. */ @@ -2802,16 +2802,15 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, if (ret) { dev_err(hba->dev, "%s: Failed to change power mode to HS-G%u, Rate-%s: %d\n", __func__, gear, ufs_hs_rate_to_str(rate), ret); - return ret; + goto link_recover_and_restore; } ret = ufs_qcom_host_sw_rx_fom(hba, pwr_mode->lane_rx, fom); - if (ret) { + if (ret) dev_err(hba->dev, "Failed to get SW FOM of TX (PreShoot: %u, DeEmphasis: %u): %d\n", d_iter->preshoot, d_iter->deemphasis, ret); - return ret; - } +link_recover_and_restore: /* Restore Device's TX Equalization settings. */ ret = ufshcd_apply_tx_eq_settings(hba, &hba->tx_eq_params[gear - 1], gear); if (ret) { From 4bd0875b7e571eeeed45fdab52672b850c1845a5 Mon Sep 17 00:00:00 2001 From: Can Guo Date: Thu, 25 Jun 2026 05:13:04 -0700 Subject: [PATCH 2/3] scsi: ufs: core: Tolerate RX_FOM read failures in TX EQTR ufshcd_get_rx_fom() aborted TX EQTR when a per-lane RX_FOM DME read failed. That makes the whole training flow fragile even though these reads can be treated as best effort. Keep TX EQTR running by logging RX_FOM read failures and continuing. Make failed lanes deterministic by initializing each lane FOM to 0 before reading and only updating it when the DME read succeeds. This avoids propagating stale or uninitialized values into EQTR evaluation. Also update the kerneldoc return description to match behavior: RX_FOM DME read failures are handled as warnings, while get_rx_fom() vops failures are still propagated to the caller. Signed-off-by: Can Guo Reviewed-by: Peter Wang Reviewed-by: Bean Huo Reviewed-by: Ziqi Chen Link: https://patch.msgid.link/20260625121306.1655467-3-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-txeq.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index a083094a0465..dda1079540f9 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -482,7 +482,8 @@ static void ufshcd_evaluate_tx_eqtr_fom(struct ufs_hba *hba, * @h_iter: host TX EQTR iterator data structure * @d_iter: device TX EQTR iterator data structure * - * Returns 0 on success, negative error code otherwise + * Returns 0 on success, negative error code if get_rx_fom vops fails. + * RX_FOM DME get failures are logged and treated as 0 FOM for that lane. */ static int ufshcd_get_rx_fom(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode, @@ -497,8 +498,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba, ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB_SEL(RX_FOM, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), &fom); - if (ret) - return ret; + if (ret) { + h_iter->fom[lane] = 0; + dev_dbg(hba->dev, "Failed to get FOM for Host TX Lane %d: %d\n", + lane, ret); + continue; + } h_iter->fom[lane] = (u8)fom; } @@ -508,8 +513,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba, ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_FOM, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), &fom); - if (ret) - return ret; + if (ret) { + d_iter->fom[lane] = 0; + dev_dbg(hba->dev, "Failed to get FOM for Device TX Lane %d: %d\n", + lane, ret); + continue; + } d_iter->fom[lane] = (u8)fom; } From f13faec1d4c16b3705d1eccffb8ba6723f1102ff Mon Sep 17 00:00:00 2001 From: Can Guo Date: Thu, 25 Jun 2026 05:13:05 -0700 Subject: [PATCH 3/3] scsi: ufs: core: Always run tx_eqtr POST_CHANGE notify ufshcd_tx_eqtr() skips POST_CHANGE notify when __ufshcd_tx_eqtr() fails. That can leave variant cleanup incomplete when PRE_CHANGE saved temporary state that POST_CHANGE is expected to restore. Always call POST_CHANGE once PRE_CHANGE has succeeded. Keep the TX EQTR result as the primary return value, and only propagate POST_CHANGE failure when TX EQTR itself succeeded. Log PRE_CHANGE and POST_CHANGE notify failures to make variant callback failures visible in TX EQTR error paths. Reviewed-by: Manivannan Sadhasivam Reviewed-by: Peter Wang Signed-off-by: Can Guo Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Reviewed-by: Ziqi Chen Link: https://patch.msgid.link/20260625121306.1655467-4-can.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-txeq.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index dda1079540f9..fa5f539632eb 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -1227,6 +1227,7 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba, { struct ufs_pa_layer_attr old_pwr_info; unsigned int noio_flag; + int notify_ret; int ret; /* @@ -1256,14 +1257,19 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba, } ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode); - if (ret) + if (ret) { + dev_err(hba->dev, "TX EQTR PRE_CHANGE notify failed: %d\n", ret); goto out_unprepare; + } ret = __ufshcd_tx_eqtr(hba, params, pwr_mode); - if (ret) - goto out_unprepare; - ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); + notify_ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); + if (notify_ret) + dev_err(hba->dev, "TX EQTR POST_CHANGE notify failed: %d\n", notify_ret); + + if (!ret) + ret = notify_ret; out_unprepare: if (ret)