All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] mmc: t210: fix autocal and 400KHz clock
@ 2020-03-16 21:59 twarren at nvidia.com
  2020-03-16 21:59 ` [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3 twarren at nvidia.com
  2020-03-16 21:59 ` [PATCH 2/2] mmc: t210: Fix 'bad' SD-card clock when doing 400KHz card detect twarren at nvidia.com
  0 siblings, 2 replies; 5+ messages in thread
From: twarren at nvidia.com @ 2020-03-16 21:59 UTC (permalink / raw
  To: u-boot

From: Tom Warren <twarren@nvidia.com>

These two patches contain fixes for two issues found on T210 MMC during 
Nano bringup. Autocal wasn't being done correctly as per the TRM, and 
the 375/400KHz MMC card detect clock wasn't using the correct parameters 
as per the TRM.

Tom Warren (2):
  mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
  mmc: t210: Fix 'bad' SD-card clock when doing 400KHz card detect

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

-- 
1.8.2.1.610.g562af5b


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
  2020-03-16 21:59 [PATCH 0/2] mmc: t210: fix autocal and 400KHz clock twarren at nvidia.com
@ 2020-03-16 21:59 ` twarren at nvidia.com
  2020-03-17 23:48   ` Jaehoon Chung
  2020-03-16 21:59 ` [PATCH 2/2] mmc: t210: Fix 'bad' SD-card clock when doing 400KHz card detect twarren at nvidia.com
  1 sibling, 1 reply; 5+ messages in thread
From: twarren at nvidia.com @ 2020-03-16 21:59 UTC (permalink / raw
  To: u-boot

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 */
 	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__);
+			__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)
-- 
1.8.2.1.610.g562af5b


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] mmc: t210: Fix 'bad' SD-card clock when doing 400KHz card detect
  2020-03-16 21:59 [PATCH 0/2] mmc: t210: fix autocal and 400KHz clock twarren at nvidia.com
  2020-03-16 21:59 ` [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3 twarren at nvidia.com
@ 2020-03-16 21:59 ` twarren at nvidia.com
  1 sibling, 0 replies; 5+ messages in thread
From: twarren at nvidia.com @ 2020-03-16 21:59 UTC (permalink / raw
  To: u-boot

From: Tom Warren <twarren@nvidia.com>

According to the HW team, for some reason the normal clock select code
picks what appears to be a perfectly valid 375KHz SD card clock, based
on the CAR clock source and SDMMC1 controller register settings (CAR =
408MHz PLLP0 divided by 68 for 6MHz, then a SD Clock Control register
divisor of 16 = 375KHz). But the resulting SD card clock, as measured by
the HW team, is 700KHz, which is out-of-spec. So the WAR is to use the
values given in the TRM PLLP table to generate a 400KHz SD-clock (CAR
clock of 24.7MHz, SD Clock Control divisor of 62) only for SDMMC1 on
T210 when the requested clock is <= 400KHz. Note that as far as I can
tell, the other requests for clocks in the Tegra MMC driver result in
valid SD clocks.

Signed-off-by: Tom Warren <twarren@nvidia.com>
---
 arch/arm/include/asm/arch-tegra/tegra_mmc.h |  2 +-
 drivers/mmc/tegra_mmc.c                     | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/arch-tegra/tegra_mmc.h b/arch/arm/include/asm/arch-tegra/tegra_mmc.h
index a8bfa46..70dcf4a 100644
--- a/arch/arm/include/asm/arch-tegra/tegra_mmc.h
+++ b/arch/arm/include/asm/arch-tegra/tegra_mmc.h
@@ -130,9 +130,9 @@ struct tegra_mmc {
 /* SDMMC1/3 settings from SDMMCx Initialization Sequence of TRM */
 #define MEMCOMP_PADCTRL_VREF	7
 #define AUTO_CAL_ENABLE		(1 << 29)
-#if defined(CONFIG_TEGRA210)
 #define AUTO_CAL_ACTIVE		(1 << 31)
 #define AUTO_CAL_START		(1 << 31)
+#if defined(CONFIG_TEGRA210)
 #define AUTO_CAL_PD_OFFSET	(0x7D << 8)
 #define AUTO_CAL_PU_OFFSET	(0 << 0)
 #define IO_TRIM_BYPASS_MASK	(1 << 2)
diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c
index a4bd679..3ca947c 100644
--- a/drivers/mmc/tegra_mmc.c
+++ b/drivers/mmc/tegra_mmc.c
@@ -376,6 +376,24 @@ static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock)
 
 	rate = clk_set_rate(&priv->clk, clock);
 	div = (rate + clock - 1) / clock;
+
+#if defined(CONFIG_TEGRA210)
+	if (priv->mmc_id == PERIPH_ID_SDMMC1 && clock <= 400000) {
+		/* clock_adjust_periph_pll_div() chooses a 'bad' clock
+		 * on SDMMC1 T210, so skip it here and force a clock
+		 * that's been spec'd in the table in the TRM for
+		 * card-detect (400KHz).
+		 */
+		uint effective_rate = clock_adjust_periph_pll_div(priv->mmc_id,
+				CLOCK_ID_PERIPH, 24727273, NULL);
+		div = 62;
+
+		debug("%s: WAR: Using SDMMC1 clock of %u, div %d to achieve %dHz card clock ...\n",
+			__func__, effective_rate, div, clock);
+	} else
+		clock_adjust_periph_pll_div(priv->mmc_id, CLOCK_ID_PERIPH, clock,
+				    &div);
+#endif
 	debug("div = %d\n", div);
 
 	writew(0, &priv->reg->clkcon);
-- 
1.8.2.1.610.g562af5b


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
  2020-03-16 21:59 ` [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3 twarren at nvidia.com
@ 2020-03-17 23:48   ` Jaehoon Chung
  2020-03-18 16:28     ` Tom Warren
  0 siblings, 1 reply; 5+ messages in thread
From: Jaehoon Chung @ 2020-03-17 23:48 UTC (permalink / raw
  To: u-boot

On 3/17/20 6:59 AM, twarren at 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__);
> +			__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;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)
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3
  2020-03-17 23:48   ` Jaehoon Chung
@ 2020-03-18 16:28     ` Tom Warren
  0 siblings, 0 replies; 5+ messages in thread
From: Tom Warren @ 2020-03-18 16:28 UTC (permalink / raw
  To: u-boot

[Tom] see below

-----Original Message-----
From: Jaehoon Chung <jh80.chung@samsung.com> 
Sent: Tuesday, March 17, 2020 4:48 PM
To: Tom Warren <TWarren@nvidia.com>; u-boot at lists.denx.de
Subject: Re: [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3

External email: Use caution opening links or attachments


On 3/17/20 6:59 AM, twarren at 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"..
[Tom] yes, the comment was/is correct, it's the number of reserved bytes that was wrong - 0x9C is correct.

>       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__);
> +                     __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;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?
[Tom] According to the HW team, yes, you need to write the offsets, then write the START/ENABLE bits.

> +     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?
[Tom] yes, AFAIR it was only for the debug printf below.

> +     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...
[Tom] The X1 TRM uses these numbers, w/o much explanation, unfortunately. They're taken from the tap/trim tables in section 32.7.6 of the X1 TRM, page 2615. See also the VENDOR_CLOCK_CNTRL_0 register, section 32.8.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)
>


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-03-18 16:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-03-16 21:59 [PATCH 0/2] mmc: t210: fix autocal and 400KHz clock twarren at nvidia.com
2020-03-16 21:59 ` [PATCH 1/2] mmc: t210: Add autocal and tap/trim updates for SDMMC1/3 twarren at nvidia.com
2020-03-17 23:48   ` Jaehoon Chung
2020-03-18 16:28     ` Tom Warren
2020-03-16 21:59 ` [PATCH 2/2] mmc: t210: Fix 'bad' SD-card clock when doing 400KHz card detect twarren at nvidia.com

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.