mmc: sdhci-of-esdhc: add erratum A011334 support
authorYangbo Lu <yangbo.lu@nxp.com>
Fri, 23 Nov 2018 03:15:35 +0000 (11:15 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 17 Dec 2018 07:26:24 +0000 (08:26 +0100)
There are timing violations in case of few division ratio options
are selected for card clock frequency. prescaler*divisor options
/3,/5,/6,/7,/9,/10,/11,/13,/14 and /15 are not available in LX2
Rev1.0. prescaler*divisor options /4,/8 and /12 only available in
LX2 Rev1.0. Applicable only for HS400 mode. so by add the erratum
A011334 support to limit the prescaler*divisor in LX2 REV1.0

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-of-esdhc.c

index fb4fe96821ce484ffa98d99a95217b9ea07d3fa1..b73d75251f8a965faee4030eb480db60e9addca9 100644 (file)
@@ -78,6 +78,7 @@ struct sdhci_esdhc {
        u8 vendor_ver;
        u8 spec_ver;
        bool quirk_incorrect_hostver;
+       bool quirk_limited_clk_division;
        bool quirk_fixup_tuning;
        unsigned int peripheral_clock;
        const struct esdhc_clk_fixup *clk_fixup;
@@ -544,6 +545,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
        struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
        int pre_div = 1;
        int div = 1;
+       int division;
        ktime_t timeout;
        long fixup = 0;
        u32 temp;
@@ -579,6 +581,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
        while (host->max_clk / pre_div / div > clock && div < 16)
                div++;
 
+       if (esdhc->quirk_limited_clk_division &&
+           clock == MMC_HS200_MAX_DTR &&
+           (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
+            host->flags & SDHCI_HS400_TUNING)) {
+               division = pre_div * div;
+               if (division <= 4) {
+                       pre_div = 4;
+                       div = 1;
+               } else if (division <= 8) {
+                       pre_div = 4;
+                       div = 2;
+               } else if (division <= 12) {
+                       pre_div = 4;
+                       div = 3;
+               } else {
+                       pr_warn("%s: using upsupported clock division.\n",
+                               mmc_hostname(host->mmc));
+               }
+       }
+
        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;
@@ -778,6 +800,10 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
        u32 val;
        int ret;
 
+       if (esdhc->quirk_limited_clk_division &&
+           host->flags & SDHCI_HS400_TUNING)
+               esdhc_of_set_clock(host, host->clock);
+
        esdhc_tuning_block_enable(host, true);
 
        hs400_tuning = host->flags & SDHCI_HS400_TUNING;
@@ -908,6 +934,11 @@ static struct soc_device_attribute soc_incorrect_hostver[] = {
        { },
 };
 
+static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = {
+       { .family = "QorIQ LX2160A", .revision = "1.0", },
+       { },
+};
+
 static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
 {
        const struct of_device_id *match;
@@ -930,6 +961,11 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
        else
                esdhc->quirk_incorrect_hostver = false;
 
+       if (soc_device_match(soc_fixup_sdhc_clkdivs))
+               esdhc->quirk_limited_clk_division = true;
+       else
+               esdhc->quirk_limited_clk_division = false;
+
        match = of_match_node(sdhci_esdhc_of_match, pdev->dev.of_node);
        if (match)
                esdhc->clk_fixup = match->data;