mmc: sd: set current limit for uhs cards
authorArindam Nath <arindam.nath@amd.com>
Thu, 5 May 2011 06:49:02 +0000 (12:19 +0530)
committerChris Ball <cjb@laptop.org>
Wed, 25 May 2011 03:53:45 +0000 (23:53 -0400)
We decide on the current limit to be set for the card based on the
Capability of Host Controller to provide current at 1.8V signalling,
and the maximum current limit of the card as indicated by CMD6
mode 0. We then set the current limit for the card using CMD6 mode 1.
As per the Physical Layer Spec v3.01, the current limit switch is
only applicable for SDR50, SDR104, and DDR50 bus speed modes. For
other UHS-I modes, we set the default current limit of 200mA.

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
include/linux/mmc/card.h
include/linux/mmc/host.h

index 6970b82171f727f24192e0adfc0166981249c41b..8e2d8012e4cb5f48b0bd1889bdc95a6f6d72e427 100644 (file)
@@ -512,6 +512,64 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
        return 0;
 }
 
+static int sd_set_current_limit(struct mmc_card *card, u8 *status)
+{
+       int current_limit = 0;
+       int err;
+
+       /*
+        * Current limit switch is only defined for SDR50, SDR104, and DDR50
+        * bus speed modes. For other bus speed modes, we set the default
+        * current limit of 200mA.
+        */
+       if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) ||
+           (card->sd_bus_speed == UHS_SDR104_BUS_SPEED) ||
+           (card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) {
+               if (card->host->caps & MMC_CAP_MAX_CURRENT_800) {
+                       if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800)
+                               current_limit = SD_SET_CURRENT_LIMIT_800;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_600)
+                               current_limit = SD_SET_CURRENT_LIMIT_600;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_400)
+                               current_limit = SD_SET_CURRENT_LIMIT_400;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_200)
+                               current_limit = SD_SET_CURRENT_LIMIT_200;
+               } else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) {
+                       if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600)
+                               current_limit = SD_SET_CURRENT_LIMIT_600;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_400)
+                               current_limit = SD_SET_CURRENT_LIMIT_400;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_200)
+                               current_limit = SD_SET_CURRENT_LIMIT_200;
+               } else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) {
+                       if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
+                               current_limit = SD_SET_CURRENT_LIMIT_400;
+                       else if (card->sw_caps.sd3_curr_limit &
+                                       SD_MAX_CURRENT_200)
+                               current_limit = SD_SET_CURRENT_LIMIT_200;
+               } else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) {
+                       if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
+                               current_limit = SD_SET_CURRENT_LIMIT_200;
+               }
+       } else
+               current_limit = SD_SET_CURRENT_LIMIT_200;
+
+       err = mmc_sd_switch(card, 1, 3, current_limit, status);
+       if (err)
+               return err;
+
+       if (((status[15] >> 4) & 0x0F) != current_limit)
+               printk(KERN_WARNING "%s: Problem setting current limit!\n",
+                       mmc_hostname(card->host));
+
+       return 0;
+}
+
 /*
  * UHS-I specific initialization procedure
  */
@@ -550,6 +608,11 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
 
        /* Set bus speed mode of the card */
        err = sd_set_bus_speed_mode(card, status);
+       if (err)
+               goto out;
+
+       /* Set current limit for the card */
+       err = sd_set_current_limit(card, status);
 
 out:
        kfree(status);
index 8994493dd940e6abad61136a41b40cff93c703ef..2a15aad2eba58418b4e5bca55e9432d407fba013 100644 (file)
@@ -2216,6 +2216,16 @@ int sdhci_add_host(struct sdhci_host *host)
 
                if (max_current_180 > 150)
                        mmc->caps |= MMC_CAP_SET_XPC_180;
+
+               /* Maximum current capabilities of the host at 1.8V */
+               if (max_current_180 >= 800)
+                       mmc->caps |= MMC_CAP_MAX_CURRENT_800;
+               else if (max_current_180 >= 600)
+                       mmc->caps |= MMC_CAP_MAX_CURRENT_600;
+               else if (max_current_180 >= 400)
+                       mmc->caps |= MMC_CAP_MAX_CURRENT_400;
+               else
+                       mmc->caps |= MMC_CAP_MAX_CURRENT_200;
        }
 
        mmc->ocr_avail = ocr_avail;
index 4ef6ded6347d034ba905239b98da749c215813f3..47b5ad3960b7d302d37649a5e7909cda4c4f4acc 100644 (file)
@@ -105,6 +105,15 @@ struct sd_switch_caps {
 #define SD_DRIVER_TYPE_C       0x04
 #define SD_DRIVER_TYPE_D       0x08
        unsigned int            sd3_curr_limit;
+#define SD_SET_CURRENT_LIMIT_200       0
+#define SD_SET_CURRENT_LIMIT_400       1
+#define SD_SET_CURRENT_LIMIT_600       2
+#define SD_SET_CURRENT_LIMIT_800       3
+
+#define SD_MAX_CURRENT_200     (1 << SD_SET_CURRENT_LIMIT_200)
+#define SD_MAX_CURRENT_400     (1 << SD_SET_CURRENT_LIMIT_400)
+#define SD_MAX_CURRENT_600     (1 << SD_SET_CURRENT_LIMIT_600)
+#define SD_MAX_CURRENT_800     (1 << SD_SET_CURRENT_LIMIT_800)
 };
 
 struct sdio_cccr {
index 62375992bdd68edb793c1351801870122f50209b..52b5dc914a8cf9ffafda1dee2ae3f384007d0465 100644 (file)
@@ -203,6 +203,10 @@ struct mmc_host {
 #define MMC_CAP_DRIVER_TYPE_A  (1 << 23)       /* Host supports Driver Type A */
 #define MMC_CAP_DRIVER_TYPE_C  (1 << 24)       /* Host supports Driver Type C */
 #define MMC_CAP_DRIVER_TYPE_D  (1 << 25)       /* Host supports Driver Type D */
+#define MMC_CAP_MAX_CURRENT_200        (1 << 26)       /* Host max current limit is 200mA */
+#define MMC_CAP_MAX_CURRENT_400        (1 << 27)       /* Host max current limit is 400mA */
+#define MMC_CAP_MAX_CURRENT_600        (1 << 28)       /* Host max current limit is 600mA */
+#define MMC_CAP_MAX_CURRENT_800        (1 << 29)       /* Host max current limit is 800mA */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */