]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
mmc: host: Add dll calibration support
authorR Raj Kumar <rrajk@nvidia.com>
Thu, 10 Jul 2014 11:30:27 +0000 (17:00 +0530)
committerPavan Kunapuli <pkunapuli@nvidia.com>
Wed, 6 May 2015 13:24:30 +0000 (06:24 -0700)
Added dll calibration support for eMMC device
which enumerates in HS400 mode at 200MHz.

Signed-off-by: R Raj Kumar <rrajk@nvidia.com>
Reviewed-on: http://git-master/r/440671
(cherry picked from commit 3274696ba2bce1c60251b29ead5783c47767e123)
Change-Id: Iaa4fcb014b72b0769dd45c3ac4cb1ec34e3a2e15
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-on: http://git-master/r/739598

drivers/mmc/core/mmc.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
include/linux/mmc/host.h

index 36868f4619bb26a00d9239c5a3d7bb5a1cbac83f..9fd71cda622f99add8a6c2bed7bf0b7db8ae9f36 100644 (file)
@@ -1546,6 +1546,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                host->card = card;
 
        mmc_free_ext_csd(ext_csd);
+
+       if (mmc_card_hs400(card) && card->host->ops->post_init) {
+               mmc_host_clk_hold(card->host);
+               card->host->ops->post_init(card->host);
+               mmc_host_clk_release(card->host);
+       }
+
        return 0;
 
 free_card:
index f643c0510fe82e75fad06fcd3a8aaf1c3c21056b..2fa899d80eb4be32a52c4567a6163f6bfeba401a 100644 (file)
 #define SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_SHIFT      8
 #define SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_MASK       0x3F
 
+#define SDHCI_VNDR_DLLCAL_CFG                          0x1b0
+#define SDHCI_VNDR_DLLCAL_CFG_EN_CALIBRATE             0x10000000
+
+#define SDHCI_VNDR_DLLCAL_CFG_STATUS                   0x1bc
+#define SDHCI_VNDR_DLLCAL_CFG_STATUS_DLL_ACTIVE                0x10000000
+
 #define SDHCI_VNDR_TUN_CTRL                            0x1c0
 /* Enable Re-tuning request only when CRC error is detected
  * in SDR50/SDR104/HS200 modes
@@ -198,6 +204,18 @@ struct sdhci_tegra {
        int drive_group_sel;
 };
 
+static unsigned long get_nearest_clock_freq(unsigned long pll_rate,
+               unsigned long desired_rate);
+static inline int sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci,
+       unsigned int tap_delay);
+static inline int sdhci_tegra_set_trim_delay(struct sdhci_host *sdhci,
+       unsigned int trim_delay);
+static inline int sdhci_tegra_set_dqs_trim_delay(struct sdhci_host *sdhci,
+       unsigned int dqs_trim_delay);
+static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci,
+       unsigned char signal_voltage);
+static void tegra_sdhci_do_dll_calibration(struct sdhci_host *sdhci);
+
 static int show_error_stats_dump(struct seq_file *s, void *data)
 {
        struct sdhci_host *host = s->private;
@@ -658,6 +676,32 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
        mutex_unlock(&tegra_host->set_clock_mutex);
 }
 
+static void tegra_sdhci_do_dll_calibration(struct sdhci_host *sdhci)
+{
+       u32 dll_cfg;
+       unsigned timeout = 5;
+
+       dll_cfg = sdhci_readl(sdhci, SDHCI_VNDR_DLLCAL_CFG);
+       dll_cfg |= SDHCI_VNDR_DLLCAL_CFG_EN_CALIBRATE;
+       sdhci_writel(sdhci, dll_cfg, SDHCI_VNDR_DLLCAL_CFG);
+
+       mdelay(1);
+
+       /* Wait until the dll calibration is done */
+       do {
+               if (!(sdhci_readl(sdhci, SDHCI_VNDR_DLLCAL_CFG_STATUS) &
+                       SDHCI_VNDR_DLLCAL_CFG_STATUS_DLL_ACTIVE))
+                       break;
+
+               mdelay(1);
+               timeout--;
+       } while (timeout);
+
+       if (!timeout) {
+               dev_err(mmc_dev(sdhci->mmc), "DLL calibration is failed\n");
+       }
+}
+
 static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci,
        unsigned char signal_voltage)
 {
index 7ba02e1f5437d6e1991ac25173b8f5c5fca3e0c3..075eb317a4729df1c9e3fded7350d33f0e7bc155 100644 (file)
@@ -1611,6 +1611,19 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
        }
 }
 
+/* Execute DLL calibration once for MMC device if it is
+ * enumerated in HS400 mode at 200MHz clock freq before
+ * starting any data transfer.
+ */
+static void sdhci_post_init(struct mmc_host *mmc)
+{
+       struct sdhci_host *host;
+
+       host = mmc_priv(mmc);
+
+       if (host->ops->post_init)
+               host->ops->post_init(host);
+}
 /*****************************************************************************\
  *                                                                           *
  * MMC callbacks                                                             *
@@ -2458,6 +2471,7 @@ static const struct mmc_host_ops sdhci_ops = {
        .card_event                     = sdhci_card_event,
        .card_busy      = sdhci_card_busy,
        .select_drive_strength          = sdhci_select_drive_strength,
+       .post_init      = sdhci_post_init,
 };
 
 /*****************************************************************************\
index a6c635610168d1285a4422348f8d346a4a827ff2..edd8e1d44c48a3ed15aab95ca22909d41c5983fd 100644 (file)
@@ -311,6 +311,7 @@ struct sdhci_ops {
        int     (*sd_error_stats)(struct sdhci_host *host, u32 int_status);
        int     (*get_drive_strength)(struct sdhci_host *host,
                unsigned int max_dtr, int host_drv, int card_drv);
+       void    (*post_init)(struct sdhci_host *host);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
index 7cc31824cfc01d3822e25bf9a3baa1dc71c2248e..905ae6f921c5dc480bda39e20df4bd878a11ca19 100644 (file)
@@ -149,6 +149,7 @@ struct mmc_host_ops {
         */
        int     (*multi_io_quirk)(struct mmc_card *card,
                                  unsigned int direction, int blk_size);
+       void    (*post_init)(struct mmc_host *host);
 };
 
 struct mmc_card;