diff options
author | Colin Cross <ccross@android.com> | 2011-05-05 21:48:54 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:37:09 -0800 |
commit | c3d7a687a3c47acda1573d1030f59e8f2253d57e (patch) | |
tree | 39878dafd992fc77ddc195e6073a122b2d5a2853 /drivers/mmc | |
parent | 285af9765fa89abd47c7820ca5ce415efeb9329e (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.c | 63 |
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; } |