Skip to content

Commit

Permalink
mmc: tmio: runtime suspend the controller, where possible
Browse files Browse the repository at this point in the history
The TMIO MMC controller cannot be powered off to save power, when no
card is plugged in, because then it will not be able to detect a new
card-insertion event. On some implementations, however, it is
possible to switch to using another source to detect card insertion.
This patch adds support for such implementations.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
  • Loading branch information
Guennadi Liakhovetski authored and Chris Ball committed May 25, 2011
1 parent d6a1f86 commit 7311bef
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 6 deletions.
3 changes: 3 additions & 0 deletions drivers/mmc/host/tmio_mmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,7 @@ int tmio_mmc_host_resume(struct device *dev);
#define tmio_mmc_host_resume NULL
#endif

int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);

#endif
64 changes: 58 additions & 6 deletions drivers/mmc/host/tmio_mmc_pio.c
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata;
unsigned long flags;

spin_lock_irqsave(&host->lock, flags);
Expand Down Expand Up @@ -775,13 +776,24 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

/* Power sequence - OFF -> UP -> ON */
if (ios->power_mode == MMC_POWER_UP) {
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && !pdata->power) {
pm_runtime_get_sync(&host->pdev->dev);
pdata->power = true;
}
/* 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 && host->set_pwr)
host->set_pwr(host->pdev, 0);
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 */
Expand Down Expand Up @@ -853,6 +865,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
if (!mmc)
return -ENOMEM;

pdata->dev = &pdev->dev;
_host = mmc_priv(mmc);
_host->pdata = pdata;
_host->mmc = mmc;
Expand Down Expand Up @@ -886,6 +899,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;

pdata->power = false;
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev);
if (ret < 0)
Expand All @@ -907,7 +921,8 @@ 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 */
pm_runtime_get_noresume(&pdev->dev);
if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD))
pm_runtime_get_noresume(&pdev->dev);

mmc_add_host(mmc);

Expand Down Expand Up @@ -937,15 +952,25 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{
struct platform_device *pdev = host->pdev;

/*
* We don't have to manipulate pdata->power here: if there is a card in
* the slot, the runtime PM is active and our .runtime_resume() will not
* be run. If there is no card in the slot and the platform can suspend
* the controller, the runtime PM is suspended and pdata->power == false,
* so, our .runtime_resume() will not try to detect a card in the slot.
*/
if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD)
pm_runtime_get_sync(&pdev->dev);

mmc_remove_host(host->mmc);
cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host);
iounmap(host->ctl);
mmc_free_host(host->mmc);

/* Compensate for pm_runtime_get_sync() in probe() above */
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);

iounmap(host->ctl);
mmc_free_host(host->mmc);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);

Expand All @@ -970,6 +995,9 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);

/* The MMC core will perform the complete set up */
host->pdata->power = false;

if (!host->pm_error)
pm_runtime_get_sync(dev);

Expand All @@ -982,4 +1010,28 @@ EXPORT_SYMBOL(tmio_mmc_host_resume);

#endif /* CONFIG_PM */

int tmio_mmc_host_runtime_suspend(struct device *dev)
{
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);

int tmio_mmc_host_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata;

tmio_mmc_reset(host);

if (pdata->power) {
/* Only entered after a card-insert interrupt */
tmio_mmc_set_ios(mmc, &mmc->ios);
mmc_detect_change(mmc, msecs_to_jiffies(100));
}

return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);

MODULE_LICENSE("GPL v2");
17 changes: 17 additions & 0 deletions include/linux/mfd/tmio.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#define tmio_ioread8(addr) readb(addr)
#define tmio_ioread16(addr) readw(addr)
Expand Down Expand Up @@ -61,6 +62,12 @@
* Some controllers can support SDIO IRQ signalling.
*/
#define TMIO_MMC_SDIO_IRQ (1 << 2)
/*
* Some platforms can detect card insertion events with controller powered
* down, in which case they have to call tmio_mmc_cd_wakeup() to power up the
* controller and report the event to the driver.
*/
#define TMIO_MMC_HAS_COLD_CD (1 << 3)

int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
Expand All @@ -82,11 +89,21 @@ struct tmio_mmc_data {
unsigned long flags;
u32 ocr_mask; /* available voltages */
struct tmio_mmc_dma *dma;
struct device *dev;
bool power;
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
int (*get_cd)(struct platform_device *host);
};

static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
{
if (pdata && !pdata->power) {
pdata->power = true;
pm_runtime_get(pdata->dev);
}
}

/*
* data for the NAND controller
*/
Expand Down

0 comments on commit 7311bef

Please sign in to comment.