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:
+31
-15
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(®);
|
||||
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(®);
|
||||
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user