[PATCH 2/3] xilinx: versal2: Clean UFS firmware interface

Michal Simek <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <26bc9aa2c02921690e126ab3401f7974ef04ad6e.1785394153.git.michal.simek@amd.com>
The M-PHY and SRAM readiness poll loops move out of the UFS controller
driver and into the firmware backend, exposed as blocking waits with
a caller-owned timeout budget. This keeps the UFS driver backend-agnostic
and lets an other backends offload the wait to the platform in a single
call instead of round-tripping every poll.

set_sram_bypass() now performs the SRAM_CSR read-modify-write in the
firmware layer.

Hook up functions are also update to reflect their usage.

Signed-off-by: Michal Simek <[email protected]>
---

 arch/arm/mach-versal2/cpu.c                   | 34 ++++++---
 arch/arm/mach-versal2/include/mach/hardware.h |  5 ++
 .../arm/mach-versal2/include/mach/sys_proto.h |  8 +-
 drivers/firmware/firmware-zynqmp.c            | 72 +++++++++++-------
 drivers/ufs/ufs-amd-versal2.c                 | 73 +++++--------------
 5 files changed, 95 insertions(+), 97 deletions(-)

diff --git a/arch/arm/mach-versal2/cpu.c b/arch/arm/mach-versal2/cpu.c
index 01efa7b79282..d72f66f4fbac 100644
--- a/arch/arm/mach-versal2/cpu.c
+++ b/arch/arm/mach-versal2/cpu.c
@@ -11,6 +11,7 @@
 #include <malloc.h>
 #include <time.h>
 #include <vsprintf.h>
+#include <wait_bit.h>
 #include <asm/armv8/mmu.h>
 #include <asm/cache.h>
 #include <asm/global_data.h>
@@ -160,25 +161,40 @@ u8 __weak versal2_get_bootmode(void)
 	return bootmode;
 }
 
-int __weak zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value)
+/*
+ * Wait for the M-PHY TX/RX config-ready status to settle (all bits cleared) or
+ * @timeout_us to elapse. The direct-MMIO fallback owns the poll loop, mirroring
+ * the EEMI backend; the timeout budget is owned by the caller.
+ */
+int __weak zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us)
 {
-	*value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_TX_RX_CFG_RDY);
-	return 0;
+	return wait_for_bit_le32((void *)(uintptr_t)(PMXC_SLCR_BASE_ADDRESS +
+						     PMXC_TX_RX_CFG_RDY),
+				 TX_RX_CFG_RDY_MASK, false, timeout_us / 1000,
+				 false);
 }
 
-int __weak zynqmp_pm_ufs_sram_csr_read(u32 *value)
+int __weak zynqmp_pm_wait_sram_init_done(u32 timeout_us)
 {
-	*value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
-	return 0;
+	return wait_for_bit_le32((void *)(uintptr_t)(PMXC_SLCR_BASE_ADDRESS +
+						     PMXC_SRAM_CSR),
+				 SRAM_CSR_INIT_DONE_MASK, true, timeout_us / 1000,
+				 false);
 }
 
-int __weak zynqmp_pm_ufs_sram_csr_write(u32 *value)
+int __weak zynqmp_pm_set_sram_bypass(void)
 {
-	writel(*value, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
+	u32 sram_csr;
+
+	sram_csr = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
+	sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK;
+	sram_csr |= SRAM_CSR_BYPASS_MASK;
+	writel(sram_csr, PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
+
 	return 0;
 }
 
-int __weak zynqmp_pm_ufs_cal_reg(u32 *value)
+int __weak zynqmp_pm_get_ufs_calibration_values(u32 *value)
 {
 	*value = readl(PMXC_EFUSE_CACHE_BASE_ADDRESS + PMXC_UFS_CAL_1_OFFSET);
 	return 0;
diff --git a/arch/arm/mach-versal2/include/mach/hardware.h b/arch/arm/mach-versal2/include/mach/hardware.h
index 7977529fd6c1..4ff732d660fd 100644
--- a/arch/arm/mach-versal2/include/mach/hardware.h
+++ b/arch/arm/mach-versal2/include/mach/hardware.h
@@ -65,6 +65,11 @@
 #define PMXC_SRAM_CSR			0x4C
 #define PMXC_TX_RX_CFG_RDY		0x54
 
+#define SRAM_CSR_INIT_DONE_MASK		BIT(0)
+#define SRAM_CSR_EXT_LD_DONE_MASK	BIT(1)
+#define SRAM_CSR_BYPASS_MASK		BIT(2)
+#define TX_RX_CFG_RDY_MASK		GENMASK(3, 0)
+
 #define PMC_GLOBAL_PGGS3_REG	0xF111005C
 #define PMC_GLOBAL_PGGS4_REG	0xF1110060
 
diff --git a/arch/arm/mach-versal2/include/mach/sys_proto.h b/arch/arm/mach-versal2/include/mach/sys_proto.h
index c1dfd184d865..d678adf9c267 100644
--- a/arch/arm/mach-versal2/include/mach/sys_proto.h
+++ b/arch/arm/mach-versal2/include/mach/sys_proto.h
@@ -22,9 +22,9 @@ u8 versal2_get_bootmode(void);
 /* EL3 clock/timer register setup, called from board_early_init_r() */
 void versal2_timer_setup(void);
 
-int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value);
-int zynqmp_pm_ufs_sram_csr_read(u32 *value);
-int zynqmp_pm_ufs_sram_csr_write(u32 *value);
-int zynqmp_pm_ufs_cal_reg(u32 *value);
+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 *value);
 
 #endif /* _ASM_ARCH_SYS_PROTO_H */
diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c
index 2bdb1c4d9f89..fae66ccb3d83 100644
--- a/drivers/firmware/firmware-zynqmp.c
+++ b/drivers/firmware/firmware-zynqmp.c
@@ -19,6 +19,7 @@
 #include <asm/ptrace.h>
 #include <asm/system.h>
 #include <linux/bitfield.h>
+#include <linux/delay.h>
 
 #if defined(CONFIG_ZYNQMP_IPI)
 #include <mailbox.h>
@@ -175,51 +176,60 @@ unsigned int zynqmp_firmware_version(void)
 };
 
 #if defined(CONFIG_ARCH_VERSAL2)
-int zynqmp_pm_ufs_get_txrx_cfgrdy(u32 *value)
+/*
+ * Poll the M-PHY TX/RX config-ready status until it settles or @timeout_us
+ * elapses. Legacy EEMI firmware only offers the per-read status primitive, so
+ * the poll loop lives here rather than in the UFS driver; the timeout budget is
+ * owned by the caller.
+ */
+int zynqmp_pm_wait_mphy_tx_rx_config_ready(u32 timeout_us)
 {
 	u32 ret_payload[PAYLOAD_ARG_CNT];
 	int ret;
 
-	if (!value)
-		return -EINVAL;
+	while (timeout_us--) {
+		ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+					IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0,
+					0, ret_payload);
+		if (ret)
+			return ret;
 
-	ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
-				IOCTL_READ_REG, TXRX_CFGRDY_OFFSET, 0, 0,
-				0, ret_payload);
-	if (ret)
-		return ret;
+		if (!(ret_payload[1] & TX_RX_CFG_RDY_MASK))
+			return 0;
 
-	*value = ret_payload[1];
+		udelay(1);
+	}
 
-	return ret;
+	return -ETIMEDOUT;
 }
 
-int zynqmp_pm_ufs_sram_csr_read(u32 *value)
+int zynqmp_pm_wait_sram_init_done(u32 timeout_us)
 {
 	u32 ret_payload[PAYLOAD_ARG_CNT];
 	int ret;
 
-	if (!value)
-		return -EINVAL;
+	while (timeout_us--) {
+		ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+					IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0,
+					0, ret_payload);
+		if (ret)
+			return ret;
 
-	ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
-				IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0,
-				0, ret_payload);
-	if (ret)
-		return ret;
+		if (ret_payload[1] & SRAM_CSR_INIT_DONE_MASK)
+			return 0;
 
-	*value = ret_payload[1];
+		udelay(1);
+	}
 
-	return ret;
+	return -ETIMEDOUT;
 }
 
-int zynqmp_pm_ufs_sram_csr_write(u32 *value)
+int zynqmp_pm_set_sram_bypass(void)
 {
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	u32 sram_csr;
 	int ret;
 
-	if (!value)
-		return -EINVAL;
-
 	ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_MASK_WRITE_REG);
 	if (ret) {
 		printf("%s: IOCTL_MASK_WRITE_REG is not supported : %d\n"
@@ -228,15 +238,21 @@ int zynqmp_pm_ufs_sram_csr_write(u32 *value)
 	}
 
 	ret = xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
-				IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET,
-				GENMASK(2, 1), *value, 0, NULL);
+				IOCTL_READ_REG, SRAM_CSR_OFFSET, 0, 0,
+				0, ret_payload);
 	if (ret)
 		return ret;
 
-	return ret;
+	sram_csr = ret_payload[1];
+	sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK;
+	sram_csr |= SRAM_CSR_BYPASS_MASK;
+
+	return xilinx_pm_request(PM_IOCTL, PM_REGNODE_PMC_IOU_SLCR,
+				 IOCTL_MASK_WRITE_REG, SRAM_CSR_OFFSET,
+				 GENMASK(2, 1), sram_csr, 0, NULL);
 }
 
-int zynqmp_pm_ufs_cal_reg(u32 *value)
+int zynqmp_pm_get_ufs_calibration_values(u32 *value)
 {
 	u32 ret_payload[PAYLOAD_ARG_CNT];
 	int ret;
diff --git a/drivers/ufs/ufs-amd-versal2.c b/drivers/ufs/ufs-amd-versal2.c
index 986147848bda..48854792cbf3 100644
--- a/drivers/ufs/ufs-amd-versal2.c
+++ b/drivers/ufs/ufs-amd-versal2.c
@@ -20,10 +20,6 @@
 #include "ufshcd-dwc.h"
 #include "ufshci-dwc.h"
 
-#define SRAM_CSR_INIT_DONE_MASK		BIT(0)
-#define SRAM_CSR_EXT_LD_DONE_MASK	BIT(1)
-#define SRAM_CSR_BYPASS_MASK		BIT(2)
-
 #define MPHY_FAST_RX_AFE_CAL		BIT(2)
 #define MPHY_FW_CALIB_CFG_VAL		BIT(8)
 
@@ -31,8 +27,6 @@
 #define MPHY_RX_OVRD_VAL		BIT(2)
 #define MPHY_RX_ACK_MASK		BIT(0)
 
-#define TX_RX_CFG_RDY_MASK		GENMASK(3, 0)
-
 #define TIMEOUT_MICROSEC		1000000L
 
 struct ufs_versal2_priv {
@@ -229,7 +223,6 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba)
 static int ufs_versal2_phy_init(struct ufs_hba *hba)
 {
 	struct ufs_versal2_priv *priv = dev_get_priv(hba->dev);
-	u32 reg, time_left;
 	int ret;
 	static const struct ufshcd_dme_attr_val rmmi_attrs[] = {
 		{ UIC_ARG_MIB(CBREFCLKCTRL2), CBREFREFCLK_GATE_OVR_EN, DME_LOCAL },
@@ -238,24 +231,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_ufs_get_txrx_cfgrdy(&reg);
-		if (ret)
-			return ret;
-
-		reg &= TX_RX_CFG_RDY_MASK;
-		if (!reg)
-			break;
-
-		mdelay(5);
-	} while (time_left);
-
-	if (!time_left) {
+	/*
+	 * Wait for Tx/Rx config_rdy. The poll loop lives in the firmware
+	 * backend (IO, EEMI or SCMI) 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));
@@ -269,24 +253,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_ufs_sram_csr_read(&reg);
-		if (ret)
-			return ret;
-
-		reg &= SRAM_CSR_INIT_DONE_MASK;
-		if (reg)
-			break;
-
-		mdelay(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);
@@ -301,7 +272,7 @@ static int ufs_versal2_init(struct ufs_hba *hba)
 	struct ufs_versal2_priv *priv = dev_get_priv(hba->dev);
 	struct clk clk;
 	unsigned long core_clk_rate = 0;
-	u32 cal, sram_csr;
+	u32 cal;
 	int ret = 0;
 
 	priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM;
@@ -345,28 +316,18 @@ static int ufs_versal2_init(struct ufs_hba *hba)
 		return ret;
 	}
 
-	ret = zynqmp_pm_ufs_sram_csr_read(&sram_csr);
-	if (ret)
+	ret = zynqmp_pm_set_sram_bypass();
+	if (ret) {
+		dev_err(hba->dev, "Bypass SRAM interface failed, err = %d\n", ret);
 		return ret;
-
-	if (!priv->phy_mode) {
-		sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK;
-		sram_csr |= SRAM_CSR_BYPASS_MASK;
-	} else {
-		dev_err(hba->dev, "Invalid phy-mode %d.\n", priv->phy_mode);
-		return -EINVAL;
 	}
 
-	ret = zynqmp_pm_ufs_sram_csr_write(&sram_csr);
-	if (ret)
-		return ret;
-
 	/* De Assert RST_UFS Reset for UFS block in PMX_IOU */
 	ret = reset_deassert(priv->rstc);
 	if (ret)
 		dev_err(hba->dev, "host reset deassert failed, err = %d\n", ret);
 
-	ret = zynqmp_pm_ufs_cal_reg(&cal);
+	ret = zynqmp_pm_get_ufs_calibration_values(&cal);
 	if (ret)
 		return ret;
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.