[PATCH v2] mmc: rockchip_sdhci: Set xx_TAP_VALUE for RK3528

Jonas Karlman jonas at kwiboo.se
Tue Jul 15 13:16:44 CEST 2025


Hi Peng,

On 7/15/2025 3:15 AM, Peng Fan wrote:
>> Subject: [PATCH v2] mmc: rockchip_sdhci: Set xx_TAP_VALUE for
>> RK3528
>>
>> eMMC erase and write support on RK3528 is somewhat unreliable,
>> sometime e.g. mmc erase and write commands will fail with an error.
>>
>> Use the delay line lock value for half card clock cycle,
>> DLL_LOCK_VALUE, to set a manual xx_TAP_VALUE to fix the unreliable
>> eMMC support.
>>
>> This is only enabled for RK3528, remaining SoCs still use the automatic
>> tap value, (DLL_LOCK_VALUE * 2) % 256, same value we configure
>> manually for RK3528.
> 
> May I ask how linux kernel handle this issue you spotted?

I am not yet sure if or how this issue may be affecting Linux.

This issue turned up when I was testing eMMC on my ArmSoM Sige1 board,
where a simple "mmc erase 40 5000" to remove an existing bootloader or
use "mmc write $fileaddr 40 5000" to write the new bootloader
unexpectedly resulted in an ERROR.

When using the generic-rk3528 target (a minimal generic device tree) the
emmc module seems to initialize correctly:

  => mmc dev 0
  rk3568_sdhci_config_dll: clock=400000 enable=0
  rk3568_sdhci_config_dll: clock=400000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=200000000 enable=0
  rk3568_sdhci_config_dll: clock=200000000 enable=1
  rk3568_sdhci_config_dll: dll_tap_value=2007600 DLL_LOCK_VALUE=3b
  switch to partitions #0, OK
  mmc0(part 0) is current device
  =>

However when using a real device tree for the board the sdhci init seem
to sometime behave differently:

  => mmc dev 0
  rk3568_sdhci_config_dll: clock=400000 enable=0
  rk3568_sdhci_config_dll: clock=400000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=200000000 enable=0
  rk3568_sdhci_config_dll: clock=200000000 enable=1
  rk3568_sdhci_config_dll: dll_tap_value=2007600 DLL_LOCK_VALUE=3b
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=200000000 enable=0
  rk3568_sdhci_config_dll: clock=200000000 enable=1
  rk3568_sdhci_config_dll: dll_tap_value=2007600 DLL_LOCK_VALUE=3b
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=25000000 enable=0
  rk3568_sdhci_config_dll: clock=25000000 enable=1
  rk3568_sdhci_config_dll: clock=52000000 enable=0
  rk3568_sdhci_config_dll: clock=52000000 enable=1
  switch to partitions #0, OK
  mmc0(part 0) is current device
  =>

or something similar could be observed.

Please note that above code that is run print out the value that would
have been used for dll_tap_value but it never uses it to configure the
manual tap values.

However when the dll_tap_value are used the initialization seem to be
back to working normally (single cycle) and erase/write starts working.

Possible that some other underlying power, clock or timing issue is
causing this discrepancy.

To my knowlede Linux is using interrups during sdhci card inizialization
compared to U-Boot, so Linux may not be affected of this issue.

Regards,
Jonas

> 
> Thanks,
> Peng.
> 
>>
>> Signed-off-by: Jonas Karlman <jonas at kwiboo.se>
>> ---
>> Changes in v2:
>> - Rename flag and field value to function name, TAPVALUE_FROM_SW
>> - Simplify and make code more understandable with use of
>> FIELD_GET/PREP,
>>   should be more clear that xx_TAP_VALUE is set to DLL_LOCK_VALUE *
>> 2
>> ---
>>  drivers/mmc/rockchip_sdhci.c | 27 ++++++++++++++++++++++-----
>>  1 file changed, 22 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/mmc/rockchip_sdhci.c
>> b/drivers/mmc/rockchip_sdhci.c index 761e3619329c..cca917da68e7
>> 100644
>> --- a/drivers/mmc/rockchip_sdhci.c
>> +++ b/drivers/mmc/rockchip_sdhci.c
>> @@ -9,6 +9,7 @@
>>  #include <dm.h>
>>  #include <dm/ofnode.h>
>>  #include <dt-structs.h>
>> +#include <linux/bitfield.h>
>>  #include <linux/delay.h>
>>  #include <linux/err.h>
>>  #include <linux/libfdt.h>
>> @@ -86,6 +87,9 @@
>>  #define DLL_CMDOUT_SRC_CLK_NEG		BIT(28)
>>  #define DLL_CMDOUT_EN_SRC_CLK_NEG	BIT(29)
>>  #define DLL_CMDOUT_BOTH_CLK_EDGE	BIT(30)
>> +#define DLL_TAPVALUE_FROM_SW		BIT(25)
>> +#define DLL_TAP_VALUE_PREP(x)
>> 	FIELD_PREP(GENMASK(15, 8), (x))
>> +#define DLL_LOCK_VALUE_GET(x)
>> 	FIELD_GET(GENMASK(7, 0), (x))
>>
>>  #define DLL_LOCK_WO_TMOUT(x) \
>>  	((((x) & DWCMSHC_EMMC_DLL_LOCKED) ==
>> DWCMSHC_EMMC_DLL_LOCKED) && \ @@ -93,6 +97,7 @@
>>  #define ROCKCHIP_MAX_CLKS		3
>>
>>  #define FLAG_INVERTER_FLAG_IN_RXCLK	BIT(0)
>> +#define FLAG_TAPVALUE_FROM_SW		BIT(1)
>>
>>  struct rockchip_sdhc_plat {
>>  	struct mmc_config cfg;
>> @@ -317,7 +322,7 @@ static int rk3568_sdhci_config_dll(struct
>> sdhci_host *host, u32 clock, bool enab
>>  	struct sdhci_data *data = (struct sdhci_data
>> *)dev_get_driver_data(priv->dev);
>>  	struct mmc *mmc = host->mmc;
>>  	int val, ret;
>> -	u32 extra, txclk_tapnum;
>> +	u32 extra, txclk_tapnum, dll_tap_value;
>>
>>  	if (!enable) {
>>  		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
>> @@ -347,7 +352,15 @@ static int rk3568_sdhci_config_dll(struct
>> sdhci_host *host, u32 clock, bool enab
>>  		if (ret)
>>  			return ret;
>>
>> -		extra = DWCMSHC_EMMC_DLL_DLYENA |
>> DLL_RXCLK_ORI_GATE;
>> +		if (data->flags & FLAG_TAPVALUE_FROM_SW)
>> +			dll_tap_value = DLL_TAPVALUE_FROM_SW |
>> +
>> 	DLL_TAP_VALUE_PREP(DLL_LOCK_VALUE_GET(val) * 2);
>> +		else
>> +			dll_tap_value = 0;
>> +
>> +		extra = DWCMSHC_EMMC_DLL_DLYENA |
>> +			DLL_RXCLK_ORI_GATE |
>> +			dll_tap_value;
>>  		if (data->flags & FLAG_INVERTER_FLAG_IN_RXCLK)
>>  			extra |= DLL_RXCLK_NO_INVERTER;
>>  		sdhci_writel(host, extra,
>> DWCMSHC_EMMC_DLL_RXCLK); @@ -361,19 +374,22 @@ static int
>> rk3568_sdhci_config_dll(struct sdhci_host *host, u32 clock, bool enab
>>  				DLL_CMDOUT_BOTH_CLK_EDGE |
>>  				DWCMSHC_EMMC_DLL_DLYENA |
>>  				data->hs400_cmdout_tapnum |
>> -				DLL_CMDOUT_TAPNUM_FROM_SW;
>> +				DLL_CMDOUT_TAPNUM_FROM_SW
>> |
>> +				dll_tap_value;
>>  			sdhci_writel(host, extra,
>> DWCMSHC_EMMC_DLL_CMDOUT);
>>  		}
>>
>>  		extra = DWCMSHC_EMMC_DLL_DLYENA |
>>  			DLL_TXCLK_TAPNUM_FROM_SW |
>>  			DLL_TXCLK_NO_INVERTER |
>> -			txclk_tapnum;
>> +			txclk_tapnum |
>> +			dll_tap_value;
>>  		sdhci_writel(host, extra,
>> DWCMSHC_EMMC_DLL_TXCLK);
>>
>>  		extra = DWCMSHC_EMMC_DLL_DLYENA |
>>  			data->hs400_strbin_tapnum |
>> -			DLL_STRBIN_TAPNUM_FROM_SW;
>> +			DLL_STRBIN_TAPNUM_FROM_SW |
>> +			dll_tap_value;
>>  		sdhci_writel(host, extra,
>> DWCMSHC_EMMC_DLL_STRBIN);
>>  	} else {
>>  		/*
>> @@ -663,6 +679,7 @@ static const struct sdhci_data rk3528_data = {
>>  	.set_ios_post = rk3568_sdhci_set_ios_post,
>>  	.set_clock = rk3568_sdhci_set_clock,
>>  	.config_dll = rk3568_sdhci_config_dll,
>> +	.flags = FLAG_TAPVALUE_FROM_SW,
>>  	.hs200_txclk_tapnum = 0xc,
>>  	.hs400_txclk_tapnum = 0x6,
>>  	.hs400_cmdout_tapnum = 0x6,
>> --
>> 2.49.0
> 



More information about the U-Boot mailing list