mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
[platform/kernel/u-boot.git] / drivers / mmc / tegra_mmc.c
index f022e93..73ac58c 100644 (file)
@@ -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)