Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
Bough Chen <[email protected]>
| Newsgroups | org.kernel.vger.linux-scsi,dev.linux.lists.imx,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260827091440.6iqfhvyihrxqv4bp@shlinux89> |
On Wed, Aug 26, 2026 at 03:40:41PM +0200, Larisa Grigore wrote: > 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-pltfrm 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. The code change the error return value for qcom and hisi, better to point this out like the following, so that QCOM/HISI guys may pay attention on it: "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." Regards Haibo Chen > > This also prepares for reusing the same UniPro-specific Hibern8 check in > a subsequent commit. > > Signed-off-by: Larisa Grigore <[email protected]> > --- > drivers/ufs/host/ufs-hisi.c | 48 +------------------ > drivers/ufs/host/ufs-qcom.c | 42 +---------------- > drivers/ufs/host/ufshcd-pltfrm.c | 79 ++++++++++++++++++++++++++++++++ > drivers/ufs/host/ufshcd-pltfrm.h | 2 + > 4 files changed, 84 insertions(+), 87 deletions(-) > > diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c > index bd223bda1ce2..76d77bc30322 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,9 @@ 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); > + err = ufshcd_check_hibern8(hba, 2, HBRN8_POLL_TOUT_MS); > if (err) > - dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n"); > + dev_err(hba->dev, "hibern8 TX FSM check failed\n"); > > 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 b2081ed6197a..c94488661bcf 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_check_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/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c > index c2dafb583cf5..034ee988924a 100644 > --- a/drivers/ufs/host/ufshcd-pltfrm.c > +++ b/drivers/ufs/host/ufshcd-pltfrm.c > @@ -9,6 +9,7 @@ > */ > > #include <linux/clk.h> > +#include <linux/delay.h> > #include <linux/module.h> > #include <linux/platform_device.h> > #include <linux/pm_opp.h> > @@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev) > } > EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove); > > +/** > + * ufshcd_check_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_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes, > + unsigned int timeout_ms) > +{ > + unsigned long timeout; > + u32 tx_fsm_val = 0; > + unsigned int i; > + bool success; > + int err; > + > + if (!num_lanes) > + return -EINVAL; > + > + timeout = jiffies + msecs_to_jiffies(timeout_ms); > + > + do { > + success = true; > + > + 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) { > + success = false; > + break; > + } > + } > + > + if (success) > + return 0; > + > + /* 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. > + */ > + 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) { > + dev_err(hba->dev, > + "%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n", > + __func__, i, tx_fsm_val); > + return -ETIMEDOUT; > + } > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(ufshcd_check_hibern8); > + > MODULE_AUTHOR("Santosh Yaragnavi <[email protected]>"); > MODULE_AUTHOR("Vinayak Holikatti <[email protected]>"); > MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver"); > diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h > index 0a18a8aed94d..ec2a8013e975 100644 > --- a/drivers/ufs/host/ufshcd-pltfrm.h > +++ b/drivers/ufs/host/ufshcd-pltfrm.h > @@ -35,5 +35,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, > void ufshcd_pltfrm_remove(struct platform_device *pdev); > int ufshcd_populate_vreg(struct device *dev, const char *name, > struct ufs_vreg **out_vreg, bool skip_current); > +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes, > + unsigned int timeout_ms); > > #endif /* UFSHCD_PLTFRM_H_ */ > -- > 2.43.0 >