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 <michal.simek@amd.com>
Link: https://patch.msgid.link/26bc9aa2c02921690e126ab3401f7974ef04ad6e.1785394153.git.michal.simek@amd.com
This commit is contained in:
Michal Simek
2026-08-12 06:55:15 +02:00
parent 9e8a0f54b0
commit f2294a98b2
5 changed files with 101 additions and 103 deletions
+31 -15
View File
@@ -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 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_wait_sram_init_done(u32 timeout_us)
{
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_set_sram_bypass(void)
{
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_sram_csr_read(u32 *value)
{
*value = readl(PMXC_SLCR_BASE_ADDRESS + PMXC_SRAM_CSR);
return 0;
}
int __weak zynqmp_pm_ufs_sram_csr_write(u32 *value)
{
writel(*value, 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;
@@ -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
@@ -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 */
+44 -28
View File
@@ -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;
+17 -56
View File
@@ -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;