mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
authorTom Warren <twarren@nvidia.com>
Wed, 29 May 2019 16:30:01 +0000 (09:30 -0700)
committerTom Warren <twarren@nvidia.com>
Thu, 2 Apr 2020 21:30:01 +0000 (14:30 -0700)
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>
arch/arm/include/asm/arch-tegra/tegra_mmc.h
drivers/mmc/tegra_mmc.c

index a2b6f63ff0e919f045cf26b89d278ff0a203980a..a8bfa466c02a602030d83b9c06f257653d942cfa 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * (C) Copyright 2009 SAMSUNG Electronics
  * Minkyu Kang <mk7.kang@samsung.com>
- * Portions Copyright (C) 2011-2012 NVIDIA Corporation
+ * Portions Copyright (C) 2011-2012,2019 NVIDIA Corporation
  */
 
 #ifndef __TEGRA_MMC_H_
@@ -52,7 +52,7 @@ struct tegra_mmc {
        unsigned char   admaerr;        /* offset 54h */
        unsigned char   res4[3];        /* RESERVED, offset 55h-57h */
        unsigned long   admaaddr;       /* offset 58h-5Fh */
-       unsigned char   res5[0xa0];     /* RESERVED, offset 60h-FBh */
+       unsigned char   res5[0x9c];     /* RESERVED, offset 60h-FBh */
        unsigned short  slotintstatus;  /* offset FCh */
        unsigned short  hcver;          /* HOST Version */
        unsigned int    venclkctl;      /* _VENDOR_CLOCK_CNTRL_0,    100h */
@@ -127,11 +127,23 @@ struct tegra_mmc {
 
 #define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE                    (1 << 1)
 
-/* SDMMC1/3 settings from section 24.6 of T30 TRM */
+/* SDMMC1/3 settings from SDMMCx Initialization Sequence of TRM */
 #define MEMCOMP_PADCTRL_VREF   7
-#define AUTO_CAL_ENABLED       (1 << 29)
+#define AUTO_CAL_ENABLE                (1 << 29)
+#if defined(CONFIG_TEGRA210)
+#define AUTO_CAL_ACTIVE                (1 << 31)
+#define AUTO_CAL_START         (1 << 31)
+#define AUTO_CAL_PD_OFFSET     (0x7D << 8)
+#define AUTO_CAL_PU_OFFSET     (0 << 0)
+#define IO_TRIM_BYPASS_MASK    (1 << 2)
+#define TRIM_VAL_SHIFT         24
+#define TRIM_VAL_MASK          (0x1F << TRIM_VAL_SHIFT)
+#define TAP_VAL_SHIFT          16
+#define TAP_VAL_MASK           (0xFF << TAP_VAL_SHIFT)
+#else
 #define AUTO_CAL_PD_OFFSET     (0x70 << 8)
 #define AUTO_CAL_PU_OFFSET     (0x62 << 0)
+#endif
 
 #endif /* __ASSEMBLY__ */
 #endif /* __TEGRA_MMC_H_ */
index f022e93552521f8d414fbfcae363d7f3373b2bce..73ac58c6c8d6ce68f7cbfd3189a9072ced0362c7 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)