Re: [PATCH V4 3/3] mmc: tegra: SDMMC pads auto-calibration

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

 



On 11/01/19 12:46 AM, Sowjanya Komatineni wrote:
> Program initial drive code offsets which will be used by auto
> calibration process.
> 
> Program fixed drive strengths for SDMMC pads in pad control
> register when auto cal timeouts.
> Fixed settings are based on Pre-SI analysis of the pad design.
> 
> Signed-off-by: Sowjanya Komatineni <skomatineni@xxxxxxxxxx>

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

> ---
>  drivers/mmc/host/sdhci-tegra.c | 160 ++++++++++++++++++++++++++++++-----------
>  1 file changed, 119 insertions(+), 41 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index e6ace31e2a41..7d681a8fa4ba 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -75,6 +75,7 @@
>  #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK	0x0000000f
>  #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL	0x7
>  #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD	BIT(31)
> +#define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK		0x07FFF000
>  
>  #define SDHCI_TEGRA_AUTO_CAL_STATUS			0x1ec
>  #define SDHCI_TEGRA_AUTO_CAL_ACTIVE			BIT(31)
> @@ -121,6 +122,8 @@ struct sdhci_tegra {
>  	struct pinctrl *pinctrl_sdmmc;
>  	struct pinctrl_state *pinctrl_state_3v3;
>  	struct pinctrl_state *pinctrl_state_1v8;
> +	struct pinctrl_state *pinctrl_state_3v3_drv;
> +	struct pinctrl_state *pinctrl_state_1v8_drv;
>  
>  	struct sdhci_tegra_autocal_offsets autocal_offsets;
>  	ktime_t last_calib;
> @@ -411,6 +414,76 @@ static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
>  	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
>  }
>  
> +static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
> +				   bool state_drvupdn)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> +	struct sdhci_tegra_autocal_offsets *offsets =
> +						&tegra_host->autocal_offsets;
> +	struct pinctrl_state *pinctrl_drvupdn = NULL;
> +	int ret = 0;
> +	u8 drvup = 0, drvdn = 0;
> +	u32 reg;
> +
> +	if (!state_drvupdn) {
> +		/* PADS Drive Strength */
> +		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
> +			if (tegra_host->pinctrl_state_1v8_drv) {
> +				pinctrl_drvupdn =
> +					tegra_host->pinctrl_state_1v8_drv;
> +			} else {
> +				drvup = offsets->pull_up_1v8_timeout;
> +				drvdn = offsets->pull_down_1v8_timeout;
> +			}
> +		} else {
> +			if (tegra_host->pinctrl_state_3v3_drv) {
> +				pinctrl_drvupdn =
> +					tegra_host->pinctrl_state_3v3_drv;
> +			} else {
> +				drvup = offsets->pull_up_3v3_timeout;
> +				drvdn = offsets->pull_down_3v3_timeout;
> +			}
> +		}
> +
> +		if (pinctrl_drvupdn != NULL) {
> +			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
> +							pinctrl_drvupdn);
> +			if (ret < 0)
> +				dev_err(mmc_dev(host->mmc),
> +					"failed pads drvupdn, ret: %d\n", ret);
> +		} else if ((drvup) || (drvdn)) {
> +			reg = sdhci_readl(host,
> +					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> +			reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
> +			reg |= (drvup << 20) | (drvdn << 12);
> +			sdhci_writel(host, reg,
> +					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> +		}
> +
> +	} else {
> +		/* Dual Voltage PADS Voltage selection */
> +		if (!tegra_host->pad_control_available)
> +			return 0;
> +
> +		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
> +			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
> +						tegra_host->pinctrl_state_1v8);
> +			if (ret < 0)
> +				dev_err(mmc_dev(host->mmc),
> +					"setting 1.8V failed, ret: %d\n", ret);
> +		} else {
> +			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
> +						tegra_host->pinctrl_state_3v3);
> +			if (ret < 0)
> +				dev_err(mmc_dev(host->mmc),
> +					"setting 3.3V failed, ret: %d\n", ret);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
>  static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
>  {
>  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> @@ -437,6 +510,7 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
>  			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
>  	}
>  
> +	/* Set initial offset before auto-calibration */
>  	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
>  
>  	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
> @@ -460,19 +534,15 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
>  	if (ret) {
>  		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
>  
> -		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
> -			pdpu = offsets.pull_down_1v8_timeout << 8 |
> -			       offsets.pull_up_1v8_timeout;
> -		else
> -			pdpu = offsets.pull_down_3v3_timeout << 8 |
> -			       offsets.pull_up_3v3_timeout;
> -
> -		/* Disable automatic calibration and use fixed offsets */
> +		/* Disable automatic cal and use fixed Drive Strengths */
>  		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
>  		reg &= ~SDHCI_AUTO_CAL_ENABLE;
>  		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
>  
> -		tegra_sdhci_set_pad_autocal_offset(host, pdpu);
> +		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
> +		if (ret < 0)
> +			dev_err(mmc_dev(host->mmc),
> +				"Setting drive strengths failed: %d\n", ret);
>  	}
>  }
>  
> @@ -511,26 +581,46 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
>  	err = device_property_read_u32(host->mmc->parent,
>  			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
>  			&autocal->pull_up_3v3_timeout);
> -	if (err)
> +	if (err) {
> +		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
> +			(tegra_host->pinctrl_state_3v3_drv == NULL))
> +			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
> +				mmc_hostname(host->mmc));
>  		autocal->pull_up_3v3_timeout = 0;
> +	}
>  
>  	err = device_property_read_u32(host->mmc->parent,
>  			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
>  			&autocal->pull_down_3v3_timeout);
> -	if (err)
> +	if (err) {
> +		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
> +			(tegra_host->pinctrl_state_3v3_drv == NULL))
> +			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
> +				mmc_hostname(host->mmc));
>  		autocal->pull_down_3v3_timeout = 0;
> +	}
>  
>  	err = device_property_read_u32(host->mmc->parent,
>  			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
>  			&autocal->pull_up_1v8_timeout);
> -	if (err)
> +	if (err) {
> +		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
> +			(tegra_host->pinctrl_state_1v8_drv == NULL))
> +			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
> +				mmc_hostname(host->mmc));
>  		autocal->pull_up_1v8_timeout = 0;
> +	}
>  
>  	err = device_property_read_u32(host->mmc->parent,
>  			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
>  			&autocal->pull_down_1v8_timeout);
> -	if (err)
> +	if (err) {
> +		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
> +			(tegra_host->pinctrl_state_1v8_drv == NULL))
> +			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
> +				mmc_hostname(host->mmc));
>  		autocal->pull_down_1v8_timeout = 0;
> +	}
>  
>  	err = device_property_read_u32(host->mmc->parent,
>  			"nvidia,pad-autocal-pull-up-offset-sdr104",
> @@ -743,32 +833,6 @@ static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
>  	return mmc_send_tuning(host->mmc, opcode, NULL);
>  }
>  
> -static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage)
> -{
> -	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> -	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> -	int ret;
> -
> -	if (!tegra_host->pad_control_available)
> -		return 0;
> -
> -	if (voltage == MMC_SIGNAL_VOLTAGE_180) {
> -		ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
> -					   tegra_host->pinctrl_state_1v8);
> -		if (ret < 0)
> -			dev_err(mmc_dev(host->mmc),
> -				"setting 1.8V failed, ret: %d\n", ret);
> -	} else {
> -		ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
> -					   tegra_host->pinctrl_state_3v3);
> -		if (ret < 0)
> -			dev_err(mmc_dev(host->mmc),
> -				"setting 3.3V failed, ret: %d\n", ret);
> -	}
> -
> -	return ret;
> -}
> -
>  static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
>  						   struct mmc_ios *ios)
>  {
> @@ -778,7 +842,7 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
>  	int ret = 0;
>  
>  	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
> -		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
> +		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
>  		if (ret < 0)
>  			return ret;
>  		ret = sdhci_start_signal_voltage_switch(mmc, ios);
> @@ -786,7 +850,7 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
>  		ret = sdhci_start_signal_voltage_switch(mmc, ios);
>  		if (ret < 0)
>  			return ret;
> -		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
> +		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
>  	}
>  
>  	if (tegra_host->pad_calib_required)
> @@ -805,6 +869,20 @@ static int tegra_sdhci_init_pinctrl_info(struct device *dev,
>  		return -1;
>  	}
>  
> +	tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
> +				tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
> +	if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
> +		if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
> +			tegra_host->pinctrl_state_1v8_drv = NULL;
> +	}
> +
> +	tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
> +				tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
> +	if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
> +		if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
> +			tegra_host->pinctrl_state_3v3_drv = NULL;
> +	}
> +
>  	tegra_host->pinctrl_state_3v3 =
>  		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
>  	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
> 




[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