
On 3/17/20 6:59 AM, twarren@nvidia.com wrote:
From: Tom Warren twarren@nvidia.com
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
arch/arm/include/asm/arch-tegra/tegra_mmc.h | 20 +++++-- drivers/mmc/tegra_mmc.c | 84 ++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 12 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra/tegra_mmc.h b/arch/arm/include/asm/arch-tegra/tegra_mmc.h index a2b6f63..a8bfa46 100644 --- a/arch/arm/include/asm/arch-tegra/tegra_mmc.h +++ b/arch/arm/include/asm/arch-tegra/tegra_mmc.h @@ -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 */
Is comment right? "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_ */ diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index f022e93..a4bd679 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -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_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__);
return; }__func__);
@@ -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;hen
- writel(val, &priv->reg->autocalcfg);
PU_OFFSET | PD_OFFSET is written to autocalcfg register. And AUTO_CAL_START | AUTO_CAL_ENABLE is written to autocalcfg again. Does it do different behavior when together with above values is set?
- 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);
Why read value at here? for just debugging message?
- 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 */
I want not to use magic number like 4. we don't know what means 4, 3, 2...
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)