summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/mmci.c
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@stericsson.com>2011-10-17 12:16:40 +0200
committerUlf HANSSON <ulf.hansson@stericsson.com>2011-10-21 12:54:59 +0200
commit4055a31f78bb0fcfcf0323386fc7c59466fa144f (patch)
tree5bdede86eb42c9733c7c126b7e57ba2057aa7b1c /drivers/mmc/host/mmci.c
parent42b4821b4731cdc19498bc654178b2d073b64ad1 (diff)
mmc: mmci: Support for pm_runtime
Runtime PM support is added to dynamically control the enable/disable of the clock and vcore regulator. In runtime_suspend the register values are saved; in runtime_resume register values are restored. We also make use of the runtime_autosuspend with a timeout value set to 50 ms. Change-Id: I6d20a9d5e7727d0cbcac1bb7af11ee0f1c28edc5 Signed-off-by: Ulf Hansson <ulf.hansson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34475
Diffstat (limited to 'drivers/mmc/host/mmci.c')
-rw-r--r--drivers/mmc/host/mmci.c156
1 files changed, 131 insertions, 25 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8d901ba1e54..4227e3a0d48 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -182,6 +182,9 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
host->cmd = NULL;
mmc_request_done(host->mmc, mrq);
+
+ pm_runtime_mark_last_busy(host->mmc->parent);
+ pm_runtime_put_autosuspend(host->mmc->parent);
}
static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
@@ -1019,6 +1022,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
return;
}
+ pm_runtime_get_sync(mmc->parent);
+
spin_lock_irqsave(&host->lock, flags);
host->mrq = mrq;
@@ -1042,6 +1047,8 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
unsigned long flags;
int ret;
+ pm_runtime_get_sync(mmc->parent);
+
if (host->plat->ios_handler &&
host->plat->ios_handler(mmc_dev(mmc), ios))
dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
@@ -1110,12 +1117,15 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mmci_set_clkreg(host, ios->clock);
- if (host->pwr != pwr) {
- host->pwr = pwr;
+ if (host->pwr_reg != pwr) {
+ host->pwr_reg = pwr;
writel(pwr, host->base + MMCIPOWER);
}
spin_unlock_irqrestore(&host->lock, flags);
+
+ pm_runtime_mark_last_busy(mmc->parent);
+ pm_runtime_put_autosuspend(mmc->parent);
}
static int mmci_get_ro(struct mmc_host *mmc)
@@ -1199,6 +1209,9 @@ static int __devinit mmci_probe(struct amba_device *dev,
host->gpio_wp = -ENOSYS;
host->gpio_cd = -ENOSYS;
host->gpio_cd_irq = -1;
+ host->irqmask0_reg = 0;
+ host->pwr_reg = 0;
+ host->clk_reg = 0;
host->hw_designer = amba_manf(dev);
host->hw_revision = amba_rev(dev);
@@ -1372,14 +1385,11 @@ static int __devinit mmci_probe(struct amba_device *dev,
goto irq0_free;
}
+ host->irqmask0_reg = MCI_IRQENABLE;
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc);
- pm_runtime_enable(mmc->parent);
- if (pm_runtime_get_sync(mmc->parent) < 0)
- dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n");
-
dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n",
mmc_hostname(mmc), amba_part(dev), amba_manf(dev),
amba_rev(dev), (unsigned long long)dev->res.start,
@@ -1387,6 +1397,10 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmci_dma_setup(host);
+ pm_runtime_set_autosuspend_delay(mmc->parent, 50);
+ pm_runtime_use_autosuspend(mmc->parent);
+ pm_runtime_put(mmc->parent);
+
mmc_add_host(mmc);
return 0;
@@ -1424,6 +1438,13 @@ static int __devexit mmci_remove(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
+ /*
+ * Make sure the host is resumed and undo the
+ * pm_runtime_put in probe.
+ */
+ pm_runtime_resume(mmc->parent);
+ pm_runtime_get_noresume(mmc->parent);
+
mmc_remove_host(mmc);
writel(0, host->base + MMCIMASK0);
@@ -1445,6 +1466,7 @@ static int __devexit mmci_remove(struct amba_device *dev)
gpio_free(host->gpio_cd);
iounmap(host->base);
+
clk_disable(host->clk);
clk_put(host->clk);
@@ -1460,49 +1482,134 @@ static int __devexit mmci_remove(struct amba_device *dev)
return 0;
}
-#ifdef CONFIG_PM
-static int mmci_suspend(struct amba_device *dev, pm_message_t state)
+#ifdef CONFIG_SUSPEND
+
+#ifdef CONFIG_PM_RUNTIME
+static void mmci_disable_irq(struct mmci_host *host) {}
+static void mmci_enable_irq(struct mmci_host *host) {}
+#else
+static void mmci_disable_irq(struct mmci_host *host)
{
- struct mmc_host *mmc = amba_get_drvdata(dev);
+ writel(0, host->base + MMCIMASK0);
+}
+static void mmci_enable_irq(struct mmci_host *host)
+{
+ writel(host->irqmask0_reg, host->base + MMCIMASK0);
+}
+#endif
+
+static int mmci_suspend(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
+ pm_runtime_get_sync(mmc->parent);
+
ret = mmc_suspend_host(mmc);
- if (ret == 0)
- writel(0, host->base + MMCIMASK0);
+ if (!ret)
+ mmci_disable_irq(host);
- if (pm_runtime_put_sync(mmc->parent) < 0)
- dev_err(mmc_dev(mmc), "failed pm_runtime_put_sync\n");
+ pm_runtime_put_sync_suspend(mmc->parent);
}
return ret;
}
-static int mmci_resume(struct amba_device *dev)
+static int mmci_resume(struct device *dev)
{
- struct mmc_host *mmc = amba_get_drvdata(dev);
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- if (pm_runtime_get_sync(mmc->parent) < 0)
- dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n");
-
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
-
+ mmci_enable_irq(host);
ret = mmc_resume_host(mmc);
}
return ret;
}
-#else
-#define mmci_suspend NULL
-#define mmci_resume NULL
#endif
+#ifdef CONFIG_PM_RUNTIME
+static int mmci_runtime_suspend(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+ unsigned long flags;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Save registers for POWER, CLOCK and IRQMASK0 */
+ host->irqmask0_reg = readl(host->base + MMCIMASK0);
+ host->pwr_reg = readl(host->base + MMCIPOWER);
+ host->clk_reg = readl(host->base + MMCICLOCK);
+
+ /*
+ * Make sure we do not get any interrupts when we disabled the
+ * clock and the regulator and as well make sure to clear the
+ * registers for clock and power.
+ */
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIPOWER);
+ writel(0, host->base + MMCICLOCK);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ clk_disable(host->clk);
+ amba_vcore_disable(adev);
+ }
+
+ return 0;
+}
+
+static int mmci_runtime_resume(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+ unsigned long flags;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ amba_vcore_enable(adev);
+ clk_enable(host->clk);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Restore registers for POWER, CLOCK and IRQMASK0 */
+ writel(host->clk_reg, host->base + MMCICLOCK);
+ writel(host->pwr_reg, host->base + MMCIPOWER);
+ writel(host->irqmask0_reg, host->base + MMCIMASK0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
+ return 0;
+}
+
+static int mmci_runtime_idle(struct device *dev)
+{
+ pm_runtime_suspend(dev);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops mmci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
+ SET_RUNTIME_PM_OPS(mmci_runtime_suspend,
+ mmci_runtime_resume,
+ mmci_runtime_idle)
+};
+
static struct amba_id mmci_ids[] = {
{
.id = 0x00041180,
@@ -1546,11 +1653,10 @@ static struct amba_id mmci_ids[] = {
static struct amba_driver mmci_driver = {
.drv = {
.name = DRIVER_NAME,
+ .pm = &mmci_dev_pm_ops,
},
.probe = mmci_probe,
.remove = __devexit_p(mmci_remove),
- .suspend = mmci_suspend,
- .resume = mmci_resume,
.id_table = mmci_ids,
};