diff mbox series

[U-Boot,1/5] mmc: sunxi: add support for automatic delay calibration

Message ID 20180929234553.31019-2-vagrant@debian.org
State Superseded
Delegated to: Jagannadha Sutradharudu Teki
Headers show
Series [U-Boot,1/5] mmc: sunxi: add support for automatic delay calibration | expand

Commit Message

Vagrant Cascadian Sept. 29, 2018, 11:45 p.m. UTC
From: Vasily Khoruzhick <anarsoul@gmail.com>

A64 supports automatic delay calibration and Linux driver uses it
instead of hardcoded delays. Add support for it to u-boot driver.

Fixes eMMC instability on Pinebook

Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Signed-off-by: Vagrant Cascadian <vagrant@debian.org>
---

 arch/arm/include/asm/arch-sunxi/mmc.h |  6 +++++-
 arch/arm/mach-sunxi/Kconfig           |  1 +
 drivers/mmc/Kconfig                   |  4 ++++
 drivers/mmc/sunxi_mmc.c               | 20 +++++++++++++++++++-
 4 files changed, 29 insertions(+), 2 deletions(-)

Comments

Andre Przywara Sept. 30, 2018, 3:16 p.m. UTC | #1
On 9/30/18 12:45 AM, Vagrant Cascadian wrote:
> From: Vasily Khoruzhick <anarsoul@gmail.com>
> 
> A64 supports automatic delay calibration and Linux driver uses it
> instead of hardcoded delays. Add support for it to u-boot driver.

So technically that should be derived from the node's compatible string,
like we do in Linux. But I see that we are not there yet in U-Boot.
Meanwhile I don't think you should introduce a new Kconfig option, you
could keep the hacky U-Boot style and just add an
#if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
to the two places instead.
A better way would be to introduce a "can_calibrate" member to struct
sunxi_mmc_priv, then set this once in sunxi_mmc_init(), guarded by the
two MACH symbols as above. This would allow an easy transition to being
DT driven later and would prevent us from forgetting about this.

Cheers,
Andre.

> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> Signed-off-by: Vagrant Cascadian <vagrant@debian.org>
> ---
> 
>  arch/arm/include/asm/arch-sunxi/mmc.h |  6 +++++-
>  arch/arm/mach-sunxi/Kconfig           |  1 +
>  drivers/mmc/Kconfig                   |  4 ++++
>  drivers/mmc/sunxi_mmc.c               | 20 +++++++++++++++++++-
>  4 files changed, 29 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/mmc.h b/arch/arm/include/asm/arch-sunxi/mmc.h
> index d98c53faaa..f2deafddd2 100644
> --- a/arch/arm/include/asm/arch-sunxi/mmc.h
> +++ b/arch/arm/include/asm/arch-sunxi/mmc.h
> @@ -46,7 +46,9 @@ struct sunxi_mmc {
>  	u32 cbda;		/* 0x94 */
>  	u32 res2[26];
>  #if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_MACH_SUN50I_H6)
> -	u32 res3[64];
> +	u32 res3[17];
> +	u32 samp_dl;
> +	u32 res4[46];
>  #endif
>  	u32 fifo;		/* 0x100 / 0x200 FIFO access address */
>  };
> @@ -130,5 +132,7 @@ struct sunxi_mmc {
>  #define SUNXI_MMC_COMMON_CLK_GATE		(1 << 16)
>  #define SUNXI_MMC_COMMON_RESET			(1 << 18)
>  
> +#define SUNXI_MMC_CAL_DL_SW_EN		(0x1 << 7)
> +
>  struct mmc *sunxi_mmc_init(int sdc_no);
>  #endif /* _SUNXI_MMC_H */
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index 686f38fec4..ae77ee9e8e 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -271,6 +271,7 @@ config MACH_SUN50I
>  	bool "sun50i (Allwinner A64)"
>  	select ARM64
>  	select DM_I2C
> +	select MMC_SUNXI_SUPPORTS_CALIBRATION
>  	select PHY_SUN4I_USB
>  	select SUNXI_DE2
>  	select SUNXI_GEN_SUN6I
> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index 0a0d4aaf6c..fb8f6697d4 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -569,6 +569,10 @@ config MMC_SUNXI_HAS_NEW_MODE
>  	bool
>  	depends on MMC_SUNXI
>  
> +config MMC_SUNXI_SUPPORTS_CALIBRATION
> +	bool
> +	depends on MMC_SUNXI
> +
>  config GENERIC_ATMEL_MCI
>  	bool "Atmel Multimedia Card Interface support"
>  	depends on DM_MMC && BLK && ARCH_AT91
> diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
> index 39f15eb423..7b064b482c 100644
> --- a/drivers/mmc/sunxi_mmc.c
> +++ b/drivers/mmc/sunxi_mmc.c
> @@ -99,11 +99,15 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
>  {
>  	unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
>  	bool new_mode = false;
> +	bool calibrate = false;
>  	u32 val = 0;
>  
>  	if (IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE) && (priv->mmc_no == 2))
>  		new_mode = true;
>  
> +	if (IS_ENABLED(CONFIG_MMC_SUNXI_SUPPORTS_CALIBRATION))
> +		calibrate = true;
> +
>  	/*
>  	 * The MMC clock has an extra /2 post-divider when operating in the new
>  	 * mode.
> @@ -174,7 +178,11 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
>  		val = CCM_MMC_CTRL_MODE_SEL_NEW;
>  		setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW);
>  #endif
> -	} else {
> +	} else if (!calibrate) {
> +		/*
> +		 * Use hardcoded delay values if controller doesn't support
> +		 * calibration
> +		 */
>  		val = CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
>  			CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
>  	}
> @@ -228,6 +236,16 @@ static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
>  	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
>  	writel(rval, &priv->reg->clkcr);
>  
> +#ifdef CONFIG_MMC_SUNXI_SUPPORTS_CALIBRATION
> +	/* A64 supports calibration of delays on MMC controller and we
> +	 * have to set delay of zero before starting calibration.
> +	 * Allwinner BSP driver sets a delay only in the case of
> +	 * using HS400 which is not supported by mainline U-Boot or
> +	 * Linux at the moment
> +	 */
> +	writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl);
> +#endif
> +
>  	/* Re-enable Clock */
>  	rval |= SUNXI_MMC_CLK_ENABLE;
>  	writel(rval, &priv->reg->clkcr);
>
Maxime Ripard Oct. 1, 2018, 8:09 a.m. UTC | #2
On Sun, Sep 30, 2018 at 04:16:36PM +0100, André Przywara wrote:
> On 9/30/18 12:45 AM, Vagrant Cascadian wrote:
> > From: Vasily Khoruzhick <anarsoul@gmail.com>
> > 
> > A64 supports automatic delay calibration and Linux driver uses it
> > instead of hardcoded delays. Add support for it to u-boot driver.
> 
> So technically that should be derived from the node's compatible string,
> like we do in Linux. But I see that we are not there yet in U-Boot.
> Meanwhile I don't think you should introduce a new Kconfig option, you
> could keep the hacky U-Boot style and just add an
> #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
> to the two places instead.

IIRC, the calibration is only needed for the eMMC though, so we'd need
to check that against the MMC number too.

Maxime
Andre Przywara Oct. 1, 2018, 8:48 a.m. UTC | #3
On Mon, 1 Oct 2018 10:09:55 +0200
Maxime Ripard <maxime.ripard@bootlin.com> wrote:

> On Sun, Sep 30, 2018 at 04:16:36PM +0100, André Przywara wrote:
> > On 9/30/18 12:45 AM, Vagrant Cascadian wrote:  
> > > From: Vasily Khoruzhick <anarsoul@gmail.com>
> > > 
> > > A64 supports automatic delay calibration and Linux driver uses it
> > > instead of hardcoded delays. Add support for it to u-boot
> > > driver.  
> > 
> > So technically that should be derived from the node's compatible
> > string, like we do in Linux. But I see that we are not there yet in
> > U-Boot. Meanwhile I don't think you should introduce a new Kconfig
> > option, you could keep the hacky U-Boot style and just add an
> > #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
> > to the two places instead.  
> 
> IIRC, the calibration is only needed for the eMMC though, so we'd need
> to check that against the MMC number too.

Is that so? I was looking at the Linux driver, and that one sets
can_calibrate for both the "normal" and eMMC A64 compatible strings.
Isn't the difference between the two the new timing mode, which the
eMMC doesn't have? This looks to be covered in U-Boot, though in a
similar hacky way.

Cheers,
Andre.
Maxime Ripard Oct. 1, 2018, 10:01 a.m. UTC | #4
On Mon, Oct 01, 2018 at 09:48:37AM +0100, Andre Przywara wrote:
> On Mon, 1 Oct 2018 10:09:55 +0200
> Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> 
> > On Sun, Sep 30, 2018 at 04:16:36PM +0100, André Przywara wrote:
> > > On 9/30/18 12:45 AM, Vagrant Cascadian wrote:  
> > > > From: Vasily Khoruzhick <anarsoul@gmail.com>
> > > > 
> > > > A64 supports automatic delay calibration and Linux driver uses it
> > > > instead of hardcoded delays. Add support for it to u-boot
> > > > driver.  
> > > 
> > > So technically that should be derived from the node's compatible
> > > string, like we do in Linux. But I see that we are not there yet in
> > > U-Boot. Meanwhile I don't think you should introduce a new Kconfig
> > > option, you could keep the hacky U-Boot style and just add an
> > > #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN50I_H6)
> > > to the two places instead.  
> > 
> > IIRC, the calibration is only needed for the eMMC though, so we'd need
> > to check that against the MMC number too.
> 
> Is that so? I was looking at the Linux driver, and that one sets
> can_calibrate for both the "normal" and eMMC A64 compatible strings.
> Isn't the difference between the two the new timing mode, which the
> eMMC doesn't have? This looks to be covered in U-Boot, though in a
> similar hacky way.

You're right, sorry for the noise...

Maxime
diff mbox series

Patch

diff --git a/arch/arm/include/asm/arch-sunxi/mmc.h b/arch/arm/include/asm/arch-sunxi/mmc.h
index d98c53faaa..f2deafddd2 100644
--- a/arch/arm/include/asm/arch-sunxi/mmc.h
+++ b/arch/arm/include/asm/arch-sunxi/mmc.h
@@ -46,7 +46,9 @@  struct sunxi_mmc {
 	u32 cbda;		/* 0x94 */
 	u32 res2[26];
 #if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_MACH_SUN50I_H6)
-	u32 res3[64];
+	u32 res3[17];
+	u32 samp_dl;
+	u32 res4[46];
 #endif
 	u32 fifo;		/* 0x100 / 0x200 FIFO access address */
 };
@@ -130,5 +132,7 @@  struct sunxi_mmc {
 #define SUNXI_MMC_COMMON_CLK_GATE		(1 << 16)
 #define SUNXI_MMC_COMMON_RESET			(1 << 18)
 
+#define SUNXI_MMC_CAL_DL_SW_EN		(0x1 << 7)
+
 struct mmc *sunxi_mmc_init(int sdc_no);
 #endif /* _SUNXI_MMC_H */
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 686f38fec4..ae77ee9e8e 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -271,6 +271,7 @@  config MACH_SUN50I
 	bool "sun50i (Allwinner A64)"
 	select ARM64
 	select DM_I2C
+	select MMC_SUNXI_SUPPORTS_CALIBRATION
 	select PHY_SUN4I_USB
 	select SUNXI_DE2
 	select SUNXI_GEN_SUN6I
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 0a0d4aaf6c..fb8f6697d4 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -569,6 +569,10 @@  config MMC_SUNXI_HAS_NEW_MODE
 	bool
 	depends on MMC_SUNXI
 
+config MMC_SUNXI_SUPPORTS_CALIBRATION
+	bool
+	depends on MMC_SUNXI
+
 config GENERIC_ATMEL_MCI
 	bool "Atmel Multimedia Card Interface support"
 	depends on DM_MMC && BLK && ARCH_AT91
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 39f15eb423..7b064b482c 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -99,11 +99,15 @@  static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
 {
 	unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
 	bool new_mode = false;
+	bool calibrate = false;
 	u32 val = 0;
 
 	if (IS_ENABLED(CONFIG_MMC_SUNXI_HAS_NEW_MODE) && (priv->mmc_no == 2))
 		new_mode = true;
 
+	if (IS_ENABLED(CONFIG_MMC_SUNXI_SUPPORTS_CALIBRATION))
+		calibrate = true;
+
 	/*
 	 * The MMC clock has an extra /2 post-divider when operating in the new
 	 * mode.
@@ -174,7 +178,11 @@  static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz)
 		val = CCM_MMC_CTRL_MODE_SEL_NEW;
 		setbits_le32(&priv->reg->ntsr, SUNXI_MMC_NTSR_MODE_SEL_NEW);
 #endif
-	} else {
+	} else if (!calibrate) {
+		/*
+		 * Use hardcoded delay values if controller doesn't support
+		 * calibration
+		 */
 		val = CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
 			CCM_MMC_CTRL_SCLK_DLY(sclk_dly);
 	}
@@ -228,6 +236,16 @@  static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc)
 	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
 	writel(rval, &priv->reg->clkcr);
 
+#ifdef CONFIG_MMC_SUNXI_SUPPORTS_CALIBRATION
+	/* A64 supports calibration of delays on MMC controller and we
+	 * have to set delay of zero before starting calibration.
+	 * Allwinner BSP driver sets a delay only in the case of
+	 * using HS400 which is not supported by mainline U-Boot or
+	 * Linux at the moment
+	 */
+	writel(SUNXI_MMC_CAL_DL_SW_EN, &priv->reg->samp_dl);
+#endif
+
 	/* Re-enable Clock */
 	rval |= SUNXI_MMC_CLK_ENABLE;
 	writel(rval, &priv->reg->clkcr);