summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2011-05-05 21:48:54 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:37:09 -0800
commitc3d7a687a3c47acda1573d1030f59e8f2253d57e (patch)
tree39878dafd992fc77ddc195e6073a122b2d5a2853 /drivers/mmc
parent285af9765fa89abd47c7820ca5ce415efeb9329e (diff)
mmc: host: sdhci-tegra: Add set_clock op
Add a set_clock op to sdhci-tegra to allow the clock to be disabled. Also add suspend and resume ops to disable the clock before suspend, and enable it during resume. Change-Id: I239cb769b54c83a1bd4a80b73e1c4845ce2d165d Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 58be962bcb26..bc5adbf65f59 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/slab.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -26,6 +27,12 @@
#include "sdhci-pltfm.h"
+#define SDHCI_VENDOR_CLOCK_CNTRL 0x100
+
+struct tegra_sdhci_host {
+ bool clk_enabled;
+};
+
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
{
u32 val;
@@ -145,12 +152,48 @@ static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
return 0;
}
+static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
+
+ pr_debug("%s %s %u enabled=%u\n", __func__,
+ mmc_hostname(sdhci->mmc), clock, tegra_host->clk_enabled);
+
+ if (clock && !tegra_host->clk_enabled) {
+ clk_enable(pltfm_host->clk);
+ sdhci_writeb(sdhci, 1, SDHCI_VENDOR_CLOCK_CNTRL);
+ tegra_host->clk_enabled = true;
+ } else if (!clock && tegra_host->clk_enabled) {
+ sdhci_writeb(sdhci, 0, SDHCI_VENDOR_CLOCK_CNTRL);
+ clk_disable(pltfm_host->clk);
+ tegra_host->clk_enabled = false;
+ }
+}
+
+static int tegra_sdhci_suspend(struct sdhci_host *sdhci, pm_message_t state)
+{
+ tegra_sdhci_set_clock(sdhci, 0);
+
+ return 0;
+}
+
+static int tegra_sdhci_resume(struct sdhci_host *sdhci)
+{
+ tegra_sdhci_set_clock(sdhci, 1);
+
+ return 0;
+}
+
static struct sdhci_ops tegra_sdhci_ops = {
.get_ro = tegra_sdhci_get_ro,
.read_l = tegra_sdhci_readl,
.read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel,
.platform_8bit_width = tegra_sdhci_8bit,
+ .set_clock = tegra_sdhci_set_clock,
+ .suspend = tegra_sdhci_suspend,
+ .resume = tegra_sdhci_resume,
};
static struct sdhci_pltfm_data sdhci_tegra_pdata = {
@@ -166,6 +209,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct tegra_sdhci_platform_data *plat;
struct sdhci_host *host;
+ struct tegra_sdhci_host *tegra_host;
struct clk *clk;
int rc;
@@ -183,6 +227,12 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
goto err_no_plat;
}
+ tegra_host = kzalloc(sizeof(struct tegra_sdhci_host), GFP_KERNEL);
+ if (tegra_host == NULL) {
+ dev_err(mmc_dev(host->mmc), "failed to allocate tegra host\n");
+ return -ENOMEM;
+ }
+
#ifdef CONFIG_MMC_EMBEDDED_SDIO
if (plat->mmc_data.embedded_sdio)
mmc_set_embedded_sdio_data(host->mmc,
@@ -247,8 +297,12 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
rc = PTR_ERR(clk);
goto err_clk_get;
}
- clk_enable(clk);
+ rc = clk_enable(clk);
+ if (rc != 0)
+ goto err_clk_put;
pltfm_host->clk = clk;
+ pltfm_host->priv = tegra_host;
+ tegra_host->clk_enabled = true;
host->mmc->pm_caps = plat->pm_flags;
@@ -267,6 +321,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
err_add_host:
clk_disable(pltfm_host->clk);
+err_clk_put:
clk_put(pltfm_host->clk);
err_clk_get:
if (gpio_is_valid(plat->wp_gpio)) {
@@ -289,6 +344,7 @@ err_cd_req:
err_power_req:
err_no_plat:
sdhci_pltfm_free(pdev);
+ kfree(tegra_host);
return rc;
}
@@ -296,6 +352,7 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
struct tegra_sdhci_platform_data *plat;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
@@ -319,10 +376,12 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
gpio_free(plat->power_gpio);
}
- clk_disable(pltfm_host->clk);
+ if (tegra_host->clk_enabled)
+ clk_disable(pltfm_host->clk);
clk_put(pltfm_host->clk);
sdhci_pltfm_free(pdev);
+ kfree(tegra_host);
return 0;
}