diff mbox series

[1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3

Message ID 1585261822-3420-2-git-send-email-tomcwarren3959@gmail.com
State Accepted
Commit 5e965e814067e7539943bbaeb47d7bf9738a701b
Delegated to: Tom Warren
Headers show
Series mmc: t210: fix autocal and 400KHz clock | expand

Commit Message

Tom Warren March 26, 2020, 10:30 p.m. UTC
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>
---
Changes for v2:
 - Added clocks.h include for TEGRA30 to fix T30 32-bit builds

 arch/arm/include/asm/arch-tegra/tegra_mmc.h | 20 +++++--
 drivers/mmc/tegra_mmc.c                     | 84 ++++++++++++++++++++++++++---
 2 files changed, 92 insertions(+), 12 deletions(-)

Comments

Jaehoon Chung April 2, 2020, 1:59 a.m. UTC | #1
Hi Tom,

On 3/27/20 7:30 AM, tomcwarren3959@gmail.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>


Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>

Best Regards,
Jaehoon Chung


> ---
> Changes for v2:
>  - Added clocks.h include for TEGRA30 to fix T30 32-bit builds
> 
>  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 */
>  	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..73ac58c 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_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)
>
diff mbox series

Patch

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 */
 	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..73ac58c 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_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)