summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorTony Lin <tony.lin@freescale.com>2011-07-12 11:09:29 +0800
committerTony Lin <tony.lin@freescale.com>2011-07-13 19:08:41 +0800
commit1dcbace9cbbf6dedad439078afb2e64d55c91179 (patch)
treec2d2c2095dbb8d6804cf5b5986d5afe7dfe9c01d /drivers/mmc
parent63657c69e7957e8d077cb97bee4910536ff91600 (diff)
ENGR00152547-04 [MX6Q]add SDHC3.0 support on uSDHC controller
modify host controller driver to meet SD3.0 spec. including voltage switch, and tuning control. add a function pointer for bus driver to do tuning preparation, in case some host controller like uSDHC does not tune automatically. it needs change delay line before tuning. Signed-off-by: Tony Lin <tony.lin@freescale.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c73
-rw-r--r--drivers/mmc/host/sdhci-esdhc.h2
-rw-r--r--drivers/mmc/host/sdhci.c9
-rw-r--r--drivers/mmc/host/sdhci.h1
4 files changed, 83 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 5ea1388e0e34..93fe44dc33cb 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -21,6 +21,7 @@
#include <linux/mmc/sdhci-pltfm.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
#include <mach/hardware.h>
#include <mach/esdhc.h>
#include "sdhci.h"
@@ -31,6 +32,21 @@
#define SDHCI_VENDOR_SPEC 0xC0
#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x00000002
#define SDHCI_MIX_CTRL 0x48
+#define SDHCI_TUNE_CTRL_STATUS 0x68
+#define SDHCI_TUNE_CTRL_STEP 0x1
+#define SDHCI_TUNE_CTRL_MIN 0x0
+#define SDHCI_TUNE_CTRL_MAX ((1 << 7) - 1)
+
+#define SDHCI_MIX_CTRL_EXE_TUNE (1 << 22)
+#define SDHCI_MIX_CTRL_SMPCLK_SEL (1 << 23)
+#define SDHCI_MIX_CTRL_FBCLK_SEL (1 << 25)
+
+#define SDHCI_VENDOR_SPEC_VSELECT (1 << 1)
+#define SDHCI_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
+
+#define SDHCI_PRESENT_STATE_CLSL (1 << 23)
+#define SDHCI_PRESENT_STATE_DLSL_L4 (0xF << 24)
+#define SDHCI_PRESENT_STATE_DLSL_H4 (0xF << 28)
#define ESDHC_FLAG_GPIO_FOR_CD_WP (1 << 0)
/*
@@ -117,6 +133,19 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
return readw(host->ioaddr + reg);
}
+void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_MIX_CTRL);
+ reg |= SDHCI_MIX_CTRL_EXE_TUNE | \
+ SDHCI_MIX_CTRL_SMPCLK_SEL | \
+ SDHCI_MIX_CTRL_FBCLK_SEL;
+ sdhci_writel(host, reg, SDHCI_MIX_CTRL);
+ sdhci_writel(host, (val << 8), SDHCI_TUNE_CTRL_STATUS);
+}
+
+
static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -146,6 +175,9 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
writel(0x08800880, host->ioaddr + SDHCI_CAPABILITIES_1);
if (cpu_is_mx6q()) {
+ imx_data->scratchpad |= \
+ (readl(host->ioaddr + SDHCI_MIX_CTRL) & (0xf << 22));
+
writel(imx_data->scratchpad,
host->ioaddr + SDHCI_MIX_CTRL);
writel(val << 16,
@@ -172,6 +204,30 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
* FSL put some DMA bits here
* If your board has a regulator, code should be here
*/
+ if (val == (SDHCI_POWER_ON | SDHCI_POWER_180)) {
+ u32 reg;
+
+ /* switch to 1.8V */
+ reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+ reg |= SDHCI_VENDOR_SPEC_VSELECT;
+ writel(reg, host->ioaddr + SDHCI_VENDOR_SPEC);
+
+ /* stop sd clock */
+ writel(reg & ~SDHCI_VENDOR_SPEC_FRC_SDCLK_ON, \
+ host->ioaddr + SDHCI_VENDOR_SPEC);
+
+ /* sleep at least 5ms */
+ mdelay(5);
+
+ /* restore sd clock status */
+ writel(reg, host->ioaddr + SDHCI_VENDOR_SPEC);
+ } else {
+ u32 reg;
+
+ reg = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+ reg &= ~SDHCI_VENDOR_SPEC_VSELECT;
+ writel(reg, host->ioaddr + SDHCI_VENDOR_SPEC);
+ }
return;
case SDHCI_HOST_CONTROL:
/* FSL messed up here, so we can just keep those two */
@@ -220,12 +276,15 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.set_clock = esdhc_set_clock,
.get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock,
+ .pre_tuning = esdhc_prepare_tuning,
};
static irqreturn_t cd_irq(int irq, void *data)
{
struct sdhci_host *sdhost = (struct sdhci_host *)data;
+ sdhci_writel(sdhost, 0, SDHCI_MIX_CTRL);
+ sdhci_writel(sdhost, 0, SDHCI_TUNE_CTRL_STATUS);
tasklet_schedule(&sdhost->card_tasklet);
return IRQ_HANDLED;
};
@@ -264,7 +323,7 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
/* write_protect can't be routed to controller, use gpio */
sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
- if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
+ if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51() || cpu_is_mx6q()))
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
if (boarddata) {
@@ -301,6 +360,15 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
/* Now we have a working card_detect again */
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
}
+ host->ocr_avail_sd = MMC_VDD_29_30 | MMC_VDD_30_31 | \
+ MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ if (boarddata->support_18v)
+ host->ocr_avail_sd |= MMC_VDD_165_195;
+
+ host->tuning_min = SDHCI_TUNE_CTRL_MIN;
+ host->tuning_max = SDHCI_TUNE_CTRL_MAX;
+ host->tuning_step = SDHCI_TUNE_CTRL_STEP;
return 0;
@@ -335,7 +403,8 @@ static void esdhc_pltfm_exit(struct sdhci_host *host)
struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
- | SDHCI_QUIRK_BROKEN_CARD_DETECTION,
+ | SDHCI_QUIRK_BROKEN_CARD_DETECTION
+ | SDHCI_QUIRK_NO_HISPD_BIT,
/* ADMA has issues. Might be fixable */
.ops = &sdhci_esdhc_ops,
.init = esdhc_pltfm_init,
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index 02ff6da7ccab..62e50ef3121f 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -48,6 +48,8 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
int div = 1;
u32 temp;
+ if (cpu_is_mx6q())
+ pre_div = 1;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9e15f41f87be..86740a3af938 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1170,6 +1170,12 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
+ if (ios->tuning_flag) {
+ /* means this request is for tuning only */
+ if (host->ops->pre_tuning)
+ host->ops->pre_tuning(host, ios->tuning);
+ goto out;
+ }
/*
* Reset the chip on each power off.
* Should clear out any weird states.
@@ -1981,6 +1987,9 @@ int sdhci_add_host(struct sdhci_host *host)
*/
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
+ mmc->tuning_min = host->tuning_min;
+ mmc->tuning_max = host->tuning_max;
+ mmc->tuning_step = host->tuning_step;
/*
* Init tasklets.
*/
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 25e8bde600d1..dcc9b91f7997 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -223,6 +223,7 @@ struct sdhci_ops {
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
+ void (*pre_tuning)(struct sdhci_host *host, u32 val);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS