]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00152547-04 [MX6Q]add SDHC3.0 support on uSDHC controller
authorTony Lin <tony.lin@freescale.com>
Tue, 12 Jul 2011 03:09:29 +0000 (11:09 +0800)
committerOliver Wendt <ow@karo-electronics.de>
Mon, 30 Sep 2013 12:09:15 +0000 (14:09 +0200)
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>
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 29b973be7140dac80cc274257baa7f140ea94e39..c0f126ab0c8dd6fa5499df49ad034a726480d08a 100644 (file)
@@ -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"
 #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,
index 02ff6da7ccabaf7fb6dd36a1e20fcbd3eb15bc5b..62e50ef3121f997bc0470ff9115f28a8e1259853 100644 (file)
@@ -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);
index 153008fff548e0f796d34772a012509ffc448970..308c36197306629e6d78ed47a4203a1070361518 100644 (file)
@@ -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.
         */
index 745c42fa41ed5fb85cfc45ff9238e37b222ae2b1..2899a9fc8df4a31f0f2d4b7e4a6b2bf64fa36c81 100644 (file)
@@ -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