summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDeepesh Gujarathi <dgujarathi@nvidia.com>2010-02-12 18:21:59 +0530
committerGary King <gking@nvidia.com>2010-02-12 14:49:31 -0800
commit82ba81e47a1b1ea0457f67e7845b08ccbb1a9f41 (patch)
tree4afefbf97e7274756c34fb73304b3b7d7d0c6e0f /drivers
parent4c8a8f6ce36f9388e980a4641d443a2a847df081 (diff)
tegra: implement mmc_ops enable, disable handlers in sdhci driver
Implement the enable and disable handlers introduced in the mmc_ops structure. Use the enable and disable handlers for dynamic clocking of SD memory devices. SD clock is enabled when the sdhci host is claimed and clk is disabled when host is released. Use delayed suspend for better performance. Change-Id: Ied5e0f00c41abd4f6d0918405356a9983ae06552
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/host/Kconfig11
-rwxr-xr-x[-rw-r--r--]drivers/mmc/host/sdhci.c53
-rwxr-xr-x[-rw-r--r--]drivers/mmc/host/sdhci.h2
3 files changed, 63 insertions, 3 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 3f8a5bb69e42..4b93bc4a82f0 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -36,6 +36,17 @@ config MMC_SDHCI
also need to enable an appropriate bus interface.
If unsure, say N.
+config MMC_SDHCI_DYNAMIC_SDMEM_CLOCK
+ depends on MMC_SDHCI
+ bool "Dynamically control the card clock for SD memory devices"
+ default n
+ help
+ On certain embedded devices, leaving the card clock enabled to
+ SD memory devices with no active transactions can increase power
+ consumption. Enable this option to automatically disable the card
+ clock after transfers complete.
+
+ If unsure, say N here.
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 4e1d6cc337ea..38cace1bfd14 100644..100755
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -18,7 +18,7 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
-
+#include <linux/mmc/card.h>
#include <linux/leds.h>
#include <linux/mmc/host.h>
@@ -35,6 +35,8 @@
#define SDHCI_USE_LEDS_CLASS
#endif
+#define SDHCI_DISABLE_TIMEOUT 50
+
static unsigned int debug_quirks = 0;
static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
@@ -134,6 +136,7 @@ static void sdhci_init(struct sdhci_host *host)
writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ host->last_clock = 0;
}
static void sdhci_activate_led(struct sdhci_host *host)
@@ -930,6 +933,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (clock == 0)
goto out;
+ host->last_clock = clock;
+
div = 0;
if (host->ops->set_clock)
div = host->ops->set_clock(host, clock);
@@ -1177,6 +1182,38 @@ out:
spin_unlock_irqrestore(&host->lock, flags);
}
+#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK
+
+int sdhci_enable(struct mmc_host *mmc) {
+
+ struct sdhci_host *host;
+
+ host = mmc_priv(mmc);
+ if (mmc->card != NULL) {
+ if (mmc->card->type != MMC_TYPE_SDIO) {
+ if (host->last_clock)
+ /* Enable clock */
+ sdhci_set_clock(host, host->last_clock);
+ }
+ }
+ return 0;
+}
+
+int sdhci_disable(struct mmc_host *mmc, int lazy) {
+
+ struct sdhci_host *host;
+
+ host = mmc_priv(mmc);
+ /* Disable clock */
+ if (mmc->card != NULL) {
+ if (mmc->card->type != MMC_TYPE_SDIO) {
+ sdhci_set_clock(host, 0);
+ }
+ }
+ return 0;
+}
+#endif
+
#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
static unsigned int sdhci_get_host_offset(struct mmc_host *mmc) {
struct sdhci_host *host;
@@ -1189,6 +1226,13 @@ static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
+#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK
+ .enable = sdhci_enable,
+ .disable = sdhci_disable,
+#else
+ .enable = NULL,
+ .disable = NULL,
+#endif
.enable_sdio_irq = sdhci_enable_sdio_irq,
#ifdef CONFIG_EMBEDDED_MMC_START_OFFSET
.get_host_offset = sdhci_get_host_offset,
@@ -1260,7 +1304,6 @@ static void sdhci_tasklet_finish(unsigned long param)
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
-
mmc_request_done(host->mmc, mrq);
}
@@ -1707,7 +1750,11 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->data_width >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
-
+#ifdef CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK
+ mmc->caps |= MMC_CAP_DISABLE;
+ /* Use delayed suspend */
+ mmc_set_disable_delay(mmc, msecs_to_jiffies(SDHCI_DISABLE_TIMEOUT));
+#endif
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 9e6359954efc..99f0b2898aab 100644..100755
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -250,6 +250,8 @@ struct sdhci_host {
unsigned int timeout_clk; /* Timeout freq (KHz) */
unsigned int clock; /* Current clock (MHz) */
+ unsigned int last_clock; /* Last used clock (MHz)*/
+
unsigned short power; /* Current voltage */
struct mmc_request *mrq; /* Current request */