]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/mmc/host/sdhci-tegra.c
mmc: Tegra: Fix calculation for partial_win_tap
[linux-2.6.git] / drivers / mmc / host / sdhci-tegra.c
index cbc542dd1b59c5ff355086ada450f4adb34aab31..7f6aa101e0d316b848252d49f69c68fe70c37e0b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2010 Google, Inc.
  *
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
 #include <linux/mmc/sd.h>
 #include <linux/regulator/consumer.h>
 #include <linux/delay.h>
-
+#include <linux/pm_runtime.h>
 #include <asm/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 
 #include <mach/gpio-tegra.h>
 #include <mach/sdhci.h>
 #include <mach/io_dpd.h>
+#include <mach/pinmux.h>
 
 #include "sdhci-pltfm.h"
 
-#define SDHCI_VENDOR_CLOCK_CNTRL       0x100
-#define SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK     0x1
-#define SDHCI_VENDOR_CLOCK_CNTRL_PADPIPE_CLKEN_OVERRIDE        0x8
-#define SDHCI_VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE       0x4
-#define SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT   8
-#define SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT       16
-#define SDHCI_VENDOR_CLOCK_CNTRL_SDR50_TUNING          0x20
-
-#define SDHCI_VENDOR_MISC_CNTRL                0x120
-#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT  0x8
-#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT   0x10
-#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_DDR50_SUPPORT   0x200
-#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SD_3_0  0x20
+#define SDHCI_VNDR_CLK_CTRL    0x100
+#define SDHCI_VNDR_CLK_CTRL_SDMMC_CLK  0x1
+#define SDHCI_VNDR_CLK_CTRL_PADPIPE_CLKEN_OVERRIDE     0x8
+#define SDHCI_VNDR_CLK_CTRL_SPI_MODE_CLKEN_OVERRIDE    0x4
+#define SDHCI_VNDR_CLK_CTRL_BASE_CLK_FREQ_SHIFT        8
+#define SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT    16
+#define SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_SHIFT   24
+#define SDHCI_VNDR_CLK_CTRL_SDR50_TUNING               0x20
+
+#define SDHCI_VNDR_MISC_CTRL           0x120
+#define SDHCI_VNDR_MISC_CTRL_ENABLE_SDR104_SUPPORT     0x8
+#define SDHCI_VNDR_MISC_CTRL_ENABLE_SDR50_SUPPORT      0x10
+#define SDHCI_VNDR_MISC_CTRL_ENABLE_DDR50_SUPPORT      0x200
+#define SDHCI_VNDR_MISC_CTRL_ENABLE_SD_3_0     0x20
+#define SDHCI_VNDR_MISC_CTRL_INFINITE_ERASE_TIMEOUT    0x1
 
 #define SDMMC_SDMEMCOMPPADCTRL 0x1E0
 #define SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK   0xF
 
 #define SDMMC_AUTO_CAL_CONFIG  0x1E4
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_START   0x80000000
 #define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE  0x20000000
 #define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT 0x8
 #define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET       0x70
 #define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET       0x62
 
+#define SDMMC_AUTO_CAL_STATUS  0x1EC
+#define SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE  0x80000000
+#define SDMMC_AUTO_CAL_STATUS_PULLDOWN_OFFSET  24
+#define PULLUP_ADJUSTMENT_OFFSET       20
+
 #define SDHOST_1V8_OCR_MASK    0x8
 #define SDHOST_HIGH_VOLT_MIN   2700000
 #define SDHOST_HIGH_VOLT_MAX   3600000
+#define SDHOST_HIGH_VOLT_2V8   2800000
 #define SDHOST_LOW_VOLT_MIN    1800000
 #define SDHOST_LOW_VOLT_MAX    1800000
 
 #define TEGRA2_SDHOST_STD_FREQ 50000000
 #define TEGRA3_SDHOST_STD_FREQ 104000000
 
-#define SD_SEND_TUNING_PATTERN 19
-#define MAX_TAP_VALUES 256
+#define MAX_DIVISOR_VALUE      128
 
-static unsigned int tegra_sdhost_min_freq;
-static unsigned int tegra_sdhost_std_freq;
+#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_8      128
+#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_4      64
+#define MAX_TAP_VALUES 255
+#define TUNING_FREQ_COUNT      2
+#define TUNING_VOLTAGES_COUNT  2
 
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+static unsigned int uhs_max_freq_MHz[] = {
+       [MMC_TIMING_UHS_SDR50] = 100,
+       [MMC_TIMING_UHS_SDR104] = 208,
+       [MMC_TIMING_MMC_HS200] = 200,
+};
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
 static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock);
-static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci);
 #endif
 
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-static void tegra11x_sdhci_post_reset_init(struct sdhci_host *sdhci);
-#endif
+static unsigned int tegra_sdhost_min_freq;
+static unsigned int tegra_sdhost_std_freq;
 
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
-static unsigned int tegra3_sdhost_max_clk[4] = {
-       208000000,      104000000,      208000000,      104000000 };
-#endif
 
-struct tegra_sdhci_hw_ops{
+struct tegra_sdhci_hw_ops {
        /* Set the internal clk and card clk.*/
        void    (*set_card_clock)(struct sdhci_host *sdhci, unsigned int clock);
-       /* Post reset vendor registers configuration */
-       void    (*sdhost_init)(struct sdhci_host *sdhci);
 };
 
 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
@@ -106,28 +118,102 @@ static struct tegra_sdhci_hw_ops tegra_2x_sdhci_ops = {
 #elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
 static struct tegra_sdhci_hw_ops tegra_3x_sdhci_ops = {
        .set_card_clock = tegra_3x_sdhci_set_card_clock,
-       .sdhost_init = tegra3_sdhci_post_reset_init,
 };
 #else
 static struct tegra_sdhci_hw_ops tegra_11x_sdhci_ops = {
-        .sdhost_init = tegra11x_sdhci_post_reset_init,
 };
 #endif
 
-#define NVQUIRK_FORCE_SDHCI_SPEC_200   BIT(0)
-#define NVQUIRK_ENABLE_BLOCK_GAP_DET   BIT(1)
+/* Erratum: Version register is invalid in HW */
+#define NVQUIRK_FORCE_SDHCI_SPEC_200           BIT(0)
+/* Erratum: Enable block gap interrupt detection */
+#define NVQUIRK_ENABLE_BLOCK_GAP_DET           BIT(1)
+/* Do not enable auto calibration if the platform doesn't support */
+#define NVQUIRK_DISABLE_AUTO_CALIBRATION       BIT(2)
+/* Set Calibration Offsets */
+#define NVQUIRK_SET_CALIBRATION_OFFSETS                BIT(3)
+/* Set Drive Strengths */
+#define NVQUIRK_SET_DRIVE_STRENGTH             BIT(4)
+/* Enable PADPIPE CLKEN */
+#define NVQUIRK_ENABLE_PADPIPE_CLKEN           BIT(5)
+/* DISABLE SPI_MODE CLKEN */
+#define NVQUIRK_DISABLE_SPI_MODE_CLKEN         BIT(6)
+/* Set tap delay */
+#define NVQUIRK_SET_TAP_DELAY                  BIT(7)
+/* Set trim delay */
+#define NVQUIRK_SET_TRIM_DELAY                 BIT(8)
+/* Enable SDHOST v3.0 support */
+#define NVQUIRK_ENABLE_SD_3_0                  BIT(9)
+/* Enable SDR50 mode */
+#define NVQUIRK_ENABLE_SDR50                   BIT(10)
+/* Enable SDR104 mode */
+#define NVQUIRK_ENABLE_SDR104                  BIT(11)
+/*Enable DDR50 mode */
+#define NVQUIRK_ENABLE_DDR50                   BIT(12)
+/* Enable Frequency Tuning for SDR50 mode */
+#define NVQUIRK_ENABLE_SDR50_TUNING            BIT(13)
+/* Enable Infinite Erase Timeout*/
+#define NVQUIRK_INFINITE_ERASE_TIMEOUT         BIT(14)
 
 struct sdhci_tegra_soc_data {
        struct sdhci_pltfm_data *pdata;
        u32 nvquirks;
 };
 
+struct sdhci_tegra_sd_stats {
+       unsigned int data_crc_count;
+       unsigned int cmd_crc_count;
+       unsigned int data_to_count;
+       unsigned int cmd_to_count;
+};
+
+enum tegra_tuning_freq {
+       TUNING_LOW_FREQ,
+       TUNING_HIGH_FREQ,
+};
+
+struct freq_tuning_params {
+       unsigned int    freq_hz;
+       unsigned int    nr_voltages;
+       unsigned int    voltages[TUNING_VOLTAGES_COUNT];
+};
+
+static struct freq_tuning_params tuning_params[TUNING_FREQ_COUNT] = {
+       [TUNING_LOW_FREQ] = {
+               .freq_hz = 82000000,
+               .nr_voltages = 1,
+               .voltages = {ULONG_MAX},
+       },
+       [TUNING_HIGH_FREQ] = {
+               .freq_hz = 156000000,
+               .nr_voltages = 2,
+               .voltages = {ULONG_MAX, 1100000},
+       },
+};
+
+struct tap_window_data {
+       unsigned int    partial_win;
+       unsigned int    full_win_begin;
+       unsigned int    full_win_end;
+       unsigned int    tuning_ui;
+       unsigned int    sampling_point;
+       bool            abandon_partial_win;
+       bool            abandon_full_win;
+};
+
+struct tegra_tuning_data {
+       unsigned int            best_tap_value;
+       bool                    select_partial_win;
+       struct tap_window_data  *tap_data[TUNING_VOLTAGES_COUNT];
+};
+
 struct sdhci_tegra {
        const struct tegra_sdhci_platform_data *plat;
        const struct sdhci_tegra_soc_data *soc_data;
        bool    clk_enabled;
        struct regulator *vdd_io_reg;
        struct regulator *vdd_slot_reg;
+       struct regulator *vcore_reg;
        /* Pointer to the chip specific HW ops */
        struct tegra_sdhci_hw_ops *hw_ops;
        /* Host controller instance */
@@ -140,15 +226,66 @@ struct sdhci_tegra {
        unsigned int max_clk_limit;
        /* max ddr clk supported by the platform */
        unsigned int ddr_clk_limit;
+       /* SD Hot Plug in Suspend State */
+       unsigned int sd_detect_in_suspend;
        struct tegra_io_dpd *dpd;
        bool card_present;
        bool is_rail_enabled;
        struct clk *emc_clk;
        unsigned int emc_max_clk;
+       struct sdhci_tegra_sd_stats *sd_stat_head;
+       unsigned int nominal_vcore_uV;
+       /* Tuning related structures and variables */
+       /* Tuning opcode to be used */
+       unsigned int tuning_opcode;
+       /* Tuning packet size */
+       unsigned int tuning_bsize;
+       /* Tuning status */
+       unsigned int tuning_status;
+#define TUNING_STATUS_DONE     1
+#define TUNING_STATUS_RETUNE   2
+       /* Freq tuning information for each sampling clock freq */
+       struct tegra_tuning_data tuning_data;
+       bool is_parent_pllc;
+};
+
+static struct clk *pll_c;
+static struct clk *pll_p;
+static unsigned long pll_c_rate;
+static unsigned long pll_p_rate;
+
+static int show_error_stats_dump(struct seq_file *s, void *data)
+{
+       struct sdhci_host *host = s->private;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct sdhci_tegra_sd_stats *head;
+
+       seq_printf(s, "ErrorStatistics:\n");
+       seq_printf(s, "DataCRC\tCmdCRC\tDataTimeout\tCmdTimeout\n");
+       head = tegra_host->sd_stat_head;
+       if (head != NULL)
+               seq_printf(s, "%d\t%d\t%d\t%d\n", head->data_crc_count,
+                       head->cmd_crc_count, head->data_to_count,
+                       head->cmd_to_count);
+       return 0;
+}
+
+static int sdhci_error_stats_dump(struct inode *inode, struct file *file)
+{
+       return single_open(file, show_error_stats_dump, inode->i_private);
+}
+
+static const struct file_operations sdhci_host_fops = {
+       .open           = sdhci_error_stats_dump,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
 };
 
 static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
 {
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
        u32 val;
 
        if (unlikely(reg == SDHCI_PRESENT_STATE)) {
@@ -156,7 +293,7 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
                val = readl(host->ioaddr + reg);
                return val | SDHCI_WRITE_PROTECT;
        }
-
+#endif
        return readl(host->ioaddr + reg);
 }
 
@@ -169,7 +306,6 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
 
        if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
                        (reg == SDHCI_HOST_VERSION))) {
-               /* Erratum: Version register is invalid in HW. */
                return SDHCI_SPEC_200;
        }
 #endif
@@ -196,7 +332,6 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
        if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
                        (reg == SDHCI_INT_ENABLE))) {
-               /* Erratum: Must enable block gap interrupt detection */
                u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
                if (val & SDHCI_INT_CARD_INT)
                        gap_ctrl |= 0x8;
@@ -215,6 +350,7 @@ static unsigned int tegra_sdhci_get_cd(struct sdhci_host *sdhci)
        return tegra_host->card_present;
 }
 
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
 static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -226,77 +362,6 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
 
        return gpio_get_value(plat->wp_gpio);
 }
-
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
-static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci)
-{
-       u16 misc_ctrl;
-       u32 vendor_ctrl;
-       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
-       struct sdhci_tegra *tegra_host = pltfm_host->priv;
-       struct tegra_sdhci_platform_data *plat = tegra_host->plat;
-
-       /* Set the base clock frequency */
-       vendor_ctrl = sdhci_readl(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
-       vendor_ctrl &= ~(0xFF << SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT);
-       vendor_ctrl |= (tegra3_sdhost_max_clk[tegra_host->instance] / 1000000) <<
-               SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT;
-       vendor_ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_PADPIPE_CLKEN_OVERRIDE;
-       vendor_ctrl &= ~SDHCI_VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE;
-
-       /* Set tap delay */
-       if (plat->tap_delay) {
-               vendor_ctrl &= ~(0xFF <<
-                       SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-               vendor_ctrl |= (plat->tap_delay <<
-                       SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-       }
-       /* Enable frequency tuning for SDR50 mode */
-       vendor_ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_SDR50_TUNING;
-       sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
-
-       /* Enable SDHOST v3.0 support */
-       misc_ctrl = sdhci_readw(sdhci, SDHCI_VENDOR_MISC_CNTRL);
-       misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_ENABLE_SD_3_0 |
-               SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT |
-               SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT;
-       sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL);
-}
-#endif
-
-#ifdef CONFIG_ARCH_TEGRA_11x_SOC
-static void tegra11x_sdhci_post_reset_init(struct sdhci_host *sdhci)
-{
-       u16 misc_ctrl;
-       u32 vendor_ctrl;
-       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
-       struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
-       struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
-       struct tegra_sdhci_platform_data *plat;
-
-       plat = pdev->dev.platform_data;
-       /* Set the base clock frequency */
-       vendor_ctrl = sdhci_readl(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
-       vendor_ctrl &= ~(0xFF << SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT);
-       vendor_ctrl |= (tegra3_sdhost_max_clk[tegra_host->instance] / 1000000) <<
-               SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT;
-       vendor_ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_PADPIPE_CLKEN_OVERRIDE;
-       /* Set tap delay */
-       if (plat->tap_delay) {
-               vendor_ctrl &= ~(0xFF <<
-                       SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-               vendor_ctrl |= (plat->tap_delay <<
-                       SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-       }
-       sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
-
-       /* Enable SDHOST v3.0 support */
-       misc_ctrl = sdhci_readw(sdhci, SDHCI_VENDOR_MISC_CNTRL);
-       misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT |
-               SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT |
-               SDHCI_VENDOR_MISC_CNTRL_ENABLE_DDR50_SUPPORT;
-       sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL);
-}
 #endif
 
 static int tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
@@ -342,12 +407,100 @@ static int tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
 
 static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
 {
+       u16 misc_ctrl;
+       u32 vendor_ctrl;
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
+       const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 
        if (mask & SDHCI_RESET_ALL) {
-               if (tegra_host->hw_ops->sdhost_init)
-                       tegra_host->hw_ops->sdhost_init(sdhci);
+               if (tegra_host->sd_stat_head != NULL) {
+                       tegra_host->sd_stat_head->data_crc_count = 0;
+                       tegra_host->sd_stat_head->cmd_crc_count = 0;
+                       tegra_host->sd_stat_head->data_to_count = 0;
+                       tegra_host->sd_stat_head->cmd_to_count = 0;
+               }
+               vendor_ctrl = sdhci_readl(sdhci, SDHCI_VNDR_CLK_CTRL);
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_PADPIPE_CLKEN) {
+                       vendor_ctrl |=
+                               SDHCI_VNDR_CLK_CTRL_PADPIPE_CLKEN_OVERRIDE;
+               }
+               if (soc_data->nvquirks & NVQUIRK_DISABLE_SPI_MODE_CLKEN) {
+                       vendor_ctrl &=
+                               ~SDHCI_VNDR_CLK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
+               }
+               if (soc_data->nvquirks & NVQUIRK_SET_TAP_DELAY) {
+                       if ((tegra_host->tuning_status == TUNING_STATUS_DONE) &&
+                               (sdhci->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
+                                       vendor_ctrl &= ~(0xFF <<
+                                       SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+                                       vendor_ctrl |=
+                                       (tegra_host->tuning_data.best_tap_value
+                                       << SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+                       } else {
+                               if (plat->tap_delay) {
+                                       vendor_ctrl &= ~(0xFF <<
+                                       SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+                                       vendor_ctrl |= (plat->tap_delay <<
+                                       SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+                               }
+                       }
+               }
+               if (soc_data->nvquirks & NVQUIRK_SET_TRIM_DELAY) {
+                       if (plat->trim_delay) {
+                               vendor_ctrl &= ~(0x1F <<
+                               SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_SHIFT);
+                               vendor_ctrl |= (plat->trim_delay <<
+                               SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_SHIFT);
+                       }
+               }
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50_TUNING)
+                       vendor_ctrl |= SDHCI_VNDR_CLK_CTRL_SDR50_TUNING;
+               sdhci_writel(sdhci, vendor_ctrl, SDHCI_VNDR_CLK_CTRL);
+
+               misc_ctrl = sdhci_readw(sdhci, SDHCI_VNDR_MISC_CTRL);
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_SD_3_0)
+                       misc_ctrl |= SDHCI_VNDR_MISC_CTRL_ENABLE_SD_3_0;
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) {
+                       misc_ctrl |=
+                       SDHCI_VNDR_MISC_CTRL_ENABLE_SDR104_SUPPORT;
+               }
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) {
+                       misc_ctrl |=
+                       SDHCI_VNDR_MISC_CTRL_ENABLE_SDR50_SUPPORT;
+               }
+               /* Enable DDR mode support only for SDMMC4 */
+               if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) {
+                       if (tegra_host->instance == 3) {
+                               misc_ctrl |=
+                               SDHCI_VNDR_MISC_CTRL_ENABLE_DDR50_SUPPORT;
+                       }
+               }
+               if (soc_data->nvquirks & NVQUIRK_INFINITE_ERASE_TIMEOUT) {
+                       misc_ctrl |=
+                       SDHCI_VNDR_MISC_CTRL_INFINITE_ERASE_TIMEOUT;
+               }
+               sdhci_writew(sdhci, misc_ctrl, SDHCI_VNDR_MISC_CTRL);
+
+               /* Mask the support for any UHS modes if specified */
+               if (plat->uhs_mask & MMC_UHS_MASK_SDR104)
+                       sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR104;
+
+               if (plat->uhs_mask & MMC_UHS_MASK_DDR50)
+                       sdhci->mmc->caps &= ~MMC_CAP_UHS_DDR50;
+
+               if (plat->uhs_mask & MMC_UHS_MASK_SDR50)
+                       sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR50;
+
+               if (plat->uhs_mask & MMC_UHS_MASK_SDR25)
+                       sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR25;
+
+               if (plat->uhs_mask & MMC_UHS_MASK_SDR12)
+                       sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR12;
+
+               if (plat->uhs_mask & MMC_MASK_HS200)
+                       sdhci->mmc->caps2 &= ~MMC_CAP2_HS200;
        }
 }
 
@@ -408,7 +561,12 @@ static irqreturn_t carddetect_irq(int irq, void *data)
                        if (tegra_host->vdd_slot_reg)
                                regulator_disable(tegra_host->vdd_slot_reg);
                        tegra_host->is_rail_enabled = 0;
-                }
+               }
+               /*
+                * Set retune request as tuning should be done next time
+                * a card is inserted.
+                */
+               tegra_host->tuning_status = TUNING_STATUS_RETUNE;
        }
 
        tasklet_schedule(&sdhost->card_tasklet);
@@ -438,6 +596,79 @@ static int tegra_sdhci_8bit(struct sdhci_host *sdhci, int bus_width)
        return 0;
 }
 
+/*
+* Calculation of nearest clock frequency for desired rate:
+* Get the divisor value, div = p / d_rate
+* 1. If it is nearer to ceil(p/d_rate) then increment the div value by 0.5 and
+* nearest_rate, i.e. result = p / (div + 0.5) = (p << 1)/((div << 1) + 1).
+* 2. If not, result = p / div
+* As the nearest clk freq should be <= to desired_rate,
+* 3. If result > desired_rate then increment the div by 0.5
+* and do, (p << 1)/((div << 1) + 1)
+* 4. Else return result
+* Here, If condtions 1 & 3 are both satisfied then to keep track of div value,
+* defined index variable.
+*/
+static unsigned long get_nearest_clock_freq(unsigned long pll_rate,
+               unsigned long desired_rate)
+{
+       unsigned long result;
+       int div;
+       int index = 1;
+
+       div = pll_rate / desired_rate;
+       if (div > MAX_DIVISOR_VALUE) {
+               div = MAX_DIVISOR_VALUE;
+               result = pll_rate / div;
+       } else {
+               if ((pll_rate % desired_rate) >= (desired_rate / 2))
+                       result = (pll_rate << 1) / ((div << 1) + index++);
+               else
+                       result = pll_rate / div;
+
+               if (desired_rate < result) {
+                       /*
+                       * Trying to get lower clock freq than desired clock,
+                       * by increasing the divisor value by 0.5
+                       */
+                       result = (pll_rate << 1) / ((div << 1) + index);
+               }
+       }
+
+       return result;
+}
+
+static void tegra_sdhci_clock_set_parent(struct sdhci_host *host,
+               unsigned long desired_rate)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct clk *parent_clk;
+       unsigned long pll_c_freq;
+       unsigned long pll_p_freq;
+       int rc;
+
+       pll_c_freq = get_nearest_clock_freq(pll_c_rate, desired_rate);
+       pll_p_freq = get_nearest_clock_freq(pll_p_rate, desired_rate);
+
+       if (pll_c_freq > pll_p_freq) {
+               if (!tegra_host->is_parent_pllc) {
+                       parent_clk = pll_c;
+                       tegra_host->is_parent_pllc = true;
+               } else
+                       return;
+       } else if (tegra_host->is_parent_pllc) {
+               parent_clk = pll_p;
+               tegra_host->is_parent_pllc = false;
+       } else
+               return;
+
+       rc = clk_set_parent(pltfm_host->clk, parent_clk);
+       if (rc)
+               pr_err("%s: failed to set pll parent clock %d\n",
+                       mmc_hostname(host->mmc), rc);
+}
+
 static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
        unsigned int clock)
 {
@@ -446,14 +677,7 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
        unsigned int clk_rate;
        unsigned int emc_clk;
 
-       /*
-        * In SDR50 mode, run the sdmmc controller at freq greater than
-        * 104MHz to ensure the core voltage is at 1.2V. If the core voltage
-        * is below 1.2V, CRC errors would occur during data transfers.
-        */
-       if (sdhci->mmc->card &&
-               (mmc_card_ddr_mode(sdhci->mmc->card) ||
-               (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50))) {
+       if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
                /*
                 * In ddr mode, tegra sdmmc controller clock frequency
                 * should be double the card clock frequency.
@@ -468,6 +692,13 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
                } else {
                        clk_rate = clock * 2;
                }
+       } else  if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50) {
+               /*
+                * In SDR50 mode, run the sdmmc controller at freq greater than
+                * 104MHz to ensure the core voltage is at 1.2V. If the core voltage
+                * is below 1.2V, CRC errors would occur during data transfers.
+                */
+               clk_rate = clock * 2;
        } else {
                if (clock <= tegra_sdhost_min_freq)
                        clk_rate = tegra_sdhost_min_freq;
@@ -481,10 +712,14 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
                (clk_rate > tegra_host->max_clk_limit))
                clk_rate = tegra_host->max_clk_limit;
 
+       tegra_sdhci_clock_set_parent(sdhci, clk_rate);
        clk_set_rate(pltfm_host->clk, clk_rate);
        sdhci->max_clk = clk_get_rate(pltfm_host->clk);
+#ifdef CONFIG_TEGRA_FPGA_PLATFORM
+       /* FPGA supports 26MHz of clock for SDMMC. */
+       sdhci->max_clk = 26000000;
+#endif
 }
-
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
 static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock)
 {
@@ -570,12 +805,13 @@ set_clk:
 out:
        sdhci->clock = clock;
 }
-#endif
+#endif /*      #ifdef CONFIG_ARCH_TEGRA_3x_SOC */
 
 static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
        u8 ctrl;
 
        pr_debug("%s %s %u enabled=%u\n", __func__,
@@ -583,13 +819,19 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
 
        if (clock) {
                /* bring out sd instance from io dpd mode */
-               tegra_io_dpd_disable(tegra_host->dpd);
+               if (tegra_host->dpd) {
+                       mutex_lock(&tegra_host->dpd->delay_lock);
+                       cancel_delayed_work_sync(&tegra_host->dpd->delay_dpd);
+                       tegra_io_dpd_disable(tegra_host->dpd);
+                       mutex_unlock(&tegra_host->dpd->delay_lock);
+               }
 
                if (!tegra_host->clk_enabled) {
-                       clk_enable(pltfm_host->clk);
-                       ctrl = sdhci_readb(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
-                       ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK;
-                       sdhci_writeb(sdhci, ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
+                       pm_runtime_get_sync(&pdev->dev);
+                       clk_prepare_enable(pltfm_host->clk);
+                       ctrl = sdhci_readb(sdhci, SDHCI_VNDR_CLK_CTRL);
+                       ctrl |= SDHCI_VNDR_CLK_CTRL_SDMMC_CLK;
+                       sdhci_writeb(sdhci, ctrl, SDHCI_VNDR_CLK_CTRL);
                        tegra_host->clk_enabled = true;
                }
                tegra_sdhci_set_clk_rate(sdhci, clock);
@@ -598,13 +840,112 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
        } else if (!clock && tegra_host->clk_enabled) {
                if (tegra_host->hw_ops->set_card_clock)
                        tegra_host->hw_ops->set_card_clock(sdhci, clock);
-               ctrl = sdhci_readb(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
-               ctrl &= ~SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK;
-               sdhci_writeb(sdhci, ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
-               clk_disable(pltfm_host->clk);
+               ctrl = sdhci_readb(sdhci, SDHCI_VNDR_CLK_CTRL);
+               ctrl &= ~SDHCI_VNDR_CLK_CTRL_SDMMC_CLK;
+               sdhci_writeb(sdhci, ctrl, SDHCI_VNDR_CLK_CTRL);
+               clk_disable_unprepare(pltfm_host->clk);
+               pm_runtime_put_sync(&pdev->dev);
                tegra_host->clk_enabled = false;
                /* io dpd enable call for sd instance */
-               tegra_io_dpd_enable(tegra_host->dpd);
+
+               if (tegra_host->dpd) {
+                       mutex_lock(&tegra_host->dpd->delay_lock);
+                       if (tegra_host->dpd->need_delay_dpd) {
+                               schedule_delayed_work(
+                                       &tegra_host->dpd->delay_dpd,
+                                       msecs_to_jiffies(100));
+                       } else {
+                               tegra_io_dpd_enable(tegra_host->dpd);
+                       }
+                       mutex_unlock(&tegra_host->dpd->delay_lock);
+               }
+       }
+}
+static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci)
+{
+       unsigned int val;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+       unsigned int timeout = 10;
+
+       /* No Calibration for sdmmc4 */
+       if (tegra_host->instance == 3)
+               return;
+
+       if (unlikely(soc_data->nvquirks & NVQUIRK_DISABLE_AUTO_CALIBRATION))
+               return;
+
+       val = sdhci_readl(sdhci, SDMMC_SDMEMCOMPPADCTRL);
+       val &= ~SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK;
+       val |= 0x7;
+       sdhci_writel(sdhci, val, SDMMC_SDMEMCOMPPADCTRL);
+
+       /* Enable Auto Calibration*/
+       val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
+       val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+       val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
+       if (unlikely(soc_data->nvquirks & NVQUIRK_SET_CALIBRATION_OFFSETS)) {
+               /* Program Auto cal PD offset(bits 8:14) */
+               val &= ~(0x7F <<
+                       SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+               val |= (SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET <<
+                       SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+               /* Program Auto cal PU offset(bits 0:6) */
+               val &= ~0x7F;
+               val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET;
+       }
+       sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
+
+       /* Wait until the calibration is done */
+       do {
+               if (!(sdhci_readl(sdhci, SDMMC_AUTO_CAL_STATUS) &
+                       SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE))
+                       break;
+
+               mdelay(1);
+               timeout--;
+       } while (timeout);
+
+       if (!timeout)
+               dev_err(mmc_dev(sdhci->mmc), "Auto calibration failed\n");
+
+       /* Disable Auto calibration */
+       val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
+       val &= ~SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+       sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
+
+       if (unlikely(soc_data->nvquirks & NVQUIRK_SET_DRIVE_STRENGTH)) {
+               unsigned int pulldown_code;
+               unsigned int pullup_code;
+               int pg;
+               int err;
+
+               pg = tegra_drive_get_pingroup(mmc_dev(sdhci->mmc));
+               if (pg != -1) {
+                       /* Get the pull down codes from auto cal status reg */
+                       pulldown_code = (
+                               sdhci_readl(sdhci, SDMMC_AUTO_CAL_STATUS) >>
+                               SDMMC_AUTO_CAL_STATUS_PULLDOWN_OFFSET);
+                       /* Set the pull down in the pinmux reg */
+                       err = tegra_drive_pinmux_set_pull_down(pg,
+                               pulldown_code);
+                       if (err)
+                               dev_err(mmc_dev(sdhci->mmc),
+                               "Failed to set pulldown codes %d err %d\n",
+                               pulldown_code, err);
+
+                       /* Calculate the pull up codes */
+                       pullup_code = pulldown_code + PULLUP_ADJUSTMENT_OFFSET;
+                       if (pullup_code >= TEGRA_MAX_PULL)
+                               pullup_code = TEGRA_MAX_PULL - 1;
+                       /* Set the pull up code in the pinmux reg */
+                       err = tegra_drive_pinmux_set_pull_up(pg, pullup_code);
+                       if (err)
+                               dev_err(mmc_dev(sdhci->mmc),
+                               "Failed to set pullup codes %d err %d\n",
+                               pullup_code, err);
+               }
        }
 }
 
@@ -613,11 +954,11 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci,
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
-       unsigned int min_uV = SDHOST_HIGH_VOLT_MIN;
-       unsigned int max_uV = SDHOST_HIGH_VOLT_MAX;
+       unsigned int min_uV = tegra_host->vddio_min_uv;
+       unsigned int max_uV = tegra_host->vddio_max_uv;
        unsigned int rc = 0;
        u16 clk, ctrl;
-       unsigned int val;
+
 
        ctrl = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
        if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
@@ -648,10 +989,12 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci,
                if (rc) {
                        dev_err(mmc_dev(sdhci->mmc), "switching to 1.8V"
                        "failed . Switching back to 3.3V\n");
-                       regulator_set_voltage(tegra_host->vdd_io_reg,
+                       rc = regulator_set_voltage(tegra_host->vdd_io_reg,
                                SDHOST_HIGH_VOLT_MIN,
                                SDHOST_HIGH_VOLT_MAX);
-                       goto out;
+                       if (rc)
+                               dev_err(mmc_dev(sdhci->mmc),
+                               "switching to 3.3V also failed\n");
                }
        }
 
@@ -665,35 +1008,6 @@ static int tegra_sdhci_signal_voltage_switch(struct sdhci_host *sdhci,
        /* Wait for 1 msec after enabling clock */
        mdelay(1);
 
-       if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
-               /* Do Auto Calibration for 1.8V signal voltage */
-               val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
-               val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
-               /* Program Auto cal PD offset(bits 8:14) */
-               val &= ~(0x7F <<
-                       SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
-               val |= (SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET <<
-                       SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
-               /* Program Auto cal PU offset(bits 0:6) */
-               val &= ~0x7F;
-               val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET;
-               sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
-
-               val = sdhci_readl(sdhci, SDMMC_SDMEMCOMPPADCTRL);
-               val &= ~SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK;
-               val |= 0x7;
-               sdhci_writel(sdhci, val, SDMMC_SDMEMCOMPPADCTRL);
-       }
-
-       return rc;
-out:
-       /* Enable the card clock */
-       clk |= SDHCI_CLOCK_CARD_EN;
-       sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
-
-       /* Wait for 1 msec for the clock to stabilize */
-       mdelay(1);
-
        return rc;
 }
 
@@ -716,6 +1030,8 @@ static void tegra_sdhci_reset(struct sdhci_host *sdhci, u8 mask)
                timeout--;
                mdelay(1);
        }
+
+       tegra_sdhci_reset_exit(sdhci, mask);
 }
 
 static void sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci,
@@ -726,42 +1042,218 @@ static void sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci,
        /* Max tap delay value is 255 */
        BUG_ON(tap_delay > MAX_TAP_VALUES);
 
-       vendor_ctrl = sdhci_readl(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
-       vendor_ctrl &= ~(0xFF << SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-       vendor_ctrl |= (tap_delay << SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
-       sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
+       vendor_ctrl = sdhci_readl(sdhci, SDHCI_VNDR_CLK_CTRL);
+       vendor_ctrl &= ~(0xFF << SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+       vendor_ctrl |= (tap_delay << SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
+       sdhci_writel(sdhci, vendor_ctrl, SDHCI_VNDR_CLK_CTRL);
 }
 
-static void sdhci_tegra_clear_set_irqs(struct sdhci_host *host,
-       u32 clear, u32 set)
+static int sdhci_tegra_sd_error_stats(struct sdhci_host *host, u32 int_status)
 {
-       u32 ier;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+       struct sdhci_tegra_sd_stats *head;
+
+       if (tegra_host->sd_stat_head == NULL) {
+               tegra_host->sd_stat_head = devm_kzalloc(&pdev->dev, sizeof(
+                                               struct sdhci_tegra_sd_stats),
+                                               GFP_KERNEL);
+               if (tegra_host->sd_stat_head == NULL)
+                       return -ENOMEM;
+       }
+       head = tegra_host->sd_stat_head;
+       if (int_status & SDHCI_INT_DATA_CRC)
+               head->data_crc_count++;
+       if (int_status & SDHCI_INT_CRC)
+               head->cmd_crc_count++;
+       if (int_status & SDHCI_INT_TIMEOUT)
+               head->cmd_to_count++;
+       if (int_status & SDHCI_INT_DATA_TIMEOUT)
+               head->data_to_count++;
+       return 0;
+}
+
+/*
+ * Calculation of best tap value for low frequencies(82MHz).
+ * X = Partial win, Y = Full win start, Z = Full win end.
+ * UI = Z - X.
+ * Full Window = Z - Y.
+ * Taps margin = mid-point of 1/2*(curr_freq/max_frequency)*UI
+ *                    = (1/2)*(1/2)*(82/200)*UI
+ *                    = (0.1025)*UI
+ * if Partial win<(0.22)*UI
+ * best tap = Y+(0.1025*UI)
+ * else
+ * best tap = (X-(Z-Y))+(0.1025*UI)
+ * If best tap<0, best tap = 0
+ */
+static void calculate_low_freq_tap_value(struct sdhci_host *sdhci)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       unsigned int curr_clock;
+       unsigned int max_clock;
+       int best_tap_value;
+       struct tap_window_data *tap_data;
+       struct tegra_tuning_data *tuning_data;
+
+       tuning_data = &tegra_host->tuning_data;
+       tap_data = tuning_data->tap_data[0];
+
+       if (tap_data->abandon_full_win) {
+               if (tap_data->abandon_partial_win) {
+                       tuning_data->best_tap_value = 0;
+                       return;
+               } else {
+                       tuning_data->select_partial_win = true;
+                       goto calculate_best_tap;
+               }
+       }
 
-       ier = sdhci_readl(host, SDHCI_INT_ENABLE);
-       ier &= ~clear;
-       ier |= set;
-       sdhci_writel(host, ier, SDHCI_INT_ENABLE);
-       sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+       tap_data->tuning_ui = tap_data->full_win_end - tap_data->partial_win;
+
+       /* Calculate the sampling point */
+       curr_clock = sdhci->max_clk / 1000000;
+       max_clock = uhs_max_freq_MHz[sdhci->mmc->ios.timing];
+       tap_data->sampling_point = ((tap_data->tuning_ui * curr_clock) /
+               max_clock);
+       tap_data->sampling_point >>= 2;
+
+       /*
+        * Check whether partial window should be used. Use partial window
+        * if partial window > 0.22(UI).
+        */
+       if ((!tap_data->abandon_partial_win) &&
+               (tap_data->partial_win > ((22 * tap_data->tuning_ui) / 100)))
+                       tuning_data->select_partial_win = true;
+
+calculate_best_tap:
+       if (tuning_data->select_partial_win) {
+               best_tap_value = (tap_data->partial_win -
+                       (tap_data->full_win_end - tap_data->full_win_begin)) +
+                       tap_data->sampling_point;
+               tuning_data->best_tap_value = (best_tap_value < 0) ? 0 :
+                       best_tap_value;
+       } else {
+               tuning_data->best_tap_value = tap_data->full_win_begin +
+                       tap_data->sampling_point;
+       }
+}
+
+/*
+ * Calculation of best tap value for high frequencies(156MHz).
+ * Tap window data at 1.25V core voltage
+ * X = Partial win, Y = Full win start, Z = Full win end.
+ * Full Window = Z-Y.
+ * UI = Z-X.
+ * Tap_margin = (0.20375)UI
+ *
+ * Tap window data at 1.1V core voltage
+ * X' = Partial win, Y' = Full win start, Z' = Full win end.
+ * UI' = Z'-X'.
+ * Full Window' = Z'-Y'.
+ * Tap_margin' = (0.20375)UI'
+ *
+ * Full_window_tap=[(Z'-0.20375UI')+(Y+0.20375UI)]/2
+ * Partial_window_tap=[(X'-0.20375UI')+(X-(Z-Y)+0x20375UI)]/2
+ * if(Partial_window_tap < 0), Partial_window_tap=0
+ *
+ * Full_window_quality=[(Z'-0.20375UI')-(Y+0.20375UI)]/2
+ * Partial_window_quality=(X'-0.20375UI')-Partial_window_tap
+ * if(Full_window_quality>Partial_window_quality) choose full window,
+ * else choose partial window.
+ * If there is no margin window for both cases,
+ * best tap=(Y+Z')/2.
+ */
+static void calculate_high_freq_tap_value(struct sdhci_host *sdhci)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       unsigned int curr_clock;
+       unsigned int max_clock;
+       struct tap_window_data *vmax_tap_data;
+       struct tap_window_data *vmid_tap_data;
+       struct tegra_tuning_data *tuning_data;
+       unsigned int full_win_tap;
+       int partial_win_start;
+       int partial_win_tap;
+       int full_win_quality;
+       int partial_win_quality;
+
+       tuning_data = &tegra_host->tuning_data;
+       vmax_tap_data = tuning_data->tap_data[0];
+       vmid_tap_data = tuning_data->tap_data[1];
+
+       curr_clock = sdhci->max_clk / 1000000;
+       max_clock = uhs_max_freq_MHz[sdhci->mmc->ios.timing];
+
+       /*
+        * Calculate the tuning_ui and sampling points for tap windows found
+        * at all core voltages.
+        */
+       vmax_tap_data->tuning_ui = vmax_tap_data->full_win_end -
+               vmax_tap_data->partial_win;
+       vmax_tap_data->sampling_point =
+               (vmax_tap_data->tuning_ui * curr_clock) / max_clock;
+       vmax_tap_data->sampling_point >>= 2;
+
+       vmid_tap_data->tuning_ui = vmid_tap_data->full_win_end -
+               vmid_tap_data->partial_win;
+       vmid_tap_data->sampling_point =
+               (vmid_tap_data->tuning_ui * curr_clock) / max_clock;
+       vmid_tap_data->sampling_point >>= 2;
+
+       full_win_tap = ((vmid_tap_data->full_win_end -
+               vmid_tap_data->sampling_point) +
+               (vmax_tap_data->full_win_begin +
+               vmax_tap_data->sampling_point));
+       full_win_tap >>= 1;
+       full_win_quality = (vmid_tap_data->full_win_end -
+               vmid_tap_data->sampling_point) -
+               (vmax_tap_data->full_win_begin +
+               vmax_tap_data->sampling_point);
+       full_win_quality >>= 1;
+
+       partial_win_start = (vmax_tap_data->partial_win -
+               (vmax_tap_data->full_win_end -
+               vmax_tap_data->full_win_begin));
+       partial_win_tap = ((vmid_tap_data->partial_win -
+               vmid_tap_data->sampling_point) +
+               (partial_win_start + vmax_tap_data->sampling_point));
+       partial_win_tap >>= 1;
+       if (partial_win_tap < 0)
+               partial_win_tap = 0;
+       partial_win_quality = (vmid_tap_data->partial_win -
+               vmid_tap_data->sampling_point) - partial_win_tap;
+
+       if ((full_win_quality <= 0) && (partial_win_quality)) {
+               dev_warn(mmc_dev(sdhci->mmc),
+                       "No margin window for both windows\n");
+               tuning_data->best_tap_value = vmax_tap_data->full_win_begin +
+                       vmid_tap_data->full_win_end;
+               tuning_data->best_tap_value >>= 1;
+       } else {
+               if (full_win_quality > partial_win_quality) {
+                       tuning_data->best_tap_value = full_win_tap;
+               } else {
+                       tuning_data->best_tap_value = partial_win_tap;
+                       tuning_data->select_partial_win = true;
+               }
+       }
 }
 
 static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
 {
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
        int err = 0;
        u8 ctrl;
-       u32 ier;
        u32 mask;
        unsigned int timeout = 10;
        int flags;
        u32 intstatus;
 
-       /*
-        * As per the Host Controller spec v3.00, tuning command
-        * generates Buffer Read Ready interrupt only, so enable that.
-        */
-       ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE);
-       sdhci_tegra_clear_set_irqs(sdhci, ier, SDHCI_INT_DATA_AVAIL |
-               SDHCI_INT_DATA_CRC);
-
        mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
        while (sdhci_readl(sdhci, SDHCI_PRESENT_STATE) & mask) {
                if (timeout == 0) {
@@ -786,8 +1278,12 @@ static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
         * In response to CMD19, the card sends 64 bytes of tuning
         * block to the Host Controller. So we set the block size
         * to 64 here.
+        * In response to CMD21, the card sends 128 bytes of tuning
+        * block for MMC_BUS_WIDTH_8 and 64 bytes for MMC_BUS_WIDTH_4
+        * to the Host Controller. So we set the block size to 64 here.
         */
-       sdhci_writew(sdhci, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+       sdhci_writew(sdhci, SDHCI_MAKE_BLKSZ(7, tegra_host->tuning_bsize),
+               SDHCI_BLOCK_SIZE);
 
        sdhci_writeb(sdhci, 0xE, SDHCI_TIMEOUT_CONTROL);
 
@@ -799,7 +1295,7 @@ static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
        flags = SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_DATA;
        /* Issue the command */
        sdhci_writew(sdhci, SDHCI_MAKE_CMD(
-               SD_SEND_TUNING_PATTERN, flags), SDHCI_COMMAND);
+               tegra_host->tuning_opcode, flags), SDHCI_COMMAND);
 
        timeout = 5;
        do {
@@ -833,20 +1329,118 @@ static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
        }
        mdelay(1);
 out:
-       sdhci_tegra_clear_set_irqs(sdhci, SDHCI_INT_DATA_AVAIL, ier);
        return err;
 }
 
-static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci)
+static int sdhci_tegra_scan_tap_values(struct sdhci_host *sdhci,
+       unsigned int starting_tap, bool expect_failure)
 {
+       unsigned int tap_value = starting_tap;
+       int err;
+
+       do {
+               /* Set the tap delay */
+               sdhci_tegra_set_tap_delay(sdhci, tap_value);
+
+               /* Run frequency tuning */
+               err = sdhci_tegra_run_frequency_tuning(sdhci);
+               if ((expect_failure && !err) ||
+                       (!expect_failure && err))
+                       break;
+               tap_value++;
+       } while (tap_value <= MAX_TAP_VALUES);
+
+       return tap_value;
+}
+
+/*
+ * While scanning for tap values, first get the partial window followed by the
+ * full window. Note that, when scanning for full win start, tuning has to be
+ * run until a passing tap value is found. Hence, failure is expected during
+ * this process and ignored.
+ */
+static int sdhci_tegra_get_tap_window_data(struct sdhci_host *sdhci,
+       struct tap_window_data *tap_data)
+{
+       unsigned int tap_value;
+       int err = 0;
+
+       if (!tap_data) {
+               dev_err(mmc_dev(sdhci->mmc), "Invalid tap data\n");
+               return -ENODATA;
+       }
+
+       /* Get the partial window data */
+       tap_value = 0;
+       tap_value = sdhci_tegra_scan_tap_values(sdhci, tap_value, false);
+       if (!tap_value) {
+               tap_data->abandon_partial_win = true;
+               tap_data->partial_win = 0;
+       } else if (tap_value > MAX_TAP_VALUES) {
+               /*
+                * If tap value is more than 0xFF, we have hit the miracle case
+                * of all tap values passing. Discard full window as passing
+                * window has covered all taps.
+                */
+               tap_data->partial_win = MAX_TAP_VALUES;
+               tap_data->abandon_full_win = true;
+               goto out;
+       } else {
+               tap_data->partial_win = tap_value - 1;
+               if (tap_value == MAX_TAP_VALUES) {
+                       /* All tap values exhausted. No full window */
+                       tap_data->abandon_full_win = true;
+                       goto out;
+               }
+       }
+
+       /* Get the full window start */
+       tap_value++;
+       tap_value = sdhci_tegra_scan_tap_values(sdhci, tap_value, true);
+       if (tap_value > MAX_TAP_VALUES) {
+               /* All tap values exhausted. No full window */
+               tap_data->abandon_full_win = true;
+               goto out;
+       } else {
+               tap_data->full_win_begin = tap_value;
+               /*
+                * If full win start is 0xFF, then set that as full win end
+                * and exit.
+                */
+               if (tap_value == MAX_TAP_VALUES) {
+                       tap_data->full_win_end = tap_value;
+                       goto out;
+               }
+       }
+
+       /* Get the full window end */
+       tap_value++;
+       tap_value = sdhci_tegra_scan_tap_values(sdhci, tap_value, false);
+       tap_data->full_win_end = tap_value - 1;
+       if (tap_value > MAX_TAP_VALUES)
+               tap_data->full_win_end = MAX_TAP_VALUES;
+out:
+       /*
+        * Mark tuning as failed if both partial and full windows are
+        * abandoned.
+        */
+       if (tap_data->abandon_partial_win && tap_data->abandon_full_win)
+               err = -EIO;
+       return err;
+}
+
+static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct tegra_tuning_data *tuning_data;
+       struct tap_window_data *tap_data;
        int err;
        u16 ctrl_2;
-       u8 *tap_delay_status;
-       unsigned int i = 0;
-       unsigned int temp_low_pass_tap = 0;
-       unsigned int temp_pass_window = 0;
-       unsigned int best_low_pass_tap = 0;
-       unsigned int best_pass_window = 0;
+       u32 ier;
+       unsigned int freq_band;
+       unsigned int i;
+       unsigned int voltage;
 
        /* Tuning is valid only in SDR104 and SDR50 modes */
        ctrl_2 = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
@@ -855,69 +1449,126 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci)
                (sdhci->flags & SDHCI_SDR50_NEEDS_TUNING))))
                        return 0;
 
-       tap_delay_status = kzalloc(MAX_TAP_VALUES, GFP_KERNEL);
-       if (tap_delay_status == NULL) {
-               dev_err(mmc_dev(sdhci->mmc), "failed to allocate memory"
-                       "for storing tap_delay_status\n");
-               err = -ENOMEM;
-               goto out;
-       }
+       /* Tuning should be done only for MMC_BUS_WIDTH_8 and MMC_BUS_WIDTH_4 */
+       if (sdhci->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+               tegra_host->tuning_bsize = MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_8;
+       else if (sdhci->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+               tegra_host->tuning_bsize = MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_4;
+       else
+               return -EINVAL;
+
+       /* Set the tuning command to be used */
+       tegra_host->tuning_opcode = opcode;
 
        /*
-        * Set each tap delay value and run frequency tuning. After each
-        * run, update the tap delay status as working or not working.
+        * Disable all interrupts signalling.Enable interrupt status
+        * detection for buffer read ready and data crc. We use
+        * polling for tuning as it involves less overhead.
         */
-       do {
-               /* Set the tap delay */
-               sdhci_tegra_set_tap_delay(sdhci, i);
-
-               /* Run frequency tuning */
-               err = sdhci_tegra_run_frequency_tuning(sdhci);
-
-               /* Update whether the tap delay worked or not */
-               tap_delay_status[i] = (err) ? 0: 1;
-               i++;
-       } while (i < 0xFF);
-
-       /* Find the best possible tap range */
-       for (i = 0; i < 0xFF; i++) {
-               temp_pass_window = 0;
+       ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE);
+       sdhci_writel(sdhci, 0, SDHCI_SIGNAL_ENABLE);
+       sdhci_writel(sdhci, SDHCI_INT_DATA_AVAIL |
+               SDHCI_INT_DATA_CRC, SDHCI_INT_ENABLE);
 
-               /* Find the first passing tap in the current window */
-               if (tap_delay_status[i]) {
-                       temp_low_pass_tap = i;
+       /*
+        * If tuning is already done and retune request is not set, then skip
+        * best tap value calculation and use the old best tap value.
+        */
+       if (tegra_host->tuning_status == TUNING_STATUS_DONE)
+               goto set_best_tap;
 
-                       /* Find the pass window */
-                       do {
-                               temp_pass_window++;
-                               i++;
-                               if (i > 0xFF)
-                                       break;
-                       } while (tap_delay_status[i]);
+       if (sdhci->max_clk > tuning_params[TUNING_LOW_FREQ].freq_hz)
+               freq_band = TUNING_HIGH_FREQ;
+       else
+               freq_band = TUNING_LOW_FREQ;
 
-                       if ((temp_pass_window > best_pass_window) && (temp_pass_window > 1)){
-                               best_low_pass_tap = temp_low_pass_tap;
-                               best_pass_window = temp_pass_window;
+       /*
+        * Run tuning and get the passing tap window info for all frequencies
+        * and core voltages required to calculate the final tap value. The
+        * standard driver calls this platform specific tuning callback after
+        * holding a lock. The spinlock needs to be released when calling
+        * non-atomic context functions like regulator calls etc.
+        */
+       tuning_data = &tegra_host->tuning_data;
+       for (i = 0; i < tuning_params[freq_band].nr_voltages; i++) {
+               spin_unlock(&sdhci->lock);
+               if (!tuning_data->tap_data[i]) {
+                       tuning_data->tap_data[i] = devm_kzalloc(
+                               mmc_dev(sdhci->mmc),
+                               sizeof(struct tap_window_data), GFP_KERNEL);
+                       if (!tuning_data->tap_data[i]) {
+                               err = -ENOMEM;
+                               dev_err(mmc_dev(sdhci->mmc),
+                                       "Insufficient memory for tap window info\n");
+                               spin_lock(&sdhci->lock);
+                               goto out;
                        }
                }
-       }
+               tap_data = tuning_data->tap_data[i];
+
+               if (tegra_host->nominal_vcore_uV) {
+                       if (!tegra_host->vcore_reg)
+                               tegra_host->vcore_reg = regulator_get(
+                               mmc_dev(sdhci->mmc), "vdd_core");
+                       if (IS_ERR_OR_NULL(tegra_host->vcore_reg)) {
+                               dev_info(mmc_dev(sdhci->mmc),
+                                       "No vdd_core %ld. Tuning might fail.\n",
+                                       PTR_ERR(tegra_host->vcore_reg));
+                               tegra_host->vcore_reg = NULL;
+                       } else {
+                               voltage = tuning_params[freq_band].voltages[i];
+                               if (voltage > tegra_host->nominal_vcore_uV)
+                                       voltage = tegra_host->nominal_vcore_uV;
+                               err = regulator_set_voltage(
+                                       tegra_host->vcore_reg, voltage,
+                                       voltage);
+                               if (err)
+                                       dev_err(mmc_dev(sdhci->mmc),
+                                               "Setting nominal core voltage failed\n");
+                       }
+               }
+               spin_lock(&sdhci->lock);
 
+               /* Get the tuning window info */
+               err = sdhci_tegra_get_tap_window_data(sdhci, tap_data);
+               if (err) {
+                       dev_err(mmc_dev(sdhci->mmc), "Failed to tuning window info\n");
+                       goto out;
+               }
+       }
 
-       pr_debug("%s: best pass tap window: start %d, end %d\n",
-               mmc_hostname(sdhci->mmc), best_low_pass_tap,
-               (best_low_pass_tap + best_pass_window));
+       /* Calculate best tap for current freq band */
+       if (freq_band == TUNING_LOW_FREQ)
+               calculate_low_freq_tap_value(sdhci);
+       else
+               calculate_high_freq_tap_value(sdhci);
 
-       /* Set the best tap */
+set_best_tap:
        sdhci_tegra_set_tap_delay(sdhci,
-               (best_low_pass_tap + ((best_pass_window * 3) / 4)));
+               tegra_host->tuning_data.best_tap_value);
 
-       /* Run frequency tuning */
+       /*
+        * Run tuning with the best tap value. If tuning fails, set the status
+        * for retuning next time enumeration is done.
+        */
        err = sdhci_tegra_run_frequency_tuning(sdhci);
+       if (err)
+               tegra_host->tuning_status = TUNING_STATUS_RETUNE;
+       else
+               tegra_host->tuning_status = TUNING_STATUS_DONE;
 
 out:
-       if (tap_delay_status)
-               kfree(tap_delay_status);
+       /* Enable the full range for core voltage if vcore_reg exists */
+       if (tegra_host->vcore_reg) {
+               spin_unlock(&sdhci->lock);
+               regulator_put(tegra_host->vcore_reg);
+               tegra_host->vcore_reg = NULL;
+               spin_lock(&sdhci->lock);
+       }
 
+       /* Enable interrupts. Enable full range for core voltage */
+       sdhci_writel(sdhci, ier, SDHCI_INT_ENABLE);
+       sdhci_writel(sdhci, ier, SDHCI_SIGNAL_ENABLE);
        return err;
 }
 
@@ -939,6 +1590,12 @@ static int tegra_sdhci_suspend(struct sdhci_host *sdhci)
                }
        }
 
+       if (tegra_host->dpd) {
+               mutex_lock(&tegra_host->dpd->delay_lock);
+               tegra_host->dpd->need_delay_dpd = 1;
+               mutex_unlock(&tegra_host->dpd->delay_lock);
+       }
+
        return 0;
 }
 
@@ -946,6 +1603,14 @@ static int tegra_sdhci_resume(struct sdhci_host *sdhci)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct platform_device *pdev;
+       struct tegra_sdhci_platform_data *plat;
+
+       pdev = to_platform_device(mmc_dev(sdhci->mmc));
+       plat = pdev->dev.platform_data;
+
+       if (gpio_is_valid(plat->cd_gpio))
+               tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0);
 
        /* Enable the power rails if any */
        if (tegra_host->card_present) {
@@ -954,11 +1619,18 @@ static int tegra_sdhci_resume(struct sdhci_host *sdhci)
                                regulator_enable(tegra_host->vdd_slot_reg);
                        if (tegra_host->vdd_io_reg) {
                                regulator_enable(tegra_host->vdd_io_reg);
-                               tegra_sdhci_signal_voltage_switch(sdhci, MMC_SIGNAL_VOLTAGE_330);
+                               if (plat->mmc_data.ocr_mask &
+                                                       SDHOST_1V8_OCR_MASK)
+                                       tegra_sdhci_signal_voltage_switch(sdhci,
+                                                       MMC_SIGNAL_VOLTAGE_180);
+                               else
+                                       tegra_sdhci_signal_voltage_switch(sdhci,
+                                                       MMC_SIGNAL_VOLTAGE_330);
                        }
                        tegra_host->is_rail_enabled = 1;
                }
        }
+
        /* Setting the min identification clock of freq 400KHz */
        tegra_sdhci_set_clock(sdhci, 400000);
 
@@ -972,23 +1644,67 @@ static int tegra_sdhci_resume(struct sdhci_host *sdhci)
        return 0;
 }
 
+static void tegra_sdhci_post_resume(struct sdhci_host *sdhci)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+
+       if (tegra_host->card_present) {
+               if (tegra_host->sd_detect_in_suspend)
+                       tasklet_schedule(&sdhci->card_tasklet);
+       } else if (tegra_host->clk_enabled) {
+               /* Turn OFF the clocks if the card is not present */
+               tegra_sdhci_set_clock(sdhci, 0);
+       }
+}
+
+static void sdhci_tegra_error_stats_debugfs(struct sdhci_host *host)
+{
+       struct dentry *root;
+
+       root = debugfs_create_dir(dev_name(mmc_dev(host->mmc)), NULL);
+       if (IS_ERR(root))
+               /* Don't complain -- debugfs just isn't enabled */
+               return;
+       if (!root)
+               /* Complain -- debugfs is enabled, but it failed to
+                * create the directory. */
+               goto err_root;
+
+       host->debugfs_root = root;
+
+       if (!debugfs_create_file("error_stats", S_IRUSR, root, host,
+                               &sdhci_host_fops))
+               goto err_node;
+       return;
+
+err_node:
+       debugfs_remove_recursive(root);
+       host->debugfs_root = NULL;
+err_root:
+       pr_err("%s: Failed to initialize debugfs functionality\n", __func__);
+       return;
+}
+
 static struct sdhci_ops tegra_sdhci_ops = {
-       .get_ro                 = tegra_sdhci_get_ro,
-       .get_cd                 = tegra_sdhci_get_cd,
-       .read_l                 = tegra_sdhci_readl,
-       .read_w                 = tegra_sdhci_readw,
-       .write_l                = tegra_sdhci_writel,
-       .platform_8bit_width    = tegra_sdhci_8bit,
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
-       .set_card_clock = tegra_3x_sdhci_set_card_clock,
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
+       .get_ro     = tegra_sdhci_get_ro,
 #endif
+       .get_cd     = tegra_sdhci_get_cd,
+       .read_l     = tegra_sdhci_readl,
+       .read_w     = tegra_sdhci_readw,
+       .write_l    = tegra_sdhci_writel,
+       .platform_8bit_width = tegra_sdhci_8bit,
        .set_clock              = tegra_sdhci_set_clock,
        .suspend                = tegra_sdhci_suspend,
        .resume                 = tegra_sdhci_resume,
+       .platform_resume        = tegra_sdhci_post_resume,
        .platform_reset_exit    = tegra_sdhci_reset_exit,
        .set_uhs_signaling      = tegra_sdhci_set_uhs_signaling,
        .switch_signal_voltage  = tegra_sdhci_signal_voltage_switch,
+       .switch_signal_voltage_exit = tegra_sdhci_do_calibration,
        .execute_freq_tuning    = sdhci_tegra_execute_tuning,
+       .sd_error_stats         = sdhci_tegra_sd_error_stats,
 };
 
 static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
@@ -996,24 +1712,44 @@ static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
                  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
                  SDHCI_QUIRK_NON_STD_VOLTAGE_SWITCHING |
+                 SDHCI_QUIRK_NON_STANDARD_TUNING |
 #endif
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
                  SDHCI_QUIRK_NONSTANDARD_CLOCK |
-                 SDHCI_QUIRK_NON_STANDARD_TUNING |
-#endif
-#ifndef CONFIG_ARCH_TEGRA_11x_SOC
-                 SDHCI_QUIRK_BROKEN_CARD_DETECTION |
 #endif
                  SDHCI_QUIRK_SINGLE_POWER_WRITE |
                  SDHCI_QUIRK_NO_HISPD_BIT |
                  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
+                 SDHCI_QUIRK_BROKEN_CARD_DETECTION |
                  SDHCI_QUIRK_NO_CALC_MAX_DISCARD_TO,
+       .quirks2 = SDHCI_QUIRK2_BROKEN_PRESET_VALUES,
        .ops  = &tegra_sdhci_ops,
 };
 
 static struct sdhci_tegra_soc_data soc_data_tegra20 = {
        .pdata = &sdhci_tegra20_pdata,
        .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+                  NVQUIRK_ENABLE_PADPIPE_CLKEN |
+                  NVQUIRK_DISABLE_SPI_MODE_CLKEN |
+                  NVQUIRK_SET_TAP_DELAY |
+                  NVQUIRK_ENABLE_SDR50_TUNING |
+                  NVQUIRK_ENABLE_SDR50 |
+                  NVQUIRK_ENABLE_SDR104 |
+#endif
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
+                   NVQUIRK_SET_DRIVE_STRENGTH |
+#endif
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+                   NVQUIRK_DISABLE_AUTO_CALIBRATION |
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+                   NVQUIRK_SET_CALIBRATION_OFFSETS |
+                   NVQUIRK_ENABLE_SD_3_0 |
+#else
+                   NVQUIRK_SET_TRIM_DELAY |
+                   NVQUIRK_ENABLE_DDR50 |
+                   NVQUIRK_INFINITE_ERASE_TIMEOUT |
+#endif
                    NVQUIRK_ENABLE_BLOCK_GAP_DET,
 };
 
@@ -1075,7 +1811,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        struct sdhci_pltfm_host *pltfm_host;
        struct tegra_sdhci_platform_data *plat;
        struct sdhci_tegra *tegra_host;
-       struct clk *clk;
        int rc;
 
        match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
@@ -1109,10 +1844,28 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        }
 
        tegra_host->plat = plat;
+       tegra_host->sd_stat_head = NULL;
        tegra_host->soc_data = soc_data;
 
        pltfm_host->priv = tegra_host;
 
+       pll_c = clk_get_sys(NULL, "pll_c");
+       if (IS_ERR(pll_c)) {
+               rc = PTR_ERR(pll_c);
+               dev_err(mmc_dev(host->mmc),
+                       "clk error in getting pll_c: %d\n", rc);
+       }
+
+       pll_p = clk_get_sys(NULL, "pll_p");
+       if (IS_ERR(pll_p)) {
+               rc = PTR_ERR(pll_p);
+               dev_err(mmc_dev(host->mmc),
+                       "clk error in getting pll_p: %d\n", rc);
+       }
+
+       pll_c_rate = clk_get_rate(pll_c);
+       pll_p_rate = clk_get_rate(pll_p);
+
 #ifdef CONFIG_MMC_EMBEDDED_SDIO
        if (plat->mmc_data.embedded_sdio)
                mmc_set_embedded_sdio_data(host->mmc,
@@ -1129,7 +1882,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                                "failed to allocate power gpio\n");
                        goto err_power_req;
                }
-               tegra_gpio_enable(plat->power_gpio);
                gpio_direction_output(plat->power_gpio, 1);
        }
 
@@ -1140,7 +1892,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                                "failed to allocate cd gpio\n");
                        goto err_cd_req;
                }
-               tegra_gpio_enable(plat->cd_gpio);
                gpio_direction_input(plat->cd_gpio);
 
                tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0);
@@ -1175,7 +1926,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                                "failed to allocate wp gpio\n");
                        goto err_wp_req;
                }
-               tegra_gpio_enable(plat->wp_gpio);
                gpio_direction_input(plat->wp_gpio);
        }
 
@@ -1186,60 +1936,70 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        if (!gpio_is_valid(plat->cd_gpio))
                tegra_host->card_present = 1;
 
-       if (!plat->mmc_data.built_in) {
-               if (plat->mmc_data.ocr_mask & SDHOST_1V8_OCR_MASK) {
-                       tegra_host->vddio_min_uv = SDHOST_LOW_VOLT_MIN;
-                       tegra_host->vddio_max_uv = SDHOST_LOW_VOLT_MAX;
-               } else {
-                       /*
-                        * Set the minV and maxV to default
-                        * voltage range of 2.7V - 3.6V
-                        */
-                       tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_MIN;
+       if (plat->mmc_data.ocr_mask & SDHOST_1V8_OCR_MASK) {
+               tegra_host->vddio_min_uv = SDHOST_LOW_VOLT_MIN;
+               tegra_host->vddio_max_uv = SDHOST_LOW_VOLT_MAX;
+       } else if (plat->mmc_data.ocr_mask & MMC_OCR_2V8_MASK) {
+                       tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_2V8;
                        tegra_host->vddio_max_uv = SDHOST_HIGH_VOLT_MAX;
-               }
-               tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc), "vddio_sdmmc");
-               if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) {
-                       dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
-                               "Assuming vddio_sdmmc is not required.\n",
-                                       "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg));
+       } else {
+               /*
+                * Set the minV and maxV to default
+                * voltage range of 2.7V - 3.6V
+                */
+               tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_MIN;
+               tegra_host->vddio_max_uv = SDHOST_HIGH_VOLT_MAX;
+       }
+
+       tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc),
+                                                       "vddio_sdmmc");
+       if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) {
+               dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
+                       "Assuming vddio_sdmmc is not required.\n",
+                       "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg));
+               tegra_host->vdd_io_reg = NULL;
+       } else {
+               rc = regulator_set_voltage(tegra_host->vdd_io_reg,
+                       tegra_host->vddio_min_uv,
+                       tegra_host->vddio_max_uv);
+               if (rc) {
+                       dev_err(mmc_dev(host->mmc), "%s regulator_set_voltage failed: %d",
+                               "vddio_sdmmc", rc);
+                       regulator_put(tegra_host->vdd_io_reg);
                        tegra_host->vdd_io_reg = NULL;
-               } else {
-                       rc = regulator_set_voltage(tegra_host->vdd_io_reg,
-                               tegra_host->vddio_min_uv,
-                               tegra_host->vddio_max_uv);
-                       if (rc) {
-                               dev_err(mmc_dev(host->mmc), "%s regulator_set_voltage failed: %d",
-                                       "vddio_sdmmc", rc);
-                               regulator_put(tegra_host->vdd_io_reg);
-                               tegra_host->vdd_io_reg = NULL;
-                       }
                }
+       }
 
-               tegra_host->vdd_slot_reg = regulator_get(mmc_dev(host->mmc), "vddio_sd_slot");
-               if (IS_ERR_OR_NULL(tegra_host->vdd_slot_reg)) {
-                       dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
-                               " Assuming vddio_sd_slot is not required.\n",
-                                       "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg));
-                       tegra_host->vdd_slot_reg = NULL;
-               }
+       tegra_host->vdd_slot_reg = regulator_get(mmc_dev(host->mmc),
+                                                       "vddio_sd_slot");
+       if (IS_ERR_OR_NULL(tegra_host->vdd_slot_reg)) {
+               dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
+                       " Assuming vddio_sd_slot is not required.\n",
+                       "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg));
+               tegra_host->vdd_slot_reg = NULL;
+       }
 
-               if (tegra_host->card_present) {
-                       if (tegra_host->vdd_slot_reg)
-                               regulator_enable(tegra_host->vdd_slot_reg);
-                       if (tegra_host->vdd_io_reg)
-                               regulator_enable(tegra_host->vdd_io_reg);
-                       tegra_host->is_rail_enabled = 1;
-               }
+       if (tegra_host->card_present) {
+               if (tegra_host->vdd_slot_reg)
+                       regulator_enable(tegra_host->vdd_slot_reg);
+               if (tegra_host->vdd_io_reg)
+                       regulator_enable(tegra_host->vdd_io_reg);
+               tegra_host->is_rail_enabled = 1;
        }
 
-       clk = clk_get(mmc_dev(host->mmc), NULL);
-       if (IS_ERR(clk)) {
+       pm_runtime_enable(&pdev->dev);
+       pltfm_host->clk = clk_get(mmc_dev(host->mmc), NULL);
+       if (IS_ERR(pltfm_host->clk)) {
                dev_err(mmc_dev(host->mmc), "clk err\n");
-               rc = PTR_ERR(clk);
+               rc = PTR_ERR(pltfm_host->clk);
                goto err_clk_get;
        }
-       rc = clk_enable(clk);
+
+       if (clk_get_parent(pltfm_host->clk) == pll_c)
+               tegra_host->is_parent_pllc = true;
+
+       pm_runtime_get_sync(&pdev->dev);
+       rc = clk_prepare_enable(pltfm_host->clk);
        if (rc != 0)
                goto err_clk_put;
 
@@ -1254,30 +2014,23 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                        clk_round_rate(tegra_host->emc_clk, ULONG_MAX);
        }
 
-       pltfm_host->clk = clk;
        pltfm_host->priv = tegra_host;
        tegra_host->clk_enabled = true;
        tegra_host->max_clk_limit = plat->max_clk_limit;
        tegra_host->ddr_clk_limit = plat->ddr_clk_limit;
+       tegra_host->sd_detect_in_suspend = plat->sd_detect_in_suspend;
        tegra_host->instance = pdev->id;
        tegra_host->dpd = tegra_io_dpd_get(mmc_dev(host->mmc));
 
        host->mmc->pm_caps |= plat->pm_caps;
        host->mmc->pm_flags |= plat->pm_flags;
 
-       /*
-        * tegra sd controller is not able to reset the command inhibit
-        * bit for the mmc sleep/awake command
-        */
-       host->mmc->caps2 |= MMC_CAP2_NO_SLEEP_CMD;
-
        host->mmc->caps |= MMC_CAP_ERASE;
        /* enable 1/8V DDR capable */
        host->mmc->caps |= MMC_CAP_1_8V_DDR;
        if (plat->is_8bit)
                host->mmc->caps |= MMC_CAP_8_BIT_DATA;
        host->mmc->caps |= MMC_CAP_SDIO_IRQ;
-
        host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
        if (plat->mmc_data.built_in) {
                host->mmc->caps |= MMC_CAP_NONREMOVABLE;
@@ -1285,15 +2038,8 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
 
 #ifdef CONFIG_MMC_BKOPS
-       host->mmc->caps |= MMC_CAP_BKOPS;
-#endif
-
-#ifdef CONFIG_MMC_EMBEDDED_SDIO
-       /* Do not turn OFF embedded sdio cards as it support Wake on Wireless */
-       if (plat->mmc_data.embedded_sdio)
-               host->mmc->pm_flags |= MMC_PM_KEEP_POWER;
+       host->mmc->caps2 |= MMC_CAP2_BKOPS;
 #endif
-
        tegra_sdhost_min_freq = TEGRA_SDHOST_MIN_FREQ;
 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
        tegra_host->hw_ops = &tegra_2x_sdhci_ops;
@@ -1304,38 +2050,46 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
 #else
        tegra_host->hw_ops = &tegra_11x_sdhci_ops;
        tegra_sdhost_std_freq = TEGRA3_SDHOST_STD_FREQ;
+       host->mmc->caps2 |= MMC_CAP2_HS200;
 #endif
+
+       if (plat->nominal_vcore_uV)
+               tegra_host->nominal_vcore_uV = plat->nominal_vcore_uV;
+       host->edp_support = plat->edp_support ? true : false;
+       if (host->edp_support)
+               for (rc = 0; rc < SD_EDP_NUM_STATES; rc++)
+                       host->edp_states[rc] = plat->edp_states[rc];
+
        rc = sdhci_add_host(host);
+
+       sdhci_tegra_error_stats_debugfs(host);
        if (rc)
                goto err_add_host;
 
+       /* Enable async suspend/resume to reduce LP0 latency */
+       device_enable_async_suspend(&pdev->dev);
+
        return 0;
 
 err_add_host:
        clk_put(tegra_host->emc_clk);
-       clk_disable(pltfm_host->clk);
+       clk_disable_unprepare(pltfm_host->clk);
+       pm_runtime_put_sync(&pdev->dev);
 err_clk_put:
        clk_put(pltfm_host->clk);
 err_clk_get:
-       if (gpio_is_valid(plat->wp_gpio)) {
-               tegra_gpio_disable(plat->wp_gpio);
+       if (gpio_is_valid(plat->wp_gpio))
                gpio_free(plat->wp_gpio);
-       }
 err_wp_req:
        if (gpio_is_valid(plat->cd_gpio))
                free_irq(gpio_to_irq(plat->cd_gpio), host);
 err_cd_irq_req:
-       if (gpio_is_valid(plat->cd_gpio)) {
-               tegra_gpio_disable(plat->cd_gpio);
+       if (gpio_is_valid(plat->cd_gpio))
                gpio_free(plat->cd_gpio);
-       }
 err_cd_req:
-       if (gpio_is_valid(plat->power_gpio)) {
-               tegra_gpio_disable(plat->power_gpio);
+       if (gpio_is_valid(plat->power_gpio))
                gpio_free(plat->power_gpio);
-       }
 err_power_req:
-       kfree(tegra_host);
 err_no_plat:
        sdhci_pltfm_free(pdev);
        return rc;
@@ -1363,28 +2117,24 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
                regulator_put(tegra_host->vdd_io_reg);
        }
 
-       if (gpio_is_valid(plat->wp_gpio)) {
-               tegra_gpio_disable(plat->wp_gpio);
+       if (gpio_is_valid(plat->wp_gpio))
                gpio_free(plat->wp_gpio);
-       }
 
        if (gpio_is_valid(plat->cd_gpio)) {
                free_irq(gpio_to_irq(plat->cd_gpio), host);
-               tegra_gpio_disable(plat->cd_gpio);
                gpio_free(plat->cd_gpio);
        }
 
-       if (gpio_is_valid(plat->power_gpio)) {
-               tegra_gpio_disable(plat->power_gpio);
+       if (gpio_is_valid(plat->power_gpio))
                gpio_free(plat->power_gpio);
-       }
 
-       if (tegra_host->clk_enabled)
-               clk_disable(pltfm_host->clk);
+       if (tegra_host->clk_enabled) {
+               clk_disable_unprepare(pltfm_host->clk);
+               pm_runtime_put_sync(&pdev->dev);
+       }
        clk_put(pltfm_host->clk);
 
        sdhci_pltfm_free(pdev);
-       kfree(tegra_host);
 
        return 0;
 }