mmc: tegra: Refactor frequency tuning code
Pavan Kunapuli [Fri, 16 Aug 2013 14:07:38 +0000 (19:07 +0530)]
Chip specific tuning frequency list.
Support low, high and max frequencies for each chip.
Generic tuning constraints update procedure for better scalability.

Synchronization between multiple devices for setting core voltage
constraints during tuning.
When tuning is being run for any interface, avoid data transfers on all
interfaces that are yet to finish tuning. This would avoid data errors
due to core voltage fluctuations as a result of tuning vcore constraints.

Fixed tuning status update as per the core voltage set status and tuning
window status.

If the verification of an already tuned best tap value fails, force
retuning.

Option to easily configure support for any two frequencies when DFS is
enabled. Simplified procedure to support higher tuning frequency count.

Modularize tuning code for better scalability, maintainability and
readability.

Bug 1330567

Change-Id: I98533a3ceb661b5b54c8903341181ad0009f4dfb
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-on: http://git-master/r/260568
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>

arch/arm/mach-tegra/include/mach/sdhci.h
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 67c3650..1189687 100644 (file)
@@ -48,6 +48,7 @@ struct tegra_sdhci_platform_data {
        int pm_caps;
        int nominal_vcore_mv;
        int min_vcore_override_mv;
+       int boot_vcore_mv;
        unsigned int max_clk_limit;
        unsigned int ddr_clk_limit;
        unsigned int tap_delay;
@@ -59,6 +60,7 @@ struct tegra_sdhci_platform_data {
        unsigned int edp_states[SD_EDP_NUM_STATES];
        bool power_off_rail;
        bool en_freq_scaling;
+       bool en_nominal_vcore_tuning;
 };
 
 #endif
index f7049cf..23d46e4 100644 (file)
 
 #include "sdhci-pltfm.h"
 
+#if 0
+#define SDHCI_TEGRA_DBG(stuff...)      pr_info(stuff)
+#else
+#define SDHCI_TEGRA_DBG(stuff...)      do {} while (0)
+#endif
+
 /* Tegra SDMMC controller vendor register defines */
 #define SDHCI_VNDR_CLK_CTRL    0x100
 #define SDHCI_VNDR_CLK_CTRL_SDMMC_CLK  0x1
 #define DEFAULT_SDHOST_FREQ    50000000
 #define SDMMC_AHB_MAX_FREQ     150000000
 #define SDMMC_EMC_MAX_FREQ     150000000
+#define SDMMC_EMC_NOM_VOLT_FREQ        900000000
 
 /* Tuning related definitions */
 #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      3
-#define TUNING_VOLTAGES_COUNT  2
+#define TUNING_VOLTAGES_COUNT  3
 #define TUNING_RETRIES 1
+#define DFS_FREQ_COUNT 2
+
+/* Tuning core voltage requirements */
+#define NOMINAL_VCORE_TUN      BIT(0)
+#define BOOT_VCORE_TUN BIT(1)
+#define MIN_OVERRIDE_VCORE_TUN BIT(2)
 
 /* Tap cmd sysfs commands */
-#define TAP_CMD_SUSPEND_CONTROLLER     0
 #define TAP_CMD_TRIM_DEFAULT_VOLTAGE   1
 #define TAP_CMD_TRIM_HIGH_VOLTAGE      2
-#define TAP_CMD_GO_COMMAND             3
 
 /*
  * Defined the chip specific quirks and clock sources. For now, the used clock
  * sources vary only from chip to chip. If the sources allowed varies from
  * platform to platform, then move the clock sources list to platform data.
+ * When filling the tuning_freq_list in soc_data, the number of entries should
+ * be equal to TUNNG_FREQ_COUNT. Depending on number DFS frequencies supported,
+ * set the desired low, high or max frequencies and set the remaining entries
+ * as 0s. The number of entries should always be equal to TUNING_FREQ_COUNT
+ * inorder to get the right tuning data.
  */
 struct sdhci_tegra_soc_data {
        struct sdhci_pltfm_data *pdata;
        u32 nvquirks;
        const char *parent_clk_list[2];
+       unsigned int tuning_freq_list[TUNING_FREQ_COUNT];
 };
 
+
 enum tegra_regulator_config_ops {
        CONFIG_REG_EN,
        CONFIG_REG_DIS,
@@ -196,51 +214,48 @@ static unsigned int uhs_max_freq_MHz[] = {
 enum tegra_tuning_freq {
        TUNING_LOW_FREQ,
        TUNING_HIGH_FREQ,
-       TUNING_HIGH_FREQ_HV,
+       TUNING_MAX_FREQ,
 };
 
-struct freq_tuning_params {
-       unsigned int    freq_hz;
-       unsigned int    nr_voltages;
-       unsigned int    voltages[TUNING_VOLTAGES_COUNT];
+
+struct freq_tuning_constraints {
+       unsigned int vcore_mask;
 };
 
-static struct freq_tuning_params tuning_params[TUNING_FREQ_COUNT] = {
-       [TUNING_LOW_FREQ] = {
-               .freq_hz = 82000000,
-               .nr_voltages = 1,
-               .voltages = {1250},
+static struct freq_tuning_constraints tuning_vcore_constraints[3] = {
+       [0] = {
+               .vcore_mask = BOOT_VCORE_TUN,
        },
-       [TUNING_HIGH_FREQ] = {
-               .freq_hz = 156000000,
-               .nr_voltages = 2,
-               .voltages = {1250, 1100},
+       [1] = {
+               .vcore_mask = BOOT_VCORE_TUN | MIN_OVERRIDE_VCORE_TUN |
+                       NOMINAL_VCORE_TUN,
        },
-       [TUNING_HIGH_FREQ_HV] = {
-               .freq_hz = 156000000,
-               .nr_voltages = 2,
-               .voltages = {1390, 1250},
+       [2] = {
+               .vcore_mask = BOOT_VCORE_TUN | NOMINAL_VCORE_TUN,
        },
 };
 
 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;
+       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;
 };
 
 struct tegra_tuning_data {
-       /* need to store multiple values if DVFS */
-       unsigned int            best_tap_value[TUNING_FREQ_COUNT];
-       bool                    select_partial_win;
-       bool                    nominal_vcore_tuning_done;
-       bool                    overide_vcore_tuning_done;
-       bool                    one_shot_tuning;
-       struct tap_window_data  *tap_data[TUNING_VOLTAGES_COUNT];
+       unsigned int freq_hz;
+       unsigned int best_tap_value;
+       unsigned int nom_best_tap_value;
+       struct freq_tuning_constraints constraints;
+       struct tap_window_data *tap_data[TUNING_VOLTAGES_COUNT];
+       u8 nr_voltages;
+       u8 freq_band;
+       bool tuning_done;
 };
 
 #ifdef CONFIG_MMC_FREQ_SCALING
@@ -275,7 +290,7 @@ struct tegra_freq_gov_data {
        unsigned int            act_load_high_threshold;
        unsigned int            max_idle_monitor_cycles;
        unsigned int            curr_freq;
-       unsigned int            freqs[TUNING_FREQ_COUNT];
+       unsigned int            freqs[DFS_FREQ_COUNT];
        unsigned int            freq_switch_count;
        bool                    monitor_idle_load;
 };
@@ -289,7 +304,7 @@ struct sdhci_tegra_sd_stats {
 
 struct sdhci_tegra {
        const struct tegra_sdhci_platform_data *plat;
-       const struct sdhci_tegra_soc_data *soc_data;
+       struct sdhci_tegra_soc_data *soc_data;
        bool    clk_enabled;
        struct regulator *vdd_io_reg;
        struct regulator *vdd_slot_reg;
@@ -310,26 +325,26 @@ struct sdhci_tegra {
        bool is_rail_enabled;
        struct clk *emc_clk;
        bool emc_clk_enabled;
-       unsigned int emc_max_clk;
        struct clk *sclk;
        bool is_sdmmc_sclk_on;
        struct sdhci_tegra_sd_stats *sd_stat_head;
-       unsigned int nominal_vcore_mv;
-       unsigned int min_vcore_override_mv;
+       int nominal_vcore_mv;
+       int min_vcore_override_mv;
+       int boot_vcore_mv;
        /* Tuning related structures and variables */
        /* Tuning opcode to be used */
        unsigned int tuning_opcode;
        /* Tuning packet size */
        unsigned int tuning_bsize;
+       /* Num of tuning freqs selected */
+       unsigned int tuning_freq_count;
+       unsigned int tap_cmd;
        /* Tuning status */
        unsigned int tuning_status;
-       unsigned int tap_cmd;
-       struct mutex retune_lock;
 #define TUNING_STATUS_DONE     1
 #define TUNING_STATUS_RETUNE   2
        /* Freq tuning information for each sampling clock freq */
-       struct tegra_tuning_data tuning_data[TUNING_FREQ_COUNT];
-       bool set_tuning_override;
+       struct tegra_tuning_data tuning_data[DFS_FREQ_COUNT];
        bool is_parent_pllc;
        struct notifier_block reboot_notify;
        struct tegra_freq_gov_data *gov_data;
@@ -339,7 +354,13 @@ static struct clk *pll_c;
 static struct clk *pll_p;
 static unsigned long pll_c_rate;
 static unsigned long pll_p_rate;
+static bool vcore_overrides_allowed;
+static bool maintain_boot_voltage;
+static unsigned int boot_volt_req_refcount;
+DEFINE_MUTEX(tuning_mutex);
 
+static struct tegra_tuning_data *sdhci_tegra_get_tuning_data(
+       struct sdhci_host *sdhci, unsigned int clock);
 static unsigned long get_nearest_clock_freq(unsigned long pll_rate,
                unsigned long desired_rate);
 static void sdhci_tegra_set_tap_delay(struct sdhci_host *sdhci,
@@ -676,8 +697,9 @@ static int sdhci_tegra_freq_gov_init(struct sdhci_host *sdhci)
        }
 
        /* Find the supported frequencies */
-       for (i = 0; i < TUNING_FREQ_COUNT; i++) {
-               freq = tuning_params[i].freq_hz;
+       dev_info(mmc_dev(sdhci->mmc), "DFS supported freqs");
+       for (i = 0; i < tegra_host->tuning_freq_count; i++) {
+               freq = tegra_host->tuning_data[i].freq_hz;
                /*
                 * Check the nearest possible clock with pll_c and pll_p as
                 * the clock sources. Choose the higher frequency.
@@ -687,6 +709,7 @@ static int sdhci_tegra_freq_gov_init(struct sdhci_host *sdhci)
                freq = get_nearest_clock_freq(pll_p_rate, freq);
                if (freq > tegra_host->gov_data->freqs[i])
                        tegra_host->gov_data->freqs[i] = freq;
+               pr_err("%d,", tegra_host->gov_data->freqs[i]);
        }
 
        tegra_host->gov_data->monitor_idle_load = false;
@@ -775,6 +798,7 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
        unsigned int best_tap_value;
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct tegra_tuning_data *tuning_data;
        const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
        const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
 
@@ -805,21 +829,15 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
 
                if (soc_data->nvquirks & NVQUIRK_SET_TAP_DELAY) {
                        if ((tegra_host->tuning_status == TUNING_STATUS_DONE)
-                               && (sdhci->mmc->pm_flags &
-                               MMC_PM_KEEP_POWER)) {
-                               if (sdhci->mmc->ios.clock >
-                                       tuning_params[TUNING_LOW_FREQ].freq_hz) {
-                                       if (tegra_host->tap_cmd == TAP_CMD_TRIM_HIGH_VOLTAGE)
-                                               best_tap_value = tegra_host->
-                                                       tuning_data[TUNING_HIGH_FREQ_HV].best_tap_value[TUNING_HIGH_FREQ_HV];
-                                       else
-                                               best_tap_value = tegra_host->
-                                                       tuning_data[TUNING_HIGH_FREQ].best_tap_value[TUNING_HIGH_FREQ];
-                               } else
-                                       best_tap_value = tegra_host->
-                                               tuning_data[TUNING_LOW_FREQ].best_tap_value[TUNING_LOW_FREQ];
+                               && (sdhci->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
+                               tuning_data = sdhci_tegra_get_tuning_data(sdhci,
+                                       sdhci->mmc->ios.clock);
+                               best_tap_value = (tegra_host->tap_cmd ==
+                                       TAP_CMD_TRIM_HIGH_VOLTAGE) ?
+                                       tuning_data->nom_best_tap_value :
+                                       tuning_data->best_tap_value;
                        } else {
-                               best_tap_value = plat->tap_delay;
+                               best_tap_value = tegra_host->plat->tap_delay;
                        }
                        vendor_ctrl &= ~(0xFF <<
                                SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT);
@@ -1061,6 +1079,10 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
        unsigned int clk_rate;
+#ifdef CONFIG_MMC_FREQ_SCALING
+       unsigned int tap_value;
+       struct tegra_tuning_data *tuning_data;
+#endif
 
        if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
                /*
@@ -1087,25 +1109,15 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
        sdhci->max_clk = 26000000;
 #endif
 
-       if (tegra_host->tuning_status == TUNING_STATUS_RETUNE)
-               sdhci->flags |= SDHCI_NEEDS_RETUNING;
-
 #ifdef CONFIG_MMC_FREQ_SCALING
        /* Set the tap delay if tuning is done and dfs is enabled */
        if (sdhci->mmc->df &&
                (tegra_host->tuning_status == TUNING_STATUS_DONE)) {
-               int freq_band;
-
-               if (clock > tuning_params[TUNING_LOW_FREQ].freq_hz) {
-                       if (tegra_host->tap_cmd == TAP_CMD_TRIM_HIGH_VOLTAGE)
-                               freq_band = TUNING_HIGH_FREQ_HV;
-                       else
-                               freq_band = TUNING_HIGH_FREQ;
-               } else
-                       freq_band = TUNING_LOW_FREQ;
-
-               sdhci_tegra_set_tap_delay(sdhci,
-                       tegra_host->tuning_data[freq_band].best_tap_value[freq_band]);
+               tuning_data = sdhci_tegra_get_tuning_data(sdhci, clock);
+               tap_value = (tegra_host->tap_cmd == TAP_CMD_TRIM_HIGH_VOLTAGE) ?
+                       tuning_data->nom_best_tap_value :
+                       tuning_data->best_tap_value;
+               sdhci_tegra_set_tap_delay(sdhci, tap_value);
        }
 #endif
 }
@@ -1397,47 +1409,58 @@ static int sdhci_tegra_sd_error_stats(struct sdhci_host *host, u32 int_status)
        return 0;
 }
 
-static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci, u8 freq_band)
+static struct tegra_tuning_data *sdhci_tegra_get_tuning_data(
+       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 tegra_tuning_data *tuning_data;
-       char *freq[TUNING_FREQ_COUNT] = {"Low", "Normal", "High"};
-
-       tuning_data = &tegra_host->tuning_data[freq_band];
-
-       if (tuning_data->tap_data[0]) {
-               pr_info("%s: Tuning window data at %d mV, frequency band: %s\n",
-                       mmc_hostname(sdhci->mmc),
-                       tuning_params[freq_band].voltages[0],
-                       freq[freq_band]);
-               pr_info("Partial window %d\n",
-                       tuning_data->tap_data[0]->partial_win);
-               pr_info("full window start %d\n",
-                       tuning_data->tap_data[0]->full_win_begin);
-               pr_info("full window end %d\n",
-                       tuning_data->tap_data[0]->full_win_end);
-       }
-
-       if (((freq_band == TUNING_HIGH_FREQ) ||
-               (freq_band == TUNING_HIGH_FREQ_HV)) &&
-               (tuning_data->tap_data[1])) {
-               pr_info("%s: Tuning window data at %d-%d mV\n",
-                       mmc_hostname(sdhci->mmc),
-                       tuning_params[freq_band].voltages[0],
-                       tuning_params[freq_band].voltages[1]);
-               pr_info("partial window %d\n",
-                   tuning_data->tap_data[1]->partial_win);
-               pr_info("full window being %d\n",
-                       tuning_data->tap_data[1]->full_win_begin);
-               pr_info("full window end %d\n",
-                       tuning_data->tap_data[1]->full_win_end);
-       }
-       pr_info("%s window chosen\n",
-               tuning_data->select_partial_win ?
-               "partial" : "full");
-       pr_info("Best tap value %d\n",
-               tuning_data->best_tap_value[freq_band]);
+       unsigned int low_freq;
+       u8 i = 0;
+
+       if (tegra_host->tuning_freq_count == 1) {
+               tuning_data = &tegra_host->tuning_data[0];
+               goto out;
+       }
+
+       /* Get the lowest supported freq */
+       for (i = 0; i < TUNING_FREQ_COUNT; ++i) {
+               low_freq = tegra_host->soc_data->tuning_freq_list[i];
+               if (low_freq)
+                       break;
+       }
+
+       if (clock <= low_freq)
+               tuning_data = &tegra_host->tuning_data[0];
+       else
+               tuning_data = &tegra_host->tuning_data[1];
+
+out:
+       return tuning_data;
+}
+
+static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci)
+{
+       struct tegra_tuning_data *tuning_data;
+       struct tap_window_data *tap_data;
+       u8 i;
+
+       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);
+       }
+       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");
 }
 
 /*
@@ -1454,56 +1477,52 @@ static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci, u8 freq_band)
  * 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)
+static unsigned int calculate_low_freq_tap_value(struct sdhci_host *sdhci,
+       struct tap_window_data *tap_data)
 {
-       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[TUNING_LOW_FREQ];
-       tap_data = tuning_data->tap_data[0];
+       unsigned int tuning_ui;
+       unsigned int sampling_point = 0;
+       bool select_partial_win;
 
        if (tap_data->abandon_full_win) {
                if (tap_data->abandon_partial_win) {
                        return 0;
                } else {
-                       tuning_data->select_partial_win = true;
+                       select_partial_win = true;
                        goto calculate_best_tap;
                }
        }
 
-       tap_data->tuning_ui = tap_data->full_win_end - tap_data->partial_win;
+       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;
+       sampling_point = ((tuning_ui * curr_clock) / (max_clock << 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;
+               (tap_data->partial_win > ((22 * tuning_ui) / 100)))
+                       select_partial_win = true;
 
 calculate_best_tap:
-       if (tuning_data->select_partial_win) {
+       if (select_partial_win)
                best_tap_value = (tap_data->partial_win -
                        (tap_data->full_win_end - tap_data->full_win_begin)) +
-                       tap_data->sampling_point;
-               if (best_tap_value < 0)
-                       best_tap_value = 0;
-       } else {
+                       sampling_point;
+       else
                best_tap_value = tap_data->full_win_begin +
-                       tap_data->sampling_point;
-       }
+                       sampling_point;
+
+       if (best_tap_value < 0)
+               best_tap_value = 0;
+
        return best_tap_value;
 }
 
@@ -1532,39 +1551,22 @@ calculate_best_tap:
  * 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, int freq_band)
+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)
 {
-       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;
-       unsigned int best_tap;
-       struct tap_window_data *vmax_tap_data;
-       struct tap_window_data *vmid_tap_data;
-       struct tegra_tuning_data *tuning_data;
+       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;
-
-       tuning_data = &tegra_host->tuning_data[freq_band];
-       vmax_tap_data = tuning_data->tap_data[0];
-       vmid_tap_data = tuning_data->tap_data[1];
-
-       /*
-        * If tuning at min override voltage is not done or one shot tuning is
-        * done, set the best tap value as 50% of the full window.
-        */
-       if (!tuning_data->overide_vcore_tuning_done ||
-               tuning_data->one_shot_tuning) {
-               dev_info(mmc_dev(sdhci->mmc),
-                       "Setting best tap as 50 percent of the full window\n");
-               best_tap = (vmax_tap_data->full_win_begin +
-                       (((vmax_tap_data->full_win_end -
-                       vmax_tap_data->full_win_begin) * 5) / 10));
-               return best_tap;
-       }
+       int best_tap_value;
 
        curr_clock = sdhci->max_clk / 1000000;
        max_clock = uhs_max_freq_MHz[sdhci->mmc->ios.timing];
@@ -1573,59 +1575,101 @@ static unsigned int calculate_high_freq_tap_value(struct sdhci_host *sdhci, int
         * 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_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;
+       vmax_sampling_point = (vmax_tuning_ui * curr_clock) / (max_clock << 2);
 
-       vmid_tap_data->tuning_ui = vmid_tap_data->full_win_end -
+       vmid_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));
+       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_tap_data->sampling_point) -
-               (vmax_tap_data->full_win_begin +
-               vmax_tap_data->sampling_point);
+               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_tap_data->sampling_point) +
-               (partial_win_start + vmax_tap_data->sampling_point));
+       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_tap_data->sampling_point) - partial_win_tap;
+               vmid_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");
-               best_tap = vmax_tap_data->full_win_begin +
+               best_tap_value = vmax_tap_data->full_win_begin +
                        vmid_tap_data->full_win_end;
-               best_tap >>= 1;
+               best_tap_value >>= 1;
        } else {
-               if (full_win_quality > partial_win_quality) {
-                       best_tap = full_win_tap;
+               if (full_win_quality > partial_win_quality)
+                       best_tap_value = full_win_tap;
+               else
+                       best_tap_value = partial_win_tap;
+       }
+
+       if (best_tap_value < 0)
+               best_tap_value = 0;
+       return best_tap_value;
+}
+
+static void sdhci_tegra_calculate_best_tap(struct sdhci_host *sdhci,
+       u8 freq_band)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct tegra_tuning_data *tuning_data;
+
+       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");
+
+       tuning_data = sdhci_tegra_get_tuning_data(sdhci, sdhci->max_clk);
+       if (freq_band == TUNING_LOW_FREQ) {
+               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 {
-                       best_tap = partial_win_tap;
-                       tuning_data->select_partial_win = true;
+                       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]);
                }
        }
-       return best_tap;
 }
 
-static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
+static int sdhci_tegra_issue_tuning_cmd(struct sdhci_host *sdhci)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
@@ -1726,7 +1770,7 @@ static int sdhci_tegra_scan_tap_values(struct sdhci_host *sdhci,
                sdhci_tegra_set_tap_delay(sdhci, tap_value);
 
                /* Run frequency tuning */
-               err = sdhci_tegra_run_frequency_tuning(sdhci);
+               err = sdhci_tegra_issue_tuning_cmd(sdhci);
                if (err && retry) {
                        retry--;
                        continue;
@@ -1818,23 +1862,326 @@ out:
        return err;
 }
 
-static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
+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;
+
+       SDHCI_TEGRA_DBG("%s: Num of tuning frequencies%d\n",
+               mmc_hostname(sdhci->mmc), tegra_host->tuning_freq_count);
+       for (i = 0; i < tegra_host->tuning_freq_count; ++i) {
+               tuning_data = &tegra_host->tuning_data[i];
+               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);
+               }
+       }
+}
+
+static unsigned int get_tuning_voltage(struct sdhci_tegra *tegra_host, u8 *mask)
+{
+       u8 i = 0;
+
+       i = ffs(*mask) - 1;
+       *mask &= ~(1 << i);
+       switch (BIT(i)) {
+       case NOMINAL_VCORE_TUN:
+               return tegra_host->nominal_vcore_mv;
+       case BOOT_VCORE_TUN:
+               return tegra_host->boot_vcore_mv;
+       case MIN_OVERRIDE_VCORE_TUN:
+               return tegra_host->min_vcore_override_mv;
+       }
+
+       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);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       unsigned int *freq_list;
+       u32 curr_clock;
+       u8 i;
+
+       curr_clock = sdhci->max_clk;
+       freq_list = tegra_host->soc_data->tuning_freq_list;
+
+       for (i = 0; i < TUNING_FREQ_COUNT; ++i)
+               if (curr_clock <= freq_list[i])
+                       return i;
+
+       return TUNING_MAX_FREQ;
+}
+
+/*
+ * 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.
+ */
+static u8 sdhci_tegra_setup_freq_constraints(struct sdhci_host *sdhci,
+       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;
+
+       if ((sdhci->mmc->ios.timing != MMC_TIMING_UHS_SDR50) &&
+               (sdhci->mmc->caps2 & MMC_CAP2_FREQ_SCALING))
+               freq_count = DFS_FREQ_COUNT;
+       else
+               freq_count = 1;
+
+       freq_band = sdhci_tegra_get_freq_point(sdhci);
+       /* Fill up the req frequencies */
+       switch (freq_count) {
+       case 1:
+               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->plat->en_nominal_vcore_tuning)
+                       tuning_data->constraints.vcore_mask &=
+                               ~NOMINAL_VCORE_TUN;
+       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 = &tegra_host->tuning_data[0];
+               for (i = (freq_band - 1); i >= 0; i--) {
+                       if (!freq_list[i])
+                               continue;
+                       tuning_data->freq_hz = freq_list[i];
+                       tuning_data->freq_band = i;
+                       tuning_data->constraints =
+                               tuning_vcore_constraints[i];
+                       if (!tegra_host->plat->en_nominal_vcore_tuning)
+                               tuning_data->constraints.vcore_mask &=
+                                       ~NOMINAL_VCORE_TUN;
+               }
+       break;
+       default:
+               dev_err(mmc_dev(sdhci->mmc), "Unsupported freq count\n");
+       }
+       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.
+ */
+static int sdhci_tegra_get_tuning_constraints(struct sdhci_host *sdhci)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       unsigned int *freq_list;
+       int err = 0;
+
+
+       /* Check if the constraints are already filled up */
+       if (tegra_host->tuning_freq_count)
+               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;
+}
+
+/*
+ * During boot, only boot voltage for vcore can be set. Check if the current
+ * voltage is allowed to be used. Nominal and min override voltages can be
+ * set once boot is done. This will be notified through late subsys init call.
+ */
+static int sdhci_tegra_set_tuning_voltage(struct sdhci_host *sdhci,
+       unsigned int voltage)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       int err = 0;
+       bool nom_emc_freq_set = false;
+
+       if (voltage && (voltage != tegra_host->boot_vcore_mv) &&
+               !vcore_overrides_allowed) {
+               SDHCI_TEGRA_DBG("%s: Override vcore %dmv not allowed \n",
+                       mmc_hostname(sdhci->mmc), voltage);
+               return -EPERM;
+       }
+
+       SDHCI_TEGRA_DBG("%s: Setting vcore override %d\n",
+               mmc_hostname(sdhci->mmc), voltage);
+       /* First clear any previous dvfs override settings */
+       err = tegra_dvfs_override_core_voltage(0);
+
+       /* EMC clock freq boost might be required for nominal core voltage */
+       if (( voltage == tegra_host->nominal_vcore_mv) &&
+               tegra_host->plat->en_nominal_vcore_tuning &&
+               tegra_host->emc_clk) {
+               err = clk_set_rate(tegra_host->emc_clk, SDMMC_EMC_NOM_VOLT_FREQ);
+               if (err)
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Failed to set emc nom clk freq %d\n", err);
+               else
+                       nom_emc_freq_set = true;
+       }
+
+       err = tegra_dvfs_override_core_voltage(voltage);
+       if (err)
+               dev_err(mmc_dev(sdhci->mmc),
+                       "failed to set vcore override %dmv\n", voltage);
+
+       /* Revert emc clock to normal freq */
+       if (nom_emc_freq_set) {
+               err = clk_set_rate(tegra_host->emc_clk, SDMMC_EMC_MAX_FREQ);
+               if (err)
+                       dev_err(mmc_dev(sdhci->mmc),
+                               "Failed to revert emc nom clk freq %d\n", err);
+       }
+
+       return err;
+}
+
+static u8 get_curr_voltage_tuning_status(struct tap_window_data *tap_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;
+
+       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;
+               /* 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)
+                       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");
+       }
+       return retuning_req;
+}
+
+static int sdhci_tegra_verify_best_tap(struct sdhci_host *sdhci,
+       u8 freq_band)
+{
+       struct tegra_tuning_data *tuning_data;
+       unsigned int best_tap_value = 0;
+       int err;
+
+       tuning_data = sdhci_tegra_get_tuning_data(sdhci, sdhci->max_clk);
+       best_tap_value = tuning_data->best_tap_value;
+
+       /* Set the best tap value */
+       sdhci_tegra_set_tap_delay(sdhci, best_tap_value);
+
+       /* Run tuning after setting the best tap value */
+       err = sdhci_tegra_issue_tuning_cmd(sdhci);
+       if (err)
+               dev_err(mmc_dev(sdhci->mmc),
+                       "%dMHz best tap value verification failed %d\n",
+                       tuning_data->freq_hz, err);
+       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;
+       unsigned int freq_band;
        int err;
        u16 ctrl_2;
        u32 ier;
-       int freq_band;
-       unsigned int i = 0;
-       unsigned int voltage = 0;
-#ifdef CONFIG_MMC_FREQ_SCALING
-       bool single_freq_tuning = true;
-#endif
-       bool vcore_override_failed = false;
-       static unsigned int vcore_lvl;
+       u8 i, set_retuning = 0;
+       bool is_retuning_req = false;
+       bool force_retuning = false;
 
        /* Tuning is valid only in SDR104 and SDR50 modes */
        ctrl_2 = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
@@ -1851,6 +2198,10 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
        else
                return -EINVAL;
 
+       SDHCI_TEGRA_DBG("%s: Starting freq tuning\n", mmc_hostname(sdhci->mmc));
+       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 */
@@ -1866,238 +2217,94 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
        sdhci_writel(sdhci, SDHCI_INT_DATA_AVAIL |
                SDHCI_INT_DATA_CRC, SDHCI_INT_ENABLE);
 
-       if (sdhci->max_clk > tuning_params[TUNING_LOW_FREQ].freq_hz) {
-               if (tegra_host->tap_cmd == TAP_CMD_TRIM_HIGH_VOLTAGE)
-                       freq_band = TUNING_HIGH_FREQ_HV;
-               else
-                       freq_band = TUNING_HIGH_FREQ;
-       } else
-               freq_band = TUNING_LOW_FREQ;
-
-       tuning_data = &tegra_host->tuning_data[freq_band];
-
        /*
         * If tuning is already done and retune request is not set, then skip
-        * best tap value calculation and use the old best tap value.
+        * best tap value calculation and use the old best tap value. If the
+        * previous best tap value verification failed, force retuning.
         */
        if (tegra_host->tuning_status == TUNING_STATUS_DONE) {
-                       dev_info(mmc_dev(sdhci->mmc),
+               freq_band = sdhci_tegra_get_freq_point(sdhci);
+               dev_info(mmc_dev(sdhci->mmc),
                        "Tuning already done. Setting tuned tap value %d\n",
-                       tuning_data->best_tap_value[freq_band]);
-                       goto set_best_tap;
+                       tegra_host->tuning_data[freq_band].best_tap_value);
+               err = sdhci_tegra_verify_best_tap(sdhci, freq_band);
+               if (err)
+                       force_retuning = true;
+               else
+                       goto out;
        }
 
-       /* Remove any previously set override voltages */
-       if (tegra_host->set_tuning_override) {
-               spin_unlock(&sdhci->lock);
-               tegra_dvfs_override_core_voltage(0);
-               spin_lock(&sdhci->lock);
-               vcore_lvl = 0;
-               tegra_host->set_tuning_override = false;
+       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");
+               goto out;
        }
 
-#ifdef CONFIG_MMC_FREQ_SCALING
-       for (freq_band = 0; freq_band < TUNING_FREQ_COUNT; freq_band++) {
-               if (sdhci->mmc->caps2 & MMC_CAP2_FREQ_SCALING) {
-                       spin_unlock(&sdhci->lock);
-                       tegra_sdhci_set_clock(sdhci,
-                               tuning_params[freq_band].freq_hz);
-                       spin_lock(&sdhci->lock);
-                       single_freq_tuning = false;
-               } else {
-                       single_freq_tuning = true;
-               }
-#endif
-
-       /*
-        * 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[freq_band];
-       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 nominal vcore is not specified, run tuning once and set
-                * the tap value. Tuning might fail but this is a better option
-                * than not trying tuning at all.
-                */
-               if (!tegra_host->nominal_vcore_mv) {
-                       dev_err(mmc_dev(sdhci->mmc),
-                               "Missing nominal vcore. Tuning might fail\n");
-                       tuning_data->one_shot_tuning = true;
-                       goto skip_vcore_override;
-               }
+       for (i = 0; i < tegra_host->tuning_freq_count; i++) {
+               tuning_data = &tegra_host->tuning_data[i];
+               if (tuning_data->tuning_done && !force_retuning)
+                       continue;
 
-               voltage = tuning_params[freq_band].voltages[i];
-               if (voltage > tegra_host->nominal_vcore_mv) {
-                       voltage = tegra_host->nominal_vcore_mv;
-                       if ((tuning_data->nominal_vcore_tuning_done) &&
-                       (tuning_params[freq_band].nr_voltages == 1)) {
-                               spin_lock(&sdhci->lock);
-                               continue;
-                       }
-               } else if (voltage < tegra_host->min_vcore_override_mv) {
-                       voltage = tegra_host->min_vcore_override_mv;
-                       if ((tuning_data->overide_vcore_tuning_done) &&
-                       (tuning_params[freq_band].nr_voltages == 1)) {
-                               spin_lock(&sdhci->lock);
-                               continue;
-                       }
-               }
-               if (voltage != vcore_lvl) {
-                       /* Boost emc clock to 900MHz before setting 1.39V */
-                       if ((voltage == 1390) && tegra_host->emc_clk) {
-                               err = clk_prepare_enable(tegra_host->emc_clk);
-                               if (err)
-                                       dev_err(mmc_dev(sdhci->mmc),
-                                       "1.39V emc freq boost failed %d\n",
-                                       err);
-                               else
-                                       tegra_host->emc_clk_enabled = true;
-                       }
-                       err = tegra_dvfs_override_core_voltage(voltage);
-                       if (err) {
-                               vcore_override_failed = true;
-                               dev_err(mmc_dev(sdhci->mmc),
-                                       "Setting tuning override_mv %d failed %d\n",
-                                       voltage, err);
-                       } else {
-                               vcore_lvl = voltage;
-                       }
-               }
-               spin_lock(&sdhci->lock);
+               SDHCI_TEGRA_DBG("%s: Setting tuning freq%d\n",
+                       mmc_hostname(sdhci->mmc), tuning_data->freq_hz);
+               tegra_sdhci_set_clock(sdhci, tuning_data->freq_hz);
 
-skip_vcore_override:
-               /* Get the tuning window info */
-               err = sdhci_tegra_get_tap_window_data(sdhci, tap_data);
-               if (err) {
-                       dev_err(mmc_dev(sdhci->mmc), "No tuning window\n");
-                       goto out;
-               }
+               set_retuning = sdhci_tegra_run_tuning(sdhci, tuning_data, force_retuning);
 
-               if (tuning_data->one_shot_tuning) {
-                       spin_lock(&sdhci->lock);
-                       tuning_data->nominal_vcore_tuning_done = true;
-                       tuning_data->overide_vcore_tuning_done = true;
-                       break;
-               }
+               sdhci_tegra_calculate_best_tap(sdhci, tuning_data->freq_band);
 
-               if (!vcore_override_failed) {
-                       if (voltage == tegra_host->nominal_vcore_mv)
-                               tuning_data->nominal_vcore_tuning_done = true;
-                       if (voltage >= tegra_host->min_vcore_override_mv)
-                               tuning_data->overide_vcore_tuning_done = true;
-               }
+               /* Dump the tuning data */
+               sdhci_tegra_dump_tuning_data(sdhci);
 
-               /* Release the override voltage setting */
-               spin_unlock(&sdhci->lock);
-               err = tegra_dvfs_override_core_voltage(0);
-               if (err)
-                       dev_err(mmc_dev(sdhci->mmc),
-                               "Clearing tuning override voltage failed %d\n",
-                                       err);
-               else
-                       vcore_lvl = 0;
-               if (tegra_host->emc_clk_enabled) {
-                       clk_disable_unprepare(tegra_host->emc_clk);
-                       tegra_host->emc_clk_enabled = false;
+               err = sdhci_tegra_verify_best_tap(sdhci,
+                               tuning_data->freq_band);
+               if (!err && !set_retuning) {
+                       tuning_data->tuning_done = true;
+                       tegra_host->tuning_status |= TUNING_STATUS_DONE;
+               } else {
+                       tegra_host->tuning_status |= TUNING_STATUS_RETUNE;
                }
-
-               spin_lock(&sdhci->lock);
        }
-       /* If setting min override voltage failed for the first time, set
-        * nominal core voltage as override until retuning is done.
-        */
-       if ((tegra_host->tuning_status != TUNING_STATUS_RETUNE) &&
-               tuning_data->nominal_vcore_tuning_done &&
-               !tuning_data->overide_vcore_tuning_done)
-               tegra_host->set_tuning_override = true;
-
-       /* Calculate best tap for current freq band */
-       if (freq_band == TUNING_LOW_FREQ)
-               tuning_data->best_tap_value[freq_band] =
-                       calculate_low_freq_tap_value(sdhci);
-       else
-               tuning_data->best_tap_value[freq_band] =
-                       calculate_high_freq_tap_value(sdhci, freq_band);
-set_best_tap:
-
-       /* Dump the tap window data */
-       sdhci_tegra_set_tap_delay(sdhci,
-                       tuning_data->best_tap_value[freq_band]);
-       sdhci_tegra_dump_tuning_data(sdhci, freq_band);
-
+       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);
 
-       /*
-        * 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) {
-               tuning_data->nominal_vcore_tuning_done = false;
-               tuning_data->overide_vcore_tuning_done = false;
-               tegra_host->tuning_status = TUNING_STATUS_RETUNE;
        } else {
-               if (tuning_data->nominal_vcore_tuning_done &&
-                       tuning_data->overide_vcore_tuning_done)
-                       tegra_host->tuning_status = TUNING_STATUS_DONE;
-               else
-                       tegra_host->tuning_status = TUNING_STATUS_RETUNE;
+               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);
        }
-#ifdef CONFIG_MMC_FREQ_SCALING
-               if (single_freq_tuning)
-                       break;
-       }
-#endif
-out:
-       /*
-        * Lock down the core voltage if tuning at override voltage failed
-        * for the first time. The override setting will be removed once
-        * retuning is called.
-        */
-       if (tegra_host->set_tuning_override) {
-               dev_info(mmc_dev(sdhci->mmc),
-                       "Nominal core voltage being set until retuning\n");
-               spin_unlock(&sdhci->lock);
-               err = tegra_dvfs_override_core_voltage(
-                               tegra_host->nominal_vcore_mv);
-               if (err)
-                       dev_err(mmc_dev(sdhci->mmc),
-                               "Setting tuning override voltage failed %d\n",
-                                       err);
-               else
-                       vcore_lvl = tegra_host->nominal_vcore_mv;
-               spin_lock(&sdhci->lock);
 
-               /* Schedule for the retuning */
-               mod_timer(&sdhci->tuning_timer, jiffies +
-                       10 * HZ);
-       }
+       if (boot_volt_req_refcount)
+               sdhci_tegra_set_tuning_voltage(sdhci,
+                       tegra_host->boot_vcore_mv);
+
 
        /* Enable interrupts. Enable full range for core voltage */
        sdhci_writel(sdhci, ier, SDHCI_INT_ENABLE);
        sdhci_writel(sdhci, ier, SDHCI_SIGNAL_ENABLE);
+       mutex_unlock(&tuning_mutex);
+
+       SDHCI_TEGRA_DBG("%s: Freq tuning done\n", mmc_hostname(sdhci->mmc));
        return err;
 }
 
+static int __init sdhci_tegra_enable_vcore_override_tuning(void)
+{
+       vcore_overrides_allowed = true;
+       maintain_boot_voltage = false;
+       return 0;
+}
+late_initcall(sdhci_tegra_enable_vcore_override_tuning);
+
 static int tegra_sdhci_suspend(struct sdhci_host *sdhci)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -2178,6 +2385,25 @@ 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.
+ */
+static void tegra_sdhci_get_bus(struct sdhci_host *sdhci)
+{
+       if (boot_volt_req_refcount)
+               mutex_lock(&tuning_mutex);
+}
+
+static void tegra_sdhci_release_bus(struct sdhci_host *sdhci)
+{
+       if (boot_volt_req_refcount)
+               mutex_unlock(&tuning_mutex);
+}
+
 static int show_polling_period(void *data, u64 *value)
 {
        struct sdhci_host *host = (struct sdhci_host *)data;
@@ -2280,6 +2506,93 @@ err_root:
        return;
 }
 
+static ssize_t sdhci_handle_boost_mode_tap(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int tap_cmd;
+       struct mmc_card *card;
+       char *p = (char *)buf;
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+       struct tegra_tuning_data *tuning_data;
+       u32 present_state;
+       bool clk_set_for_tap_prog = false;
+
+       tap_cmd = memparse(p, &p);
+
+       card = host->mmc->card;
+       if (!card)
+               return -ENODEV;
+
+       /* if not uhs -- no tuning and no tap value to set */
+       if (!mmc_sd_card_uhs(card) && !mmc_card_hs200(card))
+               return count;
+
+       /* if no change in tap value -- just exit */
+       if (tap_cmd == tegra_host->tap_cmd)
+               return count;
+
+       if ((tap_cmd != TAP_CMD_TRIM_DEFAULT_VOLTAGE) &&
+               (tap_cmd != TAP_CMD_TRIM_HIGH_VOLTAGE)) {
+               pr_info("\necho 1 > cmd_state  # to set normal voltage\n" \
+                         "echo 2 > cmd_state  # to set high voltage\n");
+               return -EINVAL;
+       }
+
+       tegra_host->tap_cmd = tap_cmd;
+       tuning_data = sdhci_tegra_get_tuning_data(host, host->max_clk);
+       /* Check if host clock is enabled */
+       if (!tegra_host->clk_enabled) {
+               /* Nothing to do if the host is not powered ON */
+               if (host->mmc->ios.power_mode != MMC_POWER_ON)
+                       return count;
+               else {
+                       tegra_sdhci_set_clock(host, host->mmc->ios.clock);
+                       clk_set_for_tap_prog = true;
+               }
+       } else {
+               /* Wait for any on-going data transfers */
+               do {
+                       present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
+               } while(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ));
+       }
+       spin_lock(&host->lock);
+       switch (tap_cmd) {
+       case TAP_CMD_TRIM_DEFAULT_VOLTAGE:
+               /* set tap value for voltage range 1.1 to 1.25 */
+               sdhci_tegra_set_tap_delay(host, tuning_data->best_tap_value);
+               break;
+
+       case TAP_CMD_TRIM_HIGH_VOLTAGE:
+               /* set tap value for voltage range 1.25 to 1.39 */
+               sdhci_tegra_set_tap_delay(host,
+                       tuning_data->nom_best_tap_value);
+               break;
+       default:
+               break;
+       }
+       spin_unlock(&host->lock);
+       if (clk_set_for_tap_prog) {
+               tegra_sdhci_set_clock(host, 0);
+               clk_set_for_tap_prog = false;
+       }
+       return count;
+}
+
+static ssize_t sdhci_show_turbo_mode(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_tegra *tegra_host = pltfm_host->priv;
+
+       return sprintf(buf, "%d\n", tegra_host->tap_cmd);
+}
+
+static DEVICE_ATTR(cmd_state, 0644, sdhci_show_turbo_mode,
+                       sdhci_handle_boost_mode_tap);
+
 static int tegra_sdhci_reboot_notify(struct notifier_block *nb,
                                unsigned long event, void *data)
 {
@@ -2337,6 +2650,8 @@ static struct sdhci_ops tegra_sdhci_ops = {
        .resume                 = tegra_sdhci_resume,
        .platform_resume        = tegra_sdhci_post_resume,
        .platform_reset_exit    = tegra_sdhci_reset_exit,
+       .platform_get_bus       = tegra_sdhci_get_bus,
+       .platform_release_bus   = tegra_sdhci_release_bus,
        .set_uhs_signaling      = tegra_sdhci_set_uhs_signaling,
        .switch_signal_voltage  = tegra_sdhci_signal_voltage_switch,
        .switch_signal_voltage_exit = tegra_sdhci_do_calibration,
@@ -2363,6 +2678,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra20 = {
                    NVQUIRK_DISABLE_AUTO_CALIBRATION |
                    NVQUIRK_ENABLE_BLOCK_GAP_DET,
        .parent_clk_list = {"pll_p"},
+       .tuning_freq_list = {0},
 };
 
 static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
@@ -2378,6 +2694,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = {
                    NVQUIRK_SET_CALIBRATION_OFFSETS |
                    NVQUIRK_ENABLE_SD_3_0,
        .parent_clk_list = {"pll_p"},
+       .tuning_freq_list = {208000000},
 };
 
 static struct sdhci_pltfm_data sdhci_tegra11_pdata = {
@@ -2395,6 +2712,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra11 = {
                    NVQUIRK_ENABLE_HS200 |
                    NVQUIRK_INFINITE_ERASE_TIMEOUT,
        .parent_clk_list = {"pll_p", "pll_c"},
+       .tuning_freq_list = {81600000, 156000000, 200000000},
 };
 
 static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
@@ -2405,91 +2723,15 @@ static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
 };
 MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
 
-static ssize_t sdhci_handle_boost_mode_tap(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-       int tap_cmd;
-       struct mmc_card *card;
-       char *p = (char *)buf;
-       struct sdhci_host *host = dev_get_drvdata(dev);
-       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct sdhci_tegra *tegra_host = pltfm_host->priv;
-       struct tegra_tuning_data *tuning_data;
-
-       tap_cmd = memparse(p, &p);
-
-       card = host->mmc->card;
-       if (!card)
-               return count;
-
-       /* if not uhs -- no tuning and no tap value to set */
-       if (!mmc_sd_card_uhs(card) && !mmc_card_hs200(card))
-               return count;
-
-       /* if no change in tap value -- just exit */
-       if (tap_cmd == tegra_host->tap_cmd)
-               return count;
-
-       switch (tap_cmd) {
-       case TAP_CMD_GO_COMMAND:
-       case TAP_CMD_SUSPEND_CONTROLLER:
-               return count;
-
-       case TAP_CMD_TRIM_DEFAULT_VOLTAGE:
-       case TAP_CMD_TRIM_HIGH_VOLTAGE:
-               break;
-
-       default:
-               pr_info("\necho 1 > cmd_state  # to set normal voltage\n" \
-                         "echo 2 > cmd_state  # to set high voltage\n");
-               return count;
-       }
-
-       spin_lock(&host->lock);
-       disable_irq(host->irq);
-       tegra_host->tap_cmd = tap_cmd;
-       tuning_data = &tegra_host->tuning_data[tap_cmd];
-       switch (tap_cmd) {
-       case TAP_CMD_TRIM_DEFAULT_VOLTAGE:
-               /* set tap value for voltage range 1.1 to 1.25 */
-               sdhci_tegra_set_tap_delay(host,
-                       tuning_data->best_tap_value[TUNING_HIGH_FREQ]);
-               break;
-
-       case TAP_CMD_TRIM_HIGH_VOLTAGE:
-               /*  set tap value for voltage range 1.25 to 1.39 volts */
-               sdhci_tegra_set_tap_delay(host,
-                       tuning_data->best_tap_value[TUNING_HIGH_FREQ_HV]);
-               break;
-       default:
-               break;
-       }
-       spin_unlock(&host->lock);
-       enable_irq(host->irq);
-       return count;
-}
-
-static ssize_t sdhci_show_turbo_mode(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       struct sdhci_host *host = dev_get_drvdata(dev);
-       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct sdhci_tegra *tegra_host = pltfm_host->priv;
-
-       return sprintf(buf, "%d\n", tegra_host->tap_cmd);
-}
-
-static DEVICE_ATTR(cmd_state, 0644, sdhci_show_turbo_mode,
-                       sdhci_handle_boost_mode_tap);
-
 static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match;
-       const struct sdhci_tegra_soc_data *soc_data;
+       struct sdhci_tegra_soc_data *soc_data;
        struct sdhci_host *host;
        struct sdhci_pltfm_host *pltfm_host;
        struct tegra_sdhci_platform_data *plat;
        struct sdhci_tegra *tegra_host;
+       unsigned int low_freq;
        int rc;
        u8 i;
 
@@ -2717,7 +2959,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                        rc = PTR_ERR(tegra_host->emc_clk);
                        goto err_clk_put;
                } else
-                       clk_set_rate(tegra_host->emc_clk, 900000000);
+                       clk_set_rate(tegra_host->emc_clk, SDMMC_EMC_MAX_FREQ);
        }
 
        tegra_host->sclk = devm_clk_get(mmc_dev(host->mmc), "sclk");
@@ -2725,7 +2967,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
                dev_err(mmc_dev(host->mmc), "Can't get sclk clock\n");
                tegra_host->sclk = NULL;
        } else {
-               clk_set_rate(tegra_host->sclk, 900000000);
+               clk_set_rate(tegra_host->sclk, SDMMC_AHB_MAX_FREQ);
        }
 
        pltfm_host->priv = tegra_host;
@@ -2735,7 +2977,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        tegra_host->sd_detect_in_suspend = plat->sd_detect_in_suspend;
        tegra_host->instance = pdev->id;
        tegra_host->tap_cmd = TAP_CMD_TRIM_DEFAULT_VOLTAGE;
-       mutex_init(&tegra_host->retune_lock);
        host->mmc->pm_caps |= plat->pm_caps;
        host->mmc->pm_flags |= plat->pm_flags;
 
@@ -2760,33 +3001,44 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        host->mmc->caps |= MMC_CAP_CMD23;
        host->mmc->caps2 |= MMC_CAP2_PACKED_CMD;
 
-#ifdef CONFIG_MMC_FREQ_SCALING
        /*
         * Enable dyamic frequency scaling support only if the platform clock
         * limit is higher than the lowest supported frequency by tuning.
         */
-       if (plat->en_freq_scaling && (plat->max_clk_limit >
-               tuning_params[TUNING_LOW_FREQ].freq_hz))
+       for (i = 0; i < TUNING_FREQ_COUNT; i++) {
+               low_freq = soc_data->tuning_freq_list[i];
+               if (low_freq)
+                       break;
+       }
+       if (plat->en_freq_scaling && (plat->max_clk_limit > low_freq))
                host->mmc->caps2 |= MMC_CAP2_FREQ_SCALING;
-#endif
 
        if (plat->nominal_vcore_mv)
                tegra_host->nominal_vcore_mv = plat->nominal_vcore_mv;
        if (plat->min_vcore_override_mv)
                tegra_host->min_vcore_override_mv = plat->min_vcore_override_mv;
+       if (plat->boot_vcore_mv)
+               tegra_host->boot_vcore_mv = plat->boot_vcore_mv;
+       dev_info(mmc_dev(host->mmc),
+               "Tuning constraints: nom_mv %d, boot_mv %d, min_or_mv %d\n",
+               tegra_host->nominal_vcore_mv, tegra_host->boot_vcore_mv,
+               tegra_host->min_vcore_override_mv);
+
+       /*
+        * If nominal voltage is equal to boot voltage, there is no need for
+        * nominal voltage tuning.
+        */
+       if (plat->nominal_vcore_mv <= plat->boot_vcore_mv)
+               plat->en_nominal_vcore_tuning = false;
 
        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];
 
-       pr_err("nominal vcore %d, override %d\n",
-               tegra_host->nominal_vcore_mv,
-               tegra_host->min_vcore_override_mv);
        rc = sdhci_add_host(host);
 
        device_create_file(&pdev->dev, &dev_attr_cmd_state);
-
        sdhci_tegra_error_stats_debugfs(host);
        if (rc)
                goto err_add_host;
index df69b43..e84fafb 100644 (file)
@@ -1341,6 +1341,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                        }
                }
 
+               /* For a data cmd, check for plat specific preparation */
+               spin_unlock_irqrestore(&host->lock, flags);
+               if (mrq->data)
+                       host->ops->platform_get_bus(host);
+               spin_lock_irqsave(&host->lock, flags);
+
                if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
                        sdhci_send_command(host, mrq->sbc);
                else
@@ -1774,17 +1780,16 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 
        sdhci_runtime_pm_get(host);
        disable_irq(host->irq);
-       spin_lock(&host->lock);
 
        if ((host->quirks2 & SDHCI_QUIRK2_NON_STANDARD_TUNING) &&
                host->ops->execute_freq_tuning) {
                err = host->ops->execute_freq_tuning(host, opcode);
-               spin_unlock(&host->lock);
                enable_irq(host->irq);
                sdhci_runtime_pm_put(host);
                return err;
        }
 
+       spin_lock(&host->lock);
        ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 
        /*
@@ -2382,6 +2387,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
                sdhci_dumpregs(host);
 
                return;
+       } else {
+               host->ops->platform_release_bus(host);
        }
 
        if (intmask & SDHCI_INT_DATA_TIMEOUT) {
index 682ddb0..583d6c7 100644 (file)
@@ -280,6 +280,8 @@ struct sdhci_ops {
 
        void    (*platform_suspend)(struct sdhci_host *host);
        void    (*platform_resume)(struct sdhci_host *host);
+       void    (*platform_get_bus)(struct sdhci_host *host);
+       void    (*platform_release_bus)(struct sdhci_host *host);
        int     (*switch_signal_voltage)(struct sdhci_host *host,
                                unsigned int signal_voltage);
        void    (*switch_signal_voltage_exit)(struct sdhci_host *host);