mmc: tegra: Implementation of new tuning algorithm
Pavan Kunapuli [Thu, 9 Jan 2014 11:46:13 +0000 (16:46 +0530)]
Implemented the new tuning algorithm that
- Removes the tap holes from auto tuning windows
- Adjust window ends with margins for Vmin for the corresponding freq
- Dynamically updates dvfs table entry if a new Vmin is found from
auto tuning calculations
- Calculates the best tap value from the windows after removing tap
holes and margin additions.

Disabling external loopback clock for SDMMC3 as per characterization
results.

Bug 1423429

Reviewed-on: http://git-master/r/359375
(cherry picked from commit 8f33c039e2a8551b04d9023702a94c49dd2fffac)

Change-Id: I49b8dca227ae9f40cbbcbc81a36130a693588056
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Signed-off-by: Naveen Kumar Arepalli <naveenk@nvidia.com>
Signed-off-by: R Raj Kumar <rrajk@nvidia.com>
Reviewed-on: http://git-master/r/363810
(cherry picked from commit e12f878c391eb732369313b53a1a94b5a673a8bc)
Reviewed-on: http://git-master/r/410189
GVS: Gerrit_Virtual_Submit
Reviewed-by: Shreshtha Sahu <ssahu@nvidia.com>
Tested-by: Shreshtha Sahu <ssahu@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

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

index 19bc4a5..1b62946 100644 (file)
 #define NVQUIRK_HIGH_FREQ_TAP_PROCEDURE                BIT(22)
 /* Disable SDMMC3 external loopback */
 #define NVQUIRK_DISABLE_EXTERNAL_LOOPBACK      BIT(23)
+#define NVQUIRK_TMP_VAR_1_5_TAP_MARGIN         BIT(24)
 
 /* Common subset of quirks for Tegra3 and later sdmmc controllers */
 #define TEGRA_SDHCI_NVQUIRKS   (NVQUIRK_ENABLE_PADPIPE_CLKEN | \
@@ -221,7 +222,10 @@ struct sdhci_tegra_soc_data {
        u32 nvquirks;
        const char *parent_clk_list[2];
        unsigned int tuning_freq_list[TUNING_FREQ_COUNT];
-       unsigned int tuning_min_volt_list[TUNING_FREQ_COUNT];
+       u8 t2t_coeffs_count;
+       u8 tap_hole_coeffs_count;
+       struct tuning_t2t_coeffs *t2t_coeffs;
+       struct tap_hole_coeffs *tap_hole_coeffs;
 };
 
 
@@ -231,18 +235,93 @@ enum tegra_regulator_config_ops {
        CONFIG_REG_SET_VOLT,
 };
 
-static unsigned int uhs_max_freq_MHz[] = {
-       [MMC_TIMING_UHS_SDR50] = 100,
-       [MMC_TIMING_UHS_SDR104] = 208,
-       [MMC_TIMING_MMC_HS200] = 200,
-};
-
 enum tegra_tuning_freq {
        TUNING_LOW_FREQ,
        TUNING_HIGH_FREQ,
        TUNING_MAX_FREQ,
 };
 
+struct tuning_t2t_coeffs {
+       const char *dev_id;
+       int vmax;
+       int vmin;
+       unsigned int t2t_vnom_slope;
+       unsigned int t2t_vnom_int;
+       unsigned int t2t_vmax_slope;
+       unsigned int t2t_vmax_int;
+       unsigned int t2t_vmin_slope;
+       unsigned int t2t_vmin_int;
+};
+
+#define SET_TUNING_COEFFS(_device_id, _vmax, _vmin, _t2t_vnom_slope,   \
+       _t2t_vnom_int, _t2t_vmax_slope, _t2t_vmax_int, _t2t_vmin_slope, \
+       _t2t_vmin_int)  \
+       {                                               \
+               .dev_id = _device_id,                   \
+               .vmax = _vmax,                          \
+               .vmin = _vmin,                          \
+               .t2t_vnom_slope = _t2t_vnom_slope,      \
+               .t2t_vnom_int = _t2t_vnom_int,          \
+               .t2t_vmax_slope = _t2t_vmax_slope,      \
+               .t2t_vmax_int = _t2t_vmax_int,          \
+               .t2t_vmin_slope = _t2t_vmin_slope,      \
+               .t2t_vmin_int = _t2t_vmin_int,          \
+       }
+
+struct tuning_t2t_coeffs t12x_tuning_coeffs[] = {
+       SET_TUNING_COEFFS("sdhci-tegra.3",      1150,   950,    27,     118295,
+               27,     118295, 48,     188148),
+       SET_TUNING_COEFFS("sdhci-tegra.2",      1250,   950,    29,     124427,
+               29, 124427,      54,    203707),
+       SET_TUNING_COEFFS("sdhci-tegra.0",      1150,   950,    25,     115933,
+               25,     115933, 47,     187224),
+};
+
+struct tap_hole_coeffs {
+       const char *dev_id;
+       unsigned int freq_khz;
+       unsigned int thole_vnom_slope;
+       unsigned int thole_vnom_int;
+       unsigned int thole_vmax_slope;
+       unsigned int thole_vmax_int;
+       unsigned int thole_vmin_slope;
+       unsigned int thole_vmin_int;
+};
+
+#define SET_TAP_HOLE_COEFFS(_device_id, _freq_khz, _thole_vnom_slope,  \
+       _thole_vnom_int, _thole_vmax_slope, _thole_vmax_int,    \
+       _thole_vmin_slope, _thole_vmin_int)     \
+       {                                       \
+               .dev_id = _device_id,           \
+               .freq_khz = _freq_khz,          \
+               .thole_vnom_slope = _thole_vnom_slope,  \
+               .thole_vnom_int = _thole_vnom_int,      \
+               .thole_vmax_slope = _thole_vmax_slope,  \
+               .thole_vmax_int = _thole_vmax_int,      \
+               .thole_vmin_slope = _thole_vmin_slope,  \
+               .thole_vmin_int = _thole_vmin_int,      \
+       }
+
+struct tap_hole_coeffs t12x_tap_hole_coeffs[] = {
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.3",    200000, 1037,   106934, 1037,
+               106934, 558,    74315),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.3",    136000, 1703,   186307, 1703,
+               186307, 890,    130617),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.3",    81600,  3090,   351666, 3090,
+               351666, 1583,   247913),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.2",    204000, 468,    36031,  468,
+               36031,  253,    21264),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.2",    136000, 1146,   117841, 1146,
+               117841, 589,    78993),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.2",    81600,  2504,   281460, 2504,
+               281460, 1262,   194452),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.0",    204000, 874,    85243,  874,
+               85243,  449,    57321),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.0",    136000, 1554,   167210, 1554,
+               167210, 793,    115672),
+       SET_TAP_HOLE_COEFFS("sdhci-tegra.0",    81600,  2916,   331143, 2916,
+               331143, 1480,   232373),
+};
 
 struct freq_tuning_constraints {
        unsigned int vcore_mask;
@@ -253,35 +332,56 @@ static struct freq_tuning_constraints tuning_vcore_constraints[3] = {
                .vcore_mask = BOOT_VCORE_TUN,
        },
        [1] = {
-               .vcore_mask = BOOT_VCORE_TUN | MIN_OVERRIDE_VCORE_TUN |
-                       NOMINAL_VCORE_TUN,
+               .vcore_mask = BOOT_VCORE_TUN,
        },
        [2] = {
-               .vcore_mask = BOOT_VCORE_TUN | NOMINAL_VCORE_TUN,
+               .vcore_mask = BOOT_VCORE_TUN,
        },
 };
 
+struct tuning_ui {
+       int ui;
+       bool is_valid_ui;
+};
+
+enum tap_win_edge_attr {
+       WIN_EDGE_BOUN_START,
+       WIN_EDGE_BOUN_END,
+       WIN_EDGE_HOLE,
+};
+
 struct tap_window_data {
-       bool            abandon_partial_win;
-       bool            abandon_full_win;
-       unsigned int voltage;
-       u8 partial_win;
-       u8 full_win_begin;
-       u8 full_win_end;
-       u8 vcore_set_status;
-       u8 found_tuning_window;
-       u8 tuning_done;
+       int win_start;
+       int win_end;
+       enum tap_win_edge_attr win_start_attr;
+       enum tap_win_edge_attr win_end_attr;
+       u8 win_size;
 };
 
+struct tuning_values {
+       int t2t_vmax;
+       int t2t_vmin;
+       int ui;
+       int ui_vmin;
+       int vmax_thole;
+       int vmin_thole;
+};
 struct tegra_tuning_data {
        unsigned int freq_hz;
-       unsigned int best_tap_value;
-       unsigned int nom_best_tap_value;
+       int best_tap_value;
+       int nom_best_tap_value;
        struct freq_tuning_constraints constraints;
-       struct tap_window_data *tap_data[TUNING_VOLTAGES_COUNT];
+       struct tap_hole_coeffs *thole_coeffs;
+       struct tuning_t2t_coeffs *t2t_coeffs;
+       struct tuning_values est_values;
+       struct tuning_values calc_values;
+       struct tap_window_data *tap_data;
+       struct tap_window_data *final_tap_data;
+       u8 num_of_valid_tap_wins;
        u8 nr_voltages;
        u8 freq_band;
        bool tuning_done;
+       bool is_partial_win_valid;
 };
 
 #ifdef CONFIG_MMC_FREQ_SCALING
@@ -372,7 +472,7 @@ struct sdhci_tegra {
        /* Tuning packet size */
        unsigned int tuning_bsize;
        /* Num of tuning freqs selected */
-       unsigned int tuning_freq_count;
+       int tuning_freq_count;
        unsigned int tap_cmd;
        /* Tuning status */
        unsigned int tuning_status;
@@ -382,6 +482,7 @@ struct sdhci_tegra {
        /* Freq tuning information for each sampling clock freq */
        struct tegra_tuning_data tuning_data[DFS_FREQ_COUNT];
        struct tegra_freq_gov_data *gov_data;
+       u32 speedo;
 };
 
 static struct clk *pll_c;
@@ -1521,7 +1622,13 @@ static void sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci,
        u32 vendor_ctrl;
 
        /* Max tap delay value is 255 */
-       BUG_ON(tap_delay > MAX_TAP_VALUES);
+       if (tap_delay > MAX_TAP_VALUES) {
+               dev_err(mmc_dev(sdhci->mmc),
+                       "Valid tap range (0-255). Setting tap value %d\n",
+                       tap_delay);
+               dump_stack();
+               return;
+       }
 
        vendor_ctrl = sdhci_readl(sdhci, SDHCI_VNDR_CLK_CTRL);
        vendor_ctrl &= ~(0xFF << SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
@@ -1576,266 +1683,294 @@ out:
        return tuning_data;
 }
 
-static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci)
+static void calculate_vmin_values(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data, int vmin, int boot_mv)
 {
-       struct tegra_tuning_data *tuning_data;
-       struct tap_window_data *tap_data;
-       u8 i;
+       struct tuning_values *est_values = &tuning_data->est_values;
+       struct tuning_values *calc_values = &tuning_data->calc_values;
+       struct tuning_t2t_coeffs *t2t_coeffs = tuning_data->t2t_coeffs;
+       struct tap_hole_coeffs *thole_coeffs = tuning_data->thole_coeffs;
+       int vmin_slope, vmin_int, temp_calc_vmin;
+       int t2t_vmax, t2t_vmin;
+       int vmax_thole, vmin_thole;
 
-       pr_info("********%s: Tuning window data********\n",
-               mmc_hostname(sdhci->mmc));
-       tuning_data = sdhci_tegra_get_tuning_data(sdhci, sdhci->max_clk);
-       for (i = 0; i < tuning_data->nr_voltages; i++) {
-               tap_data = tuning_data->tap_data[i];
-               pr_info("%dHz: voltage %dmv:\n", sdhci->max_clk,
-                       tap_data->voltage);
-               pr_info("Partial Win %d, Full win start %d, full win end %d\n",
-                       tap_data->partial_win,
-                       tap_data->full_win_begin,
-                       tap_data->full_win_end);
+       /*
+        * If current vmin is equal to vmin or vmax of tuning data, use the
+        * previously calculated estimated T2T values directly. Note that the
+        * estimated T2T_vmax is not at Vmax specified in tuning data. It is
+        * the T2T at the boot or max voltage for the current SKU. Hence,
+        * boot_mv is used in place of t2t_coeffs->vmax.
+        */
+       if (vmin == t2t_coeffs->vmin) {
+               t2t_vmin = est_values->t2t_vmin;
+       } else if (vmin == boot_mv) {
+               t2t_vmin = est_values->t2t_vmax;
+       } else {
+               /*
+                * For any intermediate voltage between boot voltage and vmin
+                * of tuning data, calculate the slope and intercept from the
+                * t2t at boot_mv and vmin and calculate the actual values.
+                */
+               t2t_vmax = 1000 / est_values->t2t_vmax;
+               t2t_vmin = 1000 / est_values->t2t_vmin;
+               vmin_slope = ((t2t_vmax - t2t_vmin) * 1000) /
+                       (boot_mv - t2t_coeffs->vmin);
+               vmin_int = (t2t_vmax * 1000 - (vmin_slope * boot_mv)) / 1000;
+               t2t_vmin = (vmin_slope * vmin) / 1000 + vmin_int;
+               t2t_vmin = (1000 / t2t_vmin);
+       }
+
+       calc_values->t2t_vmin = (t2t_vmin * calc_values->t2t_vmax) /
+               est_values->t2t_vmax;
+
+       calc_values->ui_vmin = (1000000 / (tuning_data->freq_hz / 1000000)) /
+               calc_values->t2t_vmin;
+
+       /* Calculate the vmin tap hole at vmin of tuning data */
+       temp_calc_vmin = (est_values->t2t_vmin * calc_values->t2t_vmax) /
+               est_values->t2t_vmax;
+       vmin_thole = (thole_coeffs->thole_vmin_int -
+               (thole_coeffs->thole_vmin_slope * temp_calc_vmin)) /
+               1000;
+       vmax_thole = calc_values->vmax_thole;
+
+       if (vmin == t2t_coeffs->vmin) {
+               calc_values->vmin_thole = vmin_thole;
+       } else if (vmin == boot_mv) {
+               calc_values->vmin_thole = vmax_thole;
+       } else {
+               /*
+                * Interpolate the tap hole for any intermediate voltage.
+                * Calculate the slope and intercept from the available data
+                * and use them to calculate the actual values.
+                */
+               vmin_slope = ((vmax_thole - vmin_thole) * 1000) /
+                       (boot_mv - t2t_coeffs->vmin);
+               vmin_int = (vmax_thole * 1000 - (vmin_slope * boot_mv)) / 1000;
+               calc_values->vmin_thole = (vmin_slope * vmin) / 1000 + vmin_int;
+       }
+
+       /* Adjust the partial win start for Vmin boundary */
+       if (tuning_data->is_partial_win_valid)
+               tuning_data->final_tap_data[0].win_start =
+                       (tuning_data->final_tap_data[0].win_start *
+                       tuning_data->calc_values.t2t_vmax) /
+                       tuning_data->calc_values.t2t_vmin;
+
+       pr_info("**********Tuning values*********\n");
+       pr_info("**estimated values**\n");
+       pr_info("T2T_Vmax %d, T2T_Vmin %d, 1'st_hole_Vmax %d, UI_Vmax %d\n",
+               est_values->t2t_vmax, est_values->t2t_vmin,
+               est_values->vmax_thole, est_values->ui);
+       pr_info("**Calculated values**\n");
+       pr_info("T2T_Vmax %d, 1'st_hole_Vmax %d, UI_Vmax %d\n",
+               calc_values->t2t_vmax, calc_values->vmax_thole,
+               calc_values->ui);
+       pr_info("T2T_Vmin %d, 1'st_hole_Vmin %d, UI_Vmin %d\n",
+               calc_values->t2t_vmin, calc_values->vmin_thole,
+               calc_values->ui_vmin);
+       pr_info("***********************************\n");
+}
+
+static int slide_window_start(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data,
+       int tap_value, enum tap_win_edge_attr edge_attr, int tap_hole)
+{
+       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;
+
+       if (edge_attr == WIN_EDGE_BOUN_START) {
+               if (tap_value < 0)
+                       tap_value += (1000 / tuning_data->calc_values.t2t_vmin);
+               else
+                       tap_value += (1000 / tuning_data->calc_values.t2t_vmax);
+       } else if (edge_attr == WIN_EDGE_HOLE) {
+               if (soc_data->nvquirks & NVQUIRK_TMP_VAR_1_5_TAP_MARGIN)
+                       tap_value += ((7 * tap_hole) / 100) + 2;
+               else
+                       tap_value += ((7 * tap_hole) / 100) +
+                       (((2 * (450 / tuning_data->calc_values.t2t_vmax))
+                       + 1) / 2);
        }
-       pr_info("Best tap value %d, best nom voltage tap value %d\n",
-               tuning_data->best_tap_value,
-               tuning_data->nom_best_tap_value);
-       pr_info("*****************************\n");
+
+       if (tap_value > MAX_TAP_VALUES)
+               tap_value = MAX_TAP_VALUES;
+
+       return tap_value;
 }
 
-/*
- * 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 unsigned int calculate_low_freq_tap_value(struct sdhci_host *sdhci,
-       struct tap_window_data *tap_data)
+static int slide_window_end(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data,
+       int tap_value, enum tap_win_edge_attr edge_attr, int tap_hole)
 {
-       unsigned int curr_clock;
-       unsigned int max_clock;
-       int best_tap_value;
-       unsigned int tuning_ui;
-       unsigned int sampling_point = 0;
-       bool select_partial_win = false;
-
-       tuning_ui = tap_data->full_win_end - tap_data->partial_win;
+       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;
 
-       /* Calculate the sampling point */
-       curr_clock = sdhci->max_clk / 1000000;
-       max_clock = uhs_max_freq_MHz[sdhci->mmc->ios.timing];
-       sampling_point = ((tuning_ui * curr_clock) / (max_clock << 2));
+       if (edge_attr == WIN_EDGE_BOUN_END) {
+               tap_value = (tap_value * tuning_data->calc_values.t2t_vmax) /
+                       tuning_data->calc_values.t2t_vmin;
+               tap_value -= (1000 / tuning_data->calc_values.t2t_vmin);
+       } else if (edge_attr == WIN_EDGE_HOLE) {
+               if (tap_hole > 0)
+                       tap_value = tap_hole;
+               if (soc_data->nvquirks & NVQUIRK_TMP_VAR_1_5_TAP_MARGIN)
+                       tap_value -= ((7 * tap_hole) / 100) + 2;
+               else
+                       tap_value -= ((7 * tap_hole) / 100) +
+                       (((2 * (450 / tuning_data->calc_values.t2t_vmin))
+                       + 1) / 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 * tuning_ui) / 100)))
-                       select_partial_win = true;
-
-       if (select_partial_win)
-               best_tap_value = (tap_data->partial_win -
-                       (tap_data->full_win_end - tap_data->full_win_begin)) +
-                       sampling_point;
-       else
-               best_tap_value = tap_data->full_win_begin +
-                       sampling_point;
+       return tap_value;
+}
 
-       if (best_tap_value < 0)
-               best_tap_value = 0;
+static int adjust_window_boundaries(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data,
+       struct tap_window_data *temp_tap_data)
+{
+       struct tap_window_data *tap_data;
+       int vmin_tap_hole;
+       int vmax_tap_hole;
+       u8 i = 0;
 
-       return best_tap_value;
+       vmax_tap_hole = tuning_data->calc_values.vmax_thole;
+       vmin_tap_hole = tuning_data->calc_values.vmin_thole;
+       for (i = 0; i < tuning_data->num_of_valid_tap_wins; i++) {
+               tap_data = &temp_tap_data[i];
+               tap_data->win_start = slide_window_start(sdhci, tuning_data,
+                       tap_data->win_start, tap_data->win_start_attr,
+                       vmax_tap_hole);
+               /* Update with next hole if first hole is taken care of */
+               if (tap_data->win_start_attr == WIN_EDGE_HOLE)
+                       vmax_tap_hole += tuning_data->calc_values.ui;
+
+               tap_data->win_end = slide_window_end(sdhci, tuning_data,
+                       tap_data->win_end, tap_data->win_end_attr,
+                       vmin_tap_hole);
+               /* Update with next hole if first hole is taken care of */
+               if (tap_data->win_end_attr == WIN_EDGE_HOLE)
+                       vmin_tap_hole += tuning_data->calc_values.ui_vmin;
+       }
+
+       pr_info("***********final tuning windows**********\n");
+       for (i = 0; i < tuning_data->num_of_valid_tap_wins; i++) {
+               tap_data = &temp_tap_data[i];
+               pr_info("win[%d]: %d - %d\n", i, tap_data->win_start,
+                       tap_data->win_end);
+       }
+       pr_info("********************************\n");
+       return 0;
 }
 
-/*
- * 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 unsigned int calculate_high_freq_tap_value(struct sdhci_host *sdhci,
-       struct tap_window_data *vmax_tap_data,
-       struct tap_window_data *vmid_tap_data)
-{
-       unsigned int curr_clock;
-       unsigned int max_clock;
-       unsigned int vmax_tuning_ui;
-       unsigned int vmax_sampling_point;
-       unsigned int vmid_tuning_ui;
-       unsigned int vmid_sampling_point;
-       unsigned int full_win_tap;
-       int partial_win_start;
-       int partial_win_tap;
-       int full_win_quality;
-       int partial_win_quality;
-       int best_tap_value;
+static int find_best_tap_value(struct tegra_tuning_data *tuning_data,
+       struct tap_window_data *temp_tap_data, int vmin)
+{
+       struct tap_window_data *tap_data;
+       u8 i = 0, sel_win = 0;
+       int pref_win = 0, curr_win_size = 0;
+       int best_tap_value = 0;
+
+       for (i = 0; i < tuning_data->num_of_valid_tap_wins; i++) {
+               tap_data = &temp_tap_data[i];
+               if (!i && tuning_data->is_partial_win_valid) {
+                       pref_win = tap_data->win_end - tap_data->win_start;
+                       if ((tap_data->win_end * 2) < pref_win)
+                               pref_win = tap_data->win_end * 2;
+                       sel_win = 0;
+               } else {
+                       curr_win_size = tap_data->win_end - tap_data->win_start;
+                       if ((curr_win_size > 0) && (curr_win_size > pref_win)) {
+                               pref_win = curr_win_size;
+                               sel_win = i;
+                       }
+               }
+       }
 
-       curr_clock = sdhci->max_clk / 1000000;
-       max_clock = uhs_max_freq_MHz[sdhci->mmc->ios.timing];
+       if (pref_win <= 0) {
+               pr_err("No window opening for %d vmin\n", vmin);
+               return -1;
+       }
 
-       /*
-        * Calculate the tuning_ui and sampling points for tap windows found
-        * at all core voltages.
-        */
-       vmax_tuning_ui = vmax_tap_data->full_win_end -
-               vmax_tap_data->partial_win;
-       vmax_sampling_point = (vmax_tuning_ui * curr_clock) / (max_clock << 2);
-
-       vmid_tuning_ui = vmid_tap_data->full_win_end -
-               vmid_tap_data->partial_win;
-       vmid_sampling_point = (vmid_tuning_ui * curr_clock) / (max_clock << 2);
-
-       full_win_tap = ((vmid_tap_data->full_win_end - vmid_sampling_point) +
-               (vmax_tap_data->full_win_begin + vmax_sampling_point));
-       full_win_tap >>= 1;
-       full_win_quality = (vmid_tap_data->full_win_end -
-               vmid_sampling_point) - (vmax_tap_data->full_win_begin +
-               vmax_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_sampling_point) +
-               (partial_win_start + vmax_sampling_point));
-       partial_win_tap >>= 1;
-
-       if (partial_win_tap < 0)
-               partial_win_tap = 0;
-       partial_win_quality = (vmid_tap_data->partial_win -
-               vmid_sampling_point) - partial_win_tap;
-
-       if ((full_win_quality <= 0) && (partial_win_quality <= 0)) {
-               dev_warn(mmc_dev(sdhci->mmc),
-                       "No margin window for both windows\n");
-               best_tap_value = vmax_tap_data->full_win_begin +
-                       vmid_tap_data->full_win_end;
-               best_tap_value >>= 1;
+       tap_data = &temp_tap_data[sel_win];
+       if (!sel_win && tuning_data->is_partial_win_valid) {
+               i = sel_win;
+               best_tap_value = tap_data->win_end - (pref_win / 2);
+               if (best_tap_value < 0)
+                       best_tap_value = 0;
        } else {
-               if (full_win_quality > partial_win_quality)
-                       best_tap_value = full_win_tap;
-               else
-                       best_tap_value = partial_win_tap;
+               best_tap_value = tap_data->win_start +
+                       ((tap_data->win_end - tap_data->win_start) *
+                       tuning_data->calc_values.t2t_vmin) /
+                       (tuning_data->calc_values.t2t_vmin +
+                       tuning_data->calc_values.t2t_vmax);
        }
 
-       if (best_tap_value < 0)
-               best_tap_value = 0;
+       pr_err("best tap win - (%d-%d), best tap value %d\n",
+               tap_data->win_start, tap_data->win_end, best_tap_value);
        return best_tap_value;
 }
 
-static void sdhci_tegra_calculate_best_tap(struct sdhci_host *sdhci,
-       u8 freq_band)
+static int sdhci_tegra_calculate_best_tap(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
-       struct tegra_tuning_data *tuning_data;
-       unsigned int vdd_core;
-       int delta_vdd;
-       unsigned int voltage = 0;
-
-       SDHCI_TEGRA_DBG("%s: calculating best tap for freq band %d\n",
-               mmc_hostname(sdhci->mmc), freq_band);
-       SDHCI_TEGRA_DBG("%s: Best tap %s nom best tap\n",
-               mmc_hostname(sdhci->mmc),
-               (tegra_host->plat->en_nominal_vcore_tuning) ? "different from" :
-               "same as");
+       struct tap_window_data *temp_tap_data = NULL;
+       int vmin, curr_vmin, best_tap_value = 0;
+       int err = 0;
 
-       tuning_data = sdhci_tegra_get_tuning_data(sdhci, sdhci->max_clk);
+       curr_vmin = tegra_dvfs_predict_millivolts(pltfm_host->clk,
+               tuning_data->freq_hz);
+       vmin = curr_vmin;
 
-       if (tuning_data->tap_data[0] && tuning_data->tap_data[1] &&
-                       (tegra_host->soc_data->nvquirks &
-                       NVQUIRK_HIGH_FREQ_TAP_PROCEDURE)) {
-               voltage = tuning_data->tap_data[0]->voltage -
-                       tuning_data->tap_data[1]->voltage;
-
-               if (voltage > 0 && (freq_band == TUNING_LOW_FREQ ||
-                               freq_band == TUNING_HIGH_FREQ)) {
-                       vdd_core =
-                       tegra_host->soc_data->tuning_min_volt_list[freq_band];
-                       delta_vdd = tuning_data->tap_data[1]->voltage -
-                               vdd_core;
-                       tuning_data->tap_data[1]->partial_win =
-                               ((tuning_data->tap_data[0]->partial_win -
-                                 tuning_data->tap_data[1]->partial_win) *
-                                 delta_vdd) / voltage +
-                               tuning_data->tap_data[1]->partial_win;
-                       tuning_data->tap_data[1]->full_win_begin =
-                               ((tuning_data->tap_data[0]->full_win_begin -
-                                 tuning_data->tap_data[1]->full_win_begin) *
-                                delta_vdd) / voltage +
-                               tuning_data->tap_data[1]->full_win_begin;
-                       tuning_data->tap_data[1]->full_win_end =
-                               -((tuning_data->tap_data[0]->full_win_end -
-                                 tuning_data->tap_data[1]->full_win_end) *
-                                (delta_vdd / voltage)) +
-                               tuning_data->tap_data[1]->full_win_end;
+       do {
+               SDHCI_TEGRA_DBG("%s: checking for win opening with vmin %d\n",
+                       mmc_hostname(sdhci->mmc), vmin);
+               if ((best_tap_value < 0) &&
+                       (vmin > tegra_host->boot_vcore_mv)) {
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "No best tap for any vcore range\n");
+                       return -EINVAL;
                }
-       }
 
-       if (freq_band == TUNING_LOW_FREQ) {
-               if (tegra_host->soc_data->nvquirks &
-                       NVQUIRK_HIGH_FREQ_TAP_PROCEDURE)
-                       tuning_data->nom_best_tap_value =
-                               calculate_high_freq_tap_value(sdhci,
-                               tuning_data->tap_data[0],
-                               tuning_data->tap_data[1]);
-               else
-                       tuning_data->nom_best_tap_value =
-                               calculate_low_freq_tap_value(sdhci,
-                                       tuning_data->tap_data[0]);
-               tuning_data->best_tap_value = tuning_data->nom_best_tap_value;
-       } else if (freq_band == TUNING_HIGH_FREQ) {
-               tuning_data->nom_best_tap_value =
-                       calculate_high_freq_tap_value(sdhci,
-                       tuning_data->tap_data[0], tuning_data->tap_data[1]);
-               if (!tegra_host->plat->en_nominal_vcore_tuning) {
-                       tuning_data->best_tap_value =
-                               tuning_data->nom_best_tap_value;
-               } else {
-                       tuning_data->best_tap_value =
-                               calculate_high_freq_tap_value(sdhci,
-                               tuning_data->tap_data[1],
-                               tuning_data->tap_data[2]);
-               }
-       } else if (freq_band == TUNING_MAX_FREQ) {
-               tuning_data->nom_best_tap_value = calculate_high_freq_tap_value(
-                       sdhci, tuning_data->tap_data[0],
-                       tuning_data->tap_data[0]);
-               if (!tegra_host->plat->en_nominal_vcore_tuning) {
-                       tuning_data->best_tap_value =
-                               tuning_data->nom_best_tap_value;
-               } else {
-                       tuning_data->best_tap_value =
-                               calculate_high_freq_tap_value(sdhci,
-                               tuning_data->tap_data[1],
-                               tuning_data->tap_data[1]);
+               calculate_vmin_values(sdhci, tuning_data, vmin,
+                       tegra_host->boot_vcore_mv);
+
+               if (temp_tap_data == NULL) {
+                       temp_tap_data = kzalloc(sizeof(struct tap_window_data) *
+                               tuning_data->num_of_valid_tap_wins, GFP_KERNEL);
+                       if (IS_ERR_OR_NULL(temp_tap_data)) {
+                               dev_err(mmc_dev(sdhci->mmc),
+                               "No memory for final tap value calculation\n");
+                               return -ENOMEM;
+                       }
                }
-       }
+
+               memcpy(temp_tap_data, tuning_data->final_tap_data,
+                       sizeof(struct tap_window_data) *
+                       tuning_data->num_of_valid_tap_wins);
+
+               adjust_window_boundaries(sdhci, tuning_data, temp_tap_data);
+
+               best_tap_value = find_best_tap_value(tuning_data,
+                       temp_tap_data, vmin);
+
+               if (best_tap_value < 0)
+                       vmin += 50;
+       } while (best_tap_value < 0);
+
+       tuning_data->best_tap_value = best_tap_value;
+       tuning_data->nom_best_tap_value = best_tap_value;
+
+       /* Set the new vmin if there is any change. */
+       if ((tuning_data->best_tap_value >= 0) && (curr_vmin != vmin))
+               err = tegra_dvfs_set_fmax_at_vmin(pltfm_host->clk,
+                       tuning_data->freq_hz, vmin);
+
+       kfree(temp_tap_data);
+       return err;
 }
 
 static int sdhci_tegra_issue_tuning_cmd(struct sdhci_host *sdhci)
@@ -1955,89 +2090,360 @@ static int sdhci_tegra_scan_tap_values(struct sdhci_host *sdhci,
        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)
+static int calculate_actual_tuning_values(int speedo,
+       struct tegra_tuning_data *tuning_data, int voltage_mv)
 {
-       unsigned int tap_value;
-       unsigned int full_win_percentage = 0;
-       int err = 0;
+       struct tuning_t2t_coeffs *t2t_coeffs = tuning_data->t2t_coeffs;
+       struct tap_hole_coeffs *thole_coeffs = tuning_data->thole_coeffs;
+       struct tuning_values *calc_values = &tuning_data->calc_values;
+       struct tap_window_data *tap_data;
+       int slope, inpt;
+       int vmax_thole, vmin_thole;
+       u8 i;
 
-       if (!tap_data) {
-               dev_err(mmc_dev(sdhci->mmc), "Invalid tap data\n");
-               return -ENODATA;
+       /* T2T_Vmax = (1000000/freq_MHz)/Calc_UI */
+       calc_values->t2t_vmax = (1000000 / (tuning_data->freq_hz / 1000000)) /
+               calc_values->ui;
+
+       /*
+        * Interpolate the tap hole.
+        * Vmax_1'st_hole = (Calc_T2T_Vmax*(-thole_slope)+thole_tint.
+        */
+       vmax_thole = (thole_coeffs->thole_vmax_int -
+               (thole_coeffs->thole_vmax_slope * calc_values->t2t_vmax)) /
+               1000;
+       vmin_thole = (thole_coeffs->thole_vmin_int -
+               (thole_coeffs->thole_vmin_slope * calc_values->t2t_vmax)) /
+               1000;
+       if (voltage_mv == t2t_coeffs->vmin) {
+               calc_values->vmax_thole = vmin_thole;
+       } else if (voltage_mv == t2t_coeffs->vmax) {
+               calc_values->vmax_thole = vmax_thole;
+       } else {
+               slope = (vmax_thole - vmin_thole) /
+                       (t2t_coeffs->vmax - t2t_coeffs->vmin);
+               inpt = ((vmax_thole * 1000) - (slope * 1250)) / 1000;
+               calc_values->vmax_thole = slope * voltage_mv + inpt;
+       }
+
+       /* Calculate negative margin if partial win is valid */
+       if (tuning_data->is_partial_win_valid) {
+               /* Find second boundary start and adjust partial win start */
+               for (i = 1; i < tuning_data->num_of_valid_tap_wins; i++) {
+                       tap_data = &tuning_data->tap_data[i];
+                       if (tap_data->win_start_attr == WIN_EDGE_BOUN_START) {
+                               tuning_data->tap_data[0].win_start =
+                                       (tap_data->win_start - calc_values->ui);
+                               break;
+                       }
+               }
        }
 
-       /* 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) {
+       return 0;
+}
+
+/*
+ * All coeffs are filled up in the table after multiplying by 1000. So, all
+ * calculations should have a divide by 1000 at the end.
+ */
+static int calculate_estimated_tuning_values(int speedo,
+       struct tegra_tuning_data *tuning_data, int voltage_mv)
+{
+       struct tuning_t2t_coeffs *t2t_coeffs = tuning_data->t2t_coeffs;
+       struct tap_hole_coeffs *thole_coeffs = tuning_data->thole_coeffs;
+       struct tuning_values *est_values = &tuning_data->est_values;
+       int slope, inpt;
+       int vmax_t2t, vmin_t2t;
+       int vmax_thole, vmin_thole;
+
+       /* Est_T2T_Vmax = (speedo*(-t2t_slope)+t2t_int */
+       vmax_t2t = (t2t_coeffs->t2t_vmax_int - (speedo *
+               t2t_coeffs->t2t_vmax_slope)) / 1000;
+       vmin_t2t = (t2t_coeffs->t2t_vmin_int - (speedo *
+               t2t_coeffs->t2t_vmin_slope)) / 1000;
+       est_values->t2t_vmin = vmin_t2t;
+
+       if (voltage_mv == t2t_coeffs->vmin) {
+               est_values->t2t_vmax = vmin_t2t;
+       } else if (voltage_mv == t2t_coeffs->vmax) {
+               est_values->t2t_vmax = vmax_t2t;
+       } else {
+               vmax_t2t = 1000 / vmax_t2t;
+               vmin_t2t = 1000 / vmin_t2t;
                /*
-                * 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.
+                * For any intermediate voltage between 0.95V and 1.25V,
+                * calculate the slope and intercept from the T2T and tap hole
+                * values of 0.95V and 1.25V and use them to calculate the
+                * actual values. 1/T2T is a linear function of voltage.
                 */
-               tap_data->partial_win = MAX_TAP_VALUES;
-               tap_data->abandon_full_win = true;
-               goto out;
+               slope = ((vmax_t2t - vmin_t2t) * 1000) /
+                       (t2t_coeffs->vmax - t2t_coeffs->vmin);
+               inpt = (vmax_t2t * 1000 - (slope * t2t_coeffs->vmax)) / 1000;
+               est_values->t2t_vmax = (slope * voltage_mv) / 1000 + inpt;
+               est_values->t2t_vmax = (1000 / est_values->t2t_vmax);
+       }
+
+       /* Est_UI  = (1000000/freq_MHz)/Est_T2T_Vmax */
+       est_values->ui = (1000000 / (thole_coeffs->freq_khz / 1000)) /
+               est_values->t2t_vmax;
+
+       /*
+        * Est_1'st_hole = (Est_T2T_Vmax*(-thole_slope)) + thole_int.
+        */
+       vmax_thole = (thole_coeffs->thole_vmax_int -
+               (thole_coeffs->thole_vmax_slope * est_values->t2t_vmax)) / 1000;
+       vmin_thole = (thole_coeffs->thole_vmin_int -
+               (thole_coeffs->thole_vmin_slope * est_values->t2t_vmax)) / 1000;
+
+       if (voltage_mv == t2t_coeffs->vmin) {
+               est_values->vmax_thole = vmin_thole;
+       } else if (voltage_mv == t2t_coeffs->vmax) {
+               est_values->vmax_thole = vmax_thole;
        } 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;
+               /*
+                * For any intermediate voltage between 0.95V and 1.25V,
+                * calculate the slope and intercept from the t2t and tap hole
+                * values of 0.95V and 1.25V and use them to calculate the
+                * actual values. Tap hole is a linear function of voltage.
+                */
+               slope = ((vmax_thole - vmin_thole) * 1000) /
+                       (t2t_coeffs->vmax - t2t_coeffs->vmin);
+               inpt = (vmax_thole * 1000 - (slope * t2t_coeffs->vmax)) / 1000;
+               est_values->vmax_thole = (slope * voltage_mv) / 1000 + inpt;
+       }
+       est_values->vmin_thole = vmin_thole;
+
+       return 0;
+}
+
+/*
+ * Insert the calculated holes and get the final tap windows
+ * with the boundaries and holes set.
+ */
+static int adjust_holes_in_tap_windows(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data)
+{
+       struct tap_window_data *tap_data;
+       struct tap_window_data *final_tap_data;
+       struct tuning_values *calc_values = &tuning_data->calc_values;
+       int tap_hole;
+       u8 i = 0, j = 0, num_of_wins;
+       bool get_next_hole = false;
+
+       tuning_data->final_tap_data =
+               devm_kzalloc(mmc_dev(sdhci->mmc),
+                       sizeof(struct tap_window_data) * 42, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(tuning_data->final_tap_data)) {
+               dev_err(mmc_dev(sdhci->mmc), "No mem for final tap wins\n");
+               return -ENOMEM;
+       }
+
+       num_of_wins = tuning_data->num_of_valid_tap_wins;
+       tap_hole = calc_values->vmax_thole;
+       do {
+               if (get_next_hole)
+                       tap_hole = tap_hole + calc_values->ui;
+
+               tap_data = &tuning_data->tap_data[i];
+               final_tap_data = &tuning_data->final_tap_data[j];
+               if (tap_hole < tap_data->win_start) {
+                       get_next_hole = true;
+                       continue;
+               } else if (tap_hole > tap_data->win_end) {
+                       memcpy(final_tap_data, tap_data,
+                               sizeof(struct tap_window_data));
+                       i++;
+                       j++;
+                       num_of_wins--;
+                       get_next_hole = false;
+                       continue;
+               } else if ((tap_hole >= tap_data->win_start) &&
+                       (tap_hole <= tap_data->win_end)) {
+                               final_tap_data->win_start = tap_data->win_start;
+                               final_tap_data->win_start_attr =
+                                       tap_data->win_start_attr;
+                               final_tap_data->win_end = tap_hole - 1;
+                               final_tap_data->win_end_attr = WIN_EDGE_HOLE;
+                               j++;
+                               final_tap_data =
+                                       &tuning_data->final_tap_data[j];
+                               final_tap_data->win_start = tap_hole + 1;
+                               final_tap_data->win_start_attr = WIN_EDGE_HOLE;
+                               final_tap_data->win_end = tap_data->win_end;
+                               final_tap_data->win_end_attr =
+                                       tap_data->win_end_attr;
+                               i++;
+                               j++;
+                               num_of_wins--;
+                               get_next_hole = true;
                }
+       } while (num_of_wins > 0);
+
+       /* Update the num of valid wins count after tap holes insertion */
+       tuning_data->num_of_valid_tap_wins = j;
+
+       pr_info("********tuning windows after inserting holes*****\n");
+       pr_info("WIN_ATTR legend: 0-BOUN_ST, 1-BOUN_END, 2-HOLE\n");
+       for (i = 0; i < tuning_data->num_of_valid_tap_wins; i++) {
+               final_tap_data = &tuning_data->final_tap_data[i];
+               pr_info("win[%d]:%d(%d) - %d(%d)\n", i,
+                       final_tap_data->win_start,
+                       final_tap_data->win_start_attr,
+                       final_tap_data->win_end, final_tap_data->win_end_attr);
        }
+       pr_info("***********************************************\n");
+
+       return 0;
+}
 
+/*
+ * Scan for all tap values and get all passing tap windows.
+ */
+static int sdhci_tegra_get_tap_window_data(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct tap_window_data *tap_data;
+       struct tuning_ui tuning_ui[10];
+       int err = 0;
+       unsigned int tap_value, calc_ui = 0;
+       u8 prev_boundary_end = 0, num_of_wins = 0;
+       u8 num_of_uis = 0, valid_num_uis = 0;
+       u8 ref_ui;
+       u8 j = 0;
+
+       /*
+        * Assume there are a max of 10 windows and allocate tap window
+        * structures for the same. If there are more windows, the array
+        * size can be adjusted later using realloc.
+        */
+       tuning_data->tap_data = devm_kzalloc(mmc_dev(sdhci->mmc),
+               sizeof(struct tap_window_data) * 42, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(tuning_data->tap_data)) {
+               dev_err(mmc_dev(sdhci->mmc), "No memory for tap data\n");
+               return -ENOMEM;
+       }
+
+       spin_lock(&sdhci->lock);
+       tap_value = 0;
        do {
-               /* Get the full window start */
-               tap_value++;
+               tap_data = &tuning_data->tap_data[num_of_wins];
+               /* Get the window start */
                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;
+               tap_data->win_start = min_t(u8, tap_value, MAX_TAP_VALUES);
+               tap_value++;
+               if (tap_value >= MAX_TAP_VALUES) {
+                       /* If it's first iteration, then all taps failed */
+                       if (!num_of_wins) {
+                               dev_err(mmc_dev(sdhci->mmc),
+                                       "All tap values(0-255) failed\n");
+                               spin_unlock(&sdhci->lock);
+                               return -EINVAL;
+                       } else {
+                               /* All windows obtained */
+                               break;
                        }
                }
 
-               /* Get the full window end */
-               tap_value++;
+               /* Get the window end */
                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;
-               full_win_percentage = ((tap_data->full_win_end -
-                               tap_data->full_win_begin) * 100) /
-                               (tap_data->partial_win + 1);
-       } while (full_win_percentage < 50 && tap_value < MAX_TAP_VALUES);
-
-       if (full_win_percentage < 50)
-               tap_data->abandon_full_win = true;
-out:
+               tap_data->win_end = min_t(u8, (tap_value - 1), MAX_TAP_VALUES);
+               tap_data->win_size = tap_data->win_end - tap_data->win_start;
+               tap_value++;
+
+               /*
+                * If the size of window is more than 4 taps wide, then it is a
+                * valid window. If tap value 0 has passed, then a partial
+                * window exists. Mark all the window edges as boundary edges.
+                */
+               if (tap_data->win_size > 4) {
+                       if (tap_data->win_start == 0)
+                               tuning_data->is_partial_win_valid = true;
+                       tap_data->win_start_attr = WIN_EDGE_BOUN_START;
+                       tap_data->win_end_attr = WIN_EDGE_BOUN_END;
+               } else {
+                       /* Invalid window as size is less than 5 taps */
+                       SDHCI_TEGRA_DBG("Invalid tuning win (%d-%d) ignored\n",
+                               tap_data->win_start, tap_data->win_end);
+                       continue;
+               }
+
+               /* Ignore first and last partial UIs */
+               if (tap_data->win_end_attr == WIN_EDGE_BOUN_END) {
+                       if (prev_boundary_end &&
+                               (tap_data->win_end != MAX_TAP_VALUES)) {
+                               tuning_ui[num_of_uis].ui = tap_data->win_end -
+                                       prev_boundary_end;
+                               num_of_uis++;
+                       }
+                       prev_boundary_end = tap_data->win_end;
+               }
+               num_of_wins++;
+       } while (tap_value < MAX_TAP_VALUES);
+       spin_unlock(&sdhci->lock);
+
+       tuning_data->num_of_valid_tap_wins = num_of_wins;
+       valid_num_uis = num_of_uis;
+
+       /* Calculate 0.75*est_UI */
+       ref_ui = (75 * tuning_data->est_values.ui) / 100;
+
        /*
-        * Mark tuning as failed if both partial and full windows are
-        * abandoned.
+        * Check for valid UIs and discredit invalid UIs. A UI is considered
+        * valid if it's greater than (0.75*est_UI). If an invalid UI is found,
+        * also discredit the smaller of the two adjacent windows.
         */
-       if (tap_data->abandon_partial_win && tap_data->abandon_full_win)
-               err = -EIO;
+       for (j = 0; j < num_of_uis; j++) {
+               if (tuning_ui[j].ui > ref_ui) {
+                       tuning_ui[j].is_valid_ui = true;
+               } else {
+                       tuning_ui[j].is_valid_ui = false;
+                       valid_num_uis--;
+                       /* Compare the adjacent uis */
+                       if (j > 0) {
+                               if (tuning_ui[j - 1].ui > tuning_ui[j + 1].ui) {
+                                       tuning_ui[j + 1].is_valid_ui = false;
+                                       j++;
+                               } else {
+                                       tuning_ui[j - 1].is_valid_ui = false;
+                               }
+                               valid_num_uis--;
+                       }
+               }
+       }
+
+       /* Calculate the cumulative UI if there are valid UIs left */
+       if (valid_num_uis) {
+               for (j = 0; j < num_of_uis; j++)
+                       if (tuning_ui[j].is_valid_ui)
+                               calc_ui += tuning_ui[j].ui;
+       }
+
+       if (calc_ui)
+               tuning_data->calc_values.ui = (calc_ui / valid_num_uis);
+       else
+               tuning_data->calc_values.ui = tuning_data->est_values.ui;
+
+       /* Get the calculated tuning values */
+       err = calculate_actual_tuning_values(tegra_host->speedo, tuning_data,
+               tegra_host->boot_vcore_mv);
+
+       /* Print info of all tap windows */
+       SDHCI_TEGRA_DBG("**********Auto tuning windows*************\n");
+       SDHCI_TEGRA_DBG("WIN_ATTR legend: 0-BOUN_ST, 1-BOUN_END, 2-HOLE\n");
+       for (j = 0; j < tuning_data->num_of_valid_tap_wins; j++) {
+               tap_data = &tuning_data->tap_data[j];
+               SDHCI_TEGRA_DBG("win[%d]: %d(%d) - %d(%d)\n",
+                       j, tap_data->win_start, tap_data->win_start_attr,
+                       tap_data->win_end, tap_data->win_end_attr);
+       }
+       SDHCI_TEGRA_DBG("***************************************\n");
+
+       /* Insert calculated holes into the windows */
+       err = adjust_holes_in_tap_windows(sdhci, tuning_data);
+
        return err;
 }
 
@@ -2046,8 +2452,7 @@ static void sdhci_tegra_dump_tuning_constraints(struct sdhci_host *sdhci)
        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;
-       u8 i, j;
+       u8 i;
 
        SDHCI_TEGRA_DBG("%s: Num of tuning frequencies%d\n",
                mmc_hostname(sdhci->mmc), tegra_host->tuning_freq_count);
@@ -2056,12 +2461,6 @@ static void sdhci_tegra_dump_tuning_constraints(struct sdhci_host *sdhci)
                SDHCI_TEGRA_DBG("%s: Tuning freq[%d]: %d, freq band %d\n",
                        mmc_hostname(sdhci->mmc), i,
                        tuning_data->freq_hz, tuning_data->freq_band);
-               SDHCI_TEGRA_DBG("%s: Supported voltages:",
-                       mmc_hostname(sdhci->mmc));
-               for (j = 0; j < tuning_data->nr_voltages; ++j) {
-                       tap_data = tuning_data->tap_data[j];
-                       SDHCI_TEGRA_DBG("%d,", tap_data->voltage);
-               }
        }
 }
 
@@ -2083,40 +2482,6 @@ static unsigned int get_tuning_voltage(struct sdhci_tegra *tegra_host, u8 *mask)
        return tegra_host->boot_vcore_mv;
 }
 
-static int sdhci_tegra_setup_vcore_constraints(struct sdhci_host *sdhci)
-{
-       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
-       struct sdhci_tegra *tegra_host = pltfm_host->priv;
-       struct freq_tuning_constraints *constraints;
-       struct tegra_tuning_data *tuning_data;
-       struct tap_window_data *tap_data;
-       u8 freq_count = tegra_host->tuning_freq_count;
-       u8 nr_voltages, i, j, vcore_mask;
-
-       for (i = 0; i < freq_count; i++) {
-               tuning_data = &tegra_host->tuning_data[i];
-               constraints = &tuning_data->constraints;
-               nr_voltages = hweight32(constraints->vcore_mask);
-               SDHCI_TEGRA_DBG("%s: %dHz: vcore mask %#x, nr voltages %d\n",
-                       mmc_hostname(sdhci->mmc), tuning_data->freq_hz,
-                       constraints->vcore_mask, nr_voltages);
-               vcore_mask = constraints->vcore_mask;
-               for (j = 0; j < nr_voltages; j++) {
-                       tap_data = devm_kzalloc(
-                               mmc_dev(sdhci->mmc),
-                               sizeof(struct tap_window_data),
-                               GFP_KERNEL);
-                       if (!tap_data)
-                               return -ENOMEM;
-                       tap_data->voltage = get_tuning_voltage(tegra_host,
-                               &vcore_mask);
-                       tuning_data->tap_data[j] = tap_data;
-               }
-               tuning_data->nr_voltages = nr_voltages;
-       }
-       return 0;
-}
-
 static u8 sdhci_tegra_get_freq_point(struct sdhci_host *sdhci)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -2136,19 +2501,95 @@ static u8 sdhci_tegra_get_freq_point(struct sdhci_host *sdhci)
 }
 
 /*
+ * The frequency tuning algorithm tries to calculate the tap-to-tap delay
+ * UI and estimate holes using equations and predetermined coefficients from
+ * the characterization data. The algorithm will not work without this data.
+ */
+static int find_tuning_coeffs_data(struct sdhci_host *sdhci)
+{
+       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;
+       struct tegra_tuning_data *tuning_data;
+       struct tuning_t2t_coeffs *t2t_coeffs;
+       struct tap_hole_coeffs *thole_coeffs;
+       const char *dev_id;
+       unsigned int freq_khz;
+       u8 i, j;
+       bool coeffs_set = false;
+
+       dev_id = dev_name(mmc_dev(sdhci->mmc));
+       /* Find the coeffs data for all supported frequencies */
+       for (i = 0; i < tegra_host->tuning_freq_count; i++) {
+               tuning_data = &tegra_host->tuning_data[i];
+
+               /* Skip if T2T coeffs are already found */
+               if (tuning_data->t2t_coeffs == NULL) {
+                       t2t_coeffs = soc_data->t2t_coeffs;
+                       for (j = 0; j < soc_data->t2t_coeffs_count; j++) {
+                               if (!strcmp(dev_id, t2t_coeffs->dev_id)) {
+                                       tuning_data->t2t_coeffs = t2t_coeffs;
+                                       coeffs_set = true;
+                                       dev_info(mmc_dev(sdhci->mmc),
+                                               "Found T2T coeffs data\n");
+                                       break;
+                               }
+                               t2t_coeffs++;
+                       }
+                       if (!coeffs_set) {
+                               dev_err(mmc_dev(sdhci->mmc),
+                                       "T2T coeffs data missing\n");
+                               tuning_data->t2t_coeffs = NULL;
+                               return -ENODATA;
+                       }
+               }
+
+               coeffs_set = false;
+               /* Skip if tap hole coeffs are already found */
+               if (tuning_data->thole_coeffs == NULL) {
+                       thole_coeffs = soc_data->tap_hole_coeffs;
+                       freq_khz = tuning_data->freq_hz / 1000;
+                       for (j = 0; j < soc_data->tap_hole_coeffs_count; j++) {
+                               if (!strcmp(dev_id, thole_coeffs->dev_id) &&
+                                       (freq_khz == thole_coeffs->freq_khz)) {
+                                       tuning_data->thole_coeffs =
+                                               thole_coeffs;
+                                       coeffs_set = true;
+                                       dev_info(mmc_dev(sdhci->mmc),
+                                               "%dMHz tap hole coeffs found\n",
+                                               (freq_khz / 1000));
+                                       break;
+                               }
+                               thole_coeffs++;
+                       }
+
+                       if (!coeffs_set) {
+                               dev_err(mmc_dev(sdhci->mmc),
+                                       "%dMHz Tap hole coeffs data missing\n",
+                                       (freq_khz / 1000));
+                               tuning_data->thole_coeffs = NULL;
+                               return -ENODATA;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
  * Determines the numbers of frequencies required and then fills up the tuning
  * constraints for each of the frequencies. The data of lower frequency is
- * filled first and then the higher frequency data. Currently fills constraints
- * for number of frequencies 1 and 2.
+ * filled first and then the higher frequency data. Max supported frequencies
+ * is currently two.
  */
-static u8 sdhci_tegra_setup_freq_constraints(struct sdhci_host *sdhci,
+static int setup_freq_constraints(struct sdhci_host *sdhci,
        const unsigned int *freq_list)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
        struct tegra_tuning_data *tuning_data;
-       int i;
-       u8 freq_count, freq_band;
+       int i, freq_count;
+       u8 freq_band;
 
        if ((sdhci->mmc->ios.timing != MMC_TIMING_UHS_SDR50) &&
                (sdhci->mmc->caps2 & MMC_CAP2_FREQ_SCALING))
@@ -2163,27 +2604,19 @@ static u8 sdhci_tegra_setup_freq_constraints(struct sdhci_host *sdhci,
                tuning_data = &tegra_host->tuning_data[0];
                tuning_data->freq_hz = sdhci->max_clk;
                tuning_data->freq_band = freq_band;
-               tuning_data->constraints =
-                       tuning_vcore_constraints[freq_band];
-               if (tegra_host->soc_data->nvquirks &
-                               NVQUIRK_HIGH_FREQ_TAP_PROCEDURE) {
-                       if (tuning_data->freq_band == TUNING_LOW_FREQ)
-                               tuning_data->constraints.vcore_mask |=
-                                       MIN_OVERRIDE_VCORE_TUN;
-               }
-               if (!tegra_host->plat->en_nominal_vcore_tuning)
-                       tuning_data->constraints.vcore_mask &=
-                               ~NOMINAL_VCORE_TUN;
+               tuning_data->constraints.vcore_mask =
+                       tuning_vcore_constraints[freq_band].vcore_mask;
+               tuning_data->nr_voltages =
+                       hweight32(tuning_data->constraints.vcore_mask);
        break;
        case 2:
                tuning_data = &tegra_host->tuning_data[1];
                tuning_data->freq_hz = sdhci->max_clk;
                tuning_data->freq_band = freq_band;
-               tuning_data->constraints =
-                       tuning_vcore_constraints[freq_band];
-               if (!tegra_host->plat->en_nominal_vcore_tuning)
-                       tuning_data->constraints.vcore_mask &=
-                               ~NOMINAL_VCORE_TUN;
+               tuning_data->constraints.vcore_mask =
+                       tuning_vcore_constraints[freq_band].vcore_mask;
+               tuning_data->nr_voltages =
+                       hweight32(tuning_data->constraints.vcore_mask);
 
                tuning_data = &tegra_host->tuning_data[0];
                for (i = (freq_band - 1); i >= 0; i--) {
@@ -2191,32 +2624,26 @@ static u8 sdhci_tegra_setup_freq_constraints(struct sdhci_host *sdhci,
                                continue;
                        tuning_data->freq_hz = freq_list[i];
                        tuning_data->freq_band = i;
-                       tuning_data->constraints =
-                               tuning_vcore_constraints[i];
-                       if (tegra_host->soc_data->nvquirks &
-                                       NVQUIRK_HIGH_FREQ_TAP_PROCEDURE) {
-                               if (i == TUNING_LOW_FREQ)
-                                       tuning_data->constraints.vcore_mask |=
-                                               MIN_OVERRIDE_VCORE_TUN;
-                       }
-                       if (!tegra_host->plat->en_nominal_vcore_tuning)
-                               tuning_data->constraints.vcore_mask &=
-                                       ~NOMINAL_VCORE_TUN;
+                       tuning_data->nr_voltages = 1;
+                       tuning_data->constraints.vcore_mask =
+                               tuning_vcore_constraints[i].vcore_mask;
+                       tuning_data->nr_voltages =
+                               hweight32(tuning_data->constraints.vcore_mask);
                }
        break;
        default:
                dev_err(mmc_dev(sdhci->mmc), "Unsupported freq count\n");
+               freq_count = -1;
        }
+
        return freq_count;
 }
 
 /*
- * Get the supported frequencies, core voltage levels for each frequency and
- * other tuning related constraints.
- * The supported frequencies should be determined from the list of frequencies
- * in the soc data and also consider the platform clock limits as well as any
- * DFS related restrictions.
- * Check if tuning at nominal core voltage is required.
+ * Get the supported frequencies and other tuning related constraints for each
+ * frequency. The supported frequencies should be determined from the list of
+ * frequencies in the soc data and also consider the platform clock limits as
+ * well as any DFS related restrictions.
  */
 static int sdhci_tegra_get_tuning_constraints(struct sdhci_host *sdhci)
 {
@@ -2225,17 +2652,22 @@ static int sdhci_tegra_get_tuning_constraints(struct sdhci_host *sdhci)
        const unsigned int *freq_list;
        int err = 0;
 
+       /* A valid freq count means freq constraints are already set up */
+       if (!tegra_host->tuning_freq_count) {
+               freq_list = tegra_host->soc_data->tuning_freq_list;
+               tegra_host->tuning_freq_count =
+                       setup_freq_constraints(sdhci, freq_list);
+               if (tegra_host->tuning_freq_count < 0) {
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Invalid tuning freq count\n");
+                       return -EINVAL;
+               }
+       }
 
-       /* Check if the constraints are already filled up */
-       if (tegra_host->tuning_freq_count)
+       err = find_tuning_coeffs_data(sdhci);
+       if (err)
                return err;
 
-       freq_list = tegra_host->soc_data->tuning_freq_list;
-       tegra_host->tuning_freq_count =
-               sdhci_tegra_setup_freq_constraints(sdhci, freq_list);
-
-       err = sdhci_tegra_setup_vcore_constraints(sdhci);
-
        sdhci_tegra_dump_tuning_constraints(sdhci);
 
        return err;
@@ -2297,63 +2729,59 @@ static int sdhci_tegra_set_tuning_voltage(struct sdhci_host *sdhci,
        return err;
 }
 
-static u8 get_curr_voltage_tuning_status(struct tap_window_data *tap_data)
+static int sdhci_tegra_run_tuning(struct sdhci_host *sdhci,
+       struct tegra_tuning_data *tuning_data)
 {
-       if (!tap_data->vcore_set_status)
-               maintain_boot_voltage = true;
-
-       if (!tap_data->vcore_set_status || !tap_data->found_tuning_window)
-               return 1;
-
-       return 0;
-}
-
-static u8 sdhci_tegra_run_tuning(struct sdhci_host *sdhci,
-       struct tegra_tuning_data *tuning_data, bool force_retuning)
-{
-       struct tap_window_data *tap_data;
-       int err;
-       u8 i, retuning_req = 0;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       int err = 0;
+       int voltage = 0;
+       u8 i, vcore_mask = 0;
 
+       vcore_mask = tuning_data->constraints.vcore_mask;
        for (i = 0; i < tuning_data->nr_voltages; i++) {
-               tap_data = tuning_data->tap_data[i];
-               /* Skip if tuning is already completed successfully */
-               if (tap_data->vcore_set_status &&
-                       tap_data->found_tuning_window && !force_retuning)
-                       continue;
-               err = sdhci_tegra_set_tuning_voltage(sdhci,
-                       tap_data->voltage);
-               tap_data->vcore_set_status = !err;
+               voltage = get_tuning_voltage(tegra_host, &vcore_mask);
+               err = sdhci_tegra_set_tuning_voltage(sdhci, voltage);
+               if (err) {
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Unable to set override voltage.\n");
+                       return err;
+               }
+
                /* Get the tuning window info */
-               spin_lock(&sdhci->lock);
-               err = sdhci_tegra_get_tap_window_data(sdhci, tap_data);
-               spin_unlock(&sdhci->lock);
-               SDHCI_TEGRA_DBG("%s: Tap data[%d] obtained\n",
-                       mmc_hostname(sdhci->mmc), i);
-               tap_data->found_tuning_window = !err;
-               if (err)
+               SDHCI_TEGRA_DBG("Getting tuning windows...\n");
+               err = sdhci_tegra_get_tap_window_data(sdhci, tuning_data);
+               if (err) {
                        dev_err(mmc_dev(sdhci->mmc),
-                               "Invalid tap win. Retuning req\n");
-               retuning_req |= get_curr_voltage_tuning_status(tap_data);
-               SDHCI_TEGRA_DBG("%s: Retuning req %s\n",
-                       mmc_hostname(sdhci->mmc), retuning_req ? "set" :
-                       "not set");
+                               "Failed to get tap win %d\n", err);
+                       return err;
+               }
+               SDHCI_TEGRA_DBG("%s: %d tuning window data obtained\n",
+                       mmc_hostname(sdhci->mmc), tuning_data->freq_hz);
        }
-       return retuning_req;
+       return err;
 }
 
-static int sdhci_tegra_verify_best_tap(struct sdhci_host *sdhci,
-       u8 freq_band)
+static int sdhci_tegra_verify_best_tap(struct sdhci_host *sdhci)
 {
        struct tegra_tuning_data *tuning_data;
-       unsigned int best_tap_value = 0;
-       int err;
+       int err = 0;
 
        tuning_data = sdhci_tegra_get_tuning_data(sdhci, sdhci->max_clk);
-       best_tap_value = tuning_data->best_tap_value;
+       if ((tuning_data->best_tap_value < 0) ||
+               (tuning_data->best_tap_value > MAX_TAP_VALUES)) {
+               dev_err(mmc_dev(sdhci->mmc),
+                       "Trying to verify invalid best tap value\n");
+               return -EINVAL;
+       } else {
+               dev_err(mmc_dev(sdhci->mmc),
+                       "%s: tuning freq %dhz, best tap %d\n",
+                       __func__, tuning_data->freq_hz,
+                       tuning_data->best_tap_value);
+       }
 
        /* Set the best tap value */
-       sdhci_tegra_set_tap_delay(sdhci, best_tap_value);
+       sdhci_tegra_set_tap_delay(sdhci, tuning_data->best_tap_value);
 
        /* Run tuning after setting the best tap value */
        err = sdhci_tegra_issue_tuning_cmd(sdhci);
@@ -2369,14 +2797,12 @@ 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;
-       unsigned int freq_band;
        const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
        int err;
        u16 ctrl_2;
        u32 misc_ctrl;
        u32 ier;
        u8 i, set_retuning = 0;
-       bool is_retuning_req = false;
        bool force_retuning = false;
        bool enable_lb_clk;
 
@@ -2406,9 +2832,6 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
                sdhci_writel(sdhci, misc_ctrl, SDHCI_VNDR_MISC_CTRL);
        }
        mutex_lock(&tuning_mutex);
-       if (sdhci->flags & SDHCI_NEEDS_RETUNING)
-               is_retuning_req = true;
-       sdhci->flags &= ~SDHCI_NEEDS_RETUNING;
 
        /* Set the tuning command to be used */
        tegra_host->tuning_opcode = opcode;
@@ -2429,15 +2852,14 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
         * previous best tap value verification failed, force retuning.
         */
        if (tegra_host->tuning_status == TUNING_STATUS_DONE) {
-               freq_band = sdhci_tegra_get_freq_point(sdhci);
-               dev_info(mmc_dev(sdhci->mmc),
-                       "Tuning already done. Setting tuned tap value %d\n",
-                       tegra_host->tuning_data[freq_band].best_tap_value);
-               err = sdhci_tegra_verify_best_tap(sdhci, freq_band);
-               if (err)
+               err = sdhci_tegra_verify_best_tap(sdhci);
+               if (err) {
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Prev best tap failed. Re-running tuning\n");
                        force_retuning = true;
-               else
+               } else {
                        goto out;
+               }
        }
 
        if (tegra_host->force_retune == true) {
@@ -2448,7 +2870,8 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
        tegra_host->tuning_status = 0;
        err = sdhci_tegra_get_tuning_constraints(sdhci);
        if (err) {
-               dev_err(mmc_dev(sdhci->mmc), "Failed to get tuning constraints\n");
+               dev_err(mmc_dev(sdhci->mmc),
+                       "Failed to get tuning constraints\n");
                goto out;
        }
 
@@ -2461,16 +2884,24 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
                        mmc_hostname(sdhci->mmc), tuning_data->freq_hz);
                tegra_sdhci_set_clock(sdhci, tuning_data->freq_hz);
 
-               set_retuning = sdhci_tegra_run_tuning(sdhci, tuning_data,
-                       force_retuning);
+               SDHCI_TEGRA_DBG("%s: Calculating estimated tuning values\n",
+                       mmc_hostname(sdhci->mmc));
+               err = calculate_estimated_tuning_values(tegra_host->speedo,
+                       tuning_data, tegra_host->boot_vcore_mv);
+               if (err)
+                       goto out;
 
-               sdhci_tegra_calculate_best_tap(sdhci, tuning_data->freq_band);
+               SDHCI_TEGRA_DBG("Running tuning...\n");
+               err = sdhci_tegra_run_tuning(sdhci, tuning_data);
+               if (err)
+                       goto out;
 
-               /* Dump the tuning data */
-               sdhci_tegra_dump_tuning_data(sdhci);
+               SDHCI_TEGRA_DBG("calculating best tap value\n");
+               err = sdhci_tegra_calculate_best_tap(sdhci, tuning_data);
+               if (err)
+                       goto out;
 
-               err = sdhci_tegra_verify_best_tap(sdhci,
-                               tuning_data->freq_band);
+               err = sdhci_tegra_verify_best_tap(sdhci);
                if (!err && !set_retuning) {
                        tuning_data->tuning_done = true;
                        tegra_host->tuning_status |= TUNING_STATUS_DONE;
@@ -2478,29 +2909,9 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
                        tegra_host->tuning_status |= TUNING_STATUS_RETUNE;
                }
        }
-       if (tegra_host->tuning_status & TUNING_STATUS_RETUNE)
-               mod_timer(&sdhci->tuning_timer, jiffies + 10 * HZ);
 out:
-       if (maintain_boot_voltage) {
-               ++boot_volt_req_refcount;
-               maintain_boot_voltage = false;
-               SDHCI_TEGRA_DBG("%s: Need fixed core volt %d, refcount %d\n",
-                       mmc_hostname(sdhci->mmc), tegra_host->boot_vcore_mv,
-                       boot_volt_req_refcount);
-
-       } else {
-               if (boot_volt_req_refcount && is_retuning_req)
-                       --boot_volt_req_refcount;
-               SDHCI_TEGRA_DBG("%s: Relax core volt constraint. refcount %d\n",
-                       mmc_hostname(sdhci->mmc), boot_volt_req_refcount);
-       }
-
-       if (boot_volt_req_refcount)
-               sdhci_tegra_set_tuning_voltage(sdhci,
-                       tegra_host->boot_vcore_mv);
-       else
-               sdhci_tegra_set_tuning_voltage(sdhci, 0);
-
+       /* Release any override core voltages set */
+       sdhci_tegra_set_tuning_voltage(sdhci, 0);
 
        /* Enable interrupts. Enable full range for core voltage */
        sdhci_writel(sdhci, ier, SDHCI_INT_ENABLE);
@@ -2616,22 +3027,26 @@ static void tegra_sdhci_post_resume(struct sdhci_host *sdhci)
 /*
  * For tegra specific tuning, core voltage has to be fixed at different
  * voltages to get the tap values. Fixing the core voltage during tuning for one
- * device might affect transfers of other SDMMC devices. To handle this, lock
- * tuning_mutex to prevent any transfers during tuning execution. This is not
- * required once tuning is done.
+ * device might affect transfers of other SDMMC devices. Check if tuning mutex
+ * is locked before starting a data transfer. The new tuning procedure might
+ * take at max 1.5s for completion for a single run. Taking DFS into count,
+ * setting the max timeout for tuning mutex check a 3 secs. Since tuning is
+ * run only during boot or the first time device is inserted, there wouldn't
+ * be any delays in cmd/xfer execution once devices enumeration is done.
  */
 static void tegra_sdhci_get_bus(struct sdhci_host *sdhci)
 {
-       int timeout = 100;
-               while (mutex_is_locked(&tuning_mutex)) {
-                       if (!timeout) {
-                               dev_err(mmc_dev(sdhci->mmc),
-                                       "Tuning timer not released\n");
-                               return;
-                       }
-                       msleep(10);
-                       timeout--;
-               };
+       unsigned int timeout = 300;
+
+       while (mutex_is_locked(&tuning_mutex)) {
+               msleep(10);
+               --timeout;
+               if (!timeout) {
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Tuning mutex locked for long time\n");
+                       return;
+               }
+       };
 }
 
 /*
@@ -2975,25 +3390,6 @@ static const struct sdhci_ops tegra_sdhci_ops = {
 #endif
 };
 
-static struct sdhci_pltfm_data sdhci_tegra11_pdata = {
-       .quirks = TEGRA_SDHCI_QUIRKS,
-       .quirks2 = TEGRA_SDHCI_QUIRKS2,
-       .ops  = &tegra_sdhci_ops,
-};
-
-static struct sdhci_tegra_soc_data soc_data_tegra11 = {
-       .pdata = &sdhci_tegra11_pdata,
-       .nvquirks = TEGRA_SDHCI_NVQUIRKS |
-                   NVQUIRK_SET_DRIVE_STRENGTH |
-                   NVQUIRK_SET_TRIM_DELAY |
-                   NVQUIRK_ENABLE_DDR50 |
-                   NVQUIRK_ENABLE_HS200 |
-                   NVQUIRK_INFINITE_ERASE_TIMEOUT |
-                   NVQUIRK_DISABLE_SDMMC4_CALIB,
-       .parent_clk_list = {"pll_p", "pll_c"},
-       .tuning_freq_list = {81600000, 156000000, 200000000},
-};
-
 static struct sdhci_pltfm_data sdhci_tegra12_pdata = {
        .quirks = TEGRA_SDHCI_QUIRKS,
        .quirks2 = TEGRA_SDHCI_QUIRKS2 |
@@ -3014,12 +3410,14 @@ static struct sdhci_tegra_soc_data soc_data_tegra12 = {
                    NVQUIRK_DISABLE_EXTERNAL_LOOPBACK,
        .parent_clk_list = {"pll_p", "pll_c"},
        .tuning_freq_list = {81600000, 136000000, 200000000},
-       .tuning_min_volt_list = {950, 1000, 0},
+       .t2t_coeffs = t12x_tuning_coeffs,
+       .t2t_coeffs_count = 3,
+       .tap_hole_coeffs = t12x_tap_hole_coeffs,
+       .tap_hole_coeffs_count = 9,
 };
 
 static const struct of_device_id sdhci_tegra_dt_match[] = {
        { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra12 },
-       { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra11 },
        {}
 };
 MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
@@ -3089,11 +3487,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
                soc_data = match->data;
        } else {
                /* Use id tables and remove the following chip defines */
-#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
-               soc_data = &soc_data_tegra11;
-#else
                soc_data = &soc_data_tegra12;
-#endif
        }
 
        host = sdhci_pltfm_init(pdev, soc_data->pdata);
@@ -3344,7 +3738,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
        tegra_host->ddr_clk_limit = plat->ddr_clk_limit;
        tegra_host->instance = pdev->id;
        tegra_host->tap_cmd = TAP_CMD_TRIM_DEFAULT_VOLTAGE;
-
+       tegra_host->speedo = plat->cpu_speedo;
+       dev_info(mmc_dev(host->mmc), "Speedo value %d\n", tegra_host->speedo);
        host->mmc->pm_caps |= plat->pm_caps;
        host->mmc->pm_flags |= plat->pm_flags;
 
index 32de3cc..0d5f862 100644 (file)
@@ -1,9 +1,8 @@
 /*
  * Copyright (C) 2009 Palm, Inc.
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  * Author: Yvonne Yip <y@palm.com>
  *
- * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2014, 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
@@ -64,6 +63,7 @@ struct tegra_sdhci_platform_data {
        unsigned int calib_3v3_offsets; /* Format to be filled: 0xXXXXPDPU */
        unsigned int calib_1v8_offsets; /* Format to be filled: 0xXXXXPDPU */
        bool disable_clock_gate; /* no clock gate when true */
+       u32 cpu_speedo;
 };
 
 #endif