mmc: omap_hsmmc: add runtime pm support

* Add runtime pm support to HSMMC host controller.
* Use runtime pm API to enable/disable HSMMC clock.
* Use runtime autosuspend APIs to enable auto suspend delay.

Based on OMAP HSMMC runtime implementation by Kevin Hilman and
Kishore Kadiyala.

Signed-off-by: Balaji T K <balajitk@ti.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
Balaji T K 2011-07-01 22:09:35 +05:30 committed by Chris Ball
parent 7a8c2cef3d
commit fa4aa2d48d

View file

@ -33,6 +33,7 @@
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
@ -116,6 +117,7 @@
#define OMAP_MMC4_DEVID 3
#define OMAP_MMC5_DEVID 4
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "omap_hsmmc"
@ -1156,8 +1158,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
int ret;
/* Disable the clocks */
clk_disable(host->fclk);
clk_disable(host->iclk);
pm_runtime_put_sync(host->dev);
if (host->got_dbclk)
clk_disable(host->dbclk);
@ -1168,8 +1169,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
if (!ret)
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
vdd);
clk_enable(host->iclk);
clk_enable(host->fclk);
pm_runtime_get_sync(host->dev);
if (host->got_dbclk)
clk_enable(host->dbclk);
@ -1605,7 +1605,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
u32 con;
int do_send_init_stream = 0;
mmc_host_enable(host->mmc);
pm_runtime_get_sync(host->dev);
if (ios->power_mode != host->power_mode) {
switch (ios->power_mode) {
@ -1700,8 +1700,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
if (host->power_mode == MMC_POWER_OFF)
mmc_host_disable(host->mmc);
pm_runtime_put_autosuspend(host->dev);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@ -1760,13 +1759,9 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
int err;
err = clk_enable(host->fclk);
if (err)
return err;
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
omap_hsmmc_context_restore(host);
pm_runtime_get_sync(host->dev);
return 0;
}
@ -1774,9 +1769,9 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
omap_hsmmc_context_save(host);
clk_disable(host->fclk);
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
}
@ -1819,10 +1814,7 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
return 0;
}
if (clk_enable(host->fclk) != 0) {
seq_printf(s, "can't read the regs\n");
return 0;
}
pm_runtime_get_sync(host->dev);
seq_printf(s, "SYSCONFIG:\t0x%08x\n",
OMAP_HSMMC_READ(host->base, SYSCONFIG));
@ -1839,7 +1831,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
seq_printf(s, "CAPA:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CAPA));
clk_disable(host->fclk);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
}
@ -1960,18 +1953,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_DISABLE;
if (clk_enable(host->iclk) != 0) {
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
}
if (mmc_host_enable(host->mmc) != 0) {
clk_disable(host->iclk);
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
}
pm_runtime_enable(host->dev);
pm_runtime_get_sync(host->dev);
pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(host->dev);
if (cpu_is_omap2430()) {
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@ -2098,6 +2083,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
}
omap_hsmmc_debugfs(mmc);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
@ -2113,8 +2100,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
clk_put(host->fclk);
clk_put(host->iclk);
if (host->got_dbclk) {
@ -2138,7 +2125,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
struct resource *res;
if (host) {
mmc_host_enable(host->mmc);
pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);
if (host->use_reg)
omap_hsmmc_reg_put(host);
@ -2149,8 +2136,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
free_irq(mmc_slot(host).card_detect_irq, host);
flush_work_sync(&host->mmc_carddetect_work);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
clk_put(host->iclk);
if (host->got_dbclk) {
@ -2182,6 +2169,7 @@ static int omap_hsmmc_suspend(struct device *dev)
return 0;
if (host) {
pm_runtime_get_sync(host->dev);
host->suspended = 1;
if (host->pdata->suspend) {
ret = host->pdata->suspend(&pdev->dev,
@ -2196,13 +2184,11 @@ static int omap_hsmmc_suspend(struct device *dev)
}
cancel_work_sync(&host->mmc_carddetect_work);
ret = mmc_suspend_host(host->mmc);
mmc_host_enable(host->mmc);
if (ret == 0) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
if (host->got_dbclk)
clk_disable(host->dbclk);
} else {
@ -2214,9 +2200,8 @@ static int omap_hsmmc_suspend(struct device *dev)
dev_dbg(mmc_dev(host->mmc),
"Unmask interrupt failed\n");
}
mmc_host_disable(host->mmc);
}
pm_runtime_put_sync(host->dev);
}
return ret;
}
@ -2232,14 +2217,7 @@ static int omap_hsmmc_resume(struct device *dev)
return 0;
if (host) {
ret = clk_enable(host->iclk);
if (ret)
goto clk_en_err;
if (mmc_host_enable(host->mmc) != 0) {
clk_disable(host->iclk);
goto clk_en_err;
}
pm_runtime_get_sync(host->dev);
if (host->got_dbclk)
clk_enable(host->dbclk);
@ -2259,14 +2237,13 @@ static int omap_hsmmc_resume(struct device *dev)
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
}
return ret;
clk_en_err:
dev_dbg(mmc_dev(host->mmc),
"Failed to enable MMC clocks during resume\n");
return ret;
}
#else
@ -2274,9 +2251,33 @@ static int omap_hsmmc_resume(struct device *dev)
#define omap_hsmmc_resume NULL
#endif
static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_save(host);
dev_dbg(mmc_dev(host->mmc), "disabled\n");
return 0;
}
static int omap_hsmmc_runtime_resume(struct device *dev)
{
struct omap_hsmmc_host *host;
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_restore(host);
dev_dbg(mmc_dev(host->mmc), "enabled\n");
return 0;
}
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
.runtime_suspend = omap_hsmmc_runtime_suspend,
.runtime_resume = omap_hsmmc_runtime_resume,
};
static struct platform_driver omap_hsmmc_driver = {