diff mbox series

[v2,1/2] mmc: sdhci_am654: Add retry tuning

Message ID 20240821192435.1619271-2-jm@ti.com (mailing list archive)
State New
Headers show
Series Add retry tuning sequence | expand

Commit Message

Judith Mendez Aug. 21, 2024, 7:24 p.m. UTC
Add retry tuning up to 10 times if we fail to find
a failing region or no passing itapdly. This is
necessary since some eMMC has been observed to never
find a failing itapdly on the first couple of tuning
iterations, but eventually does. It has been observed that
the tuning algorithm does not need to loop more than 10
times before finding a failing itapdly.

Signed-off-by: Judith Mendez <jm@ti.com>
---
Changes since v1:
- Change logic in patch 1/2 from using recursive aproach
  to calling a function iteratively for retuning
---
 drivers/mmc/host/sdhci_am654.c | 54 ++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 12 deletions(-)

Comments

Adrian Hunter Aug. 23, 2024, 1:45 p.m. UTC | #1
On 21/08/24 22:24, Judith Mendez wrote:
> Add retry tuning up to 10 times if we fail to find
> a failing region or no passing itapdly. This is
> necessary since some eMMC has been observed to never
> find a failing itapdly on the first couple of tuning
> iterations, but eventually does. It has been observed that
> the tuning algorithm does not need to loop more than 10
> times before finding a failing itapdly.
> 
> Signed-off-by: Judith Mendez <jm@ti.com>

Seems to have compile errors. Looks like 'dev' lines belong in
next patch.

drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_calculate_itap’:
drivers/mmc/host/sdhci_am654.c:453:24: error: unused variable ‘dev’ [-Werror=unused-variable]
  453 |         struct device *dev = mmc_dev(host->mmc);
      |                        ^~~
drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_do_tuning’:
drivers/mmc/host/sdhci_am654.c:508:24: error: unused variable ‘dev’ [-Werror=unused-variable]
  508 |         struct device *dev = mmc_dev(host->mmc);
      |                        ^~~
drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_platform_execute_tuning’:
drivers/mmc/host/sdhci_am654.c:553:24: error: unused variable ‘dev’ [-Werror=unused-variable]
  553 |         struct device *dev = mmc_dev(host->mmc);
      |                        ^~~
cc1: all warnings being treated as errors

> ---
> Changes since v1:
> - Change logic in patch 1/2 from using recursive aproach
>   to calling a function iteratively for retuning
> ---
>  drivers/mmc/host/sdhci_am654.c | 54 ++++++++++++++++++++++++++--------
>  1 file changed, 42 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
> index 64e10f7c9faa3..612f29fd7dfef 100644
> --- a/drivers/mmc/host/sdhci_am654.c
> +++ b/drivers/mmc/host/sdhci_am654.c
> @@ -86,6 +86,7 @@
>  
>  #define CLOCK_TOO_SLOW_HZ	50000000
>  #define SDHCI_AM654_AUTOSUSPEND_DELAY	-1
> +#define RETRY_TUNING_MAX	10
>  
>  /* Command Queue Host Controller Interface Base address */
>  #define SDHCI_AM654_CQE_BASE_ADDR 0x200
> @@ -151,6 +152,7 @@ struct sdhci_am654_data {
>  	u32 flags;
>  	u32 quirks;
>  	bool dll_enable;
> +	u32 tuning_loop;

Could use a comment explaining tuning_loop usage.

>  
>  #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
>  };
> @@ -453,12 +455,14 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
>  	int prev_fail_end = -1;
>  	u8 i;
>  
> -	if (!num_fails)
> -		return ITAPDLY_LAST_INDEX >> 1;
> +	if (!num_fails) {
> +		/* Retry tuning */
> +		return -1;
> +	}
>  
>  	if (fail_window->length == ITAPDLY_LENGTH) {
> -		dev_err(dev, "No passing ITAPDLY, return 0\n");
> -		return 0;
> +		/* Retry tuning */
> +		return -1;
>  	}
>  
>  	first_fail_start = fail_window->start;
> @@ -494,16 +498,18 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
>  	return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap;
>  }
>  
> -static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
> -					       u32 opcode)
> +static int sdhci_am654_do_tuning(struct sdhci_host *host,
> +				 u32 opcode)
>  {
>  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>  	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
>  	unsigned char timing = host->mmc->ios.timing;
>  	struct window fail_window[ITAPDLY_LENGTH];
> +	struct device *dev = mmc_dev(host->mmc);
>  	u8 curr_pass, itap;
>  	u8 fail_index = 0;
>  	u8 prev_pass = 1;
> +	int ret = 0;
>  
>  	memset(fail_window, 0, sizeof(fail_window));
>  
> @@ -532,15 +538,38 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
>  	if (fail_window[fail_index].length != 0)
>  		fail_index++;
>  
> -	itap = sdhci_am654_calculate_itap(host, fail_window, fail_index,
> -					  sdhci_am654->dll_enable);
> +	ret = sdhci_am654_calculate_itap(host, fail_window, fail_index,
> +					 sdhci_am654->dll_enable);
>  
> -	sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
> +	return ret;

Kernel style is to return directly i.e.

	return sdhci_am654_calculate_itap(host, fail_window, fail_index, sdhci_am654->dll_enable);

then don't need ret.

> +}
>  
> -	/* Save ITAPDLY */
> -	sdhci_am654->itap_del_sel[timing] = itap;
> +static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
> +					       u32 opcode)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
> +	unsigned char timing = host->mmc->ios.timing;
> +	struct device *dev = mmc_dev(host->mmc);
> +	int itapdly;
> +	int ret = 0;
>  
> -	return 0;
> +	itapdly = sdhci_am654_do_tuning(host, opcode);
> +
> +	while (sdhci_am654->tuning_loop < RETRY_TUNING_MAX && itapdly < 0) {
> +		sdhci_am654->tuning_loop++;
> +		itapdly = sdhci_am654_do_tuning(host, opcode);
> +	}

Better to try to have sdhci_am654_do_tuning() appear only once
e.g. something like:

	do {
		itapdly = sdhci_am654_do_tuning(host, opcode);
		if (itapdly >= 0)
			break;
	} while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX);


> +
> +	if (itapdly >= 0) {
> +		sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
> +		/* Save ITAPDLY */
> +		sdhci_am654->itap_del_sel[timing] = itapdly;
> +	} else {
> +		ret = -1;
> +	}

It is easier to read if the error path is separate e.g.

	if (itapdly < 0)
		return -1;

	sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
	/* Save ITAPDLY */
	sdhci_am654->itap_del_sel[timing] = itapdly;

	return 0;

Doesn't need ret then either.

> +
> +	return ret;
>  }
>  
>  static const struct sdhci_ops sdhci_am654_ops = {
> @@ -908,6 +937,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
>  		goto err_pltfm_free;
>  	}
>  
> +	sdhci_am654->tuning_loop = 0;

It is a bit arbitrary having this at probe time.  Something like
putting it in an mmc card_init callback might make more sense?

>  	host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
>  
>  	pm_runtime_get_noresume(dev);
Judith Mendez Aug. 23, 2024, 8:44 p.m. UTC | #2
Hi Adrian,

On 8/23/24 8:45 AM, Adrian Hunter wrote:
> On 21/08/24 22:24, Judith Mendez wrote:
>> Add retry tuning up to 10 times if we fail to find
>> a failing region or no passing itapdly. This is
>> necessary since some eMMC has been observed to never
>> find a failing itapdly on the first couple of tuning
>> iterations, but eventually does. It has been observed that
>> the tuning algorithm does not need to loop more than 10
>> times before finding a failing itapdly.
>>
>> Signed-off-by: Judith Mendez <jm@ti.com>
> 
> Seems to have compile errors. Looks like 'dev' lines belong in
> next patch.
> 
> drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_calculate_itap’:
> drivers/mmc/host/sdhci_am654.c:453:24: error: unused variable ‘dev’ [-Werror=unused-variable]
>    453 |         struct device *dev = mmc_dev(host->mmc);
>        |                        ^~~
> drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_do_tuning’:
> drivers/mmc/host/sdhci_am654.c:508:24: error: unused variable ‘dev’ [-Werror=unused-variable]
>    508 |         struct device *dev = mmc_dev(host->mmc);
>        |                        ^~~
> drivers/mmc/host/sdhci_am654.c: In function ‘sdhci_am654_platform_execute_tuning’:
> drivers/mmc/host/sdhci_am654.c:553:24: error: unused variable ‘dev’ [-Werror=unused-variable]
>    553 |         struct device *dev = mmc_dev(host->mmc);
>        |                        ^~~
> cc1: all warnings being treated as errors

ok, will move to second patch.

> 
>> ---
>> Changes since v1:
>> - Change logic in patch 1/2 from using recursive aproach
>>    to calling a function iteratively for retuning
>> ---
>>   drivers/mmc/host/sdhci_am654.c | 54 ++++++++++++++++++++++++++--------
>>   1 file changed, 42 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
>> index 64e10f7c9faa3..612f29fd7dfef 100644
>> --- a/drivers/mmc/host/sdhci_am654.c
>> +++ b/drivers/mmc/host/sdhci_am654.c
>> @@ -86,6 +86,7 @@
>>   
>>   #define CLOCK_TOO_SLOW_HZ	50000000
>>   #define SDHCI_AM654_AUTOSUSPEND_DELAY	-1
>> +#define RETRY_TUNING_MAX	10
>>   
>>   /* Command Queue Host Controller Interface Base address */
>>   #define SDHCI_AM654_CQE_BASE_ADDR 0x200
>> @@ -151,6 +152,7 @@ struct sdhci_am654_data {
>>   	u32 flags;
>>   	u32 quirks;
>>   	bool dll_enable;
>> +	u32 tuning_loop;
> 
> Could use a comment explaining tuning_loop usage.

Sure no problem, will add.
> 
>>   
>>   #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
>>   };
>> @@ -453,12 +455,14 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
>>   	int prev_fail_end = -1;
>>   	u8 i;
>>   
>> -	if (!num_fails)
>> -		return ITAPDLY_LAST_INDEX >> 1;
>> +	if (!num_fails) {
>> +		/* Retry tuning */
>> +		return -1;
>> +	}
>>   
>>   	if (fail_window->length == ITAPDLY_LENGTH) {
>> -		dev_err(dev, "No passing ITAPDLY, return 0\n");
>> -		return 0;
>> +		/* Retry tuning */
>> +		return -1;
>>   	}
>>   
>>   	first_fail_start = fail_window->start;
>> @@ -494,16 +498,18 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
>>   	return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap;
>>   }
>>   
>> -static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
>> -					       u32 opcode)
>> +static int sdhci_am654_do_tuning(struct sdhci_host *host,
>> +				 u32 opcode)
>>   {
>>   	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>>   	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
>>   	unsigned char timing = host->mmc->ios.timing;
>>   	struct window fail_window[ITAPDLY_LENGTH];
>> +	struct device *dev = mmc_dev(host->mmc);
>>   	u8 curr_pass, itap;
>>   	u8 fail_index = 0;
>>   	u8 prev_pass = 1;
>> +	int ret = 0;
>>   
>>   	memset(fail_window, 0, sizeof(fail_window));
>>   
>> @@ -532,15 +538,38 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
>>   	if (fail_window[fail_index].length != 0)
>>   		fail_index++;
>>   
>> -	itap = sdhci_am654_calculate_itap(host, fail_window, fail_index,
>> -					  sdhci_am654->dll_enable);
>> +	ret = sdhci_am654_calculate_itap(host, fail_window, fail_index,
>> +					 sdhci_am654->dll_enable);
>>   
>> -	sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
>> +	return ret;
> 
> Kernel style is to return directly i.e.
> 
> 	return sdhci_am654_calculate_itap(host, fail_window, fail_index, sdhci_am654->dll_enable);
> 
> then don't need ret.

Will fix.

> 
>> +}
>>   
>> -	/* Save ITAPDLY */
>> -	sdhci_am654->itap_del_sel[timing] = itap;
>> +static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
>> +					       u32 opcode)
>> +{
>> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
>> +	unsigned char timing = host->mmc->ios.timing;
>> +	struct device *dev = mmc_dev(host->mmc);
>> +	int itapdly;
>> +	int ret = 0;
>>   
>> -	return 0;
>> +	itapdly = sdhci_am654_do_tuning(host, opcode);
>> +
>> +	while (sdhci_am654->tuning_loop < RETRY_TUNING_MAX && itapdly < 0) {
>> +		sdhci_am654->tuning_loop++;
>> +		itapdly = sdhci_am654_do_tuning(host, opcode);
>> +	}
> 
> Better to try to have sdhci_am654_do_tuning() appear only once
> e.g. something like:
> 
> 	do {
> 		itapdly = sdhci_am654_do_tuning(host, opcode);
> 		if (itapdly >= 0)
> 			break;
> 	} while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX);
> 

I generally do not like using do while loops, but in this case
it is the more appropriate solution, thanks, will fix.

> 
>> +
>> +	if (itapdly >= 0) {
>> +		sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
>> +		/* Save ITAPDLY */
>> +		sdhci_am654->itap_del_sel[timing] = itapdly;
>> +	} else {
>> +		ret = -1;
>> +	}
> 
> It is easier to read if the error path is separate e.g.
> 
> 	if (itapdly < 0)
> 		return -1;
> 
> 	sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
> 	/* Save ITAPDLY */
> 	sdhci_am654->itap_del_sel[timing] = itapdly;
> 
> 	return 0;
> 
> Doesn't need ret then either.


ok, yes this looks cleaner, thanks.

> 
>> +
>> +	return ret;
>>   }
>>   
>>   static const struct sdhci_ops sdhci_am654_ops = {
>> @@ -908,6 +937,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
>>   		goto err_pltfm_free;
>>   	}
>>   
>> +	sdhci_am654->tuning_loop = 0;
> 
> It is a bit arbitrary having this at probe time.  Something like
> putting it in an mmc card_init callback might make more sense?

Sure I can move this. Thanks for reviewing!
~ Judith

> 
>>   	host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
>>   
>>   	pm_runtime_get_noresume(dev);
>
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index 64e10f7c9faa3..612f29fd7dfef 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -86,6 +86,7 @@ 
 
 #define CLOCK_TOO_SLOW_HZ	50000000
 #define SDHCI_AM654_AUTOSUSPEND_DELAY	-1
+#define RETRY_TUNING_MAX	10
 
 /* Command Queue Host Controller Interface Base address */
 #define SDHCI_AM654_CQE_BASE_ADDR 0x200
@@ -151,6 +152,7 @@  struct sdhci_am654_data {
 	u32 flags;
 	u32 quirks;
 	bool dll_enable;
+	u32 tuning_loop;
 
 #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
 };
@@ -453,12 +455,14 @@  static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
 	int prev_fail_end = -1;
 	u8 i;
 
-	if (!num_fails)
-		return ITAPDLY_LAST_INDEX >> 1;
+	if (!num_fails) {
+		/* Retry tuning */
+		return -1;
+	}
 
 	if (fail_window->length == ITAPDLY_LENGTH) {
-		dev_err(dev, "No passing ITAPDLY, return 0\n");
-		return 0;
+		/* Retry tuning */
+		return -1;
 	}
 
 	first_fail_start = fail_window->start;
@@ -494,16 +498,18 @@  static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window
 	return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap;
 }
 
-static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
-					       u32 opcode)
+static int sdhci_am654_do_tuning(struct sdhci_host *host,
+				 u32 opcode)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
 	unsigned char timing = host->mmc->ios.timing;
 	struct window fail_window[ITAPDLY_LENGTH];
+	struct device *dev = mmc_dev(host->mmc);
 	u8 curr_pass, itap;
 	u8 fail_index = 0;
 	u8 prev_pass = 1;
+	int ret = 0;
 
 	memset(fail_window, 0, sizeof(fail_window));
 
@@ -532,15 +538,38 @@  static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
 	if (fail_window[fail_index].length != 0)
 		fail_index++;
 
-	itap = sdhci_am654_calculate_itap(host, fail_window, fail_index,
-					  sdhci_am654->dll_enable);
+	ret = sdhci_am654_calculate_itap(host, fail_window, fail_index,
+					 sdhci_am654->dll_enable);
 
-	sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]);
+	return ret;
+}
 
-	/* Save ITAPDLY */
-	sdhci_am654->itap_del_sel[timing] = itap;
+static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
+					       u32 opcode)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
+	unsigned char timing = host->mmc->ios.timing;
+	struct device *dev = mmc_dev(host->mmc);
+	int itapdly;
+	int ret = 0;
 
-	return 0;
+	itapdly = sdhci_am654_do_tuning(host, opcode);
+
+	while (sdhci_am654->tuning_loop < RETRY_TUNING_MAX && itapdly < 0) {
+		sdhci_am654->tuning_loop++;
+		itapdly = sdhci_am654_do_tuning(host, opcode);
+	}
+
+	if (itapdly >= 0) {
+		sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]);
+		/* Save ITAPDLY */
+		sdhci_am654->itap_del_sel[timing] = itapdly;
+	} else {
+		ret = -1;
+	}
+
+	return ret;
 }
 
 static const struct sdhci_ops sdhci_am654_ops = {
@@ -908,6 +937,7 @@  static int sdhci_am654_probe(struct platform_device *pdev)
 		goto err_pltfm_free;
 	}
 
+	sdhci_am654->tuning_loop = 0;
 	host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
 
 	pm_runtime_get_noresume(dev);