mmc: sdhci-cadence: Add support for Cadence sdmmc v6

Cadence SDMMC v6 controller has a lot of changes on initialize
compared to v4 controller. PHY is needed by v6 controller.

Signed-off-by: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
Co-developed-by: Alex Soo <yuklin.soo@starfivetech.com>
Signed-off-by: Wei Liang Lim <weiliang.lim@starfivetech.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
This commit is contained in:
Kuan Lim Lee 2023-11-28 14:38:30 +08:00 committed by Tom Rini
parent 208fc7a9f9
commit fe11aa0b8c
4 changed files with 375 additions and 51 deletions

View file

@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_ATMEL) += atmel_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += bcm2835_sdhci.o
obj-$(CONFIG_MMC_SDHCI_BCMSTB) += bcmstb_sdhci.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence6.o
obj-$(CONFIG_MMC_SDHCI_CV1800B) += cv1800b_sdhci.o
obj-$(CONFIG_MMC_SDHCI_AM654) += am654_sdhci.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += iproc_sdhci.o

View file

@ -16,56 +16,7 @@
#include <linux/libfdt.h>
#include <mmc.h>
#include <sdhci.h>
/* HRS - Host Register Set (specific to Cadence) */
#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
#define SDHCI_CDNS_HRS04_ACK BIT(26)
#define SDHCI_CDNS_HRS04_RD BIT(25)
#define SDHCI_CDNS_HRS04_WR BIT(24)
#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
#define SDHCI_CDNS_HRS06_MODE_SD 0x0
#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
/* PHY */
#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
/*
* The tuned val register is 6 bit-wide, but not the whole of the range is
* available. The range 0-42 seems to be available (then 43 wraps around to 0)
* but I am not quite sure if it is official. Use only 0 to 39 for safety.
*/
#define SDHCI_CDNS_MAX_TUNING_LOOP 40
struct sdhci_cdns_plat {
struct mmc_config cfg;
struct mmc mmc;
void __iomem *hrs_addr;
};
#include "sdhci-cadence.h"
struct sdhci_cdns_phy_cfg {
const char *property;
@ -162,6 +113,9 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
tmp &= ~SDHCI_CDNS_HRS06_MODE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06);
if (device_is_compatible(mmc->dev, "cdns,sd6hc"))
sdhci_cdns6_phy_adj(mmc->dev, plat, mode);
}
static const struct sdhci_ops sdhci_cdns_ops = {
@ -175,6 +129,9 @@ static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
u32 tmp;
int i, ret;
if (device_is_compatible(plat->mmc.dev, "cdns,sd6hc"))
return sdhci_cdns6_set_tune_val(plat, val);
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
return -EINVAL;
@ -281,7 +238,10 @@ static int sdhci_cdns_probe(struct udevice *dev)
if (ret)
return ret;
ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
if (device_is_compatible(dev, "cdns,sd6hc"))
ret = sdhci_cdns6_phy_init(dev, plat);
else
ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
if (ret)
return ret;
@ -300,6 +260,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
static const struct udevice_id sdhci_cdns_match[] = {
{ .compatible = "socionext,uniphier-sd4hc" },
{ .compatible = "cdns,sd4hc" },
{ .compatible = "cdns,sd6hc" },
{ /* sentinel */ }
};

View file

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2016 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
*/
#ifndef SDHCI_CADENCE_H_
#define SDHCI_CADENCE_H_
/* HRS - Host Register Set (specific to Cadence) */
/* PHY access port */
#define SDHCI_CDNS_HRS04 0x10
/* Cadence V4 HRS04 Description*/
#define SDHCI_CDNS_HRS04_ACK BIT(26)
#define SDHCI_CDNS_HRS04_RD BIT(25)
#define SDHCI_CDNS_HRS04_WR BIT(24)
#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
#define SDHCI_CDNS_HRS05 0x14
/* eMMC control */
#define SDHCI_CDNS_HRS06 0x18
#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
#define SDHCI_CDNS_HRS06_MODE_SD 0x0
#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
/* Cadence V4 PHY Setting*/
#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
#define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
#define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
#define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
/*
* The tuned val register is 6 bit-wide, but not the whole of the range is
* available. The range 0-42 seems to be available (then 43 wraps around to 0)
* but I am not quite sure if it is official. Use only 0 to 39 for safety.
*/
#define SDHCI_CDNS_MAX_TUNING_LOOP 40
struct sdhci_cdns_plat {
struct mmc_config cfg;
struct mmc mmc;
void __iomem *hrs_addr;
};
int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode);
int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat);
int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val);
#endif

View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0-or-platform_driver
/*
* Copyright (C) 2023 Starfive.
* Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
*/
#include <dm.h>
#include <asm/global_data.h>
#include <dm/device_compat.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/sizes.h>
#include <linux/libfdt.h>
#include <mmc.h>
#include <sdhci.h>
#include "sdhci-cadence.h"
/* IO Delay Information */
#define SDHCI_CDNS_HRS07 0X1C
#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16)
#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0)
/* PHY Control and Status */
#define SDHCI_CDNS_HRS09 0x24
#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16)
#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15)
#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3)
#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2)
#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1)
#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0)
/* SDCLK adjustment */
#define SDHCI_CDNS_HRS10 0x28
#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16)
/* CMD/DAT output delay */
#define SDHCI_CDNS_HRS16 0x40
/* PHY Special Function Registers */
/* register to control the DQ related timing */
#define PHY_DQ_TIMING_REG_ADDR 0x2000
/* register to control the DQS related timing */
#define PHY_DQS_TIMING_REG_ADDR 0x2004
/* register to control the gate and loopback control related timing */
#define PHY_GATE_LPBK_CTRL_REG_ADDR 0x2008
/* register to control the Master DLL logic */
#define PHY_DLL_MASTER_CTRL_REG_ADDR 0x200C
/* register to control the Slave DLL logic */
#define PHY_DLL_SLAVE_CTRL_REG_ADDR 0x2010
#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY GENMASK(31, 24)
#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY GENMASK(7, 0)
#define SDHCI_CDNS6_PHY_CFG_NUM 4
#define SDHCI_CDNS6_CTRL_CFG_NUM 4
struct sdhci_cdns6_phy_cfg {
const char *property;
u32 val;
};
struct sdhci_cdns6_ctrl_cfg {
const char *property;
u32 val;
};
static struct sdhci_cdns6_phy_cfg sd_ds_phy_cfgs[] = {
{ "cdns,phy-dqs-timing-delay-sd-ds", 0x00380004, },
{ "cdns,phy-gate-lpbk_ctrl-delay-sd-ds", 0x01A00040, },
{ "cdns,phy-dll-slave-ctrl-sd-ds", 0x00000000, },
{ "cdns,phy-dq-timing-delay-sd-ds", 0x00000001, },
};
static struct sdhci_cdns6_phy_cfg emmc_sdr_phy_cfgs[] = {
{ "cdns,phy-dqs-timing-delay-semmc-sdr", 0x00380004, },
{ "cdns,phy-gate-lpbk_ctrl-delay-emmc-sdr", 0x01A00040, },
{ "cdns,phy-dll-slave-ctrl-emmc-sdr", 0x00000000, },
{ "cdns,phy-dq-timing-delay-emmc-sdr", 0x00000001, },
};
static struct sdhci_cdns6_phy_cfg emmc_ddr_phy_cfgs[] = {
{ "cdns,phy-dqs-timing-delay-emmc-ddr", 0x00380004, },
{ "cdns,phy-gate-lpbk_ctrl-delay-emmc-ddr", 0x01A00040, },
{ "cdns,phy-dll-slave-ctrl-emmc-ddr", 0x00000000, },
{ "cdns,phy-dq-timing-delay-emmc-ddr", 0x10000001, },
};
static struct sdhci_cdns6_phy_cfg emmc_hs200_phy_cfgs[] = {
{ "cdns,phy-dqs-timing-delay-emmc-hs200", 0x00380004, },
{ "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs200", 0x01A00040, },
{ "cdns,phy-dll-slave-ctrl-emmc-hs200", 0x00DADA00, },
{ "cdns,phy-dq-timing-delay-emmc-hs200", 0x00000001, },
};
static struct sdhci_cdns6_phy_cfg emmc_hs400_phy_cfgs[] = {
{ "cdns,phy-dqs-timing-delay-emmc-hs400", 0x00280004, },
{ "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs400", 0x01A00040, },
{ "cdns,phy-dll-slave-ctrl-emmc-hs400", 0x00DAD800, },
{ "cdns,phy-dq-timing-delay-emmc-hs400", 0x00000001, },
};
static struct sdhci_cdns6_ctrl_cfg sd_ds_ctrl_cfgs[] = {
{ "cdns,ctrl-hrs09-timing-delay-sd-ds", 0x0001800C, },
{ "cdns,ctrl-hrs10-lpbk_ctrl-delay-sd-ds", 0x00020000, },
{ "cdns,ctrl-hrs16-slave-ctrl-sd-ds", 0x00000000, },
{ "cdns,ctrl-hrs07-timing-delay-sd-ds", 0x00080000, },
};
static struct sdhci_cdns6_ctrl_cfg emmc_sdr_ctrl_cfgs[] = {
{ "cdns,ctrl-hrs09-timing-delay-emmc-sdr", 0x0001800C, },
{ "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-sdr", 0x00030000, },
{ "cdns,ctrl-hrs16-slave-ctrl-emmc-sdr", 0x00000000, },
{ "cdns,ctrl-hrs07-timing-delay-emmc-sdr", 0x00080000, },
};
static struct sdhci_cdns6_ctrl_cfg emmc_ddr_ctrl_cfgs[] = {
{ "cdns,ctrl-hrs09-timing-delay-emmc-ddr", 0x0001800C, },
{ "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-ddr", 0x00020000, },
{ "cdns,ctrl-hrs16-slave-ctrl-emmc-ddr", 0x11000001, },
{ "cdns,ctrl-hrs07-timing-delay-emmc-ddr", 0x00090001, },
};
static struct sdhci_cdns6_ctrl_cfg emmc_hs200_ctrl_cfgs[] = {
{ "cdns,ctrl-hrs09-timing-delay-emmc-hs200", 0x00018000, },
{ "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs200", 0x00080000, },
{ "cdns,ctrl-hrs16-slave-ctrl-emmc-hs200", 0x00000000, },
{ "cdns,ctrl-hrs07-timing-delay-emmc-hs200", 0x00090000, },
};
static struct sdhci_cdns6_ctrl_cfg emmc_hs400_ctrl_cfgs[] = {
{ "cdns,ctrl-hrs09-timing-delay-emmc-hs400", 0x00018000, },
{ "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs400", 0x00080000, },
{ "cdns,ctrl-hrs16-slave-ctrl-emmc-hs400", 0x11000000, },
{ "cdns,ctrl-hrs07-timing-delay-emmc-hs400", 0x00080000, },
};
static u32 sdhci_cdns6_read_phy_reg(struct sdhci_cdns_plat *plat, u32 addr)
{
writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
return readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
}
static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u32 val)
{
writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
}
static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
{
void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
u32 tmp;
int ret;
tmp = readl(reg);
tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
/* Switch On DLL Reset */
if (reset)
tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 0);
else
tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 1);
writel(tmp, reg);
/* After reset, wait until HRS09.PHY_INIT_COMPLETE is set to 1 within 3000us*/
if (!reset) {
ret = readl_poll_timeout(reg, tmp, (tmp & SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
3000);
}
return ret;
}
int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode)
{
DECLARE_GLOBAL_DATA_PTR;
struct sdhci_cdns6_phy_cfg *sdhci_cdns6_phy_cfgs;
struct sdhci_cdns6_ctrl_cfg *sdhci_cdns6_ctrl_cfgs;
const fdt32_t *prop;
u32 tmp;
int i, ret;
switch (mode) {
case SDHCI_CDNS_HRS06_MODE_SD:
sdhci_cdns6_phy_cfgs = sd_ds_phy_cfgs;
sdhci_cdns6_ctrl_cfgs = sd_ds_ctrl_cfgs;
break;
case SDHCI_CDNS_HRS06_MODE_MMC_SDR:
sdhci_cdns6_phy_cfgs = emmc_sdr_phy_cfgs;
sdhci_cdns6_ctrl_cfgs = emmc_sdr_ctrl_cfgs;
break;
case SDHCI_CDNS_HRS06_MODE_MMC_DDR:
sdhci_cdns6_phy_cfgs = emmc_ddr_phy_cfgs;
sdhci_cdns6_ctrl_cfgs = emmc_ddr_ctrl_cfgs;
break;
case SDHCI_CDNS_HRS06_MODE_MMC_HS200:
sdhci_cdns6_phy_cfgs = emmc_hs200_phy_cfgs;
sdhci_cdns6_ctrl_cfgs = emmc_hs200_ctrl_cfgs;
break;
case SDHCI_CDNS_HRS06_MODE_MMC_HS400:
sdhci_cdns6_phy_cfgs = emmc_hs400_phy_cfgs;
sdhci_cdns6_ctrl_cfgs = emmc_hs400_ctrl_cfgs;
break;
default:
return -EINVAL;
}
for (i = 0; i < SDHCI_CDNS6_PHY_CFG_NUM; i++) {
prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
sdhci_cdns6_phy_cfgs[i].property, NULL);
if (prop)
sdhci_cdns6_phy_cfgs[i].val = *prop;
}
for (i = 0; i < SDHCI_CDNS6_CTRL_CFG_NUM; i++) {
prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
sdhci_cdns6_ctrl_cfgs[i].property, NULL);
if (prop)
sdhci_cdns6_ctrl_cfgs[i].val = *prop;
}
/* Switch On the DLL Reset */
sdhci_cdns6_reset_phy_dll(plat, true);
sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
/* Switch Off the DLL Reset */
ret = sdhci_cdns6_reset_phy_dll(plat, false);
if (ret) {
printf("sdhci_cdns6_reset_phy is not completed\n");
return ret;
}
/* Set PHY DQ TIMING control register */
sdhci_cdns6_write_phy_reg(plat, PHY_DQ_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[3].val);
/* Set HRS09 register */
tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS09);
tmp &= ~(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
SDHCI_CDNS_HRS09_EXTENDED_RD_MODE |
SDHCI_CDNS_HRS09_RDDATA_EN |
SDHCI_CDNS_HRS09_RDCMD_EN);
tmp |= sdhci_cdns6_ctrl_cfgs[0].val;
writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS09);
/* Set HRS10 register */
tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS10);
tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
tmp |= sdhci_cdns6_ctrl_cfgs[1].val;
writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS10);
/* Set HRS16 register */
writel(sdhci_cdns6_ctrl_cfgs[2].val, plat->hrs_addr + SDHCI_CDNS_HRS16);
/* Set HRS07 register */
writel(sdhci_cdns6_ctrl_cfgs[3].val, plat->hrs_addr + SDHCI_CDNS_HRS07);
return 0;
}
int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat)
{
return sdhci_cdns6_phy_adj(dev, plat, SDHCI_CDNS_HRS06_MODE_SD);
}
int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
{
u32 tmp, tuneval;
tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP;
tmp = sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
tmp &= ~(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY |
PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY);
tmp |= FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) |
FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY, tuneval);
sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
return 0;
}