Merge branch '2020-06-15-misc-bugfixes'
[oweals/u-boot.git] / drivers / mmc / sdhci-cadence.c
index 0b174fc44d7bbf0f280d4af13796fda4534fb125..7b5010b6558953a3a785011ef25430eb94020efd 100644 (file)
@@ -1,17 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2016 Socionext Inc.
  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <dm.h>
+#include <dm/device_compat.h>
 #include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/sizes.h>
-#include <libfdt.h>
+#include <linux/libfdt.h>
 #include <mmc.h>
 #include <sdhci.h>
 
@@ -171,6 +173,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
 {
        void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS06;
        u32 tmp;
+       int i, ret;
 
        if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
                return -EINVAL;
@@ -178,11 +181,23 @@ static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
        tmp = readl(reg);
        tmp &= ~SDHCI_CDNS_HRS06_TUNE;
        tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
-       tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
-       writel(tmp, reg);
 
-       return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
-                                 1);
+       /*
+        * Workaround for IP errata:
+        * The IP6116 SD/eMMC PHY design has a timing issue on receive data
+        * path. Send tune request twice.
+        */
+       for (i = 0; i < 2; i++) {
+               tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
+               writel(tmp, reg);
+
+               ret = readl_poll_timeout(reg, tmp,
+                                        !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), 1);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 
 static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev,
@@ -270,12 +285,13 @@ static int sdhci_cdns_probe(struct udevice *dev)
        if (ret)
                return ret;
 
+       host->mmc = &plat->mmc;
+       host->mmc->dev = dev;
        ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
        if (ret)
                return ret;
 
        upriv->mmc = &plat->mmc;
-       host->mmc = &plat->mmc;
        host->mmc->priv = host;
 
        return sdhci_probe(dev);