On 1 August 2018 at 08:37, 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 v3: > replace the tuning tag with tuning function return value > > drivers/mmc/host/sdhci-esdhc.h | 1 + > drivers/mmc/host/sdhci-of-esdhc.c | 45 ++++++++++++++++++++++++++++++++++++- > drivers/mmc/host/sdhci.c | 10 +++++--- > drivers/mmc/host/sdhci.h | 2 + > 4 files changed, 53 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..a8a8502 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,10 +709,26 @@ 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; > + int err; > > /* Use tuning block for tuning procedure */ > esdhc_clock_enable(host, false); > @@ -722,7 +741,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); > + err = sdhci_execute_tuning(mmc, opcode); > + if (err == -SDHCI_TUNING_ERR && 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 err; > } > > #ifdef CONFIG_PM_SLEEP > @@ -897,6 +935,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..b858bd4 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,14 @@ 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 0; This should be treated as an error as well. Perhaps use -ETIMEDOUT. > } > > ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); > if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { > if (ctrl & SDHCI_CTRL_TUNED_CLK) > - return; /* Success! */ > + return 0; /* Success! */ > + return -SDHCI_TUNING_ERR; First, would it possible to use a generic error code? Maybe -EAGAIN. Second, the means that you are now bailing out immediately, which means the sdhci_reset_tuning() is not going to be called. Is that what you want? In such, that should likely be a separate change. > break; > } > > @@ -2309,7 +2310,8 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) > > sdhci_start_tuning(host); > > - __sdhci_execute_tuning(host, opcode); > + if (__sdhci_execute_tuning(host, opcode)) > + err = -SDHCI_TUNING_ERR; Hmm. To avoid us from changing to return error codes, while we perhaps shouldn't, it seems like we actually need to store the error code in new local sdhci variable. Something like this: host->tuning_err = __sdhci_execute_tuning(); Then instead of checking only the return value from sdhci_execute_tuning() from the esdhc_execute_tuning(), you also need to check if host->tuning_err == -EAGAIN as a trigger to re-try. Would that work? > > sdhci_end_tuning(host); > out: > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h > index 23966f8..39d85e1 100644 > --- a/drivers/mmc/host/sdhci.h > +++ b/drivers/mmc/host/sdhci.h > @@ -168,6 +168,8 @@ > > #define SDHCI_ACMD12_ERR 0x3C > > +#define SDHCI_TUNING_ERR 0x2 > + > #define SDHCI_HOST_CONTROL2 0x3E > #define SDHCI_CTRL_UHS_MASK 0x0007 > #define SDHCI_CTRL_UHS_SDR12 0x0000 Kind regards Uffe -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html