mmc: sd: add support for uhs bus speed mode selection
authorArindam Nath <arindam.nath@amd.com>
Thu, 5 May 2011 06:49:01 +0000 (12:19 +0530)
committerChris Ball <cjb@laptop.org>
Wed, 25 May 2011 03:53:45 +0000 (23:53 -0400)
This patch adds support for setting UHS-I bus speed mode during UHS-I
initialization procedure. Since both the host and card can support
more than one bus speed, we select the highest speed based on both of
their capabilities. First we set the bus speed mode for the card using
CMD6 mode 1, and then we program the host controller to support the
required speed mode. We also set High Speed Enable in case one of the
UHS-I modes is selected. We take care to reset SD clock before setting
UHS mode in the Host Control2 register, and then re-enable it as per
the Host Controller spec v3.00. We then set the clock frequency for
the UHS-I mode selected.

Tested by Zhangfei Gao with a Toshiba uhs card and general hs card,
on mmp2 in SDMA mode.

Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao <zhangfei.gao@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/core/sd.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
include/linux/mmc/card.h
include/linux/mmc/host.h

index 5b7c99855635e6fda75e25e3c14969cb3b95a74e..6970b82171f727f24192e0adfc0166981249c41b 100644 (file)
@@ -452,6 +452,66 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
        return 0;
 }
 
+static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
+{
+       unsigned int bus_speed = 0, timing = 0;
+       int err;
+
+       /*
+        * If the host doesn't support any of the UHS-I modes, fallback on
+        * default speed.
+        */
+       if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+           MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+               return 0;
+
+       if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
+           (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
+                       bus_speed = UHS_SDR104_BUS_SPEED;
+                       timing = MMC_TIMING_UHS_SDR104;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+       } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+                  (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
+                       bus_speed = UHS_DDR50_BUS_SPEED;
+                       timing = MMC_TIMING_UHS_DDR50;
+                       card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+       } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+                   MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
+                   SD_MODE_UHS_SDR50)) {
+                       bus_speed = UHS_SDR50_BUS_SPEED;
+                       timing = MMC_TIMING_UHS_SDR50;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
+       } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+                   MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
+                  (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
+                       bus_speed = UHS_SDR25_BUS_SPEED;
+                       timing = MMC_TIMING_UHS_SDR25;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+       } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+                   MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
+                   MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
+                   SD_MODE_UHS_SDR12)) {
+                       bus_speed = UHS_SDR12_BUS_SPEED;
+                       timing = MMC_TIMING_UHS_SDR12;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+       }
+
+       card->sd_bus_speed = bus_speed;
+       err = mmc_sd_switch(card, 1, 0, bus_speed, status);
+       if (err)
+               return err;
+
+       if ((status[16] & 0xF) != bus_speed)
+               printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
+                       mmc_hostname(card->host));
+       else {
+               mmc_set_timing(card->host, timing);
+               mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+       }
+
+       return 0;
+}
+
 /*
  * UHS-I specific initialization procedure
  */
@@ -485,6 +545,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 
        /* Set the driver strength for the card */
        err = sd_select_driver_type(card, status);
+       if (err)
+               goto out;
+
+       /* Set bus speed mode of the card */
+       err = sd_set_bus_speed_mode(card, status);
 
 out:
        kfree(status);
index 409cde5970ae2dcf8573409e8710f404b11b1fa6..8994493dd940e6abad61136a41b40cff93c703ef 100644 (file)
@@ -1244,7 +1244,16 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                ctrl &= ~SDHCI_CTRL_HISPD;
 
        if (host->version >= SDHCI_SPEC_300) {
-               u16 ctrl_2;
+               u16 clk, ctrl_2;
+               unsigned int clock;
+
+               /* In case of UHS-I modes, set High Speed Enable */
+               if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+                   (ios->timing == MMC_TIMING_UHS_SDR104) ||
+                   (ios->timing == MMC_TIMING_UHS_DDR50) ||
+                   (ios->timing == MMC_TIMING_UHS_SDR25) ||
+                   (ios->timing == MMC_TIMING_UHS_SDR12))
+                       ctrl |= SDHCI_CTRL_HISPD;
 
                ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
                if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) {
@@ -1267,8 +1276,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                         * need to reset SD Clock Enable before changing High
                         * Speed Enable to avoid generating clock gliches.
                         */
-                       u16 clk;
-                       unsigned int clock;
 
                        /* Reset SD Clock Enable */
                        clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
@@ -1282,6 +1289,33 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        host->clock = 0;
                        sdhci_set_clock(host, clock);
                }
+
+               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)
+                       ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+               else if (ios->timing == MMC_TIMING_UHS_SDR25)
+                       ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+               else if (ios->timing == MMC_TIMING_UHS_SDR50)
+                       ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+               else if (ios->timing == MMC_TIMING_UHS_SDR104)
+                       ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+               else if (ios->timing == MMC_TIMING_UHS_DDR50)
+                       ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+
+               /* Reset SD Clock Enable */
+               clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+               clk &= ~SDHCI_CLOCK_CARD_EN;
+               sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+               sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+
+               /* Re-enable SD Clock */
+               clock = host->clock;
+               host->clock = 0;
+               sdhci_set_clock(host, clock);
        } else
                sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
index 667bf8874be7793c8f0c11f6a426715e6aa0f587..d96f6afcca1fa84ab51ab3c8c947b9cc2e2d0e32 100644 (file)
 #define SDHCI_ACMD12_ERR       0x3C
 
 #define SDHCI_HOST_CONTROL2            0x3E
+#define  SDHCI_CTRL_UHS_MASK           0x0007
+#define   SDHCI_CTRL_UHS_SDR12         0x0000
+#define   SDHCI_CTRL_UHS_SDR25         0x0001
+#define   SDHCI_CTRL_UHS_SDR50         0x0002
+#define   SDHCI_CTRL_UHS_SDR104                0x0003
+#define   SDHCI_CTRL_UHS_DDR50         0x0004
 #define  SDHCI_CTRL_VDD_180            0x0008
 #define  SDHCI_CTRL_DRV_TYPE_MASK      0x0030
 #define   SDHCI_CTRL_DRV_TYPE_B                0x0000
index 539327260dc1107683429a2054bb682b7e4914fa..4ef6ded6347d034ba905239b98da749c215813f3 100644 (file)
@@ -81,7 +81,24 @@ struct sd_ssr {
 
 struct sd_switch_caps {
        unsigned int            hs_max_dtr;
+       unsigned int            uhs_max_dtr;
+#define UHS_SDR104_MAX_DTR     208000000
+#define UHS_SDR50_MAX_DTR      100000000
+#define UHS_DDR50_MAX_DTR      50000000
+#define UHS_SDR25_MAX_DTR      UHS_DDR50_MAX_DTR
+#define UHS_SDR12_MAX_DTR      25000000
        unsigned int            sd3_bus_mode;
+#define UHS_SDR12_BUS_SPEED    0
+#define UHS_SDR25_BUS_SPEED    1
+#define UHS_SDR50_BUS_SPEED    2
+#define UHS_SDR104_BUS_SPEED   3
+#define UHS_DDR50_BUS_SPEED    4
+
+#define SD_MODE_UHS_SDR12      (1 << UHS_SDR12_BUS_SPEED)
+#define SD_MODE_UHS_SDR25      (1 << UHS_SDR25_BUS_SPEED)
+#define SD_MODE_UHS_SDR50      (1 << UHS_SDR50_BUS_SPEED)
+#define SD_MODE_UHS_SDR104     (1 << UHS_SDR104_BUS_SPEED)
+#define SD_MODE_UHS_DDR50      (1 << UHS_DDR50_BUS_SPEED)
        unsigned int            sd3_drv_type;
 #define SD_DRIVER_TYPE_B       0x01
 #define SD_DRIVER_TYPE_A       0x02
@@ -166,6 +183,8 @@ struct mmc_card {
        const char              **info;         /* info strings */
        struct sdio_func_tuple  *tuples;        /* unknown common tuples */
 
+       unsigned int            sd_bus_speed;   /* Bus Speed Mode set for the card */
+
        struct dentry           *debugfs_root;
 };
 
index 949e4d5259898defb452842ed87946317917be71..62375992bdd68edb793c1351801870122f50209b 100644 (file)
@@ -50,6 +50,11 @@ struct mmc_ios {
 #define MMC_TIMING_LEGACY      0
 #define MMC_TIMING_MMC_HS      1
 #define MMC_TIMING_SD_HS       2
+#define MMC_TIMING_UHS_SDR12   MMC_TIMING_LEGACY
+#define MMC_TIMING_UHS_SDR25   MMC_TIMING_SD_HS
+#define MMC_TIMING_UHS_SDR50   3
+#define MMC_TIMING_UHS_SDR104  4
+#define MMC_TIMING_UHS_DDR50   5
 
        unsigned char   ddr;                    /* dual data rate used */