> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Yong Wang
> Sent: Friday, April 01, 2011 3:58 PM
> To: [email protected]
> Subject: [Meego-kernel] [PATCH v2] mmc: sdhci: add runtime pm support
> 
> Linus Walleij's mmc clock gating framework patch only gates the clock to
> the MMC/SD card. This patch adds runtime pm capability for sdhci so that
> the host controller itself can be clock gated as well when idle.
> 
> Signed-off-by: Pierre Tardy <[email protected]>
> Signed-off-by: Yunpeng Gao <[email protected]>
> Signed-off-by: Chuanxiao Dong <[email protected]>
> Signed-off-by: Yong Wang <[email protected]>
> ---
>  drivers/mmc/host/sdhci-pci.c |  101
> ++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/sdhci.c     |   48 ++++++++++++++++++++
>  drivers/mmc/host/sdhci.h     |    5 ++
>  include/linux/mmc/sdhci.h    |    1 +
>  4 files changed, 155 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
> index bfd631a..845b566 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>
> 
> @@ -748,6 +749,95 @@ static int sdhci_pci_resume (struct pci_dev *pdev)
> 
>  #endif /* CONFIG_PM */
> 
> +#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;
> +     pm_message_t state = { .event = PM_EVENT_SUSPEND };
> +     int i, ret;
> +
> +     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_host(slot->host);
> +
> +             if (ret) {
> +                     for (i--; i >= 0; i--)
> +                             sdhci_runtime_resume_host(chip->slots[i]->host);
> +                     return ret;
> +             }
> +     }
> +
> +     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_host(chip->slots[i]->host);
> +                     return ret;
> +             }
> +     }
> +
> +     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;
> +
> +     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_host(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,
> +};
> +
> 
> /******************************************************************
> ***********\
>   *
> *
>   * Device probing/removal
> *
> @@ -942,6 +1032,12 @@ static int __devinit sdhci_pci_probe(struct pci_dev
> *pdev,
>               chip->slots[i] = slot;
>       }
> 
> +     pm_runtime_put_noidle(&pdev->dev);
> +     pm_runtime_allow(&pdev->dev);
> +     pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
> +     pm_runtime_use_autosuspend(&pdev->dev);
> +     pm_suspend_ignore_children(&pdev->dev, 1);
> +
>       return 0;
> 
>  free:
> @@ -968,6 +1064,8 @@ static void __devexit sdhci_pci_remove(struct pci_dev
> *pdev)
>               kfree(chip);
>       }
> 
> +     pm_runtime_forbid(&pdev->dev);
> +     pm_runtime_get_noresume(&pdev->dev);
>       pci_disable_device(pdev);
>  }
> 
> @@ -978,6 +1076,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 a25db42..99fb35c 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -20,6 +20,7 @@
>  #include <linux/slab.h>
>  #include <linux/scatterlist.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/pm_runtime.h>
> 
>  #include <linux/leds.h>
> 
> @@ -1157,6 +1158,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct
> mmc_ios *ios)
>  {
>       struct sdhci_host *host;
>       unsigned long flags;
> +     unsigned int lastclock;
>       u8 ctrl;
> 
>       host = mmc_priv(mmc);
> @@ -1166,6 +1168,22 @@ static void sdhci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
>       if (host->flags & SDHCI_DEVICE_DEAD)
>               goto out;
> 
> +     lastclock = host->iosclock;
> +     host->iosclock = ios->clock;
> +     if (lastclock == 0 && ios->clock != 0) {
> +             spin_unlock_irqrestore(&host->lock, flags);
> +             pm_runtime_get_sync(host->mmc->parent);
> +             spin_lock_irqsave(&host->lock, flags);
> +     } else if (lastclock != 0 && ios->clock == 0) {
> +             spin_unlock_irqrestore(&host->lock, flags);
> +             pm_runtime_mark_last_busy(host->mmc->parent);
> +             pm_runtime_put_autosuspend(host->mmc->parent);
> +             spin_lock_irqsave(&host->lock, flags);
I saw there is a potential bug when we disabled the runtime pm, actually I 
already saw this bug on MFLD.
To my understanding, I think we need at least to add "host->pwr = 0;" here. I 
found on MFLD, each time when remove the SD card, the power control register of 
host controller becomes 0x0. So if you doesn't force to clean pwr, the next 
time sdhci_set_power will return with doing nothing. This only happened if the 
runtime pm feature is disabled.

> +     }
> +     /* no need to configure the rest.. */
> +     if (host->iosclock == 0)
> +             goto out;
> +
>       /*
>        * Reset the chip on each power off.
>        * Should clear out any weird states.
> @@ -1707,6 +1725,35 @@ EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
> 
>  #endif /* CONFIG_PM */
> 
> +#ifdef CONFIG_PM_RUNTIME
> +
> +int sdhci_runtime_suspend_host(struct sdhci_host *host)
> +{
> +     int ret = 0;
> +
> +     if (host->vmmc)
> +             ret = regulator_disable(host->vmmc);
> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
> +
> +int sdhci_runtime_resume_host(struct sdhci_host *host)
> +{
> +     int ret = 0;
> +
> +     if (host->vmmc) {
> +             int ret = regulator_enable(host->vmmc);
> +             if (ret)
> +                     return ret;
> +     }
After power up the host controller, the registers need also to be reconfigured. 
I think there missed the reconfiguration part.

> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
> +
> +#endif
> +
> 
> /******************************************************************
> ***********\
>   *
> *
>   * Device allocation/registration
> *
> @@ -1727,6 +1774,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
> 
>       host = mmc_priv(mmc);
>       host->mmc = mmc;
> +     host->iosclock = 0;
> 
>       return host;
>  }
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index e42d7f0..c27aa7f 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -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_host(struct sdhci_host *host);
> +extern int sdhci_runtime_resume_host(struct sdhci_host *host);
> +#endif
> +
>  #endif /* __SDHCI_HW_H */
> diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
> index 1fdc673..c534fe4 100644
> --- a/include/linux/mmc/sdhci.h
> +++ b/include/linux/mmc/sdhci.h
> @@ -114,6 +114,7 @@ struct sdhci_host {
>       unsigned int timeout_clk;       /* Timeout freq (KHz) */
> 
>       unsigned int clock;     /* Current clock (MHz) */
> +     unsigned int iosclock;  /* Last clock asked via set_ios  */
>       u8 pwr;                 /* Current voltage */
> 
>       struct mmc_request *mrq;        /* Current request */
> --
> 1.5.5.1
> 
> _______________________________________________
> MeeGo-kernel mailing list
> [email protected]
> http://lists.meego.com/listinfo/meego-kernel
_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to