summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c150
-rw-r--r--drivers/mmc/host/sdhci.c4
-rw-r--r--drivers/mmc/host/sdhci.h4
3 files changed, 134 insertions, 24 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index f6c216ac44ab..7c5a94740dd1 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -29,6 +29,7 @@
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/mmc/sd.h>
+#include <linux/mmc/mmc.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
@@ -94,10 +95,12 @@
#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_8 128
#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_4 64
#define MAX_TAP_VALUES 255
-#define TUNING_FREQ_COUNT 2
+#define TUNING_FREQ_COUNT 3
#define TUNING_VOLTAGES_COUNT 2
#define TUNING_RETRIES 1
+struct sdhci_host *sdhci_host_for_sdio;
+
static unsigned int uhs_max_freq_MHz[] = {
[MMC_TIMING_UHS_SDR50] = 100,
[MMC_TIMING_UHS_SDR104] = 208,
@@ -179,6 +182,7 @@ struct sdhci_tegra_sd_stats {
enum tegra_tuning_freq {
TUNING_LOW_FREQ,
TUNING_HIGH_FREQ,
+ TUNING_HIGH_FREQ_HV,
};
struct freq_tuning_params {
@@ -196,7 +200,12 @@ static struct freq_tuning_params tuning_params[TUNING_FREQ_COUNT] = {
[TUNING_HIGH_FREQ] = {
.freq_hz = 156000000,
.nr_voltages = 2,
- .voltages = {ULONG_MAX, 1100},
+ .voltages = {1250, 1100},
+ },
+ [TUNING_HIGH_FREQ_HV] = {
+ .freq_hz = 156000000,
+ .nr_voltages = 2,
+ .voltages = {1390, 1250},
},
};
@@ -212,6 +221,7 @@ struct tap_window_data {
struct tegra_tuning_data {
unsigned int best_tap_value;
+ unsigned int best_hv_tap_value;
bool select_partial_win;
bool nominal_vcore_tuning_done;
bool overide_vcore_tuning_done;
@@ -255,6 +265,8 @@ struct sdhci_tegra {
unsigned int tuning_bsize;
/* Tuning status */
unsigned int tuning_status;
+ unsigned int tuning_turbo_enable;
+ bool is_hv_tuning_done;
#define TUNING_STATUS_DONE 1
#define TUNING_STATUS_RETUNE 2
/* Freq tuning information for each sampling clock freq */
@@ -1097,7 +1109,8 @@ static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci, u8 freq_band)
struct sdhci_tegra *tegra_host = pltfm_host->priv;
if (tegra_host->tuning_data.tap_data[0]) {
- dev_info(mmc_dev(sdhci->mmc), "Tuning window data at 1.25V\n");
+ dev_info(mmc_dev(sdhci->mmc), "Tuning window data at %d mV\n",
+ tuning_params[freq_band].voltages[0]);
pr_info("Partial window %d\n",
tegra_host->tuning_data.tap_data[0]->partial_win);
pr_info("full window start %d\n",
@@ -1106,9 +1119,11 @@ static void sdhci_tegra_dump_tuning_data(struct sdhci_host *sdhci, u8 freq_band)
tegra_host->tuning_data.tap_data[0]->full_win_end);
}
- if ((freq_band == TUNING_HIGH_FREQ) &&
+ if (((freq_band == TUNING_HIGH_FREQ) ||
+ (freq_band == TUNING_HIGH_FREQ_HV)) &&
(tegra_host->tuning_data.tap_data[1])) {
- dev_info(mmc_dev(sdhci->mmc), "Tuning window data at 1.1V\n");
+ dev_info(mmc_dev(sdhci->mmc), "Tuning window data at %d mV\n",
+ tuning_params[freq_band].voltages[1]);
pr_info("partial window %d\n",
tegra_host->tuning_data.tap_data[1]->partial_win);
pr_info("full window being %d\n",
@@ -1215,12 +1230,14 @@ calculate_best_tap:
* If there is no margin window for both cases,
* best tap=(Y+Z')/2.
*/
-static void calculate_high_freq_tap_value(struct sdhci_host *sdhci)
+static void calculate_high_freq_tap_value(struct sdhci_host *sdhci,
+ bool hv_tuning_call)
{
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;
@@ -1242,9 +1259,13 @@ static void calculate_high_freq_tap_value(struct sdhci_host *sdhci)
tuning_data->one_shot_tuning) {
dev_info(mmc_dev(sdhci->mmc),
"Setting best tap as 50 percent of the full window\n");
- tuning_data->best_tap_value = (vmax_tap_data->full_win_begin +
+ best_tap = (vmax_tap_data->full_win_begin +
(((vmax_tap_data->full_win_end -
vmax_tap_data->full_win_begin) * 5) / 10));
+ if (hv_tuning_call)
+ tuning_data->best_hv_tap_value = best_tap;
+ else
+ tuning_data->best_tap_value = best_tap;
return;
}
@@ -1293,18 +1314,22 @@ static void calculate_high_freq_tap_value(struct sdhci_host *sdhci)
if ((full_win_quality <= 0) && (partial_win_quality)) {
dev_warn(mmc_dev(sdhci->mmc),
"No margin window for both windows\n");
- tuning_data->best_tap_value = vmax_tap_data->full_win_begin +
+ best_tap = vmax_tap_data->full_win_begin +
vmid_tap_data->full_win_end;
- tuning_data->best_tap_value >>= 1;
+ best_tap >>= 1;
} else {
if (full_win_quality > partial_win_quality) {
- tuning_data->best_tap_value = full_win_tap;
+ best_tap = full_win_tap;
tuning_data->select_partial_win = false;
} else {
- tuning_data->best_tap_value = partial_win_tap;
+ best_tap = partial_win_tap;
tuning_data->select_partial_win = true;
}
}
+ if (hv_tuning_call)
+ tuning_data->best_hv_tap_value = best_tap;
+ else
+ tuning_data->best_tap_value = best_tap;
}
static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
@@ -1510,7 +1535,8 @@ out:
return err;
}
-static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
+static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode,
+ bool hv_tuning_call)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
@@ -1555,21 +1581,27 @@ 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)
- freq_band = TUNING_HIGH_FREQ;
- else
+ if (sdhci->max_clk > tuning_params[TUNING_LOW_FREQ].freq_hz) {
+ if (hv_tuning_call)
+ freq_band = TUNING_HIGH_FREQ_HV;
+ else
+ freq_band = TUNING_HIGH_FREQ;
+ } else {
freq_band = TUNING_LOW_FREQ;
+ }
tuning_data = &tegra_host->tuning_data;
/*
* If tuning is already done and retune request is not set, then skip
* best tap value calculation and use the old best tap value.
*/
- if (tegra_host->tuning_status == TUNING_STATUS_DONE) {
- dev_info(mmc_dev(sdhci->mmc),
+ if (!hv_tuning_call) {
+ if (tegra_host->tuning_status == TUNING_STATUS_DONE) {
+ dev_info(mmc_dev(sdhci->mmc),
"Tuning already done. Setting tuned tap value %d\n",
tegra_host->tuning_data.best_tap_value);
- goto set_best_tap;
+ goto set_best_tap;
+ }
}
/* Remove any previously set override voltages */
@@ -1691,15 +1723,20 @@ skip_vcore_override:
if (freq_band == TUNING_LOW_FREQ)
calculate_low_freq_tap_value(sdhci);
else
- calculate_high_freq_tap_value(sdhci);
+ calculate_high_freq_tap_value(sdhci, hv_tuning_call);
set_best_tap:
/* Dump the tap window data */
sdhci_tegra_dump_tuning_data(sdhci, freq_band);
- sdhci_tegra_set_tap_delay(sdhci,
- tegra_host->tuning_data.best_tap_value);
+ if (hv_tuning_call) {
+ sdhci_tegra_set_tap_delay(sdhci,
+ tegra_host->tuning_data.best_hv_tap_value);
+ } else {
+ sdhci_tegra_set_tap_delay(sdhci,
+ tegra_host->tuning_data.best_tap_value);
+ }
/*
* Run tuning with the best tap value. If tuning fails, set the status
@@ -1711,6 +1748,10 @@ set_best_tap:
tuning_data->overide_vcore_tuning_done = false;
tegra_host->tuning_status = TUNING_STATUS_RETUNE;
} else {
+ if (hv_tuning_call)
+ tegra_host->is_hv_tuning_done = true;
+ else
+ tegra_host->is_hv_tuning_done = false;
if (tuning_data->nominal_vcore_tuning_done &&
tuning_data->overide_vcore_tuning_done)
tegra_host->tuning_status = TUNING_STATUS_DONE;
@@ -2008,6 +2049,68 @@ static int tegra_sdhci_reboot_notify(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static ssize_t sdhci_handle_boost_mode_tap(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int tap_enable;
+ u32 present_state;
+ char *p = (char *)buf;
+ /* struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
+ * struct sdhci_host *host = mmc_priv(mmc);
+ * struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ * struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ */
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci_host_for_sdio);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ tap_enable = memparse(p, &p);
+
+ present_state = sdhci_readl(sdhci_host_for_sdio, SDHCI_PRESENT_STATE);
+ while ((present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
+ present_state = sdhci_readl(sdhci_host_for_sdio,
+ SDHCI_PRESENT_STATE);
+ }
+
+ if (tap_enable == 1) {
+ /* set tap value which is calculated for 1.25 to 1.39 volts*/
+ tegra_host->tuning_turbo_enable = 1;
+ dev_err(mmc_dev(sdhci_host_for_sdio->mmc),
+ "Setting tap value for Voltage turbo mode\n");
+ if (tegra_host->is_hv_tuning_done) {
+ sdhci_tegra_set_tap_delay(sdhci_host_for_sdio,
+ tegra_host->tuning_data.best_hv_tap_value);
+ } else {
+ disable_irq(sdhci_host_for_sdio->irq);
+ spin_lock(&sdhci_host_for_sdio->lock);
+ sdhci_tegra_execute_tuning(sdhci_host_for_sdio,
+ MMC_SEND_TUNING_BLOCK, true);
+ spin_unlock(&sdhci_host_for_sdio->lock);
+ enable_irq(sdhci_host_for_sdio->irq);
+ }
+ } else if (tap_enable == 0) {
+ /* set tap value calculated for voltage range 1.1 to 1.25 */
+ tegra_host->tuning_turbo_enable = 0;
+ dev_err(mmc_dev(sdhci_host_for_sdio->mmc),
+ "Setting tap value for voltage non-turbo mode\n");
+ sdhci_tegra_set_tap_delay(sdhci_host_for_sdio,
+ tegra_host->tuning_data.best_tap_value);
+ } else {
+ dev_err(mmc_dev(sdhci_host_for_sdio->mmc),
+ "Wrong value 1: enable turbo mode 0: disable turbo mode\n");
+ }
+ return count;
+}
+
+static ssize_t sdhci_show_turbo_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci_host_for_sdio);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ return sprintf(buf, "%d\n", tegra_host->tuning_turbo_enable);
+}
+
+static DEVICE_ATTR(handle_turbo_mode, 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;
@@ -2273,6 +2376,11 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
rc = sdhci_add_host(host);
+ if (tegra_host->instance == 0) {
+ device_create_file(&pdev->dev, &dev_attr_handle_turbo_mode);
+ sdhci_host_for_sdio = host;
+ }
+
sdhci_tegra_error_stats_debugfs(host);
if (rc)
goto err_add_host;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fc5387d70f9b..7bea3afd7638 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2,6 +2,7 @@
* linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
*
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ * Copyright (c) 2013 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1021,7 +1022,6 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
-
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
@@ -1746,7 +1746,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if ((host->quirks & SDHCI_QUIRK_NON_STANDARD_TUNING) &&
host->ops->execute_freq_tuning) {
- err = host->ops->execute_freq_tuning(host, opcode);
+ err = host->ops->execute_freq_tuning(host, opcode, false);
spin_unlock(&host->lock);
enable_irq(host->irq);
sdhci_runtime_pm_put(host);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 17a855eb585f..dec8533f6122 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -4,6 +4,7 @@
* Header file for Host Controller registers and I/O accessors.
*
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ * Copyright (c) 2013 NVIDIA Corporation, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -284,7 +285,8 @@ struct sdhci_ops {
void (*switch_signal_voltage_exit)(struct sdhci_host *host);
int (*suspend)(struct sdhci_host *host);
int (*resume)(struct sdhci_host *host);
- int (*execute_freq_tuning)(struct sdhci_host *sdhci, u32 opcode);
+ int (*execute_freq_tuning)(struct sdhci_host *sdhci,
+ u32 opcode, bool hr_tuning_call);
int (*get_tuning_counter)(struct sdhci_host *sdhci);
int (*sd_error_stats)(struct sdhci_host *host, u32 int_status);
};