Re: [PATCH] mmc: sdhci-omap: Workaround errata regarding SDR104/HS200 tuning failures (i929)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 29/11/18 9:05 PM, Faiz Abbas wrote:
> Errata i929 in certain OMAP5/DRA7XX/AM57XX silicon revisions
> (SPRZ426D - November 2014 - Revised February 2018 [1]) mentions
> unexpected tuning pattern errors. A small failure band may be present
> in the tuning range which may be missed by the current algorithm.
> Furthermore, the failure bands vary with temperature leading to
> different optimum tuning values for different temperatures.
> 
> As suggested in the related Application Report (SPRACA9B - October 2017
> - Revised July 2018 [2]), tuning should be done in two stages.
> In stage 1, assign the optimum ratio in the maximum pass window for the
> current temperature. In stage 2, if the chosen value is close to the
> small failure band, move away from it in the appropriate direction.
> 
> References:
> [1] http://www.ti.com/lit/pdf/sprz426
> [2] http://www.ti.com/lit/pdf/SPRACA9
> 
> Signed-off-by: Faiz Abbas <faiz_abbas@xxxxxx>

Acked-by: Adrian Hunter <adrian.hunter@xxxxxxxxx>

> ---
>  drivers/mmc/host/Kconfig      |  2 +
>  drivers/mmc/host/sdhci-omap.c | 90 ++++++++++++++++++++++++++++++++++-
>  2 files changed, 91 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1b58739d9744..6d3553f06f27 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -969,6 +969,8 @@ config MMC_SDHCI_XENON
>  config MMC_SDHCI_OMAP
>  	tristate "TI SDHCI Controller Support"
>  	depends on MMC_SDHCI_PLTFM && OF
> +	select THERMAL
> +	select TI_SOC_THERMAL
>  	help
>  	  This selects the Secure Digital Host Controller Interface (SDHCI)
>  	  support present in TI's DRA7 SOCs. The controller supports
> diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
> index b3cb39d0db6f..9ccce7ef3a60 100644
> --- a/drivers/mmc/host/sdhci-omap.c
> +++ b/drivers/mmc/host/sdhci-omap.c
> @@ -27,6 +27,7 @@
>  #include <linux/regulator/consumer.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/sys_soc.h>
> +#include <linux/thermal.h>
>  
>  #include "sdhci-pltfm.h"
>  
> @@ -286,14 +287,18 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  	struct sdhci_host *host = mmc_priv(mmc);
>  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>  	struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
> +	struct thermal_zone_device *thermal_dev;
>  	struct device *dev = omap_host->dev;
>  	struct mmc_ios *ios = &mmc->ios;
>  	u32 start_window = 0, max_window = 0;
> +	bool single_point_failure = false;
>  	u8 cur_match, prev_match = 0;
>  	u32 length = 0, max_len = 0;
>  	u32 phase_delay = 0;
> +	int temperature;
>  	int ret = 0;
>  	u32 reg;
> +	int i;
>  
>  	/* clock tuning is not needed for upto 52MHz */
>  	if (ios->clock <= 52000000)
> @@ -303,6 +308,16 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  	if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
>  		return 0;
>  
> +	thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal");
> +	if (IS_ERR(thermal_dev)) {
> +		dev_err(dev, "Unable to get thermal zone for tuning\n");
> +		return PTR_ERR(thermal_dev);
> +	}
> +
> +	ret = thermal_zone_get_temp(thermal_dev, &temperature);
> +	if (ret)
> +		return ret;
> +
>  	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
>  	reg |= DLL_SWT;
>  	sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
> @@ -317,6 +332,11 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  
>  	omap_host->is_tuning = true;
>  
> +	/*
> +	 * Stage 1: Search for a maximum pass window ignoring any
> +	 * any single point failures. If the tuning value ends up
> +	 * near it, move away from it in stage 2 below
> +	 */
>  	while (phase_delay <= MAX_PHASE_DELAY) {
>  		sdhci_omap_set_dll(omap_host, phase_delay);
>  
> @@ -324,10 +344,15 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  		if (cur_match) {
>  			if (prev_match) {
>  				length++;
> +			} else if (single_point_failure) {
> +				/* ignore single point failure */
> +				length++;
>  			} else {
>  				start_window = phase_delay;
>  				length = 1;
>  			}
> +		} else {
> +			single_point_failure = prev_match;
>  		}
>  
>  		if (length > max_len) {
> @@ -345,13 +370,76 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  		goto tuning_error;
>  	}
>  
> +	/*
> +	 * Assign tuning value as a ratio of maximum pass window based
> +	 * on temperature
> +	 */
> +	if (temperature < -20000)
> +		phase_delay = min(max_window + 4 * max_len - 24,
> +				  max_window +
> +				  DIV_ROUND_UP(13 * max_len, 16) * 4);
> +	else if (temperature < 20000)
> +		phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4;
> +	else if (temperature < 40000)
> +		phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4;
> +	else if (temperature < 70000)
> +		phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4;
> +	else if (temperature < 90000)
> +		phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4;
> +	else if (temperature < 120000)
> +		phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4;
> +	else
> +		phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4;
> +
> +	/*
> +	 * Stage 2: Search for a single point failure near the chosen tuning
> +	 * value in two steps. First in the +3 to +10 range and then in the
> +	 * +2 to -10 range. If found, move away from it in the appropriate
> +	 * direction by the appropriate amount depending on the temperature.
> +	 */
> +	for (i = 3; i <= 10; i++) {
> +		sdhci_omap_set_dll(omap_host, phase_delay + i);
> +
> +		if (mmc_send_tuning(mmc, opcode, NULL)) {
> +			if (temperature < 10000)
> +				phase_delay += i + 6;
> +			else if (temperature < 20000)
> +				phase_delay += i - 12;
> +			else if (temperature < 70000)
> +				phase_delay += i - 8;
> +			else
> +				phase_delay += i - 6;
> +
> +			goto single_failure_found;
> +		}
> +	}
> +
> +	for (i = 2; i >= -10; i--) {
> +		sdhci_omap_set_dll(omap_host, phase_delay + i);
> +
> +		if (mmc_send_tuning(mmc, opcode, NULL)) {
> +			if (temperature < 10000)
> +				phase_delay += i + 12;
> +			else if (temperature < 20000)
> +				phase_delay += i + 8;
> +			else if (temperature < 70000)
> +				phase_delay += i + 8;
> +			else if (temperature < 90000)
> +				phase_delay += i + 10;
> +			else
> +				phase_delay += i + 12;
> +
> +			goto single_failure_found;
> +		}
> +	}
> +
> +single_failure_found:
>  	reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
>  	if (!(reg & AC12_SCLK_SEL)) {
>  		ret = -EIO;
>  		goto tuning_error;
>  	}
>  
> -	phase_delay = max_window + 4 * (max_len >> 1);
>  	sdhci_omap_set_dll(omap_host, phase_delay);
>  
>  	omap_host->is_tuning = false;
> 




[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux