mmc: enable runtime PM support of sdhci host controller
PATCH v2 2/3 mmc: enable runtime PM support of sdhci host controller
Quoted 2 lines
From
fab85166c387c5f590ab2d85ab73af52c684a6a5Mon Sep 17 00:00:00 2001
From: Yunpeng Gao <[email protected]>
Date: Sun, 28 Nov 2010 08:27:46 +0800
Subject: [PATCH] Enable runtime pm support of sdhci host controller with pci interface.
Follow the kernel runtime pm framework, enable runtime pm support of the sdhci host controller with pci interface.
--- drivers/mmc/host/sdhci-pci.c | 120 ++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci.c | 44 +++++++++++++++ drivers/mmc/host/sdhci.h | 5 ++ 3 files changed, 169 insertions(+), 0 deletions(-)
drivers/mmc/host/sdhci-pci.c +120 -0
@@ -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>
@@ -934,6 +935,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, chip->slots[i] = slot; } +pm_runtime_set_active (&pdev->dev); +pm_runtime_enable (&pdev->dev); +pm_runtime_allow (&pdev->dev); +return 0 ; free:
@@ -960,9 +965,121 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)kfree (chip); } +pm_runtime_forbid (&pdev->dev); +pm_runtime_disable (&pdev->dev); +pci_disable_device (pdev); } +#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) +{ +return 0 ; +} + +#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,
@@ -970,6 +1087,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 + }, };/*****************************************************************************\
drivers/mmc/host/sdhci.c +44 -0
50 lines not on this page · raw message
drivers/mmc/host/sdhci.h +5 -0
@@ -326,4 +326,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 */
Signature · 1 line
1.6.6.1
drivers/mmc/host/sdhci.c EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); · line 1717
Quoted 6 lines
drivers/mmc/host/sdhci.c +6
-0+int sdhci_runtime_suspend (struct sdhci_host *host) +{ + int ret = 0; + + if (host->vmmc) + ret = regulator_disable(host->vmmc);
Please dont suspend the card's power in the sdhci's suspend function. This means the card will die.
Think of the wifi idle use case where the wifi is working, but sdio bus is not used. sdhci will suspend, but wifi card wont. manipulation of the host->vmmc must be done in set_ios() (I will send the patch later in the week...)
Regards,
Signature · 1 line
Pierre