mmc: tmio_mmc: support the generic MMC GPIO card hotplug helper
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Thu, 9 Feb 2012 21:57:09 +0000 (22:57 +0100)
committerChris Ball <cjb@laptop.org>
Tue, 27 Mar 2012 16:20:15 +0000 (12:20 -0400)
If the platform specifies the TMIO_MMC_HAS_COLD_CD flag, use the generic
MMC GPIO card hotplug helper.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_pio.c
include/linux/mfd/tmio.h

index 8531d8d44fc139526109cc0e2141651a9548d324..ede2f4e5b952fd781013c13ea788a0e3b7929cd4 100644 (file)
@@ -53,10 +53,6 @@ struct tmio_mmc_host {
        void (*set_pwr)(struct platform_device *host, int state);
        void (*set_clk_div)(struct platform_device *host, int state);
 
-       int                     pm_error;
-       /* recognise system-wide suspend in runtime PM methods */
-       bool                    pm_global;
-
        /* pio related stuff */
        struct scatterlist      *sg_ptr;
        struct scatterlist      *sg_orig;
index 49f7f218cee1971c4752a1ed338b3e9a124afa33..42970ab6e803d72e36473294f32d9775e9c624ac 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/mfd/tmio.h>
+#include <linux/mmc/cd-gpio.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/tmio.h>
 #include <linux/module.h>
@@ -791,8 +792,10 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        spin_unlock_irqrestore(&host->lock, flags);
 
        /*
-        * pdata->power == false only if COLD_CD is available, otherwise only
-        * in short time intervals during probing or resuming
+        * pdata->power toggles between false and true in both cases - either
+        * or not the controller can be runtime-suspended during inactivity.
+        * But if the controller has to be kept on, the runtime-pm usage_count
+        * is kept positive, so no suspending actually takes place.
         */
        if (ios->power_mode == MMC_POWER_ON && ios->clock) {
                if (!pdata->power) {
@@ -916,7 +919,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
        else
                mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
-       _host->native_hotplug = !(pdata->flags & TMIO_MMC_HAS_COLD_CD ||
+       _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
                                  mmc->caps & MMC_CAP_NEEDS_POLL ||
                                  mmc->caps & MMC_CAP_NONREMOVABLE);
 
@@ -933,8 +936,9 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
         *  3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL
         *  4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE
         *
-        *  While we increment the rtpm counter for all scenarios when the mmc
-        *  core activates us by calling an appropriate set_ios(), we must
+        *  While we increment the runtime PM counter for all scenarios when
+        *  the mmc core activates us by calling an appropriate set_ios(), we
+        *  must additionally ensure that in case 2) the tmio mmc hardware stays
         *  additionally ensure that in case 2) the tmio mmc hardware stays
         *  powered on during runtime for the card detection to work.
         */
@@ -973,6 +977,14 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
 
        tmio_mmc_enable_mmc_irqs(_host, irq_mask);
 
+       if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
+               ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio);
+               if (ret < 0) {
+                       tmio_mmc_host_remove(_host);
+                       return ret;
+               }
+       }
+
        *host = _host;
 
        return 0;
@@ -990,20 +1002,22 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
 void tmio_mmc_host_remove(struct tmio_mmc_host *host)
 {
        struct platform_device *pdev = host->pdev;
+       struct tmio_mmc_data *pdata = host->pdata;
+       struct mmc_host *mmc = host->mmc;
+
+       if (pdata->flags & TMIO_MMC_USE_GPIO_CD)
+               /*
+                * This means we can miss a card-eject, but this is anyway
+                * possible, because of delayed processing of hotplug events.
+                */
+               mmc_cd_gpio_free(mmc);
 
-       /*
-        * 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->native_hotplug)
                pm_runtime_get_sync(&pdev->dev);
 
        dev_pm_qos_hide_latency_limit(&pdev->dev);
 
-       mmc_remove_host(host->mmc);
+       mmc_remove_host(mmc);
        cancel_work_sync(&host->done);
        cancel_delayed_work_sync(&host->delayed_reset_work);
        tmio_mmc_release_dma(host);
@@ -1012,7 +1026,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
        pm_runtime_disable(&pdev->dev);
 
        iounmap(host->ctl);
-       mmc_free_host(host->mmc);
+       mmc_free_host(mmc);
 }
 EXPORT_SYMBOL(tmio_mmc_host_remove);
 
@@ -1026,8 +1040,6 @@ int tmio_mmc_host_suspend(struct device *dev)
        if (!ret)
                tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
 
-       host->pm_error = pm_runtime_put_sync(dev);
-
        return ret;
 }
 EXPORT_SYMBOL(tmio_mmc_host_suspend);
@@ -1037,20 +1049,10 @@ int tmio_mmc_host_resume(struct device *dev)
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct tmio_mmc_host *host = mmc_priv(mmc);
 
-       /* The MMC core will perform the complete set up */
-       host->pdata->power = false;
-
-       host->pm_global = true;
-       if (!host->pm_error)
-               pm_runtime_get_sync(dev);
-
-       if (host->pm_global) {
-               /* Runtime PM resume callback didn't run */
-               tmio_mmc_reset(host);
-               tmio_mmc_enable_dma(host, true);
-               host->pm_global = false;
-       }
+       tmio_mmc_reset(host);
+       tmio_mmc_enable_dma(host, true);
 
+       /* The MMC core will perform the complete set up */
        return mmc_resume_host(mmc);
 }
 EXPORT_SYMBOL(tmio_mmc_host_resume);
@@ -1067,19 +1069,10 @@ 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);
        tmio_mmc_enable_dma(host, true);
 
-       if (pdata->power) {
-               /* Only entered after a card-insert interrupt */
-               if (!mmc->card)
-                       tmio_mmc_set_ios(mmc, &mmc->ios);
-               mmc_detect_change(mmc, msecs_to_jiffies(100));
-       }
-       host->pm_global = false;
-
        return 0;
 }
 EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
index 0dc98044d8b78ce4eab56245d4160373d7cba6d3..5a197de4aac9905c4608dbba3a179c907142dd76 100644 (file)
@@ -1,8 +1,10 @@
 #ifndef MFD_TMIO_H
 #define MFD_TMIO_H
 
+#include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/io.h>
+#include <linux/jiffies.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
@@ -64,8 +66,8 @@
 #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.
+ * down, using a GPIO IRQ, in which case they have to fill in cd_irq, cd_gpio,
+ * and cd_flags fields of struct tmio_mmc_data.
  */
 #define TMIO_MMC_HAS_COLD_CD           (1 << 3)
 /*
  * idle before writing to some registers.
  */
 #define TMIO_MMC_HAS_IDLE_WAIT         (1 << 4)
+/*
+ * A GPIO is used for card hotplug detection. We need an extra flag for this,
+ * because 0 is a valid GPIO number too, and requiring users to specify
+ * cd_gpio < 0 to disable GPIO hotplug would break backwards compatibility.
+ */
+#define TMIO_MMC_USE_GPIO_CD           (1 << 5)
 
 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);
@@ -98,18 +106,23 @@ struct tmio_mmc_data {
        struct tmio_mmc_dma             *dma;
        struct device                   *dev;
        bool                            power;
+       unsigned int                    cd_gpio;
        void (*set_pwr)(struct platform_device *host, int state);
        void (*set_clk_div)(struct platform_device *host, int state);
        int (*get_cd)(struct platform_device *host);
        int (*write16_hook)(struct tmio_mmc_host *host, int addr);
 };
 
+/*
+ * This function is deprecated and will be removed soon. Please, convert your
+ * platform to use drivers/mmc/core/cd-gpio.c
+ */
+#include <linux/mmc/host.h>
 static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
 {
-       if (pdata && !pdata->power) {
-               pdata->power = true;
-               pm_runtime_get(pdata->dev);
-       }
+       if (pdata)
+               mmc_detect_change(dev_get_drvdata(pdata->dev),
+                                 msecs_to_jiffies(100));
 }
 
 /*