From: Larisa Grigore <larisa.grigore@oss.nxp.com>
To: "Larisa Grigore" <larisa.grigore@oss.nxp.com>,
"NXP S32 Linux Team" <s32@nxp.com>,
"Alim Akhtar" <alim.akhtar@samsung.com>,
"Avri Altman" <avri.altman@sandisk.com>,
"Bart Van Assche" <bvanassche@acm.org>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Frank Li" <Frank.Li@nxp.com>,
"Sascha Hauer" <s.hauer@pengutronix.de>,
"Pengutronix Kernel Team" <kernel@pengutronix.de>,
"Fabio Estevam" <festevam@gmail.com>,
"James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>,
"Martin K. Petersen" <mkp@kernel.org>,
"Sai Krishna Potthuri" <sai.krishna.potthuri@amd.com>,
"Ajay Neeli" <ajay.neeli@amd.com>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Pedro Sousa " <pedrom.sousa@synopsys.com>
Cc: linux-scsi@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-arm-msm@vger.kernel.org, clizzi@redhat.com,
aruizrui@redhat.com, eballetb@redhat.com, echanude@redhat.com
Subject: [PATCH v3 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
Date: Wed, 16 Sep 2026 10:44:04 +0200 [thread overview]
Message-ID: <20260916084411.941297-4-larisa.grigore@oss.nxp.com> (raw)
In-Reply-To: <20260916084411.941297-1-larisa.grigore@oss.nxp.com>
Factor out the common logic used to poll TX_FSM_STATE until the TX lanes
enter Hibern8 into ufshcd_check_hibern8().
The HiSilicon and Qualcomm drivers currently implement similar checks
using the UniPro TX_FSM_STATE attribute after link/PHY bring-up. Move
this logic to a common helper in ufshcd so it can be shared across host
controller drivers.
Switch the HiSilicon and Qualcomm drivers to use the new helper and drop
their local implementations. While at it, normalize the "TX lane failed to
reach Hibern8" error path to return -ETIMEDOUT instead of the raw
TX_FSM_STATE value (qcom) or -1 (hisi), so all callers get a proper errno.
This is an intentional, harmless change of the returned error value; the
pass/fail behaviour at the call sites is unchanged.
Unlike the initial implementation, the timeout error is now reported only
once by ufshcd_dme_check_tx_hibern8() after its final check, instead of
per lane.
This also prepares for reusing the same UniPro-specific Hibern8 check in
a subsequent commit.
Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
drivers/ufs/core/ufshcd.c | 86 +++++++++++++++++++++++++++++++++++++
drivers/ufs/host/ufs-hisi.c | 48 +--------------------
drivers/ufs/host/ufs-qcom.c | 42 +-----------------
include/ufs/ufshcd.h | 3 ++
4 files changed, 91 insertions(+), 88 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 2ba244cf40ac..f34b7fe54a16 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -4448,6 +4448,92 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
}
EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
+/**
+ * ufshcd_poll_tx_hibern8_lanes - Check TX_FSM_STATE of all TX lanes once
+ * @hba: host controller instance
+ * @num_lanes: number of TX lanes to check
+ *
+ * Read TX_FSM_STATE for every lane and verify it reached Hibern8.
+ *
+ * Return: 0 if all lanes are in Hibern8, -EAGAIN if any lane is not (yet)
+ * in Hibern8, or a negative errno (e.g. -ETIMEDOUT from a hard UIC command
+ * timeout) if the attribute read fails.
+ */
+static int ufshcd_poll_tx_hibern8_lanes(struct ufs_hba *hba,
+ unsigned int num_lanes)
+{
+ u32 tx_fsm_val = 0;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < num_lanes; i++) {
+ err = ufshcd_dme_get(hba,
+ UIC_ARG_MIB_SEL(TX_FSM_STATE,
+ UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+ &tx_fsm_val);
+ if (err) {
+ dev_err(hba->dev,
+ "%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
+ __func__, i, err);
+ return err;
+ }
+
+ if (tx_fsm_val != TX_STATE_HIBERN8)
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/**
+ * ufshcd_dme_check_tx_hibern8 - Check if all TX lanes entered Hibern8 state
+ * @hba: host controller instance
+ * @num_lanes: number of TX lanes to check
+ * @timeout_ms: timeout in milliseconds for all lanes
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int ufshcd_dme_check_tx_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
+ unsigned int timeout_ms)
+{
+ unsigned long timeout;
+ int err;
+
+ if (!num_lanes)
+ return -EINVAL;
+
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
+
+ do {
+ err = ufshcd_poll_tx_hibern8_lanes(hba, num_lanes);
+ /*
+ * -EAGAIN means the lanes are simply not in HIBERN8 yet, so
+ * keep polling. Any other error is a real failure and must
+ * be returned immediately instead of being retried.
+ */
+ if (err != -EAGAIN)
+ return err;
+
+ /* sleep for max. 200us */
+ usleep_range(100, 200);
+ } while (time_before(jiffies, timeout));
+
+ /*
+ * We might have been scheduled out for long during polling, so do
+ * one final check before reporting timeout.
+ */
+ err = ufshcd_poll_tx_hibern8_lanes(hba, num_lanes);
+ if (err == -EAGAIN) {
+ dev_err(hba->dev,
+ "%s: timeout waiting for TX lanes to enter HIBERN8\n",
+ __func__);
+ err = -ETIMEDOUT;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ufshcd_dme_check_tx_hibern8);
+
/**
* ufshcd_dme_rmw - get modify set a DME attribute
* @hba: per adapter instance
diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index bd223bda1ce2..b84075dfe00d 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -22,50 +22,6 @@
#include <ufs/ufshci.h>
#include <ufs/ufs_quirks.h>
-static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
-{
- int err = 0;
- u32 tx_fsm_val_0 = 0;
- u32 tx_fsm_val_1 = 0;
- unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
-
- do {
- err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
- &tx_fsm_val_0);
- err |= ufshcd_dme_get(hba,
- UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
- if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
- tx_fsm_val_1 == TX_STATE_HIBERN8))
- break;
-
- /* sleep for max. 200us */
- usleep_range(100, 200);
- } while (time_before(jiffies, timeout));
-
- /*
- * we might have scheduled out for long during polling so
- * check the state again.
- */
- if (time_after(jiffies, timeout)) {
- err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
- &tx_fsm_val_0);
- err |= ufshcd_dme_get(hba,
- UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
- }
-
- if (err) {
- dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
- __func__, err);
- } else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
- tx_fsm_val_1 != TX_STATE_HIBERN8) {
- err = -1;
- dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
- __func__, tx_fsm_val_0, tx_fsm_val_1);
- }
-
- return err;
-}
-
static void ufs_hisi_clk_init(struct ufs_hba *hba)
{
struct ufs_hisi_host *host = ufshcd_get_variant(hba);
@@ -224,9 +180,7 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
/* Unipro VS_mphy_disable */
ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0);
- err = ufs_hisi_check_hibern8(hba);
- if (err)
- dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
+ err = ufshcd_dme_check_tx_hibern8(hba, 2, HBRN8_POLL_TOUT_MS);
if (!(host->caps & UFS_HISI_CAP_PHY10nm))
ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 1e492dac8d93..815ca82a0878 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -382,46 +382,6 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
return 0;
}
-static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
-{
- int err;
- u32 tx_fsm_val;
- unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
-
- do {
- err = ufshcd_dme_get(hba,
- UIC_ARG_MIB_SEL(TX_FSM_STATE,
- UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
- &tx_fsm_val);
- if (err || tx_fsm_val == TX_STATE_HIBERN8)
- break;
-
- /* sleep for max. 200us */
- usleep_range(100, 200);
- } while (time_before(jiffies, timeout));
-
- /*
- * we might have scheduled out for long during polling so
- * check the state again.
- */
- if (time_after(jiffies, timeout))
- err = ufshcd_dme_get(hba,
- UIC_ARG_MIB_SEL(TX_FSM_STATE,
- UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
- &tx_fsm_val);
-
- if (err) {
- dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
- __func__, err);
- } else if (tx_fsm_val != TX_STATE_HIBERN8) {
- err = tx_fsm_val;
- dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
- __func__, err);
- }
-
- return err;
-}
-
static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
{
ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1);
@@ -607,7 +567,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
break;
case POST_CHANGE:
/* check if UFS PHY moved from DISABLED to HIBERN8 */
- err = ufs_qcom_check_hibern8(hba);
+ err = ufshcd_dme_check_tx_hibern8(hba, 1, HBRN8_POLL_TOUT_MS);
ufs_qcom_enable_hw_clk_gating(hba);
ufs_qcom_ice_enable(host);
ufs_qcom_config_ice_allocator(host);
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index dfd302f2dc7c..0d9d0a26eb20 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -1556,6 +1556,9 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
u32 *mib_val, u8 peer);
+extern int ufshcd_dme_check_tx_hibern8(struct ufs_hba *hba,
+ unsigned int num_lanes,
+ unsigned int timeout_ms);
extern int ufshcd_change_power_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *pwr_mode,
enum ufshcd_pmc_policy pmc_policy);
--
2.43.0
next prev parent reply other threads:[~2026-09-16 8:44 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-16 8:44 [PATCH v3 00/10] Add S32N79RDB UFS support Larisa Grigore
2026-09-16 8:44 ` [PATCH v3 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
2026-09-16 16:14 ` Frank Li
2026-09-17 9:00 ` Larisa Ileana Grigore
2026-09-16 8:44 ` [PATCH v3 02/10] ufs: unipro: Add TX/RX FSM state attributes Larisa Grigore
2026-09-16 8:44 ` Larisa Grigore [this message]
2026-09-16 16:22 ` [PATCH v3 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Frank Li
2026-09-17 9:00 ` Larisa Ileana Grigore
2026-09-16 8:44 ` [PATCH v3 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
2026-09-16 16:29 ` Frank Li
2026-09-16 8:44 ` [PATCH v3 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers Larisa Grigore
2026-09-16 8:44 ` [PATCH v3 06/10] dt-bindings: ufs: Add NXP S32N79 UFS host controller Larisa Grigore
2026-09-16 16:34 ` Frank Li
2026-09-16 8:44 ` [PATCH v3 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
2026-09-16 8:44 ` [PATCH v3 08/10] arm64: dts: freescale: s32n79: Add UFS host controller Larisa Grigore
2026-09-16 8:44 ` [PATCH v3 09/10] arm64: dts: freescale: s32n79-rdb: Enable UFS Larisa Grigore
2026-09-16 8:44 ` [PATCH v3 10/10] MAINTAINERS: Add NXP S32N7 UFS host controller entry Larisa Grigore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260916084411.941297-4-larisa.grigore@oss.nxp.com \
--to=larisa.grigore@oss.nxp.com \
--cc=Frank.Li@nxp.com \
--cc=James.Bottomley@HansenPartnership.com \
--cc=ajay.neeli@amd.com \
--cc=alim.akhtar@samsung.com \
--cc=aruizrui@redhat.com \
--cc=avri.altman@sandisk.com \
--cc=bvanassche@acm.org \
--cc=clizzi@redhat.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=eballetb@redhat.com \
--cc=echanude@redhat.com \
--cc=festevam@gmail.com \
--cc=imx@lists.linux.dev \
--cc=kernel@pengutronix.de \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=mani@kernel.org \
--cc=mkp@kernel.org \
--cc=pedrom.sousa@synopsys.com \
--cc=robh@kernel.org \
--cc=s.hauer@pengutronix.de \
--cc=s32@nxp.com \
--cc=sai.krishna.potthuri@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®