diff options
author | Tony Lin <tony.lin@freescale.com> | 2011-07-12 11:09:29 +0800 |
---|---|---|
committer | Tony Lin <tony.lin@freescale.com> | 2011-07-13 19:08:41 +0800 |
commit | 1dcbace9cbbf6dedad439078afb2e64d55c91179 (patch) | |
tree | c2d2c2095dbb8d6804cf5b5986d5afe7dfe9c01d /drivers/mmc | |
parent | 63657c69e7957e8d077cb97bee4910536ff91600 (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.c | 73 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc.h | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 9 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 1 |
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 |