summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Simek <[email protected]>2026-07-30 08:49:17 +0200
committerMichal Simek <[email protected]>2026-08-12 06:55:15 +0200
commitf2294a98b28627b7b2075d5d19d2909de6b27667 (patch)
tree374da9f91172070d2177b9762daab7c0b24b17d6
parent9e8a0f54b0231d11e481fbf7832a8780e6201b71 (diff)
xilinx: versal2: Clean UFS firmware interface
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]> Link: https://patch.msgid.link/26bc9aa2c02921690e126ab3401f7974ef04ad6e.1785394153.git.michal.simek@amd.com
-rw-r--r--arch/arm/mach-versal2/cpu.c34
-rw-r--r--arch/arm/mach-versal2/include/mach/hardware.h5
-rw-r--r--arch/arm/mach-versal2/include/mach/sys_proto.h8
-rw-r--r--drivers/firmware/firmware-zynqmp.c72
-rw-r--r--drivers/ufs/ufs-amd-versal2.c73
5 files changed, 95 insertions, 97 deletions
diff --git a/arch/arm/mach-versal2/cpu.c b/arch/arm/mach-versal2/cpu.c
index 01efa7b7928..d72f66f4fba 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 7977529fd6c..4ff732d660f 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 c1dfd184d86..d678adf9c26 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 2bdb1c4d9f8..fae66ccb3d8 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 986147848bd..48854792cbf 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;