summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPavan Kunapuli <pkunapuli@nvidia.com>2014-01-09 17:16:13 +0530
committerHarry Hong <hhong@nvidia.com>2014-01-27 22:25:27 -0800
commit4a084ac0fb7e801a1ce24d099edf8eef3d79f5ad (patch)
tree9cd1f8a7864c09157afbd4866b9375612ccc5ddd /drivers
parent7155355d224be8661ff3bc0604dbfa1a20fffc51 (diff)
mmc: tegra: Implementation of new tuning algorithm
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 1423423 Reviewed-on: http://git-master/r/352306 (cherry picked from commit 26615d96514fc9ccbdcdc9ab89d92def5c1bf977) Change-Id: I56fd58473ab5b7642d703fc17f0afd5820fe64ea Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com> Reviewed-on: http://git-master/r/356523 Reviewed-by: Harry Hong <hhong@nvidia.com> Tested-by: Harry Hong <hhong@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c1382
1 files changed, 915 insertions, 467 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index bcca95d2b9cd..4878ddc7d6fa 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -60,19 +60,20 @@
#define SDHCI_VNDR_CLK_CTRL_SPI_MODE_CLKEN_OVERRIDE 0x4
#define SDHCI_VNDR_CLK_CNTL_INPUT_IO_CLK 0x2
#define SDHCI_VNDR_CLK_CTRL_BASE_CLK_FREQ_SHIFT 8
-#define SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT 16
-#define SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_SHIFT 24
+#define SDHCI_VNDR_CLK_CTRL_TAP_VALUE_SHIFT 16
+#define SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_SHIFT 24
#define SDHCI_VNDR_CLK_CTRL_SDR50_TUNING 0x20
-#define SDHCI_VNDR_MISC_CTRL 0x120
+#define SDHCI_VNDR_MISC_CTRL 0x120
#define SDHCI_VNDR_MISC_CTRL_ENABLE_SDR104_SUPPORT 0x8
#define SDHCI_VNDR_MISC_CTRL_ENABLE_SDR50_SUPPORT 0x10
#define SDHCI_VNDR_MISC_CTRL_ENABLE_DDR50_SUPPORT 0x200
-#define SDHCI_VNDR_MISC_CTRL_ENABLE_SD_3_0 0x20
+#define SDHCI_VNDR_MISC_CTRL_ENABLE_SD_3_0 0x20
#define SDHCI_VNDR_MISC_CTRL_INFINITE_ERASE_TIMEOUT 0x1
+#define SDHCI_VNDR_MISC_CTRL_EN_EXT_LOOPBACK_SHIFT 17
-#define SDMMC_SDMEMCOMPPADCTRL 0x1E0
-#define SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK 0xF
+#define SDMMC_SDMEMCOMPPADCTRL 0x1E0
+#define SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK 0xF
#define SDMMC_AUTO_CAL_CONFIG 0x1E4
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_START 0x80000000
@@ -124,6 +125,8 @@
#define NVQUIRK_EN_FEEDBACK_CLK BIT(17)
/* Shadow write xfer mode reg and write it alongwith CMD register */
#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(18)
+/* Disable SDMMC3 external loopback */
+#define NVQUIRK_DISABLE_EXTERNAL_LOOPBACK BIT(19)
/* Common subset of quirks for Tegra3 and later sdmmc controllers */
#define TEGRA_SDHCI_NVQUIRKS (NVQUIRK_ENABLE_PADPIPE_CLKEN | \
@@ -196,6 +199,10 @@ struct sdhci_tegra_soc_data {
u32 nvquirks;
const char *parent_clk_list[2];
unsigned int tuning_freq_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;
};
@@ -205,18 +212,99 @@ 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 t11x_tuning_coeffs[] = {
+ SET_TUNING_COEFFS("sdhci-tegra.3", 1250, 950, 55, 135434,
+ 73, 170493, 243, 455948),
+ SET_TUNING_COEFFS("sdhci-tegra.2", 1250, 950, 50, 129738,
+ 73, 168898, 241, 453050),
+ SET_TUNING_COEFFS("sdhci-tegra.0", 1250, 950, 62, 143469,
+ 82, 180096, 238, 444285),
+};
+
+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 t11x_tap_hole_coeffs[] = {
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.3", 200000, 765, 102357, 507,
+ 81144, 131, 36346),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.3", 156000, 1042, 142044, 776,
+ 121659, 152, 48728),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.3", 136000, 1215, 167702, 905,
+ 143825, 207, 63477),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.3", 81600, 1925, 284516, 1528,
+ 253188, 366, 120001),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.2", 204000, 472, 53312, 318,
+ 41756, 84, 15496),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.2", 156000, 765, 95512, 526,
+ 77404, 134, 33032),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.2", 136000, 949, 121887, 656,
+ 99684, 165, 43992),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.2", 81600, 1901, 259035, 1334,
+ 215539, 326, 100986),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.0", 204000, 411, 54495, 305,
+ 46415, 91, 20366),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.0", 156000, 715, 97623, 516,
+ 82375, 145, 38278),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.0", 136000, 905, 124579, 648,
+ 104850, 179, 50204),
+ SET_TAP_HOLE_COEFFS("sdhci-tegra.0", 81600, 1893, 264746, 1333,
+ 221722, 354, 109880),
+};
struct freq_tuning_constraints {
unsigned int vcore_mask;
@@ -227,35 +315,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
@@ -339,7 +448,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;
@@ -351,6 +460,7 @@ struct sdhci_tegra {
bool is_parent_pllc;
struct tegra_freq_gov_data *gov_data;
bool power_off_rail;
+ int speedo;
};
static struct clk *pll_c;
@@ -796,7 +906,7 @@ static int tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
{
- u16 misc_ctrl;
+ u32 misc_ctrl;
u32 vendor_ctrl;
unsigned int best_tap_value;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -858,7 +968,7 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
vendor_ctrl |= SDHCI_VNDR_CLK_CTRL_SDR50_TUNING;
sdhci_writel(sdhci, vendor_ctrl, SDHCI_VNDR_CLK_CTRL);
- misc_ctrl = sdhci_readw(sdhci, SDHCI_VNDR_MISC_CTRL);
+ misc_ctrl = sdhci_readl(sdhci, SDHCI_VNDR_MISC_CTRL);
if (soc_data->nvquirks & NVQUIRK_ENABLE_SD_3_0)
misc_ctrl |= SDHCI_VNDR_MISC_CTRL_ENABLE_SD_3_0;
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) {
@@ -880,7 +990,12 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *sdhci, u8 mask)
else if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
misc_ctrl |= SDHCI_VNDR_MISC_CTRL_ENABLE_DDR50_SUPPORT;
- sdhci_writew(sdhci, misc_ctrl, SDHCI_VNDR_MISC_CTRL);
+ /* External loopback is valid for sdmmc3 only */
+ if ((soc_data->nvquirks & NVQUIRK_DISABLE_EXTERNAL_LOOPBACK) &&
+ (tegra_host->instance == 2))
+ misc_ctrl &= ~(1 <<
+ SDHCI_VNDR_MISC_CTRL_EN_EXT_LOOPBACK_SHIFT);
+ sdhci_writel(sdhci, misc_ctrl, SDHCI_VNDR_MISC_CTRL);
/* Mask Auto CMD23 if CMD23 is enabled */
if ((sdhci->mmc->caps & MMC_CAP_CMD23) &&
@@ -1392,7 +1507,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);
@@ -1448,235 +1569,269 @@ 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 tegra_tuning_data *tuning_data,
+ int tap_value, enum tap_win_edge_attr edge_attr, int tap_hole)
+{
+ 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) {
+ tap_value += ((7 * tap_hole) / 100) + 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");
+
+ 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 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;
-
- if (tap_data->abandon_full_win) {
- if (tap_data->abandon_partial_win) {
- return 0;
- } else {
- select_partial_win = true;
- goto calculate_best_tap;
- }
+ 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;
+ tap_value -= ((7 * tap_hole) / 100) + 2;
}
- 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];
- 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 * tuning_ui) / 100)))
- select_partial_win = true;
-
-calculate_best_tap:
- 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 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(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(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)
+{
+ 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 this vmin\n");
+ 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)) {
- 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;
+ struct tap_window_data *temp_tap_data = NULL;
+ int vmin, curr_vmin, best_tap_value = 0;
+ int err = 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");
+ curr_vmin = tegra_dvfs_predict_millivolts(pltfm_host->clk,
+ tuning_data->freq_hz);
+ vmin = curr_vmin;
- 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 {
- tuning_data->best_tap_value =
- calculate_high_freq_tap_value(sdhci,
- tuning_data->tap_data[1],
- tuning_data->tap_data[2]);
+ 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;
}
- } 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(tuning_data, temp_tap_data);
+
+ best_tap_value = find_best_tap_value(tuning_data,
+ temp_tap_data);
+
+ 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)
@@ -1803,95 +1958,359 @@ 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;
+
+ /* T2T_Vmax = (1000000/freq_MHz)/Calc_UI */
+ calc_values->t2t_vmax = (1000000 / (tuning_data->freq_hz / 1000000)) /
+ calc_values->ui;
- if (!tap_data) {
- dev_err(mmc_dev(sdhci->mmc), "Invalid tap data\n");
- return -ENODATA;
+ /*
+ * 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 < 0) {
- err = -EIO;
- goto out;
- } else 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 < 0) {
- err = -EIO;
- goto out;
- } else 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");
+ 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;
}
@@ -1900,8 +2319,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);
@@ -1910,12 +2328,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);
- }
}
}
@@ -1937,40 +2349,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);
@@ -1990,19 +2368,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;
+ 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,
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))
@@ -2017,21 +2471,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->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--) {
@@ -2039,26 +2491,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->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)
{
@@ -2067,17 +2519,22 @@ static int sdhci_tegra_get_tuning_constraints(struct sdhci_host *sdhci)
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;
@@ -2138,63 +2595,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)
-{
- 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)
+static int sdhci_tegra_run_tuning(struct sdhci_host *sdhci,
+ struct tegra_tuning_data *tuning_data)
{
- 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);
@@ -2210,12 +2663,10 @@ 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;
u8 i, set_retuning = 0;
- bool is_retuning_req = false;
bool force_retuning = false;
/* Tuning is valid only in SDR104 and SDR50 modes */
@@ -2235,9 +2686,6 @@ static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
pr_err("%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 */
tegra_host->tuning_opcode = opcode;
@@ -2258,15 +2706,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) {
@@ -2277,7 +2724,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;
}
@@ -2290,15 +2738,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;
@@ -2306,29 +2763,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);
@@ -2431,11 +2868,15 @@ 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. Check if tuning mutex
- * is locked before starting a data transfer.
+ * 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)
{
- unsigned int timeout = 100;
+ unsigned int timeout = 300;
while (mutex_is_locked(&tuning_mutex)) {
msleep(10);
@@ -2769,9 +3210,14 @@ static struct sdhci_tegra_soc_data soc_data_tegra11 = {
NVQUIRK_SET_TRIM_DELAY |
NVQUIRK_ENABLE_DDR50 |
NVQUIRK_ENABLE_HS200 |
- NVQUIRK_INFINITE_ERASE_TIMEOUT,
+ NVQUIRK_INFINITE_ERASE_TIMEOUT |
+ NVQUIRK_DISABLE_EXTERNAL_LOOPBACK,
.parent_clk_list = {"pll_p", "pll_c"},
.tuning_freq_list = {81600000, 156000000, 200000000},
+ .t2t_coeffs = t11x_tuning_coeffs,
+ .t2t_coeffs_count = 3,
+ .tap_hole_coeffs = t11x_tap_hole_coeffs,
+ .tap_hole_coeffs_count = 12,
};
static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
@@ -3043,6 +3489,8 @@ 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;
+ 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;