Re: [PATCH] firmware: xilinx: ufs: move PHY/SRAM ready polling into the firmware backend
Sai Krishna Potthuri <[email protected]> Tue, 11 Aug 2026 19:55:48 +0530
| Newsgroups | gmane.linux.kernel,gmane.linux.ports.arm.kernel,gmane.linux.scsi |
|---|---|
| Message-ID | <[email protected]> |
On 8/4/2026 8:32 PM, Michal Simek wrote: > The Versal Gen 2 UFS driver polls the firmware for M-PHY TX/RX > configuration readiness and SRAM initialisation completion with two > open-coded do/while loops. Each iteration is a full firmware round-trip > (PM_IOCTL/IOCTL_READ_REG of a protected PMC_IOU_SLCR register), so the > loop can issue up to a million EEMI calls, and it hard-codes the wait > policy inside the controller driver. > > Introduce coarse blocking helpers, zynqmp_pm_wait_mphy_tx_rx_config_ready() > and zynqmp_pm_wait_sram_init_done(), that take a caller-supplied timeout > budget and contain the poll loop. The loop is EEMI-specific (legacy > firmware only exposes the per-read status primitive) so it lives in the > firmware driver, keeping the UFS driver backend-agnostic: a future > backend can offload the wait to the platform in a single call without > touching the controller driver again. The existing per-read primitives stay > exported, so the current EEMI interface is unchanged. > > The timeout budget remains owned by the UFS driver (the consumer that > knows the hardware) and is passed down, so EEMI and any future backend > stay consistent. > > Signed-off-by: Michal Simek <[email protected]> Reviewed-by: Sai Krishna Potthuri <[email protected]> Regards Sai Krishna > --- > > drivers/firmware/xilinx/zynqmp-ufs.c | 70 ++++++++++++++++++++++-- > drivers/ufs/host/ufs-amd-versal2.c | 46 ++++------------ > include/linux/firmware/xlnx-zynqmp-ufs.h | 8 +-- > 3 files changed, 82 insertions(+), 42 deletions(-) > > diff --git a/drivers/firmware/xilinx/zynqmp-ufs.c b/drivers/firmware/xilinx/zynqmp-ufs.c > index 85da8a822f3a..81ccf61a037c 100644 > --- a/drivers/firmware/xilinx/zynqmp-ufs.c > +++ b/drivers/firmware/xilinx/zynqmp-ufs.c > @@ -5,6 +5,7 @@ > * Copyright (C) 2025 Advanced Micro Devices, Inc. > */ > > +#include <linux/delay.h> > #include <linux/firmware/xlnx-zynqmp.h> > #include <linux/module.h> > > @@ -33,7 +34,7 @@ > * > * Return: Returns 0 on success or error value on failure. > */ > -int zynqmp_pm_is_mphy_tx_rx_config_ready(bool *is_ready) > +static int zynqmp_pm_is_mphy_tx_rx_config_ready(bool *is_ready) > { > u32 regval; > int ret; > @@ -53,7 +54,6 @@ int zynqmp_pm_is_mphy_tx_rx_config_ready(bool *is_ready) > > return ret; > } > -EXPORT_SYMBOL_GPL(zynqmp_pm_is_mphy_tx_rx_config_ready); > > /** > * zynqmp_pm_is_sram_init_done - check SRAM initialization > @@ -61,7 +61,7 @@ EXPORT_SYMBOL_GPL(zynqmp_pm_is_mphy_tx_rx_config_ready); > * > * Return: Returns 0 on success or error value on failure. > */ > -int zynqmp_pm_is_sram_init_done(bool *is_done) > +static int zynqmp_pm_is_sram_init_done(bool *is_done) > { > u32 regval; > int ret; > @@ -81,7 +81,69 @@ int zynqmp_pm_is_sram_init_done(bool *is_done) > > return ret; > } > -EXPORT_SYMBOL_GPL(zynqmp_pm_is_sram_init_done); > + > +/** > + * zynqmp_pm_wait_mphy_tx_rx_config_ready - wait for M-PHY TX-RX config ready > + * @timeout_us: Caller-supplied timeout budget in microseconds > + * > + * Poll the M-PHY TX-RX configuration-ready status until it settles or the > + * timeout elapses. The poll loop is EEMI-specific (legacy firmware only offers > + * the per-read status primitive), so it lives here in the firmware driver > + * rather than in the UFS controller driver; an SCMI-based backend can instead > + * offload the wait to the platform in a single call. The timeout budget is > + * owned by the caller (UFS driver), keeping the policy with the consumer. > + * > + * Return: Returns 0 once ready, -ETIMEDOUT on timeout, or error value. > + */ > +int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us) > +{ > + bool is_ready; > + int ret; > + > + while (timeout_us--) { > + ret = zynqmp_pm_is_mphy_tx_rx_config_ready(&is_ready); > + if (ret) > + return ret; > + > + if (!is_ready) > + return 0; > + > + usleep_range(1, 5); > + } > + > + return -ETIMEDOUT; > +} > +EXPORT_SYMBOL_GPL(zynqmp_pm_wait_mphy_tx_rx_config_ready); > + > +/** > + * zynqmp_pm_wait_sram_init_done - wait for SRAM initialization to complete > + * @timeout_us: Caller-supplied timeout budget in microseconds > + * > + * Poll the SRAM initialization-done status until it is set or the timeout > + * elapses. As with the M-PHY wait, the poll loop is EEMI-specific and kept in > + * the firmware driver so the UFS controller driver stays backend-agnostic. > + * > + * Return: Returns 0 once done, -ETIMEDOUT on timeout, or error value. > + */ > +int zynqmp_pm_wait_sram_init_done(u32 timeout_us) > +{ > + bool is_done; > + int ret; > + > + while (timeout_us--) { > + ret = zynqmp_pm_is_sram_init_done(&is_done); > + if (ret) > + return ret; > + > + if (is_done) > + return 0; > + > + usleep_range(1, 5); > + } > + > + return -ETIMEDOUT; > +} > +EXPORT_SYMBOL_GPL(zynqmp_pm_wait_sram_init_done); > > /** > * zynqmp_pm_set_sram_bypass - Set SRAM bypass Control > diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c > index 2154d6286817..dff0c2c95486 100644 > --- a/drivers/ufs/host/ufs-amd-versal2.c > +++ b/drivers/ufs/host/ufs-amd-versal2.c > @@ -225,8 +225,6 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba) > static int ufs_versal2_phy_init(struct ufs_hba *hba) > { > struct ufs_versal2_host *host = ufshcd_get_variant(hba); > - u32 time_left; > - bool is_ready; > int ret; > static const struct ufshcd_dme_attr_val rmmi_attrs[] = { > { UIC_ARG_MIB(CBREFCLKCTRL2), CBREFREFCLK_GATE_OVR_EN, DME_LOCAL }, > @@ -235,23 +233,15 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) > { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL } > }; > > - /* Wait for Tx/Rx config_rdy */ > - time_left = TIMEOUT_MICROSEC; > - do { > - time_left--; > - ret = zynqmp_pm_is_mphy_tx_rx_config_ready(&is_ready); > - if (ret) > - return ret; > - > - if (!is_ready) > - break; > - > - usleep_range(1, 5); > - } while (time_left); > - > - if (!time_left) { > + /* > + * Wait for Tx/Rx config_rdy. The poll loop lives in the firmware > + * backend (EEMI today, SCMI in future) so this driver stays > + * backend-agnostic; the timeout budget stays here with the consumer. > + */ > + ret = zynqmp_pm_wait_mphy_tx_rx_config_ready(TIMEOUT_MICROSEC); > + if (ret) { > dev_err(hba->dev, "Tx/Rx configuration signal busy.\n"); > - return -ETIMEDOUT; > + return ret; > } > > ret = ufshcd_dwc_dme_set_attrs(hba, rmmi_attrs, ARRAY_SIZE(rmmi_attrs)); > @@ -264,23 +254,11 @@ static int ufs_versal2_phy_init(struct ufs_hba *hba) > return ret; > } > > - /* Wait for SRAM init done */ > - time_left = TIMEOUT_MICROSEC; > - do { > - time_left--; > - ret = zynqmp_pm_is_sram_init_done(&is_ready); > - if (ret) > - return ret; > - > - if (is_ready) > - break; > - > - usleep_range(1, 5); > - } while (time_left); > - > - if (!time_left) { > + /* Wait for SRAM init done (poll handled by the firmware backend). */ > + ret = zynqmp_pm_wait_sram_init_done(TIMEOUT_MICROSEC); > + if (ret) { > dev_err(hba->dev, "SRAM initialization failed.\n"); > - return -ETIMEDOUT; > + return ret; > } > > ret = ufs_versal2_setup_phy(hba); > diff --git a/include/linux/firmware/xlnx-zynqmp-ufs.h b/include/linux/firmware/xlnx-zynqmp-ufs.h > index d3538dd5822a..00383dd835f2 100644 > --- a/include/linux/firmware/xlnx-zynqmp-ufs.h > +++ b/include/linux/firmware/xlnx-zynqmp-ufs.h > @@ -9,17 +9,17 @@ > #define __FIRMWARE_XLNX_ZYNQMP_UFS_H__ > > #if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE) > -int zynqmp_pm_is_mphy_tx_rx_config_ready(bool *is_ready); > -int zynqmp_pm_is_sram_init_done(bool *is_done); > +int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us); > +int zynqmp_pm_wait_sram_init_done(u32 timeout_us); > int zynqmp_pm_set_sram_bypass(void); > int zynqmp_pm_get_ufs_calibration_values(u32 *val); > #else > -static inline int zynqmp_pm_is_mphy_tx_rx_config_ready(bool *is_ready) > +static inline int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us) > { > return -ENODEV; > } > > -static inline int zynqmp_pm_is_sram_init_done(bool *is_done) > +static inline int zynqmp_pm_wait_sram_init_done(u32 timeout_us) > { > return -ENODEV; > } > --- > base-commit: 848acc8ffe1b7cd5f1bf427b93069becfebc2c9d > branch: xnext/ufs >