From: Tony Lin Date: Tue, 12 Jul 2011 03:09:29 +0000 (+0800) Subject: ENGR00152547-04 [MX6Q]add SDHC3.0 support on uSDHC controller X-Git-Tag: v3.0.35-fsl_4.1.0~2429 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=a68ae41b82603281c8c1933674cf29adcdde8a1e;p=karo-tx-linux.git 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 --- diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 29b973be7140..c0f126ab0c8d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #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); @@ -147,6 +176,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, @@ -173,6 +205,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 */ @@ -221,12 +277,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 153008fff548..308c36197306 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1280,6 +1280,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. @@ -2690,6 +2696,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 745c42fa41ed..2899a9fc8df4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -274,6 +274,7 @@ struct sdhci_ops { void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); + void (*pre_tuning)(struct sdhci_host *host, u32 val); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS