diff mbox

[v2,07/12] mmc: sd: set current limit for uhs cards

Message ID 1299238369-1768-8-git-send-email-arindam.nath@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Arindam Nath March 4, 2011, 11:32 a.m. UTC
None
diff mbox

Patch

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ec0d8e6..df98a2c 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -550,6 +550,46 @@  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)
+{
+	struct mmc_host *host = card->host;
+	int mmc_host_max_current_180, current_limit;
+	int err;
+
+	/* sanity check */
+	if (!host->ops->get_max_current_180)
+		return 0;
+
+	/* Maximum current supported by host at 1.8V */
+	mmc_host_max_current_180 = host->ops->get_max_current_180(host);
+
+	if (mmc_host_max_current_180 >= 800) {
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_800)
+			current_limit = SD_SET_CURRENT_LIMIT_800;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+			current_limit = SD_SET_CURRENT_LIMIT_600;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+	} else if (mmc_host_max_current_180 >= 600) {
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_600)
+			current_limit = SD_SET_CURRENT_LIMIT_600;
+		else if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+	} else if (mmc_host_max_current_180 >= 400)
+		if (card->sw_caps.uhs_curr_limit & SD_MAX_CURRENT_400)
+			current_limit = SD_SET_CURRENT_LIMIT_400;
+
+	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
  */
@@ -590,6 +630,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);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f127fa2..245cc39 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1462,12 +1462,36 @@  static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc)
 	return -EAGAIN;
 }
 
+static int sdhci_get_max_current_180(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	u32 max_current_caps;
+	unsigned long flags;
+	int max_current_180;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/* Maximum current is 4 times the register value for 1.8V */
+	max_current_180 = ((max_current_caps & SDHCI_MAX_CURRENT_180_MASK) >>
+			   SDHCI_MAX_CURRENT_180_SHIFT) *
+			   SDHCI_MAX_CURRENT_MULTIPLIER;
+
+	return max_current_180;
+}
+
 static const struct mmc_host_ops sdhci_ops = {
 	.request	= sdhci_request,
 	.set_ios	= sdhci_set_ios,
 	.get_ro		= sdhci_get_ro,
 	.enable_sdio_irq = sdhci_enable_sdio_irq,
 	.start_signal_voltage_switch	= sdhci_start_signal_voltage_switch,
+	.get_max_current_180		= sdhci_get_max_current_180,
 };
 
 /*****************************************************************************\
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0b24c41..a6811ae 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -98,6 +98,15 @@  struct sd_switch_caps {
 #define SD_DRIVER_TYPE_C	0x04
 #define SD_DRIVER_TYPE_D	0x08
 	unsigned int		uhs_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 {
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 4dfff6d..e84cd05 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -128,6 +128,7 @@  struct mmc_host_ops {
 	void	(*init_card)(struct mmc_host *host, struct mmc_card *card);
 
 	int	(*start_signal_voltage_switch)(struct mmc_host *host);
+	int	(*get_max_current_180)(struct mmc_host *mmc);
 };
 
 struct mmc_card;