diff options
author | Xinyu Chen <xinyu.chen@freescale.com> | 2012-02-07 11:04:37 +0800 |
---|---|---|
committer | Xinyu Chen <xinyu.chen@freescale.com> | 2012-02-07 11:04:37 +0800 |
commit | 468940b0f4a7d24526b8fcb62f03aac789cbfa81 (patch) | |
tree | db213ff6a9b32eb2367066199ce28715e3ddcac1 /drivers/mmc | |
parent | b377e57b416972c57eb946876a60bec666731bf5 (diff) | |
parent | 4a77281fc5b763f6e12ac1fd9b12ff68a9d91acc (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.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 34 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.c | 43 | ||||
-rw-r--r-- | drivers/mmc/core/sd_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 99 |
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 |