mmc: Add support for mmc v4 wide-bus modes
authorPhilip Langdale <philipl@overt.org>
Sun, 29 Oct 2006 09:14:19 +0000 (10:14 +0100)
committerPierre Ossman <drzeus@drzeus.cx>
Fri, 1 Dec 2006 17:22:44 +0000 (18:22 +0100)
This change adds support for the mmc4 4-bit wide-bus mode.

The mmc4 spec defines 8-bit and 4-bit transfer modes. As we do not support
any 8-bit hardware, this patch only adds support for the 4-bit mode, but
it can easily be built upon when the time comes.

The 4-bit mode is electrically compatible with SD's 4-bit mode but the
procedure for turning it on is different. This patch implements only
the essential parts of the procedure as defined by the spec. Two additional
steps are recommended but not compulsory. I am documenting them here so
that there's a record.

1) A bus-test mechanism is implemented using dedicated mmc commands which allow
for testing the functionality of the data bus at the electrical level. This is
pretty paranoid and they way the commands work is not compatible with the mmc
subsystem (they don't set valid CRC values).

2) MMC v4 cards can indicate they would like to draw more than the default
amount of current in wide-bus modes. We currently will never switch the card
into a higher draw mode. Supposedly, allowing the card to draw more current
will let it perform better, but the specs seem to indicate that the card will
function correctly without the mode change. Empirical testing supports this
interpretation.

Signed-off-by: Philip Langdale <philipl@overt.org>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
drivers/mmc/mmc.c
include/linux/mmc/protocol.h

index 2d5b93000dee545bf9c4dcebfa6b02f21cb67058..1593a6a632cf77a7bb46c96eb73f568459ed746d 100644 (file)
@@ -397,23 +397,23 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card)
                return err;
 
        /*
-        * Default bus width is 1 bit.
-        */
-       host->ios.bus_width = MMC_BUS_WIDTH_1;
-
-       /*
-        * We can only change the bus width of the selected
-        * card so therefore we have to put the handling
+        * We can only change the bus width of SD cards when
+        * they are selected so we have to put the handling
         * here.
+        *
+        * The card is in 1 bit mode by default so
+        * we only need to change if it supports the
+        * wider version.
         */
-       if (host->caps & MMC_CAP_4_BIT_DATA) {
+       if (mmc_card_sd(card) &&
+               (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+
                /*
-                * The card is in 1 bit mode by default so
-                * we only need to change if it supports the
-                * wider version.
-                */
-               if (mmc_card_sd(card) &&
-                       (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+               * Default bus width is 1 bit.
+               */
+               host->ios.bus_width = MMC_BUS_WIDTH_1;
+
+               if (host->caps & MMC_CAP_4_BIT_DATA) {
                        struct mmc_command cmd;
                        cmd.opcode = SD_APP_SET_BUS_WIDTH;
                        cmd.arg = SD_BUS_WIDTH_4;
@@ -1055,6 +1055,29 @@ static void mmc_process_ext_csds(struct mmc_host *host)
                }
 
                mmc_card_set_highspeed(card);
+
+               /* Check for host support for wide-bus modes. */
+               if (!(host->caps & MMC_CAP_4_BIT_DATA)) {
+                       continue;
+               }
+
+               /* Activate 4-bit support. */
+               cmd.opcode = MMC_SWITCH;
+               cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+                         (EXT_CSD_BUS_WIDTH << 16) |
+                         (EXT_CSD_BUS_WIDTH_4 << 8) |
+                         EXT_CSD_CMD_SET_NORMAL;
+               cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+               err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+               if (err != MMC_ERR_NONE) {
+                       printk("%s: failed to switch card to "
+                              "mmc v4 4-bit bus mode.\n",
+                              mmc_hostname(card->host));
+                       continue;
+               }
+
+               host->ios.bus_width = MMC_BUS_WIDTH_4;
        }
 
        kfree(ext_csd);
index 311b6547f561fcbe65255ff423a870bdc1000d12..45c51fd85786da19d5d7616f64ac55ff0243e084 100644 (file)
@@ -256,6 +256,7 @@ struct _mmc_csd {
  * EXT_CSD fields
  */
 
+#define EXT_CSD_BUS_WIDTH      183     /* R/W */
 #define EXT_CSD_HS_TIMING      185     /* R/W */
 #define EXT_CSD_CARD_TYPE      196     /* RO */
 
@@ -270,6 +271,10 @@ struct _mmc_csd {
 #define EXT_CSD_CARD_TYPE_26   (1<<0)  /* Card can run at 26MHz */
 #define EXT_CSD_CARD_TYPE_52   (1<<1)  /* Card can run at 52MHz */
 
+#define EXT_CSD_BUS_WIDTH_1    0       /* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4    1       /* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8    2       /* Card is in 8 bit mode */
+
 /*
  * MMC_SWITCH access modes
  */