@@ -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
+ },
};
/*****************************************************************************\
@@ -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 *
@@ -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 */