Re: [PATCH V4 3/3] mmc: tegra: SDMMC pads auto-calibration
From: Adrian Hunter
Date: Mon Jan 14 2019 - 08:26:30 EST
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)) {
>