summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorXinyu Chen <xinyu.chen@freescale.com>2012-02-07 11:04:37 +0800
committerXinyu Chen <xinyu.chen@freescale.com>2012-02-07 11:04:37 +0800
commit468940b0f4a7d24526b8fcb62f03aac789cbfa81 (patch)
treedb213ff6a9b32eb2367066199ce28715e3ddcac1 /drivers/mmc
parentb377e57b416972c57eb946876a60bec666731bf5 (diff)
parent4a77281fc5b763f6e12ac1fd9b12ff68a9d91acc (diff)
Merge remote-tracking branch 'origin/imx_3.0.15' into imx_3.0.15_android
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/core.h1
-rw-r--r--drivers/mmc/core/sd.c34
-rw-r--r--drivers/mmc/core/sd_ops.c43
-rw-r--r--drivers/mmc/core/sd_ops.h1
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c99
5 files changed, 173 insertions, 5 deletions
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 14664f1fb16f..84370cad499d 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -33,6 +33,7 @@ void mmc_init_erase(struct mmc_card *card);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+void mmc_set_tuning(struct mmc_host *host, unsigned int tuning);
void mmc_gate_clock(struct mmc_host *host);
void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_ungated(struct mmc_host *host);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0635da51a970..5a7d8f5d41e0 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -613,19 +613,45 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (err)
goto out;
- /* Set bus speed mode of the card */
- err = sd_set_bus_speed_mode(card, status);
+ /* Set current limit for the card */
+ err = sd_set_current_limit(card, status);
if (err)
goto out;
- /* Set current limit for the card */
- err = sd_set_current_limit(card, status);
+ /* Set bus speed mode of the card */
+ err = sd_set_bus_speed_mode(card, status);
if (err)
goto out;
/* SPI mode doesn't define CMD19 */
+#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
+ if (!mmc_host_is_spi(card->host)) {
+ int min, max, avg;
+
+ min = card->host->tuning_min;
+ while (min < card->host->tuning_max) {
+ mmc_set_tuning(card->host, min);
+ if (!mmc_send_tuning_cmd(card))
+ break;
+ min += card->host->tuning_step;
+ }
+
+ max = min;
+ while (max < card->host->tuning_max) {
+ mmc_set_tuning(card->host, max);
+ if (!mmc_send_tuning_cmd(card))
+ break;
+ max += card->host->tuning_step;
+ }
+
+ avg = (min + max) / 2;
+ mmc_set_tuning(card->host, avg);
+ mmc_send_tuning_cmd(card);
+ }
+#else
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
err = card->host->ops->execute_tuning(card->host);
+#endif
out:
kfree(status);
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 021fed153804..afafc8c81d01 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -345,6 +345,49 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
return 0;
}
+int mmc_send_tuning_cmd(struct mmc_card *card)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+ char scr[64];
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+ memset(scr, 0, 64);
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = MMC_SEND_TUNING_BLOCK;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, scr, 64);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
+
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{
int err;
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
index ffc2305d905f..2142da4b611c 100644
--- a/drivers/mmc/core/sd_ops.h
+++ b/drivers/mmc/core/sd_ops.h
@@ -20,6 +20,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
u8 value, u8 *resp);
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
+int mmc_send_tuning_cmd(struct mmc_card *card);
#endif
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9362f00623b6..5aec0bfe6fd6 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -39,6 +39,7 @@
#define SDHCI_MIX_CTRL_EXE_TUNE (1 << 22)
#define SDHCI_MIX_CTRL_SMPCLK_SEL (1 << 23)
+#define SDHCI_MIX_CTRL_AUTOTUNE_EN (1 << 24)
#define SDHCI_MIX_CTRL_FBCLK_SEL (1 << 25)
#define SDHCI_VENDOR_SPEC_VSELECT (1 << 1)
@@ -67,6 +68,8 @@
struct pltfm_imx_data {
int flags;
u32 scratchpad;
+ /* uhs mode for sdhc host control2 */
+ unsigned char uhs_mode;
};
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
@@ -99,7 +102,7 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
val |= SDHCI_CARD_PRESENT;
}
- if (reg == SDHCI_INT_STATUS && cpu_is_mx6q())
+ if (reg == SDHCI_INT_STATUS && cpu_is_mx6q()) {
/*
* on mx6q, there is low possibility that
* DATA END interrupt comes ealier than DMA
@@ -110,6 +113,32 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
if ((val & SDHCI_INT_DATA_END) && \
!(val & SDHCI_INT_DMA_END))
val = readl(host->ioaddr + reg);
+ } else if (reg == SDHCI_CAPABILITIES_1 && cpu_is_mx6q()) {
+ /*
+ * on mx6q, no cap_1 available, fake one.
+ */
+ val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | \
+ SDHCI_SUPPORT_SDR50;
+ } else if (reg == SDHCI_MAX_CURRENT && cpu_is_mx6q()) {
+ /*
+ * on mx6q, no max current available, fake one.
+ */
+ val = 0;
+ val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
+ val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
+ val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
+ }
+
+ if (reg == SDHCI_PRESENT_STATE && cpu_is_mx6q()) {
+ u32 fsl_prss = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+ /* save the least 20 bits */
+ val = fsl_prss & 0x000FFFFF;
+ /* move dat[0-3] line signal bits */
+ val |= (fsl_prss & 0x0F000000) >> 4;
+ /* move cmd line signal bits */
+ val |= (fsl_prss & 0x00800000) << 1;
+ }
+
return val;
}
@@ -174,9 +203,34 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{
+ u16 ret;
+ u32 val;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = pltfm_host->priv;
+
if (unlikely(reg == SDHCI_HOST_VERSION))
reg ^= 2;
+ switch (reg) {
+ case SDHCI_HOST_CONTROL2:
+ ret = 0;
+ /* collect bit info from several regs */
+ val = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+ ret |= (val & SDHCI_VENDOR_SPEC_VSELECT)
+ ? SDHCI_CTRL_VDD_180 : 0;
+
+ val = readl(host->ioaddr + SDHCI_MIX_CTRL);
+ ret |= (val & SDHCI_MIX_CTRL_EXE_TUNE)
+ ? SDHCI_CTRL_EXEC_TUNING : 0;
+ ret |= (val & SDHCI_MIX_CTRL_SMPCLK_SEL)
+ ? 0 : SDHCI_CTRL_TUNED_CLK ;
+ ret |= SDHCI_CTRL_UHS_MASK & imx_data->uhs_mode;
+ /* no preset enable available */
+ ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+ return ret;
+ }
+
return readw(host->ioaddr + reg);
}
@@ -187,6 +241,7 @@ void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
reg = sdhci_readl(host, SDHCI_MIX_CTRL);
reg |= SDHCI_MIX_CTRL_EXE_TUNE | \
SDHCI_MIX_CTRL_SMPCLK_SEL | \
+ SDHCI_MIX_CTRL_AUTOTUNE_EN | \
SDHCI_MIX_CTRL_FBCLK_SEL;
sdhci_writel(host, reg, SDHCI_MIX_CTRL);
sdhci_writel(host, (val << 8), SDHCI_TUNE_CTRL_STATUS);
@@ -197,8 +252,50 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
+ u32 orig_reg;
switch (reg) {
+ case SDHCI_CLOCK_CONTROL:
+ orig_reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+ if (val & SDHCI_CLOCK_CARD_EN) {
+ writel(orig_reg | SDHCI_VENDOR_SPEC_FRC_SDCLK_ON, \
+ host->ioaddr + SDHCI_VENDOR_SPEC);
+ } else {
+ writel(orig_reg & ~SDHCI_VENDOR_SPEC_FRC_SDCLK_ON, \
+ host->ioaddr + SDHCI_VENDOR_SPEC);
+ }
+
+ return;
+ case SDHCI_HOST_CONTROL2:
+ orig_reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+ if (val & SDHCI_CTRL_VDD_180) {
+ orig_reg |= SDHCI_VENDOR_SPEC_VSELECT;
+ writel(orig_reg, host->ioaddr + SDHCI_VENDOR_SPEC);
+ } else {
+ orig_reg &= ~SDHCI_VENDOR_SPEC_VSELECT;
+ writel(orig_reg, host->ioaddr + SDHCI_VENDOR_SPEC);
+ }
+
+ /*
+ * FSL sdhc controls bus and signal voltage via one bit
+ * VSELECT in VENDOR_SPEC, which has been set in
+ * SDHCI_POWER_CONTROL. So we skip the SDHCI_CTRL_VDD_180
+ * here.
+ *
+ * ignore exec_tuning flag written to SDHCI_HOST_CONTROL2,
+ * tuning will be handled differently for FSL SDHC ip.
+ */
+ orig_reg = readl(host->ioaddr + SDHCI_MIX_CTRL);
+ orig_reg &= ~SDHCI_MIX_CTRL_SMPCLK_SEL;
+
+ orig_reg |= (val & SDHCI_CTRL_TUNED_CLK)
+ ? 0 : SDHCI_MIX_CTRL_SMPCLK_SEL;
+
+ writel(orig_reg, host->ioaddr + SDHCI_MIX_CTRL);
+
+ imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
+
+ return;
case SDHCI_TRANSFER_MODE:
/*
* Postpone this write, we must do it together with a