mmc: host: Adds support for eMMC 4.5 HS200 mode
authorGirish K S <girish.shivananjappa@linaro.org>
Fri, 6 Jan 2012 04:26:39 +0000 (09:56 +0530)
committerChris Ball <cjb@laptop.org>
Thu, 12 Jan 2012 20:17:16 +0000 (15:17 -0500)
This patch adds support for the HS200 mode on the host side.
Also enables the tuning feature required when the HS200 mode
is selected.

Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
include/linux/mmc/sdhci.h

index 0636e9a..96f4e54 100644 (file)
@@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
 static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc);
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
 static void sdhci_tuning_timer(unsigned long data);
 
 #ifdef CONFIG_PM_RUNTIME
@@ -1014,7 +1014,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
                flags |= SDHCI_CMD_INDEX;
 
        /* CMD19 is special in that the Data Present Select should be set */
-       if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+       if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+           cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
                flags |= SDHCI_CMD_DATA;
 
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                if ((host->flags & SDHCI_NEEDS_RETUNING) &&
                    !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
                        spin_unlock_irqrestore(&host->lock, flags);
-                       sdhci_execute_tuning(mmc);
+                       sdhci_execute_tuning(mmc, mrq->cmd->opcode);
                        spin_lock_irqsave(&host->lock, flags);
 
                        /* Restore original mmc_request structure */
@@ -1382,7 +1383,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
                unsigned int clock;
 
                /* In case of UHS-I modes, set High Speed Enable */
-               if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+               if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+                   (ios->timing == MMC_TIMING_UHS_SDR50) ||
                    (ios->timing == MMC_TIMING_UHS_SDR104) ||
                    (ios->timing == MMC_TIMING_UHS_DDR50) ||
                    (ios->timing == MMC_TIMING_UHS_SDR25))
@@ -1435,7 +1437,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
                        ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
                        /* Select Bus Speed Mode for host */
                        ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
-                       if (ios->timing == MMC_TIMING_UHS_SDR12)
+                       if (ios->timing == MMC_TIMING_MMC_HS200)
+                               ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+                       else if (ios->timing == MMC_TIMING_UHS_SDR12)
                                ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
                        else if (ios->timing == MMC_TIMING_UHS_SDR25)
                                ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
@@ -1682,7 +1686,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
        return err;
 }
 
-static int sdhci_execute_tuning(struct mmc_host *mmc)
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 {
        struct sdhci_host *host;
        u16 ctrl;
@@ -1690,6 +1694,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
        int tuning_loop_counter = MAX_TUNING_LOOP;
        unsigned long timeout;
        int err = 0;
+       bool requires_tuning_nonuhs = false;
 
        host = mmc_priv(mmc);
 
@@ -1700,13 +1705,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
        ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 
        /*
-        * Host Controller needs tuning only in case of SDR104 mode
-        * and for SDR50 mode when Use Tuning for SDR50 is set in
+        * The Host Controller needs tuning only in case of SDR104 mode
+        * and for SDR50 mode when Use Tuning for SDR50 is set in the
         * Capabilities register.
+        * If the Host Controller supports the HS200 mode then the
+        * tuning function has to be executed.
         */
+       if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+           (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
+            host->flags & SDHCI_HS200_NEEDS_TUNING))
+               requires_tuning_nonuhs = true;
+
        if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
-           (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
-           (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+           requires_tuning_nonuhs)
                ctrl |= SDHCI_CTRL_EXEC_TUNING;
        else {
                spin_unlock(&host->lock);
@@ -1742,7 +1753,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
                if (!tuning_loop_counter && !timeout)
                        break;
 
-               cmd.opcode = MMC_SEND_TUNING_BLOCK;
+               cmd.opcode = opcode;
                cmd.arg = 0;
                cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
                cmd.retries = 0;
@@ -1757,7 +1768,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
                 * block to the Host Controller. So we set the block size
                 * to 64 here.
                 */
-               sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+               if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+                       if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+                               sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+                                            SDHCI_BLOCK_SIZE);
+                       else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+                               sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+                                            SDHCI_BLOCK_SIZE);
+               } else {
+                       sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+                                    SDHCI_BLOCK_SIZE);
+               }
 
                /*
                 * The tuning block is sent by the card to the host controller.
@@ -2140,12 +2161,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
 
 static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 {
+       u32 command;
        BUG_ON(intmask == 0);
 
        /* CMD19 generates _only_ Buffer Read Ready interrupt */
        if (intmask & SDHCI_INT_DATA_AVAIL) {
-               if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
-                   MMC_SEND_TUNING_BLOCK) {
+               command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+               if (command == MMC_SEND_TUNING_BLOCK ||
+                   command == MMC_SEND_TUNING_BLOCK_HS200) {
                        host->tuning_done = 1;
                        wake_up(&host->buf_ready_int);
                        return;
@@ -2747,10 +2770,14 @@ int sdhci_add_host(struct sdhci_host *host)
        if (caps[1] & SDHCI_SUPPORT_DDR50)
                mmc->caps |= MMC_CAP_UHS_DDR50;
 
-       /* Does the host needs tuning for SDR50? */
+       /* Does the host need tuning for SDR50? */
        if (caps[1] & SDHCI_USE_SDR50_TUNING)
                host->flags |= SDHCI_SDR50_NEEDS_TUNING;
 
+       /* Does the host need tuning for HS200? */
+       if (mmc->caps2 & MMC_CAP2_HS200)
+               host->flags |= SDHCI_HS200_NEEDS_TUNING;
+
        /* Driver Type(s) (A, C, D) supported by the host */
        if (caps[1] & SDHCI_DRIVER_TYPE_A)
                mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
index a04d4d0..ad265b9 100644 (file)
 #define   SDHCI_CTRL_UHS_SDR50         0x0002
 #define   SDHCI_CTRL_UHS_SDR104                0x0003
 #define   SDHCI_CTRL_UHS_DDR50         0x0004
+#define   SDHCI_CTRL_HS_SDR200         0x0005 /* reserved value in SDIO spec */
 #define  SDHCI_CTRL_VDD_180            0x0008
 #define  SDHCI_CTRL_DRV_TYPE_MASK      0x0030
 #define   SDHCI_CTRL_DRV_TYPE_B                0x0000
index dad7a46..c750f85 100644 (file)
@@ -119,6 +119,7 @@ struct sdhci_host {
 #define SDHCI_AUTO_CMD23       (1<<7)  /* Auto CMD23 support */
 #define SDHCI_PV_ENABLED       (1<<8)  /* Preset value enabled */
 #define SDHCI_SDIO_IRQ_ENABLED (1<<9)  /* SDIO irq enabled */
+#define SDHCI_HS200_NEEDS_TUNING (1<<10)       /* HS200 needs tuning */
 
        unsigned int version;   /* SDHCI spec. version */