Commit 71d111cd authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Chris Ball

mmc: tmio: maximize power saving

This patch uses runtime PM to allow the system to power down the MMC
controller, when the MMC closk is switched off.
Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent b9269fdd
...@@ -53,6 +53,8 @@ struct tmio_mmc_host { ...@@ -53,6 +53,8 @@ struct tmio_mmc_host {
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
int pm_error; int pm_error;
/* recognise system-wide suspend in runtime PM methods */
bool pm_global;
/* pio related stuff */ /* pio related stuff */
struct scatterlist *sg_ptr; struct scatterlist *sg_ptr;
......
...@@ -546,6 +546,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, ...@@ -546,6 +546,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
irqreturn_t tmio_mmc_irq(int irq, void *devid) irqreturn_t tmio_mmc_irq(int irq, void *devid)
{ {
struct tmio_mmc_host *host = devid; struct tmio_mmc_host *host = devid;
struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata; struct tmio_mmc_data *pdata = host->pdata;
unsigned int ireg, irq_mask, status; unsigned int ireg, irq_mask, status;
unsigned int sdio_ireg, sdio_irq_mask, sdio_status; unsigned int sdio_ireg, sdio_irq_mask, sdio_status;
...@@ -567,13 +568,13 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) ...@@ -567,13 +568,13 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
if (sdio_ireg && !host->sdio_irq_enabled) { if (sdio_ireg && !host->sdio_irq_enabled) {
pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n", pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n",
sdio_status, sdio_irq_mask, sdio_ireg); sdio_status, sdio_irq_mask, sdio_ireg);
tmio_mmc_enable_sdio_irq(host->mmc, 0); tmio_mmc_enable_sdio_irq(mmc, 0);
goto out; goto out;
} }
if (host->mmc->caps & MMC_CAP_SDIO_IRQ && if (mmc->caps & MMC_CAP_SDIO_IRQ &&
sdio_ireg & TMIO_SDIO_STAT_IOIRQ) sdio_ireg & TMIO_SDIO_STAT_IOIRQ)
mmc_signal_sdio_irq(host->mmc); mmc_signal_sdio_irq(mmc);
if (sdio_ireg) if (sdio_ireg)
goto out; goto out;
...@@ -586,7 +587,9 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) ...@@ -586,7 +587,9 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid)
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) { if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT | tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT |
TMIO_STAT_CARD_REMOVE); TMIO_STAT_CARD_REMOVE);
if (!work_pending(&host->mmc->detect.work)) if ((((ireg & TMIO_STAT_CARD_REMOVE) && mmc->card) ||
((ireg & TMIO_STAT_CARD_INSERT) && !mmc->card)) &&
!work_pending(&mmc->detect.work))
mmc_detect_change(host->mmc, msecs_to_jiffies(100)); mmc_detect_change(host->mmc, msecs_to_jiffies(100));
goto out; goto out;
} }
...@@ -743,33 +746,30 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -743,33 +746,30 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
if (ios->clock) /*
tmio_mmc_set_clock(host, ios->clock); * pdata->power == false only if COLD_CD is available, otherwise only
* in short time intervals during probing or resuming
/* Power sequence - OFF -> UP -> ON */ */
if (ios->power_mode == MMC_POWER_UP) { if (ios->power_mode == MMC_POWER_ON && ios->clock) {
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && !pdata->power) { if (!pdata->power) {
pm_runtime_get_sync(&host->pdev->dev); pm_runtime_get_sync(&host->pdev->dev);
pdata->power = true; pdata->power = true;
} }
tmio_mmc_set_clock(host, ios->clock);
/* power up SD bus */ /* power up SD bus */
if (host->set_pwr) if (host->set_pwr)
host->set_pwr(host->pdev, 1); host->set_pwr(host->pdev, 1);
} else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
/* power down SD bus */
if (ios->power_mode == MMC_POWER_OFF) {
if (host->set_pwr)
host->set_pwr(host->pdev, 0);
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
pdata->power) {
pdata->power = false;
pm_runtime_put(&host->pdev->dev);
}
}
tmio_mmc_clk_stop(host);
} else {
/* start bus clock */ /* start bus clock */
tmio_mmc_clk_start(host); tmio_mmc_clk_start(host);
} else if (ios->power_mode != MMC_POWER_UP) {
if (host->set_pwr)
host->set_pwr(host->pdev, 0);
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
pdata->power) {
pdata->power = false;
pm_runtime_put(&host->pdev->dev);
}
tmio_mmc_clk_stop(host);
} }
switch (ios->bus_width) { switch (ios->bus_width) {
...@@ -897,8 +897,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -897,8 +897,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
tmio_mmc_request_dma(_host, pdata); tmio_mmc_request_dma(_host, pdata);
/* We have to keep the device powered for its card detection to work */ /* We have to keep the device powered for its card detection to work */
if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD)) if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD)) {
pdata->power = true;
pm_runtime_get_noresume(&pdev->dev); pm_runtime_get_noresume(&pdev->dev);
}
mmc_add_host(mmc); mmc_add_host(mmc);
...@@ -975,11 +977,16 @@ int tmio_mmc_host_resume(struct device *dev) ...@@ -975,11 +977,16 @@ int tmio_mmc_host_resume(struct device *dev)
/* The MMC core will perform the complete set up */ /* The MMC core will perform the complete set up */
host->pdata->power = false; host->pdata->power = false;
host->pm_global = true;
if (!host->pm_error) if (!host->pm_error)
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
tmio_mmc_reset(mmc_priv(mmc)); if (host->pm_global) {
tmio_mmc_request_dma(host, host->pdata); /* Runtime PM resume callback didn't run */
tmio_mmc_reset(host);
tmio_mmc_request_dma(host, host->pdata);
host->pm_global = false;
}
return mmc_resume_host(mmc); return mmc_resume_host(mmc);
} }
...@@ -1000,12 +1007,15 @@ int tmio_mmc_host_runtime_resume(struct device *dev) ...@@ -1000,12 +1007,15 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
struct tmio_mmc_data *pdata = host->pdata; struct tmio_mmc_data *pdata = host->pdata;
tmio_mmc_reset(host); tmio_mmc_reset(host);
tmio_mmc_request_dma(host, host->pdata);
if (pdata->power) { if (pdata->power) {
/* Only entered after a card-insert interrupt */ /* Only entered after a card-insert interrupt */
tmio_mmc_set_ios(mmc, &mmc->ios); if (!mmc->card)
tmio_mmc_set_ios(mmc, &mmc->ios);
mmc_detect_change(mmc, msecs_to_jiffies(100)); mmc_detect_change(mmc, msecs_to_jiffies(100));
} }
host->pm_global = false;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment