aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/omap_hsmmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c268
1 files changed, 137 insertions, 131 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index dedf3da..c507190 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -28,16 +28,20 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/io.h>
#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>
#include <plat/mmc.h>
#include <plat/cpu.h>
+#include <plat/omap-pm.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
@@ -166,6 +170,7 @@ struct omap_hsmmc_host {
unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
+ unsigned int master_clock;
unsigned char bus_mode;
unsigned char power_mode;
u32 *buffer;
@@ -177,7 +182,6 @@ struct omap_hsmmc_host {
int slot_id;
int got_dbclk;
int response_busy;
- int context_loss;
int dpm_state;
int vdd;
int protect_card;
@@ -188,6 +192,30 @@ struct omap_hsmmc_host {
struct omap_mmc_platform_data *pdata;
};
+static void omap_hsmmc_status_notify_cb(int card_present, void *dev_id)
+{
+ struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;
+ unsigned int status, oldstat;
+
+ pr_debug("%s: card_present %d\n", mmc_hostname(host->mmc),
+ card_present);
+
+ if (!mmc_slot(host).mmc_data.status) {
+ mmc_detect_change(host->mmc, 0);
+ return;
+ }
+
+ status = mmc_slot(host).mmc_data.status(mmc_dev(host->mmc));
+
+ oldstat = mmc_slot(host).mmc_data.card_present;
+ mmc_slot(host).mmc_data.card_present = status;
+ if (status ^ oldstat) {
+ pr_debug("%s: Slot status change detected (%d -> %d)\n",
+ mmc_hostname(host->mmc), oldstat, status);
+ mmc_detect_change(host->mmc, 0);
+ }
+}
+
static int omap_hsmmc_card_detect(struct device *dev, int slot)
{
struct omap_mmc_platform_data *mmc = dev->platform_data;
@@ -593,21 +621,11 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
- struct omap_mmc_platform_data *pdata = host->pdata;
- int context_loss = 0;
u32 hctl, capa, con;
u16 dsor = 0;
unsigned long timeout;
- if (pdata->get_context_loss_count) {
- context_loss = pdata->get_context_loss_count(host->dev);
- if (context_loss < 0)
- return 1;
- }
-
- dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
- context_loss == host->context_loss ? "not " : "");
- if (host->context_loss == context_loss)
+ if (!omap_pm_was_context_lost(host->dev))
return 1;
/* Wait for hardware reset */
@@ -676,11 +694,11 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
}
if (ios->clock) {
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
+ dsor = host->master_clock / ios->clock;
if (dsor < 1)
dsor = 1;
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
+ if (host->master_clock / dsor > ios->clock)
dsor++;
if (dsor > 250)
@@ -707,8 +725,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
else
OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
out:
- host->context_loss = context_loss;
-
dev_dbg(mmc_dev(host->mmc), "context is restored\n");
return 0;
}
@@ -718,15 +734,7 @@ out:
*/
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{
- struct omap_mmc_platform_data *pdata = host->pdata;
- int context_loss;
-
- if (pdata->get_context_loss_count) {
- context_loss = pdata->get_context_loss_count(host->dev);
- if (context_loss < 0)
- return;
- host->context_loss = context_loss;
- }
+ return;
}
#else
@@ -1155,8 +1163,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);
@@ -1167,8 +1174,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);
@@ -1534,7 +1540,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) {
@@ -1593,11 +1599,11 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
if (ios->clock) {
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
+ dsor = host->master_clock / ios->clock;
if (dsor < 1)
dsor = 1;
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
+ if (host->master_clock / dsor > ios->clock)
dsor++;
if (dsor > 250)
@@ -1629,10 +1635,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);
- else
- mmc_host_lazy_disable(host->mmc);
+ pm_runtime_put_sync(host->dev);
}
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1709,8 +1712,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");
@@ -1729,12 +1732,12 @@ 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) {
- clk_disable(host->fclk);
+ pm_runtime_put_sync(host->dev);
mmc_release_host(host->mmc);
return err;
}
@@ -1746,7 +1749,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);
@@ -1800,13 +1803,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");
@@ -1820,8 +1818,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);
@@ -1841,9 +1839,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);
@@ -1902,32 +1899,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,
@@ -1953,30 +1947,22 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
{
struct mmc_host *mmc = s->private;
struct omap_hsmmc_host *host = mmc_priv(mmc);
- int context_loss = 0;
- if (host->pdata->get_context_loss_count)
- context_loss = host->pdata->get_context_loss_count(host->dev);
seq_printf(s, "mmc%d:\n"
" enabled:\t%d\n"
" dpm_state:\t%d\n"
" nesting_cnt:\t%d\n"
- " ctx_loss:\t%d:%d\n"
- "\nregs:\n",
+ " ct",
mmc->index, mmc->enabled ? 1 : 0,
- host->dpm_state, mmc->nesting_cnt,
- host->context_loss, context_loss);
+ host->dpm_state, mmc->nesting_cnt);
if (host->suspended || host->dpm_state == OFF) {
seq_printf(s, "host suspended, can't read registers\n");
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));
@@ -1993,7 +1979,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;
}
@@ -2078,6 +2064,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
+ host->master_clock = OMAP_MMC_MASTER_CLOCK;
+ if (mmc_slot(host).features & HSMMC_HAS_48MHZ_MASTER_CLK)
+ host->master_clock = OMAP_MMC_MASTER_CLOCK / 2;
+
platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
@@ -2119,18 +2109,17 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
/* we start off in DISABLED state */
host->dpm_state = DISABLED;
- if (clk_enable(host->iclk) != 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 (mmc_host_enable(host->mmc) != 0) {
- clk_disable(host->iclk);
- 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");
@@ -2168,34 +2157,25 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
+ mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
+ if (mmc_slot(host).mmc_data.built_in)
+ mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY;
+
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,
@@ -2236,7 +2216,12 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
}
pdata->suspend = omap_hsmmc_suspend_cdirq;
pdata->resume = omap_hsmmc_resume_cdirq;
+ } else if (mmc_slot(host).mmc_data.register_status_notify) {
+ mmc_slot(host).mmc_data.register_status_notify(omap_hsmmc_status_notify_cb, host);
}
+ if (mmc_slot(host).mmc_data.status)
+ mmc_slot(host).mmc_data.card_present =
+ mmc_slot(host).mmc_data.status(mmc_dev(host->mmc));
omap_hsmmc_disable_irq(host);
@@ -2275,9 +2260,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);
@@ -2311,7 +2296,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
flush_work_sync(&host->mmc_carddetect_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) {
@@ -2343,6 +2329,8 @@ 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,
@@ -2352,18 +2340,19 @@ static int omap_hsmmc_suspend(struct device *dev)
"Unable to handle MMC board"
" level suspend\n");
host->suspended = 0;
+ pm_runtime_put_sync(host->dev);
return ret;
}
}
cancel_work_sync(&host->mmc_carddetect_work);
+ if (mmc_slot(host).mmc_data.built_in)
+ host->mmc->pm_flags |= MMC_PM_KEEP_POWER;
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 {
@@ -2375,9 +2364,9 @@ 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;
}
@@ -2393,14 +2382,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);
@@ -2421,15 +2403,10 @@ static int omap_hsmmc_resume(struct device *dev)
if (ret == 0)
host->suspended = 0;
- mmc_host_lazy_disable(host->mmc);
+ pm_runtime_put_sync(host->dev);
}
return ret;
-
-clk_en_err:
- dev_dbg(mmc_dev(host->mmc),
- "Failed to enable MMC clocks during resume\n");
- return ret;
}
#else
@@ -2437,9 +2414,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 = {