#include <linux/mmc/sd.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
-
+#include <linux/pm_runtime.h>
#include <asm/gpio.h>
#include <mach/gpio-tegra.h>
#include <mach/sdhci.h>
#include <mach/io_dpd.h>
+#include <mach/pinmux.h>
#include "sdhci-pltfm.h"
#define SDHCI_VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE 0x4
#define SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT 8
#define SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT 16
+#define SDHCI_VENDOR_CLOCK_CNTRL_TRIM_VALUE_SHIFT 24
#define SDHCI_VENDOR_CLOCK_CNTRL_SDR50_TUNING 0x20
#define SDHCI_VENDOR_MISC_CNTRL 0x120
#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT 0x8
#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT 0x10
+#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_DDR50_SUPPORT 0x200
#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SD_3_0 0x20
+#define SDHCI_VENDOR_MISC_CNTRL_INFINITE_ERASE_TIMEOUT 0x1
#define SDMMC_SDMEMCOMPPADCTRL 0x1E0
#define SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK 0xF
#define SDMMC_AUTO_CAL_CONFIG 0x1E4
+#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_START 0x80000000
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE 0x20000000
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT 0x8
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET 0x70
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET 0x62
+#define SDMMC_AUTO_CAL_STATUS 0x1EC
+#define SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE 0x80000000
+#define SDMMC_AUTO_CAL_STATUS_PULLDOWN_OFFSET 24
+#define PULLUP_ADJUSTMENT_OFFSET 20
+
#define SDHOST_1V8_OCR_MASK 0x8
#define SDHOST_HIGH_VOLT_MIN 2700000
#define SDHOST_HIGH_VOLT_MAX 3600000
+#define SDHOST_HIGH_VOLT_2V8 2800000
#define SDHOST_LOW_VOLT_MIN 1800000
#define SDHOST_LOW_VOLT_MAX 1800000
#define TEGRA2_SDHOST_STD_FREQ 50000000
#define TEGRA3_SDHOST_STD_FREQ 104000000
-#define SD_SEND_TUNING_PATTERN 19
+#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_8 128
+#define MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_4 64
#define MAX_TAP_VALUES 256
-static unsigned int tegra_sdhost_min_freq;
-static unsigned int tegra_sdhost_std_freq;
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock);
static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci);
+#endif
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static void tegra11x_sdhci_post_reset_init(struct sdhci_host *sdhci);
+#endif
+
+static unsigned int tegra_sdhost_min_freq;
+static unsigned int tegra_sdhost_std_freq;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
static unsigned int tegra3_sdhost_max_clk[4] = {
208000000, 104000000, 208000000, 104000000 };
+#endif
struct tegra_sdhci_hw_ops{
/* Set the internal clk and card clk.*/
void (*sdhost_init)(struct sdhci_host *sdhci);
};
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
static struct tegra_sdhci_hw_ops tegra_2x_sdhci_ops = {
};
-#endif
-
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
static struct tegra_sdhci_hw_ops tegra_3x_sdhci_ops = {
.set_card_clock = tegra_3x_sdhci_set_card_clock,
.sdhost_init = tegra3_sdhci_post_reset_init,
};
+#else
+static struct tegra_sdhci_hw_ops tegra_11x_sdhci_ops = {
+ .sdhost_init = tegra11x_sdhci_post_reset_init,
+};
#endif
-#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
-#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
+#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
+#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
+#define NVQUIRK_DISABLE_AUTO_CALIBRATION BIT(2)
+#define NVQUIRK_SET_CALIBRATION_OFFSETS BIT(3)
+#define NVQUIRK_SET_DRIVE_STRENGTH BIT(4)
struct sdhci_tegra_soc_data {
struct sdhci_pltfm_data *pdata;
};
struct sdhci_tegra {
- struct tegra_sdhci_platform_data *pdata;
+ const struct tegra_sdhci_platform_data *plat;
const struct sdhci_tegra_soc_data *soc_data;
bool clk_enabled;
struct regulator *vdd_io_reg;
unsigned int vddio_max_uv;
/* max clk supported by the platform */
unsigned int max_clk_limit;
+ /* max ddr clk supported by the platform */
+ unsigned int ddr_clk_limit;
struct tegra_io_dpd *dpd;
bool card_present;
bool is_rail_enabled;
+ struct clk *emc_clk;
+ unsigned int emc_max_clk;
};
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
{
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
u32 val;
if (unlikely(reg == SDHCI_PRESENT_STATE)) {
val = readl(host->ioaddr + reg);
return val | SDHCI_WRITE_PROTECT;
}
-
+#endif
return readl(host->ioaddr + reg);
}
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+#endif
/* Seems like we're getting spurious timeout and crc errors, so
* disable signalling of them. In case of real errors software
return tegra_host->card_present;
}
-static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
+static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
return gpio_get_value(plat->wp_gpio);
}
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci)
{
u16 misc_ctrl;
u32 vendor_ctrl;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
- struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
- struct tegra_sdhci_platform_data *plat;
+ const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
- plat = pdev->dev.platform_data;
/* Set the base clock frequency */
vendor_ctrl = sdhci_readl(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
vendor_ctrl &= ~(0xFF << SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT);
SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT;
sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL);
}
+#endif
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static void tegra11x_sdhci_post_reset_init(struct sdhci_host *sdhci)
+{
+ u16 misc_ctrl;
+ u32 vendor_ctrl;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
+ struct tegra_sdhci_platform_data *plat;
+
+ plat = pdev->dev.platform_data;
+ /* Set the base clock frequency */
+ vendor_ctrl = sdhci_readl(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
+ vendor_ctrl &= ~(0xFF << SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT);
+ vendor_ctrl |= (tegra3_sdhost_max_clk[tegra_host->instance] / 1000000) <<
+ SDHCI_VENDOR_CLOCK_CNTRL_BASE_CLK_FREQ_SHIFT;
+ vendor_ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_PADPIPE_CLKEN_OVERRIDE;
+ vendor_ctrl &= ~SDHCI_VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE;
+
+ /* Set tap delay */
+ if (plat->tap_delay) {
+ vendor_ctrl &= ~(0xFF <<
+ SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
+ vendor_ctrl |= (plat->tap_delay <<
+ SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT);
+ }
+
+ /* Set trim delay */
+ if (plat->trim_delay) {
+ vendor_ctrl &= ~(0x1F <<
+ SDHCI_VENDOR_CLOCK_CNTRL_TRIM_VALUE_SHIFT);
+ vendor_ctrl |= (plat->trim_delay <<
+ SDHCI_VENDOR_CLOCK_CNTRL_TRIM_VALUE_SHIFT);
+ }
+ sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
+
+ /* Enable SDHOST v3.0 support */
+ misc_ctrl = sdhci_readw(sdhci, SDHCI_VENDOR_MISC_CNTRL);
+ misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT |
+ SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT |
+ SDHCI_VENDOR_MISC_CNTRL_ENABLE_DDR50_SUPPORT;
+ misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_INFINITE_ERASE_TIMEOUT;
+ sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL);
+}
+#endif
static int tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
unsigned int uhs)
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
+ /* For HS200 we need to set UHS_MODE_SEL to SDR104.
+ * It works as SDR 104 in SD 4-bit mode and HS200 in eMMC 8-bit mode.
+ */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
switch (uhs) {
case MMC_TIMING_UHS_SDR12:
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
break;
case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
break;
case MMC_TIMING_UHS_DDR50:
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
if (mask & SDHCI_RESET_ALL) {
if (tegra_host->hw_ops->sdhost_init)
tegra_host->hw_ops->sdhost_init(sdhci);
+
+ /* Mask the support for any UHS modes if specified */
+ if (plat->uhs_mask & MMC_UHS_MASK_SDR104)
+ sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR104;
+
+ if (plat->uhs_mask & MMC_UHS_MASK_DDR50)
+ sdhci->mmc->caps &= ~MMC_CAP_UHS_DDR50;
+
+ if (plat->uhs_mask & MMC_UHS_MASK_SDR50)
+ sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR50;
+
+ if (plat->uhs_mask & MMC_UHS_MASK_SDR25)
+ sdhci->mmc->caps &= ~MMC_CAP_UHS_SDR25;
}
}
return IRQ_HANDLED;
};
-static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
+static int tegra_sdhci_8bit(struct sdhci_host *sdhci, int bus_width)
{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_tegra *tegra_host = pltfm_host->priv;
- const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
+ struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
+ const struct tegra_sdhci_platform_data *plat;
u32 ctrl;
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ plat = pdev->dev.platform_data;
+
+ ctrl = sdhci_readb(sdhci, SDHCI_HOST_CONTROL);
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS;
ctrl |= SDHCI_CTRL_8BITBUS;
else
ctrl &= ~SDHCI_CTRL_4BITBUS;
}
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ sdhci_writeb(sdhci, ctrl, SDHCI_HOST_CONTROL);
return 0;
}
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
unsigned int clk_rate;
+ unsigned int emc_clk;
- if (sdhci->mmc->card &&
- mmc_card_ddr_mode(sdhci->mmc->card)) {
+ if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
/*
* In ddr mode, tegra sdmmc controller clock frequency
* should be double the card clock frequency.
*/
- clk_rate = clock * 2;
+ if (tegra_host->ddr_clk_limit) {
+ clk_rate = tegra_host->ddr_clk_limit * 2;
+ if (tegra_host->emc_clk) {
+ emc_clk = clk_get_rate(tegra_host->emc_clk);
+ if (emc_clk == tegra_host->emc_max_clk)
+ clk_rate = clock * 2;
+ }
+ } else {
+ clk_rate = clock * 2;
+ }
+ } else if (sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50) {
+ /*
+ * In SDR50 mode, run the sdmmc controller at freq greater than
+ * 104MHz to ensure the core voltage is at 1.2V. If the core voltage
+ * is below 1.2V, CRC errors would occur during data transfers.
+ */
+ clk_rate = clock * 2;
} else {
if (clock <= tegra_sdhost_min_freq)
clk_rate = tegra_sdhost_min_freq;
clk_rate = tegra_sdhost_std_freq;
else
clk_rate = clock;
-
- /*
- * In SDR50 mode, run the sdmmc controller at 208MHz to ensure
- * the core voltage is at 1.2V. If the core voltage is below 1.2V, CRC
- * errors would occur during data transfers.
- */
- if ((sdhci->mmc->ios.timing == MMC_TIMING_UHS_SDR50) &&
- (clk_rate == tegra_sdhost_std_freq))
- clk_rate <<= 1;
}
if (tegra_host->max_clk_limit &&
clk_set_rate(pltfm_host->clk, clk_rate);
sdhci->max_clk = clk_get_rate(pltfm_host->clk);
+#ifdef CONFIG_TEGRA_FPGA_PLATFORM
+ /* FPGA supports 26MHz of clock for SDMMC. */
+ sdhci->max_clk = 26000000;
+#endif
}
-
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static void tegra_3x_sdhci_set_card_clock(struct sdhci_host *sdhci, unsigned int clock)
{
int div;
if (clock && clock == sdhci->clock)
return;
+ /*
+ * Disable the card clock before disabling the internal
+ * clock to avoid abnormal clock waveforms.
+ */
+ clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
sdhci_writew(sdhci, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0)
out:
sdhci->clock = clock;
}
+#endif /* #ifdef CONFIG_ARCH_TEGRA_3x_SOC */
static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
u8 ctrl;
pr_debug("%s %s %u enabled=%u\n", __func__,
if (clock) {
/* bring out sd instance from io dpd mode */
- tegra_io_dpd_disable(tegra_host->dpd);
+ if (tegra_host->dpd) {
+ mutex_lock(&tegra_host->dpd->delay_lock);
+ cancel_delayed_work_sync(&tegra_host->dpd->delay_dpd);
+ tegra_io_dpd_disable(tegra_host->dpd);
+ mutex_unlock(&tegra_host->dpd->delay_lock);
+ }
if (!tegra_host->clk_enabled) {
- clk_enable(pltfm_host->clk);
+ pm_runtime_get_sync(&pdev->dev);
+ clk_prepare_enable(pltfm_host->clk);
ctrl = sdhci_readb(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
ctrl |= SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK;
sdhci_writeb(sdhci, ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
ctrl = sdhci_readb(sdhci, SDHCI_VENDOR_CLOCK_CNTRL);
ctrl &= ~SDHCI_VENDOR_CLOCK_CNTRL_SDMMC_CLK;
sdhci_writeb(sdhci, ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
- clk_disable(pltfm_host->clk);
+ clk_disable_unprepare(pltfm_host->clk);
+ pm_runtime_put_sync(&pdev->dev);
tegra_host->clk_enabled = false;
/* io dpd enable call for sd instance */
- tegra_io_dpd_enable(tegra_host->dpd);
+
+ if (tegra_host->dpd) {
+ mutex_lock(&tegra_host->dpd->delay_lock);
+ if (tegra_host->dpd->need_delay_dpd) {
+ schedule_delayed_work(
+ &tegra_host->dpd->delay_dpd,
+ msecs_to_jiffies(100));
+ } else {
+ tegra_io_dpd_enable(tegra_host->dpd);
+ }
+ mutex_unlock(&tegra_host->dpd->delay_lock);
+ }
+ }
+}
+static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci)
+{
+ unsigned int val;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+ unsigned int timeout = 10;
+
+ /* No Calibration for sdmmc4 */
+ if (tegra_host->instance == 3)
+ return;
+
+ /*
+ * Do not enable auto calibration if the platform doesn't
+ * support it.
+ */
+ if (unlikely(soc_data->nvquirks & NVQUIRK_DISABLE_AUTO_CALIBRATION))
+ return;
+
+ val = sdhci_readl(sdhci, SDMMC_SDMEMCOMPPADCTRL);
+ val &= ~SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK;
+ val |= 0x7;
+ sdhci_writel(sdhci, val, SDMMC_SDMEMCOMPPADCTRL);
+
+ /* Enable Auto Calibration*/
+ val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
+ val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+ val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
+ if (unlikely(soc_data->nvquirks & NVQUIRK_SET_CALIBRATION_OFFSETS)) {
+ /* Program Auto cal PD offset(bits 8:14) */
+ val &= ~(0x7F <<
+ SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+ val |= (SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET <<
+ SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
+ /* Program Auto cal PU offset(bits 0:6) */
+ val &= ~0x7F;
+ val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET;
+ }
+ sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
+
+ /* Wait until the calibration is done */
+ do {
+ if (!(sdhci_readl(sdhci, SDMMC_AUTO_CAL_STATUS) &
+ SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE))
+ break;
+
+ mdelay(1);
+ timeout--;
+ } while (timeout);
+
+ if (!timeout)
+ dev_err(mmc_dev(sdhci->mmc), "Auto calibration failed\n");
+
+ /* Disable Auto calibration */
+ val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
+ val &= ~SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+ sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
+
+ if (unlikely(soc_data->nvquirks & NVQUIRK_SET_DRIVE_STRENGTH)) {
+ unsigned int pulldown_code;
+ unsigned int pullup_code;
+ int pg;
+ int err;
+
+ pg = tegra_drive_get_pingroup(mmc_dev(sdhci->mmc));
+ if (pg != -1) {
+ /* Get the pull down codes from auto cal status reg */
+ pulldown_code = (
+ sdhci_readl(sdhci, SDMMC_AUTO_CAL_STATUS) >>
+ SDMMC_AUTO_CAL_STATUS_PULLDOWN_OFFSET);
+ /* Set the pull down in the pinmux reg */
+ err = tegra_drive_pinmux_set_pull_down(pg,
+ pulldown_code);
+ if (err)
+ dev_err(mmc_dev(sdhci->mmc),
+ "Failed to set pulldown codes %d err %d\n",
+ pulldown_code, err);
+
+ /* Calculate the pull up codes */
+ pullup_code = pulldown_code + PULLUP_ADJUSTMENT_OFFSET;
+ if (pullup_code >= TEGRA_MAX_PULL)
+ pullup_code = TEGRA_MAX_PULL - 1;
+ /* Set the pull up code in the pinmux reg */
+ err = tegra_drive_pinmux_set_pull_up(pg, pullup_code);
+ if (err)
+ dev_err(mmc_dev(sdhci->mmc),
+ "Failed to set pullup codes %d err %d\n",
+ pullup_code, err);
+ }
}
}
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
- unsigned int min_uV = SDHOST_HIGH_VOLT_MIN;
- unsigned int max_uV = SDHOST_HIGH_VOLT_MAX;
+ unsigned int min_uV = tegra_host->vddio_min_uv;
+ unsigned int max_uV = tegra_host->vddio_max_uv;
unsigned int rc = 0;
u16 clk, ctrl;
- unsigned int val;
- /* Switch OFF the card clock to prevent glitches on the clock line */
- clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL);
- clk &= ~SDHCI_CLOCK_CARD_EN;
- sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
ctrl = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
if (ctrl & SDHCI_CTRL_VDD_180)
ctrl &= ~SDHCI_CTRL_VDD_180;
}
+
+ /* Check if the slot can support the required voltage */
+ if (min_uV > tegra_host->vddio_max_uv)
+ return 0;
+
+ /* Switch OFF the card clock to prevent glitches on the clock line */
+ clk = sdhci_readw(sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
+
+ /* Set/clear the 1.8V signalling */
sdhci_writew(sdhci, ctrl, SDHCI_HOST_CONTROL2);
/* Switch the I/O rail voltage */
if (rc) {
dev_err(mmc_dev(sdhci->mmc), "switching to 1.8V"
"failed . Switching back to 3.3V\n");
- regulator_set_voltage(tegra_host->vdd_io_reg,
+ rc = regulator_set_voltage(tegra_host->vdd_io_reg,
SDHOST_HIGH_VOLT_MIN,
SDHOST_HIGH_VOLT_MAX);
- goto out;
+ if (rc)
+ dev_err(mmc_dev(sdhci->mmc),
+ "switching to 3.3V also failed\n");
}
}
/* Wait for 1 msec after enabling clock */
mdelay(1);
- if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
- /* Do Auto Calibration for 1.8V signal voltage */
- val = sdhci_readl(sdhci, SDMMC_AUTO_CAL_CONFIG);
- val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
- /* Program Auto cal PD offset(bits 8:14) */
- val &= ~(0x7F <<
- SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
- val |= (SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET <<
- SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET_SHIFT);
- /* Program Auto cal PU offset(bits 0:6) */
- val &= ~0x7F;
- val |= SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET;
- sdhci_writel(sdhci, val, SDMMC_AUTO_CAL_CONFIG);
-
- val = sdhci_readl(sdhci, SDMMC_SDMEMCOMPPADCTRL);
- val &= ~SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_MASK;
- val |= 0x7;
- sdhci_writel(sdhci, val, SDMMC_SDMEMCOMPPADCTRL);
- }
-
- return rc;
-out:
- /* Enable the card clock */
- clk |= SDHCI_CLOCK_CARD_EN;
- sdhci_writew(sdhci, clk, SDHCI_CLOCK_CONTROL);
-
- /* Wait for 1 msec for the clock to stabilize */
- mdelay(1);
-
return rc;
}
sdhci_writel(sdhci, vendor_ctrl, SDHCI_VENDOR_CLOCK_CNTRL);
}
-static void sdhci_tegra_clear_set_irqs(struct sdhci_host *host,
- u32 clear, u32 set)
-{
- u32 ier;
-
- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
- ier &= ~clear;
- ier |= set;
- sdhci_writel(host, ier, SDHCI_INT_ENABLE);
- sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
-}
-
-static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci)
+static int sdhci_tegra_run_frequency_tuning(struct sdhci_host *sdhci, u32 opcode, u32 block_size)
{
int err = 0;
u8 ctrl;
- u32 ier;
u32 mask;
unsigned int timeout = 10;
int flags;
u32 intstatus;
- /*
- * As per the Host Controller spec v3.00, tuning command
- * generates Buffer Read Ready interrupt only, so enable that.
- */
- ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE);
- sdhci_tegra_clear_set_irqs(sdhci, ier, SDHCI_INT_DATA_AVAIL |
- SDHCI_INT_DATA_CRC);
-
mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
while (sdhci_readl(sdhci, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
* In response to CMD19, the card sends 64 bytes of tuning
* block to the Host Controller. So we set the block size
* to 64 here.
+ * In response to CMD21, the card sends 128 bytes of tuning
+ * block for MMC_BUS_WIDTH_8 and 64 bytes for MMC_BUS_WIDTH_4
+ * to the Host Controller. So we set the block size to 64 here.
*/
- sdhci_writew(sdhci, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+ sdhci_writew(sdhci, SDHCI_MAKE_BLKSZ(7, block_size), SDHCI_BLOCK_SIZE);
sdhci_writeb(sdhci, 0xE, SDHCI_TIMEOUT_CONTROL);
flags = SDHCI_CMD_RESP_SHORT | SDHCI_CMD_CRC | SDHCI_CMD_DATA;
/* Issue the command */
sdhci_writew(sdhci, SDHCI_MAKE_CMD(
- SD_SEND_TUNING_PATTERN, flags), SDHCI_COMMAND);
+ opcode, flags), SDHCI_COMMAND);
timeout = 5;
do {
}
mdelay(1);
out:
- sdhci_tegra_clear_set_irqs(sdhci, SDHCI_INT_DATA_AVAIL, ier);
return err;
}
-static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci)
+static int sdhci_tegra_execute_tuning(struct sdhci_host *sdhci, u32 opcode)
{
int err;
+ u32 block_size;
u16 ctrl_2;
u8 *tap_delay_status;
unsigned int i = 0;
unsigned int temp_pass_window = 0;
unsigned int best_low_pass_tap = 0;
unsigned int best_pass_window = 0;
+ u32 ier;
/* Tuning is valid only in SDR104 and SDR50 modes */
ctrl_2 = sdhci_readw(sdhci, SDHCI_HOST_CONTROL2);
if (tap_delay_status == NULL) {
dev_err(mmc_dev(sdhci->mmc), "failed to allocate memory"
"for storing tap_delay_status\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ /* Tuning should be done only for MMC_BUS_WIDTH_8 and MMC_BUS_WIDTH_4 */
+ if (sdhci->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ block_size = MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_8;
+ else if (sdhci->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ block_size = MMC_TUNING_BLOCK_SIZE_BUS_WIDTH_4;
+ else
+ return -EINVAL;
+ /*
+ * Disable all interrupts signalling.Enable interrupt status
+ * detection for buffer read ready and data crc. We use
+ * polling for tuning as it involves less overhead.
+ */
+ ier = sdhci_readl(sdhci, SDHCI_INT_ENABLE);
+ sdhci_writel(sdhci, 0, SDHCI_SIGNAL_ENABLE);
+ sdhci_writel(sdhci, SDHCI_INT_DATA_AVAIL |
+ SDHCI_INT_DATA_CRC, SDHCI_INT_ENABLE);
+
/*
* Set each tap delay value and run frequency tuning. After each
* run, update the tap delay status as working or not working.
sdhci_tegra_set_tap_delay(sdhci, i);
/* Run frequency tuning */
- err = sdhci_tegra_run_frequency_tuning(sdhci);
+ err = sdhci_tegra_run_frequency_tuning(sdhci, opcode, block_size);
/* Update whether the tap delay worked or not */
tap_delay_status[i] = (err) ? 0: 1;
(best_low_pass_tap + ((best_pass_window * 3) / 4)));
/* Run frequency tuning */
- err = sdhci_tegra_run_frequency_tuning(sdhci);
+ err = sdhci_tegra_run_frequency_tuning(sdhci, opcode, block_size);
+
+ /* Enable the normal interrupts signalling */
+ sdhci_writel(sdhci, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(sdhci, ier, SDHCI_SIGNAL_ENABLE);
-out:
if (tap_delay_status)
kfree(tap_delay_status);
}
}
+ if (tegra_host->dpd) {
+ mutex_lock(&tegra_host->dpd->delay_lock);
+ tegra_host->dpd->need_delay_dpd = 1;
+ mutex_unlock(&tegra_host->dpd->delay_lock);
+ }
+
return 0;
}
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ struct platform_device *pdev;
+ struct tegra_sdhci_platform_data *plat;
+ pdev = to_platform_device(mmc_dev(sdhci->mmc));
+ plat = pdev->dev.platform_data;
/* Enable the power rails if any */
if (tegra_host->card_present) {
if (!tegra_host->is_rail_enabled) {
regulator_enable(tegra_host->vdd_slot_reg);
if (tegra_host->vdd_io_reg) {
regulator_enable(tegra_host->vdd_io_reg);
- tegra_sdhci_signal_voltage_switch(sdhci, MMC_SIGNAL_VOLTAGE_330);
+ if (plat->mmc_data.ocr_mask &
+ SDHOST_1V8_OCR_MASK)
+ tegra_sdhci_signal_voltage_switch(sdhci,
+ MMC_SIGNAL_VOLTAGE_180);
+ else
+ tegra_sdhci_signal_voltage_switch(sdhci,
+ MMC_SIGNAL_VOLTAGE_330);
}
tegra_host->is_rail_enabled = 1;
}
}
static struct sdhci_ops tegra_sdhci_ops = {
+#ifndef CONFIG_ARCH_TEGRA_11x_SOC
.get_ro = tegra_sdhci_get_ro,
+#endif
.get_cd = tegra_sdhci_get_cd,
.read_l = tegra_sdhci_readl,
.read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel,
.platform_8bit_width = tegra_sdhci_8bit,
- .set_clock = tegra_sdhci_set_clock,
- .suspend = tegra_sdhci_suspend,
- .resume = tegra_sdhci_resume,
- .platform_reset_exit = tegra_sdhci_reset_exit,
- .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
- .switch_signal_voltage = tegra_sdhci_signal_voltage_switch,
- .execute_freq_tuning = sdhci_tegra_execute_tuning,
+ .set_clock = tegra_sdhci_set_clock,
+ .suspend = tegra_sdhci_suspend,
+ .resume = tegra_sdhci_resume,
+ .platform_reset_exit = tegra_sdhci_reset_exit,
+ .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
+ .switch_signal_voltage = tegra_sdhci_signal_voltage_switch,
+ .switch_signal_voltage_exit = tegra_sdhci_do_calibration,
+ .execute_freq_tuning = sdhci_tegra_execute_tuning,
};
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_NON_STD_VOLTAGE_SWITCHING |
+ SDHCI_QUIRK_NON_STANDARD_TUNING |
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
SDHCI_QUIRK_NONSTANDARD_CLOCK |
- SDHCI_QUIRK_NON_STANDARD_TUNING |
#endif
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
- SDHCI_QUIRK_NO_CALC_MAX_DISCARD_TO |
- SDHCI_QUIRK_BROKEN_CARD_DETECTION,
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_NO_CALC_MAX_DISCARD_TO,
+ .quirks2 = SDHCI_QUIRK2_BROKEN_PRESET_VALUES,
.ops = &tegra_sdhci_ops,
};
static struct sdhci_tegra_soc_data soc_data_tegra20 = {
.pdata = &sdhci_tegra20_pdata,
.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
+ NVQUIRK_SET_DRIVE_STRENGTH |
+#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ NVQUIRK_DISABLE_AUTO_CALIBRATION |
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ NVQUIRK_SET_CALIBRATION_OFFSETS |
+#endif
NVQUIRK_ENABLE_BLOCK_GAP_DET,
};
-#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct tegra_sdhci_platform_data *plat;
- struct sdhci_host *host;
struct sdhci_tegra *tegra_host;
- struct clk *clk;
int rc;
match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
"failed to allocate power gpio\n");
goto err_power_req;
}
- tegra_gpio_enable(plat->power_gpio);
gpio_direction_output(plat->power_gpio, 1);
}
"failed to allocate cd gpio\n");
goto err_cd_req;
}
- tegra_gpio_enable(plat->cd_gpio);
gpio_direction_input(plat->cd_gpio);
tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0);
"failed to allocate wp gpio\n");
goto err_wp_req;
}
- tegra_gpio_enable(plat->wp_gpio);
gpio_direction_input(plat->wp_gpio);
}
if (!gpio_is_valid(plat->cd_gpio))
tegra_host->card_present = 1;
- if (!plat->mmc_data.built_in) {
- if (plat->mmc_data.ocr_mask & SDHOST_1V8_OCR_MASK) {
- tegra_host->vddio_min_uv = SDHOST_LOW_VOLT_MIN;
- tegra_host->vddio_max_uv = SDHOST_LOW_VOLT_MAX;
- } else {
- /*
- * Set the minV and maxV to default
- * voltage range of 2.7V - 3.6V
- */
- tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_MIN;
+ if (plat->mmc_data.ocr_mask & SDHOST_1V8_OCR_MASK) {
+ tegra_host->vddio_min_uv = SDHOST_LOW_VOLT_MIN;
+ tegra_host->vddio_max_uv = SDHOST_LOW_VOLT_MAX;
+ } else if (plat->mmc_data.ocr_mask & MMC_OCR_2V8_MASK) {
+ tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_2V8;
tegra_host->vddio_max_uv = SDHOST_HIGH_VOLT_MAX;
- }
- tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc), "vddio_sdmmc");
- if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) {
- dev_err(mmc_dev(host->mmc), "%s regulator not found: %ld\n",
- "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg));
+ } else {
+ /*
+ * Set the minV and maxV to default
+ * voltage range of 2.7V - 3.6V
+ */
+ tegra_host->vddio_min_uv = SDHOST_HIGH_VOLT_MIN;
+ tegra_host->vddio_max_uv = SDHOST_HIGH_VOLT_MAX;
+ }
+
+ tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc),
+ "vddio_sdmmc");
+ if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) {
+ dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
+ "Assuming vddio_sdmmc is not required.\n",
+ "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg));
+ tegra_host->vdd_io_reg = NULL;
+ } else {
+ rc = regulator_set_voltage(tegra_host->vdd_io_reg,
+ tegra_host->vddio_min_uv,
+ tegra_host->vddio_max_uv);
+ if (rc) {
+ dev_err(mmc_dev(host->mmc), "%s regulator_set_voltage failed: %d",
+ "vddio_sdmmc", rc);
+ regulator_put(tegra_host->vdd_io_reg);
tegra_host->vdd_io_reg = NULL;
- } else {
- rc = regulator_set_voltage(tegra_host->vdd_io_reg,
- tegra_host->vddio_min_uv,
- tegra_host->vddio_max_uv);
- if (rc) {
- dev_err(mmc_dev(host->mmc), "%s regulator_set_voltage failed: %d",
- "vddio_sdmmc", rc);
- }
}
+ }
- tegra_host->vdd_slot_reg = regulator_get(mmc_dev(host->mmc), "vddio_sd_slot");
- if (IS_ERR_OR_NULL(tegra_host->vdd_slot_reg)) {
- dev_err(mmc_dev(host->mmc), "%s regulator not found: %ld\n",
- "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg));
- tegra_host->vdd_slot_reg = NULL;
- }
+ tegra_host->vdd_slot_reg = regulator_get(mmc_dev(host->mmc),
+ "vddio_sd_slot");
+ if (IS_ERR_OR_NULL(tegra_host->vdd_slot_reg)) {
+ dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld."
+ " Assuming vddio_sd_slot is not required.\n",
+ "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg));
+ tegra_host->vdd_slot_reg = NULL;
+ }
- if (tegra_host->card_present) {
- if (tegra_host->vdd_slot_reg)
- regulator_enable(tegra_host->vdd_slot_reg);
- if (tegra_host->vdd_io_reg)
- regulator_enable(tegra_host->vdd_io_reg);
- tegra_host->is_rail_enabled = 1;
- }
+ if (tegra_host->card_present) {
+ if (tegra_host->vdd_slot_reg)
+ regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->vdd_io_reg)
+ regulator_enable(tegra_host->vdd_io_reg);
+ tegra_host->is_rail_enabled = 1;
}
- clk = clk_get(mmc_dev(host->mmc), NULL);
- if (IS_ERR(clk)) {
+ pm_runtime_enable(&pdev->dev);
+ pltfm_host->clk = clk_get(mmc_dev(host->mmc), NULL);
+ if (IS_ERR(pltfm_host->clk)) {
dev_err(mmc_dev(host->mmc), "clk err\n");
- rc = PTR_ERR(clk);
+ rc = PTR_ERR(pltfm_host->clk);
goto err_clk_get;
}
- rc = clk_enable(clk);
+ pm_runtime_get_sync(&pdev->dev);
+ rc = clk_prepare_enable(pltfm_host->clk);
if (rc != 0)
goto err_clk_put;
- pltfm_host->clk = clk;
+
+ if (!strcmp(dev_name(mmc_dev(host->mmc)), "sdhci-tegra.3")) {
+ tegra_host->emc_clk = clk_get(mmc_dev(host->mmc), "emc");
+ if (IS_ERR(tegra_host->emc_clk)) {
+ dev_err(mmc_dev(host->mmc), "clk err\n");
+ rc = PTR_ERR(tegra_host->emc_clk);
+ goto err_clk_put;
+ }
+ tegra_host->emc_max_clk =
+ clk_round_rate(tegra_host->emc_clk, ULONG_MAX);
+ }
+
pltfm_host->priv = tegra_host;
tegra_host->clk_enabled = true;
tegra_host->max_clk_limit = plat->max_clk_limit;
+ tegra_host->ddr_clk_limit = plat->ddr_clk_limit;
tegra_host->instance = pdev->id;
tegra_host->dpd = tegra_io_dpd_get(mmc_dev(host->mmc));
- host->mmc->pm_caps = plat->pm_flags;
+ host->mmc->pm_caps |= plat->pm_caps;
+ host->mmc->pm_flags |= plat->pm_flags;
+
+ /*
+ * tegra sd controller is not able to reset the command inhibit
+ * bit for the mmc sleep/awake command
+ */
+ host->mmc->caps2 |= MMC_CAP2_NO_SLEEP_CMD;
host->mmc->caps |= MMC_CAP_ERASE;
- host->mmc->caps |= MMC_CAP_DISABLE;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
if (plat->is_8bit)
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
host->mmc->caps |= MMC_CAP_SDIO_IRQ;
-
host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
if (plat->mmc_data.built_in) {
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
- host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
}
- /* Do not turn OFF embedded sdio cards as it support Wake on Wireless */
- if (plat->mmc_data.embedded_sdio)
- host->mmc->pm_flags |= MMC_PM_KEEP_POWER;
+ host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
+
+#ifdef CONFIG_MMC_BKOPS
+ host->mmc->caps2 |= MMC_CAP2_BKOPS;
+#endif
tegra_sdhost_min_freq = TEGRA_SDHOST_MIN_FREQ;
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
tegra_host->hw_ops = &tegra_2x_sdhci_ops;
tegra_sdhost_std_freq = TEGRA2_SDHOST_STD_FREQ;
-#else
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
tegra_host->hw_ops = &tegra_3x_sdhci_ops;
tegra_sdhost_std_freq = TEGRA3_SDHOST_STD_FREQ;
+#else
+ tegra_host->hw_ops = &tegra_11x_sdhci_ops;
+ tegra_sdhost_std_freq = TEGRA3_SDHOST_STD_FREQ;
#endif
-
rc = sdhci_add_host(host);
if (rc)
goto err_add_host;
+ /* Enable async suspend/resume to reduce LP0 latency */
+ device_enable_async_suspend(&pdev->dev);
+
return 0;
err_add_host:
- clk_disable(pltfm_host->clk);
+ clk_put(tegra_host->emc_clk);
+ clk_disable_unprepare(pltfm_host->clk);
+ pm_runtime_put_sync(&pdev->dev);
err_clk_put:
clk_put(pltfm_host->clk);
err_clk_get:
- if (gpio_is_valid(plat->wp_gpio)) {
- tegra_gpio_disable(plat->wp_gpio);
+ if (gpio_is_valid(plat->wp_gpio))
gpio_free(plat->wp_gpio);
- }
err_wp_req:
if (gpio_is_valid(plat->cd_gpio))
free_irq(gpio_to_irq(plat->cd_gpio), host);
err_cd_irq_req:
- if (gpio_is_valid(plat->cd_gpio)) {
- tegra_gpio_disable(plat->cd_gpio);
+ if (gpio_is_valid(plat->cd_gpio))
gpio_free(plat->cd_gpio);
- }
err_cd_req:
- if (gpio_is_valid(plat->power_gpio)) {
- tegra_gpio_disable(plat->power_gpio);
+ if (gpio_is_valid(plat->power_gpio))
gpio_free(plat->power_gpio);
- }
err_power_req:
-err_no_mem:
- kfree(tegra_host);
err_no_plat:
sdhci_pltfm_free(pdev);
return rc;
regulator_put(tegra_host->vdd_io_reg);
}
- if (gpio_is_valid(plat->wp_gpio)) {
- tegra_gpio_disable(plat->wp_gpio);
+ if (gpio_is_valid(plat->wp_gpio))
gpio_free(plat->wp_gpio);
- }
if (gpio_is_valid(plat->cd_gpio)) {
free_irq(gpio_to_irq(plat->cd_gpio), host);
- tegra_gpio_disable(plat->cd_gpio);
gpio_free(plat->cd_gpio);
}
- if (gpio_is_valid(plat->power_gpio)) {
- tegra_gpio_disable(plat->power_gpio);
+ if (gpio_is_valid(plat->power_gpio))
gpio_free(plat->power_gpio);
- }
- if (tegra_host->clk_enabled)
- clk_disable(pltfm_host->clk);
+ if (tegra_host->clk_enabled) {
+ clk_disable_unprepare(pltfm_host->clk);
+ pm_runtime_put_sync(&pdev->dev);
+ }
clk_put(pltfm_host->clk);
sdhci_pltfm_free(pdev);
- kfree(tegra_host);
return 0;
}