| From b3250d284d1eafa128d1955c4142697d9227e9e6 Mon Sep 17 00:00:00 2001 |
| From: Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
| Date: Thu, 14 Jul 2011 12:16:59 +0200 |
| Subject: 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: Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
| Signed-off-by: Chris Ball <cjb@laptop.org> |
| (cherry picked from commit 71d111cd34ee119c93d056ad9e84dc0e82367f82) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/mmc/host/tmio_mmc.h | 2 ++ |
| drivers/mmc/host/tmio_mmc_pio.c | 64 ++++++++++++++++++++++++----------------- |
| 2 files changed, 39 insertions(+), 27 deletions(-) |
| |
| diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h |
| index f0d7c43..ba0d8e6 100644 |
| --- a/drivers/mmc/host/tmio_mmc.h |
| +++ b/drivers/mmc/host/tmio_mmc.h |
| @@ -53,6 +53,8 @@ struct tmio_mmc_host { |
| void (*set_clk_div)(struct platform_device *host, int state); |
| |
| int pm_error; |
| + /* recognise system-wide suspend in runtime PM methods */ |
| + bool pm_global; |
| |
| /* pio related stuff */ |
| struct scatterlist *sg_ptr; |
| diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c |
| index a2f76ad..221ffb7 100644 |
| --- a/drivers/mmc/host/tmio_mmc_pio.c |
| +++ b/drivers/mmc/host/tmio_mmc_pio.c |
| @@ -546,6 +546,7 @@ out: |
| irqreturn_t tmio_mmc_irq(int irq, void *devid) |
| { |
| struct tmio_mmc_host *host = devid; |
| + struct mmc_host *mmc = host->mmc; |
| struct tmio_mmc_data *pdata = host->pdata; |
| unsigned int ireg, irq_mask, status; |
| unsigned int sdio_ireg, sdio_irq_mask, sdio_status; |
| @@ -567,13 +568,13 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) |
| if (sdio_ireg && !host->sdio_irq_enabled) { |
| pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n", |
| sdio_status, sdio_irq_mask, sdio_ireg); |
| - tmio_mmc_enable_sdio_irq(host->mmc, 0); |
| + tmio_mmc_enable_sdio_irq(mmc, 0); |
| goto out; |
| } |
| |
| - if (host->mmc->caps & MMC_CAP_SDIO_IRQ && |
| + if (mmc->caps & MMC_CAP_SDIO_IRQ && |
| sdio_ireg & TMIO_SDIO_STAT_IOIRQ) |
| - mmc_signal_sdio_irq(host->mmc); |
| + mmc_signal_sdio_irq(mmc); |
| |
| if (sdio_ireg) |
| goto out; |
| @@ -586,7 +587,9 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) |
| if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) { |
| tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT | |
| 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)); |
| goto out; |
| } |
| @@ -743,33 +746,30 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) |
| |
| spin_unlock_irqrestore(&host->lock, flags); |
| |
| - if (ios->clock) |
| - tmio_mmc_set_clock(host, ios->clock); |
| - |
| - /* Power sequence - OFF -> UP -> ON */ |
| - if (ios->power_mode == MMC_POWER_UP) { |
| - if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && !pdata->power) { |
| + /* |
| + * pdata->power == false only if COLD_CD is available, otherwise only |
| + * in short time intervals during probing or resuming |
| + */ |
| + if (ios->power_mode == MMC_POWER_ON && ios->clock) { |
| + if (!pdata->power) { |
| pm_runtime_get_sync(&host->pdev->dev); |
| pdata->power = true; |
| } |
| + tmio_mmc_set_clock(host, ios->clock); |
| /* power up SD bus */ |
| if (host->set_pwr) |
| 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 */ |
| 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) { |
| @@ -897,8 +897,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, |
| tmio_mmc_request_dma(_host, pdata); |
| |
| /* 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); |
| + } |
| |
| mmc_add_host(mmc); |
| |
| @@ -975,11 +977,16 @@ int tmio_mmc_host_resume(struct device *dev) |
| /* The MMC core will perform the complete set up */ |
| host->pdata->power = false; |
| |
| + host->pm_global = true; |
| if (!host->pm_error) |
| pm_runtime_get_sync(dev); |
| |
| - tmio_mmc_reset(mmc_priv(mmc)); |
| - tmio_mmc_request_dma(host, host->pdata); |
| + if (host->pm_global) { |
| + /* 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); |
| } |
| @@ -1000,12 +1007,15 @@ int tmio_mmc_host_runtime_resume(struct device *dev) |
| struct tmio_mmc_data *pdata = host->pdata; |
| |
| tmio_mmc_reset(host); |
| + tmio_mmc_request_dma(host, host->pdata); |
| |
| if (pdata->power) { |
| /* 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)); |
| } |
| + host->pm_global = false; |
| |
| return 0; |
| } |
| -- |
| 1.7.10.2.565.gbd578b5 |
| |