summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorKevin Hilman <khilman@deeprootsystems.com>2010-06-01 02:08:59 +0530
committerSantosh Shilimkar <santosh.shilimkar@ti.com>2010-06-01 11:18:47 +0530
commitec948ffddbffe36c2d6ac5fa16adacf0334b5502 (patch)
tree1a3d942727db09f3f27d891b31bbac8fc34662ba /drivers/mmc
parentdc1b02f78418488ee68fdee4b86b6eac4381ab2a (diff)
OMAP2/3 MMC: initial conversion to runtime PM
Convert the HSMMC driver to use the runtime PM layer. A notable aspect of this is that the use of the dev_attr data from the omap_hwmod allows the redaction of all of the integration-specific hacks inside this driver. Regulator control has not yet been converted; the driver still uses the platform_data set_power hook. In converting to runtime PM layer, the clock frameworks is no longer used for fclk and iclk. Instead, the runtime PM get and put functions are used. These result in calls into the OMAP runtime PM core which internally uses the omap_device API to disable/re-enable the device. (Note that the 2430 debounce clock is not currently handled here, only the fclk and iclk are managed.) Based on an initial conversion of this driver to use omap_device by by Paul Walmsely: http://marc.info/?l=linux-omap&m=124419789124570&w=2 and then converted to use runtime PM API instead of omap_device API. The omap_hsmmc driver has some hacks in this version to compensate for the fact that the main runtime PM core prevents runtime transitions during suspend. This prevens us from using common hooks for runtime PM and static PM. Current workaround is to hack in some extra put/get calls in the suspend/resume path while this issue is being discussed on linux-pm. Cc: Paul Walmsley <paul@pwsan.com> Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Kishore Kadiyala <kishore.kadiyala@ti.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c168
1 files changed, 87 insertions, 81 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e7dd9fef4610..4e30c7199bc7 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -32,6 +32,8 @@
#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>
@@ -1095,8 +1097,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);
@@ -1107,8 +1108,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);
@@ -1477,6 +1477,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
+ struct omap_mmc_platform_data *pdata = host->pdata;
u16 dsor = 0;
unsigned long regval;
unsigned long timeout;
@@ -1523,7 +1524,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
}
- if (host->id == OMAP_MMC1_DEVID) {
+ if (pdata->dev_attr->flags & MMC_INTERNAL_XCVR) {
/* Only MMC1 can interface at 3V without some flavor
* of external transceiver; but they all handle 1.8V.
*/
@@ -1607,7 +1608,7 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
u32 hctl, capa, value;
/* Only MMC1 supports 3.0V */
- if (host->id == OMAP_MMC1_DEVID) {
+ if (host->pdata->dev_attr->flags & MMC_INTERNAL_XCVR) {
hctl = SDVS30;
capa = VS30 | VS18;
} else {
@@ -1650,8 +1651,8 @@ enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
/* Handler for [ENABLED -> DISABLED] transition */
static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
{
- omap_hsmmc_context_save(host);
- clk_disable(host->fclk);
+ pm_runtime_put_sync(host->dev);
+
host->dpm_state = DISABLED;
dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n");
@@ -1670,8 +1671,8 @@ static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
if (!mmc_try_claim_host(host->mmc))
return 0;
- clk_enable(host->fclk);
- omap_hsmmc_context_restore(host);
+ pm_runtime_get_sync(host->dev);
+
if (mmc_card_can_sleep(host->mmc)) {
err = mmc_card_sleep(host->mmc);
if (err < 0) {
@@ -1687,7 +1688,7 @@ static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
new_state == CARDSLEEP);
/* FIXME: turn off bus power and perhaps interrupts too */
- clk_disable(host->fclk);
+ pm_runtime_put_sync(host->dev);
host->dpm_state = new_state;
mmc_release_host(host->mmc);
@@ -1741,13 +1742,8 @@ static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
/* Handler for [DISABLED -> ENABLED] transition */
static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host)
{
- int err;
+ pm_runtime_get_sync(host->dev);
- err = clk_enable(host->fclk);
- if (err < 0)
- return err;
-
- omap_hsmmc_context_restore(host);
host->dpm_state = ENABLED;
dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
@@ -1761,8 +1757,8 @@ static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
if (!mmc_try_claim_host(host->mmc))
return 0;
- clk_enable(host->fclk);
- omap_hsmmc_context_restore(host);
+ pm_runtime_get_sync(host->dev);
+
if (mmc_slot(host).set_sleep)
mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
host->vdd, host->dpm_state == CARDSLEEP);
@@ -1782,9 +1778,8 @@ static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
/* Handler for [OFF -> ENABLED] transition */
static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
{
- clk_enable(host->fclk);
+ pm_runtime_get_sync(host->dev);
- omap_hsmmc_context_restore(host);
omap_hsmmc_conf_bus_power(host);
mmc_power_restore_host(host->mmc);
@@ -1843,32 +1838,29 @@ static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy)
}
}
-static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
+static int omap_hsmmc_enable_simple(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);
+
+ dev_dbg(mmc_dev(host->mmc), "enabled\n");
return 0;
}
-static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable_simple(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_put_sync(host->dev);
+
+ dev_dbg(mmc_dev(host->mmc), "idle\n");
return 0;
}
static const struct mmc_host_ops omap_hsmmc_ops = {
- .enable = omap_hsmmc_enable_fclk,
- .disable = omap_hsmmc_disable_fclk,
+ .enable = omap_hsmmc_enable_simple,
+ .disable = omap_hsmmc_disable_simple,
.request = omap_hsmmc_request,
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
@@ -1912,10 +1904,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));
@@ -1932,7 +1921,7 @@ 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_put_sync(host->dev);
return 0;
}
@@ -2062,18 +2051,17 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
/* we start off in DISABLED state */
host->dpm_state = DISABLED;
- if (mmc_host_enable(host->mmc) != 0) {
- clk_put(host->iclk);
- clk_put(host->fclk);
- goto err1;
- }
+ pm_runtime_enable(host->dev);
+#ifndef CONFIG_PM_RUNTIME
+ /*
+ * If runtime PM is not enabled, ensure clocks are always enabled.
+ */
+ clk_enable(host->iclk);
+ clk_enable(host->fclk);
+#endif
- if (clk_enable(host->iclk) != 0) {
- mmc_host_disable(host->mmc);
- clk_put(host->iclk);
- clk_put(host->fclk);
+ if (mmc_host_enable(host->mmc) != 0)
goto err1;
- }
if (cpu_is_omap2430()) {
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@@ -2115,32 +2103,19 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_conf_bus_power(host);
- /* Select DMA lines */
- switch (host->id) {
- case OMAP_MMC1_DEVID:
- host->dma_line_tx = OMAP24XX_DMA_MMC1_TX;
- host->dma_line_rx = OMAP24XX_DMA_MMC1_RX;
- break;
- case OMAP_MMC2_DEVID:
- host->dma_line_tx = OMAP24XX_DMA_MMC2_TX;
- host->dma_line_rx = OMAP24XX_DMA_MMC2_RX;
- break;
- case OMAP_MMC3_DEVID:
- host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
- host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
- break;
- case OMAP_MMC4_DEVID:
- host->dma_line_tx = OMAP44XX_DMA_MMC4_TX;
- host->dma_line_rx = OMAP44XX_DMA_MMC4_RX;
- break;
- case OMAP_MMC5_DEVID:
- host->dma_line_tx = OMAP44XX_DMA_MMC5_TX;
- host->dma_line_rx = OMAP44XX_DMA_MMC5_RX;
- break;
- default:
- dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+ if (!res) {
+ dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
+ goto err_irq;
+ }
+ host->dma_line_tx = res->start;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+ if (!res) {
+ dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
goto err_irq;
}
+ host->dma_line_rx = res->end;
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
@@ -2221,9 +2196,9 @@ err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
mmc_host_disable(host->mmc);
- clk_disable(host->iclk);
clk_put(host->fclk);
clk_put(host->iclk);
+
if (host->got_dbclk) {
clk_disable(host->dbclk);
clk_put(host->dbclk);
@@ -2257,7 +2232,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
flush_scheduled_work();
mmc_host_disable(host->mmc);
- clk_disable(host->iclk);
+ pm_runtime_suspend(host->dev);
+
clk_put(host->fclk);
clk_put(host->iclk);
if (host->got_dbclk) {
@@ -2313,7 +2289,7 @@ static int omap_hsmmc_suspend(struct device *dev)
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 {
@@ -2325,6 +2301,12 @@ static int omap_hsmmc_suspend(struct device *dev)
dev_dbg(mmc_dev(host->mmc),
"Unmask interrupt failed\n");
}
+
+ /*
+ * Directly call platform_bus suspend. runtime PM
+ * PM lock is held during system suspend, so will
+ * not be auto-matically called
+ */
mmc_host_disable(host->mmc);
}
@@ -2343,12 +2325,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;
}
@@ -2387,9 +2364,38 @@ clk_en_err:
#define omap_hsmmc_resume NULL
#endif
+/* called just before device is disabled */
+static int omap_hsmmc_runtime_suspend(struct device *dev)
+{
+ struct omap_hsmmc_host *host;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ host = platform_get_drvdata(to_platform_device(dev));
+ omap_hsmmc_context_save(host);
+
+ return 0;
+}
+
+/* called after device is (re)enabled, ONLY if context was lost */
+static int omap_hsmmc_runtime_resume(struct device *dev)
+{
+ struct omap_hsmmc_host *host;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ host = platform_get_drvdata(to_platform_device(dev));
+ omap_hsmmc_context_restore(host);
+
+ 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 = {