mirror of
https://xff.cz/git/u-boot/
synced 2025-09-01 08:42:12 +02:00
mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
As per the T210 TRM, when running at 3.3v, the SDMMC1 tap/trim and autocal values need to be set to condition the signals correctly before talking to the SD-card. This is the same as what's being done in CBoot, but it gets reset when the SDMMC1 HW is soft-reset during SD driver init, so needs to be repeated here. Also set autocal and tap/trim for SDMMC3, although no T210 boards use it for SD-card at this time. Signed-off-by: Tom Warren <twarren@nvidia.com> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* (C) Copyright 2009 SAMSUNG Electronics
|
||||
* Minkyu Kang <mk7.kang@samsung.com>
|
||||
* Jaehoon Chung <jh80.chung@samsung.com>
|
||||
* Portions Copyright 2011-2016 NVIDIA Corporation
|
||||
* Portions Copyright 2011-2019 NVIDIA Corporation
|
||||
*/
|
||||
|
||||
#include <bouncebuf.h>
|
||||
@@ -15,6 +15,9 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-tegra/tegra_mmc.h>
|
||||
#include <linux/err.h>
|
||||
#if defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA210)
|
||||
#include <asm/arch/clock.h>
|
||||
#endif
|
||||
|
||||
struct tegra_mmc_plat {
|
||||
struct mmc_config cfg;
|
||||
@@ -30,6 +33,7 @@ struct tegra_mmc_priv {
|
||||
struct gpio_desc wp_gpio; /* Write Protect GPIO */
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
int mmc_id; /* peripheral id */
|
||||
};
|
||||
|
||||
static void tegra_mmc_set_power(struct tegra_mmc_priv *priv,
|
||||
@@ -446,16 +450,19 @@ static int tegra_mmc_set_ios(struct udevice *dev)
|
||||
|
||||
static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv)
|
||||
{
|
||||
#if defined(CONFIG_TEGRA30)
|
||||
#if defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA210)
|
||||
u32 val;
|
||||
u16 clk_con;
|
||||
int timeout;
|
||||
int id = priv->mmc_id;
|
||||
|
||||
debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)priv->reg);
|
||||
debug("%s: sdmmc address = %p, id = %d\n", __func__,
|
||||
priv->reg, id);
|
||||
|
||||
/* Set the pad drive strength for SDMMC1 or 3 only */
|
||||
if (priv->reg != (void *)0x78000000 &&
|
||||
priv->reg != (void *)0x78000400) {
|
||||
if (id != PERIPH_ID_SDMMC1 && id != PERIPH_ID_SDMMC3) {
|
||||
debug("%s: settings are only valid for SDMMC1/SDMMC3!\n",
|
||||
__func__);
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -464,11 +471,65 @@ static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv)
|
||||
val |= MEMCOMP_PADCTRL_VREF;
|
||||
writel(val, &priv->reg->sdmemcmppadctl);
|
||||
|
||||
/* Disable SD Clock Enable before running auto-cal as per TRM */
|
||||
clk_con = readw(&priv->reg->clkcon);
|
||||
debug("%s: CLOCK_CONTROL = 0x%04X\n", __func__, clk_con);
|
||||
clk_con &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
|
||||
writew(clk_con, &priv->reg->clkcon);
|
||||
|
||||
val = readl(&priv->reg->autocalcfg);
|
||||
val &= 0xFFFF0000;
|
||||
val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED;
|
||||
val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET;
|
||||
writel(val, &priv->reg->autocalcfg);
|
||||
#endif
|
||||
val |= AUTO_CAL_START | AUTO_CAL_ENABLE;
|
||||
writel(val, &priv->reg->autocalcfg);
|
||||
debug("%s: AUTO_CAL_CFG = 0x%08X\n", __func__, val);
|
||||
udelay(1);
|
||||
timeout = 100; /* 10 mSec max (100*100uS) */
|
||||
do {
|
||||
val = readl(&priv->reg->autocalsts);
|
||||
udelay(100);
|
||||
} while ((val & AUTO_CAL_ACTIVE) && --timeout);
|
||||
val = readl(&priv->reg->autocalsts);
|
||||
debug("%s: Final AUTO_CAL_STATUS = 0x%08X, timeout = %d\n",
|
||||
__func__, val, timeout);
|
||||
|
||||
/* Re-enable SD Clock Enable when auto-cal is done */
|
||||
clk_con |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
|
||||
writew(clk_con, &priv->reg->clkcon);
|
||||
clk_con = readw(&priv->reg->clkcon);
|
||||
debug("%s: final CLOCK_CONTROL = 0x%04X\n", __func__, clk_con);
|
||||
|
||||
if (timeout == 0) {
|
||||
printf("%s: Warning: Autocal timed out!\n", __func__);
|
||||
/* TBD: Set CFG2TMC_SDMMC1_PAD_CAL_DRV* regs here */
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TEGRA210)
|
||||
u32 tap_value, trim_value;
|
||||
|
||||
/* Set tap/trim values for SDMMC1/3 @ <48MHz here */
|
||||
val = readl(&priv->reg->venspictl); /* aka VENDOR_SYS_SW_CNTL */
|
||||
val &= IO_TRIM_BYPASS_MASK;
|
||||
if (id == PERIPH_ID_SDMMC1) {
|
||||
tap_value = 4; /* default */
|
||||
if (val)
|
||||
tap_value = 3;
|
||||
trim_value = 2;
|
||||
} else { /* SDMMC3 */
|
||||
tap_value = 3;
|
||||
trim_value = 3;
|
||||
}
|
||||
|
||||
val = readl(&priv->reg->venclkctl);
|
||||
val &= ~TRIM_VAL_MASK;
|
||||
val |= (trim_value << TRIM_VAL_SHIFT);
|
||||
val &= ~TAP_VAL_MASK;
|
||||
val |= (tap_value << TAP_VAL_SHIFT);
|
||||
writel(val, &priv->reg->venclkctl);
|
||||
debug("%s: VENDOR_CLOCK_CNTRL = 0x%08X\n", __func__, val);
|
||||
#endif /* T210 */
|
||||
#endif /* T30/T210 */
|
||||
}
|
||||
|
||||
static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc)
|
||||
@@ -514,6 +575,13 @@ static int tegra_mmc_init(struct udevice *dev)
|
||||
unsigned int mask;
|
||||
debug(" tegra_mmc_init called\n");
|
||||
|
||||
#if defined(CONFIG_TEGRA210)
|
||||
priv->mmc_id = clock_decode_periph_id(dev);
|
||||
if (priv->mmc_id == PERIPH_ID_NONE) {
|
||||
printf("%s: Missing/invalid peripheral ID\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
tegra_mmc_reset(priv, mmc);
|
||||
|
||||
#if defined(CONFIG_TEGRA124_MMC_DISABLE_EXT_LOOPBACK)
|
||||
|
Reference in New Issue
Block a user