mmc: Support for high speed SD cards
authorPierre Ossman <drzeus@drzeus.cx>
Wed, 8 Nov 2006 22:03:10 +0000 (23:03 +0100)
committerPierre Ossman <drzeus@drzeus.cx>
Fri, 1 Dec 2006 17:53:37 +0000 (18:53 +0100)
Modern SD cards support a clock speed of 50 MHz. Make sure we test for
this capability and do the song and dance required to activate it.

Activating high speed support actually modifies the TRAN_SPEED field
of the CSD. But as the spec says that the cards must report 50 MHz,
we might as well skip re-reading the CSD.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
drivers/mmc/mmc.c
include/linux/mmc/card.h
include/linux/mmc/protocol.h

index 82b7643..9d19002 100644 (file)
@@ -1157,6 +1157,116 @@ static void mmc_read_scrs(struct mmc_host *host)
        mmc_deselect_cards(host);
 }
 
+static void mmc_read_switch_caps(struct mmc_host *host)
+{
+       int err;
+       struct mmc_card *card;
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       unsigned char *status;
+       struct scatterlist sg;
+
+       status = kmalloc(64, GFP_KERNEL);
+       if (!status) {
+               printk(KERN_WARNING "%s: Unable to allocate buffer for "
+                       "reading switch capabilities.\n",
+                       mmc_hostname(host));
+               return;
+       }
+
+       list_for_each_entry(card, &host->cards, node) {
+               if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT))
+                       continue;
+               if (!mmc_card_sd(card))
+                       continue;
+               if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+                       continue;
+
+               err = mmc_select_card(host, card);
+               if (err != MMC_ERR_NONE) {
+                       mmc_card_set_dead(card);
+                       continue;
+               }
+
+               memset(&cmd, 0, sizeof(struct mmc_command));
+
+               cmd.opcode = SD_SWITCH;
+               cmd.arg = 0x00FFFFF1;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               memset(&data, 0, sizeof(struct mmc_data));
+
+               mmc_set_data_timeout(&data, card, 0);
+
+               data.blksz = 64;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               memset(&mrq, 0, sizeof(struct mmc_request));
+
+               mrq.cmd = &cmd;
+               mrq.data = &data;
+
+               sg_init_one(&sg, status, 64);
+
+               mmc_wait_for_req(host, &mrq);
+
+               if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
+                       mmc_card_set_dead(card);
+                       continue;
+               }
+
+               if (status[13] & 0x02)
+                       card->sw_caps.hs_max_dtr = 50000000;
+
+               memset(&cmd, 0, sizeof(struct mmc_command));
+
+               cmd.opcode = SD_SWITCH;
+               cmd.arg = 0x80FFFFF1;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               memset(&data, 0, sizeof(struct mmc_data));
+
+               mmc_set_data_timeout(&data, card, 0);
+
+               data.blksz = 64;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               memset(&mrq, 0, sizeof(struct mmc_request));
+
+               mrq.cmd = &cmd;
+               mrq.data = &data;
+
+               sg_init_one(&sg, status, 64);
+
+               mmc_wait_for_req(host, &mrq);
+
+               if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) {
+                       mmc_card_set_dead(card);
+                       continue;
+               }
+
+               if ((status[16] & 0xF) != 1) {
+                       printk(KERN_WARNING "%s: Problem switching card "
+                               "into high-speed mode!\n",
+                               mmc_hostname(host));
+                       continue;
+               }
+
+               mmc_card_set_highspeed(card);
+       }
+
+       kfree(status);
+
+       mmc_deselect_cards(host);
+}
+
 static unsigned int mmc_calculate_clock(struct mmc_host *host)
 {
        struct mmc_card *card;
@@ -1164,9 +1274,12 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)
 
        list_for_each_entry(card, &host->cards, node)
                if (!mmc_card_dead(card)) {
-                       if (mmc_card_highspeed(card)) {
+                       if (mmc_card_highspeed(card) && mmc_card_sd(card)) {
+                               if (max_dtr > card->sw_caps.hs_max_dtr)
+                                       max_dtr = card->sw_caps.hs_max_dtr;
+                       } else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) {
                                if (max_dtr > card->ext_csd.hs_max_dtr)
-                                       max_dtr = card->ext_csd.hs_max_dtr;
+                                       max_dtr = card->ext_csd.hs_max_dtr;
                        } else if (max_dtr > card->csd.max_dtr) {
                                max_dtr = card->csd.max_dtr;
                        }
@@ -1288,9 +1401,10 @@ static void mmc_setup(struct mmc_host *host)
 
        mmc_read_csds(host);
 
-       if (host->mode == MMC_MODE_SD)
+       if (host->mode == MMC_MODE_SD) {
                mmc_read_scrs(host);
-       else
+               mmc_read_switch_caps(host);
+       } else
                mmc_process_ext_csds(host);
 }
 
index ce25256..d0e6a54 100644 (file)
@@ -50,6 +50,10 @@ struct sd_scr {
 #define SD_SCR_BUS_WIDTH_4     (1<<2)
 };
 
+struct sd_switch_caps {
+       unsigned int            hs_max_dtr;
+};
+
 struct mmc_host;
 
 /*
@@ -66,7 +70,7 @@ struct mmc_card {
 #define MMC_STATE_BAD          (1<<2)          /* unrecognised device */
 #define MMC_STATE_SDCARD       (1<<3)          /* is an SD card */
 #define MMC_STATE_READONLY     (1<<4)          /* card is read-only */
-#define MMC_STATE_HIGHSPEED    (1<<5)          /* card is in mmc4 highspeed mode */
+#define MMC_STATE_HIGHSPEED    (1<<5)          /* card is in high speed mode */
        u32                     raw_cid[4];     /* raw card CID */
        u32                     raw_csd[4];     /* raw card CSD */
        u32                     raw_scr[2];     /* raw card SCR */
@@ -74,6 +78,7 @@ struct mmc_card {
        struct mmc_csd          csd;            /* card specific */
        struct mmc_ext_csd      ext_csd;        /* mmc v4 extended card specific */
        struct sd_scr           scr;            /* extra SD information */
+       struct sd_switch_caps   sw_caps;        /* switch (CMD6) caps */
 };
 
 #define mmc_card_present(c)    ((c)->state & MMC_STATE_PRESENT)
index 45c51fd..2dce60c 100644 (file)
@@ -82,6 +82,7 @@
   /* class 8 */
 /* This is basically the same command as for MMC with some quirks. */
 #define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
+#define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */
 
   /* Application commands */
 #define SD_APP_SET_BUS_WIDTH      6   /* ac   [1:0] bus width    R1  */
  */
 
 /*
+ * SD_SWITCH argument format:
+ *
+ *      [31] Check (0) or switch (1)
+ *      [30:24] Reserved (0)
+ *      [23:20] Function group 6
+ *      [19:16] Function group 5
+ *      [15:12] Function group 4
+ *      [11:8] Function group 3
+ *      [7:4] Function group 2
+ *      [3:0] Function group 1
+ */
+
+/*
   MMC status in R1
   Type
        e : error bit
@@ -285,6 +299,14 @@ struct _mmc_csd {
 #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */
 
 /*
+ * SCR field definitions
+ */
+
+#define SCR_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.01 */
+#define SCR_SPEC_VER_1      1           /* Implements system specification 1.10 */
+#define SCR_SPEC_VER_2      2           /* Implements system specification 2.00 */
+
+/*
  * SD bus widths
  */
 #define SD_BUS_WIDTH_1      0