Commit 7311bef0 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Chris Ball

mmc: tmio: runtime suspend the controller, where possible

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: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent d6a1f863
...@@ -131,4 +131,7 @@ int tmio_mmc_host_resume(struct device *dev); ...@@ -131,4 +131,7 @@ int tmio_mmc_host_resume(struct device *dev);
#define tmio_mmc_host_resume NULL #define tmio_mmc_host_resume NULL
#endif #endif
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
#endif #endif
...@@ -746,6 +746,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -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) 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_host *host = mmc_priv(mmc);
struct tmio_mmc_data *pdata = host->pdata;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
...@@ -775,13 +776,24 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -775,13 +776,24 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Power sequence - OFF -> UP -> ON */ /* Power sequence - OFF -> UP -> ON */
if (ios->power_mode == MMC_POWER_UP) { 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 */ /* 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) { } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
/* power down SD bus */ /* power down SD bus */
if (ios->power_mode == MMC_POWER_OFF && host->set_pwr) if (ios->power_mode == MMC_POWER_OFF) {
if (host->set_pwr)
host->set_pwr(host->pdev, 0); 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); tmio_mmc_clk_stop(host);
} else { } else {
/* start bus clock */ /* start bus clock */
...@@ -853,6 +865,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -853,6 +865,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
if (!mmc) if (!mmc)
return -ENOMEM; return -ENOMEM;
pdata->dev = &pdev->dev;
_host = mmc_priv(mmc); _host = mmc_priv(mmc);
_host->pdata = pdata; _host->pdata = pdata;
_host->mmc = mmc; _host->mmc = mmc;
...@@ -886,6 +899,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -886,6 +899,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
else else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
pdata->power = false;
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev); ret = pm_runtime_resume(&pdev->dev);
if (ret < 0) if (ret < 0)
...@@ -907,6 +921,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, ...@@ -907,6 +921,7 @@ 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))
pm_runtime_get_noresume(&pdev->dev); pm_runtime_get_noresume(&pdev->dev);
mmc_add_host(mmc); mmc_add_host(mmc);
...@@ -937,15 +952,25 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) ...@@ -937,15 +952,25 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{ {
struct platform_device *pdev = host->pdev; 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); mmc_remove_host(host->mmc);
cancel_delayed_work_sync(&host->delayed_reset_work); cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host); 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_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
iounmap(host->ctl);
mmc_free_host(host->mmc);
} }
EXPORT_SYMBOL(tmio_mmc_host_remove); EXPORT_SYMBOL(tmio_mmc_host_remove);
...@@ -970,6 +995,9 @@ int tmio_mmc_host_resume(struct device *dev) ...@@ -970,6 +995,9 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); 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) if (!host->pm_error)
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
...@@ -982,4 +1010,28 @@ EXPORT_SYMBOL(tmio_mmc_host_resume); ...@@ -982,4 +1010,28 @@ EXPORT_SYMBOL(tmio_mmc_host_resume);
#endif /* CONFIG_PM */ #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"); MODULE_LICENSE("GPL v2");
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#define tmio_ioread8(addr) readb(addr) #define tmio_ioread8(addr) readb(addr)
#define tmio_ioread16(addr) readw(addr) #define tmio_ioread16(addr) readw(addr)
...@@ -61,6 +62,12 @@ ...@@ -61,6 +62,12 @@
* Some controllers can support SDIO IRQ signalling. * Some controllers can support SDIO IRQ signalling.
*/ */
#define TMIO_MMC_SDIO_IRQ (1 << 2) #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_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
...@@ -82,11 +89,21 @@ struct tmio_mmc_data { ...@@ -82,11 +89,21 @@ struct tmio_mmc_data {
unsigned long flags; unsigned long flags;
u32 ocr_mask; /* available voltages */ u32 ocr_mask; /* available voltages */
struct tmio_mmc_dma *dma; struct tmio_mmc_dma *dma;
struct device *dev;
bool power;
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
int (*get_cd)(struct platform_device *host); 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 * data for the NAND controller
*/ */
......
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