[RFC,PATCHv3 1/3] mmc: sdhci-pci: Enable runtime PM support

From: Pierre Tardy
Date: Tue Mar 01 2011 - 13:15:58 EST


From: Yunpeng Gao <yunpeng.gao@xxxxxxxxx>

Follow the kernel runtime PM framework, enable runtime PM support of the
sdhci host controller with pci interface.

Note that this patch implements runtime_pm but now actually detects
activity.
It relies on higher level (childrens) to do actual waking up
Activity detection is put in a following patch

This patch also does not enable wakeups, so card insertion will not work
if runtime_pm is activated.
This is also dealt in a following patch

Original version from: Yunpeng Gao <yunpeng.gao@xxxxxxxxx>
with modifications by: Pierre Tardy <pierre.tardy@xxxxxxxxx>

Signed-off-by: Pierre Tardy <pierre.tardy@xxxxxxxxx>
---
drivers/mmc/host/sdhci-pci.c | 120 +++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci.c | 31 +++++++++++
drivers/mmc/host/sdhci.h | 5 ++
3 files changed, 155 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 2f8d468..f167a55 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -18,6 +18,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/pm_runtime.h>

#include <linux/mmc/host.h>

@@ -1031,6 +1032,8 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;

+ pm_runtime_set_active(&pdev->dev);
+
slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
if (slots == 0)
@@ -1086,7 +1089,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,

chip->slots[i] = slot;
}
-
+ pm_runtime_put_noidle(&pdev->dev);
return 0;

free:
@@ -1114,8 +1117,120 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
}

pci_disable_device(pdev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int sdhci_pci_runtime_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ int i, ret;
+ mmc_pm_flag_t pm_flags = 0;
+ pm_message_t state;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ for (i = 0; i < chip->num_slots; i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_runtime_suspend(slot->host);
+
+ if (ret) {
+ for (i--; i >= 0; i--)
+ sdhci_runtime_resume(chip->slots[i]->host);
+ return ret;
+ }
+
+ pm_flags |= slot->host->mmc->pm_flags;
+ }
+
+ state.event = PM_EVENT_AUTO_SUSPEND;
+ if (chip->fixes && chip->fixes->suspend) {
+ ret = chip->fixes->suspend(chip, state);
+ if (ret) {
+ for (i = chip->num_slots - 1; i >= 0; i--)
+ sdhci_runtime_resume(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
+ pci_save_state(pdev);
+ if (pm_flags & MMC_PM_KEEP_POWER) {
+ if (pm_flags & MMC_PM_WAKE_SDIO_IRQ)
+ pci_enable_wake(pdev, PCI_D3hot, 1);
+ } else {
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_disable_device(pdev);
+ }
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
}

+static int sdhci_pci_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ int i, ret;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ if (chip->fixes && chip->fixes->resume) {
+ ret = chip->fixes->resume(chip);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < chip->num_slots; i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_runtime_resume(slot->host);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sdhci_pci_runtime_idle(struct device *dev)
+{
+ pm_runtime_autosuspend(dev);
+ return -EAGAIN;
+}
+
+#else
+
+#define sdhci_pci_runtime_suspend NULL
+#define sdhci_pci_runtime_resume NULL
+#define sdhci_pci_runtime_idle NULL
+
+#endif
+
+static const struct dev_pm_ops sdhci_pci_pm_ops = {
+ .runtime_suspend = sdhci_pci_runtime_suspend,
+ .runtime_resume = sdhci_pci_runtime_resume,
+ .runtime_idle = sdhci_pci_runtime_idle,
+};
+
static struct pci_driver sdhci_driver = {
.name = "sdhci-pci",
.id_table = pci_ids,
@@ -1123,6 +1238,9 @@ static struct pci_driver sdhci_driver = {
.remove = __devexit_p(sdhci_pci_remove),
.suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume,
+ .driver = {
+ .pm = &sdhci_pci_pm_ops
+ },
};

/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9e15f41..cd79a28 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1715,6 +1715,37 @@ EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);

#endif /* CONFIG_PM */

+#ifdef CONFIG_PM_RUNTIME
+
+int sdhci_runtime_suspend(struct sdhci_host *host)
+{
+ /* nothing to do yet */
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_runtime_suspend);
+
+int sdhci_runtime_resume(struct sdhci_host *host)
+{
+ int ret = 0;
+
+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
+ host->pwr = 0; /* force power reprogram */
+ host->clock = 0; /* force clock reprogram */
+ sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmiowb();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_resume);
+
+#endif /* CONFIG_PM_RUNTIME */
+
/*****************************************************************************\
* *
* Device allocation/registration *
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6e0969e..1f032c0 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -327,4 +327,9 @@ extern int sdhci_resume_host(struct sdhci_host *host);
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
#endif

+#ifdef CONFIG_PM_RUNTIME
+extern int sdhci_runtime_suspend(struct sdhci_host *host);
+extern int sdhci_runtime_resume(struct sdhci_host *host);
+#endif
+
#endif /* __SDHCI_HW_H */
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/