On 10 August 2018 at 12:11, Yinbo Zhu <yinbo.zhu@xxxxxxx> wrote: > In tuning mode of operation, when TBCTL[TB_EN] is set, eSDHC may report > one of the following errors : > 1)Tuning error while running tuning operation where SYSCTL2[SAMPCLKSEL] > will not get set even when SYSCTL2[EXTN] is reset. OR > 2)Data transaction error (e.g. IRQSTAT[DCE], IRQSTAT[DEBE]) during data > transaction errors. > This issue occurs when the data window sampled within eSDHC is in full > cycle. So, in that case, eSDHC is not able to find out the start and > end points of the data window and sets the sampling pointer at default > location (which is middle of the internal SD clock). If this sampling > point coincides with the data eye boundary, then it can result in the > above mentioned errors. Impact: Tuning mode of operation for SDR50, > SDR104 or HS200 speed modes may not work properly > Workaround: In case eSDHC reports tuning error or data errors in tuning > mode of operation, by add the erratum A008171 support to fix the issue. > > Signed-off-by: Yinbo Zhu <yinbo.zhu@xxxxxxx> > --- > Change in v4: > Use generic error code in tuning function. > > drivers/mmc/host/sdhci-esdhc.h | 1 + > drivers/mmc/host/sdhci-of-esdhc.c | 44 ++++++++++++++++++++++++++++++++++++- > drivers/mmc/host/sdhci.c | 9 ++++--- > drivers/mmc/host/sdhci.h | 1 + > 4 files changed, 50 insertions(+), 5 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h > index dfa58f8..3f16d9c 100644 > --- a/drivers/mmc/host/sdhci-esdhc.h > +++ b/drivers/mmc/host/sdhci-esdhc.h > @@ -60,6 +60,7 @@ > /* Tuning Block Control Register */ > #define ESDHC_TBCTL 0x120 > #define ESDHC_TB_EN 0x00000004 > +#define ESDHC_TBPTR 0x128 > > /* Control Register for DMA transfer */ > #define ESDHC_DMA_SYSCTL 0x40c > diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c > index c9685c6..fcc29c3 100644 > --- a/drivers/mmc/host/sdhci-of-esdhc.c > +++ b/drivers/mmc/host/sdhci-of-esdhc.c > @@ -77,8 +77,10 @@ struct sdhci_esdhc { > u8 vendor_ver; > u8 spec_ver; > bool quirk_incorrect_hostver; > + bool quirk_fixup_tuning; > unsigned int peripheral_clock; > const struct esdhc_clk_fixup *clk_fixup; > + u32 div_ratio; > }; > > /** > @@ -574,6 +576,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) > dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", > clock, host->max_clk / pre_div / div); > host->mmc->actual_clock = host->max_clk / pre_div / div; > + esdhc->div_ratio = pre_div * div; > pre_div >>= 1; > div--; > > @@ -706,9 +709,24 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc, > } > } > > +static struct soc_device_attribute soc_fixup_tuning[] = { > + { .family = "QorIQ T1040", .revision = "1.0", }, > + { .family = "QorIQ T2080", .revision = "1.0", }, > + { .family = "QorIQ T1023", .revision = "1.0", }, > + { .family = "QorIQ LS1021A", .revision = "1.0", }, > + { .family = "QorIQ LS1080A", .revision = "1.0", }, > + { .family = "QorIQ LS2080A", .revision = "1.0", }, > + { .family = "QorIQ LS1012A", .revision = "1.0", }, > + { .family = "QorIQ LS1043A", .revision = "1.*", }, > + { .family = "QorIQ LS1046A", .revision = "1.0", }, > + { }, > +}; > + > static int esdhc_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_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); > u32 val; > > /* Use tuning block for tuning procedure */ > @@ -722,7 +740,26 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) > sdhci_writel(host, val, ESDHC_TBCTL); > esdhc_clock_enable(host, true); > > - return sdhci_execute_tuning(mmc, opcode); > + sdhci_execute_tuning(mmc, opcode); > + if (-EAGAIN == host->tuning_err && esdhc->quirk_fixup_tuning) { I would rather use this style: if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) { > + > + /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and > + * program TBPTR[TB_WNDW_START_PTR] = 5*DIV_RATIO > + */ > + val = sdhci_readl(host, ESDHC_TBPTR); > + val = (val & ~((0x7f << 8) | 0x7f)) | > + (3 * esdhc->div_ratio) | ((5 * esdhc->div_ratio) << 8); > + sdhci_writel(host, val, ESDHC_TBPTR); > + > + /* program the software tuning mode by setting > + * TBCTL[TB_MODE]=2'h3 > + */ > + val = sdhci_readl(host, ESDHC_TBCTL); > + val |= 0x3; > + sdhci_writel(host, val, ESDHC_TBCTL); > + sdhci_execute_tuning(mmc, opcode); > + } > + return 0; > } > > #ifdef CONFIG_PM_SLEEP > @@ -897,6 +934,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) > > pltfm_host = sdhci_priv(host); > esdhc = sdhci_pltfm_priv(pltfm_host); > + if (soc_device_match(soc_fixup_tuning)) > + esdhc->quirk_fixup_tuning = true; > + else > + esdhc->quirk_fixup_tuning = false; > + > if (esdhc->vendor_ver == VENDOR_V_22) > host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; > > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c > index 1c828e0..caf1839 100644 > --- a/drivers/mmc/host/sdhci.c > +++ b/drivers/mmc/host/sdhci.c > @@ -2213,7 +2213,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) > > } > > -static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) > +static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) > { > int i; > > @@ -2230,13 +2230,13 @@ static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) > pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", > mmc_hostname(host->mmc)); > sdhci_abort_tuning(host, opcode); > - return; > + return -ETIMEDOUT; > } > > ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); > if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { > if (ctrl & SDHCI_CTRL_TUNED_CLK) > - return; /* Success! */ > + return -ETIMEDOUT; /* Success! */ This should be: return 0; I realize that in practice it doesn't matter for you, as you only look at -EAGAIN. > break; > } > > @@ -2248,6 +2248,7 @@ static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) > pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", > mmc_hostname(host->mmc)); > sdhci_reset_tuning(host); > + return -EAGAIN; > } > > int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) > @@ -2309,7 +2310,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) > > sdhci_start_tuning(host); > > - __sdhci_execute_tuning(host, opcode); > + host->tuning_err = __sdhci_execute_tuning(host, opcode); > > sdhci_end_tuning(host); > out: > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h > index 23966f8..6c4ed6d 100644 > --- a/drivers/mmc/host/sdhci.h > +++ b/drivers/mmc/host/sdhci.h > @@ -554,6 +554,7 @@ struct sdhci_host { > > unsigned int tuning_count; /* Timer count for re-tuning */ > unsigned int tuning_mode; /* Re-tuning mode supported by host */ > + unsigned int tuning_err; /* Error code for re-tuning */ > #define SDHCI_TUNING_MODE_1 0 > #define SDHCI_TUNING_MODE_2 1 > #define SDHCI_TUNING_MODE_3 2 > -- > 1.7.1 > I did the trivial changes, resolved a conflict and queued this up for v4.20, thanks! Kind regards Uffe