| From 8605d396d99e7c52ec9a8b5f67835735ce9fa7c2 Mon Sep 17 00:00:00 2001 |
| From: Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
| Date: Thu, 9 Feb 2012 22:57:09 +0100 |
| Subject: mmc: tmio_mmc: support the generic MMC GPIO card hotplug helper |
| |
| 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> |
| (cherry picked from commit c8be24c2afd3ed2445bbf8f542af35a9787fc0e8) |
| |
| Conflicts: |
| |
| drivers/mmc/host/tmio_mmc_pio.c |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/mmc/host/tmio_mmc.h | 4 --- |
| drivers/mmc/host/tmio_mmc_pio.c | 67 ++++++++++++++++++----------------------- |
| include/linux/mfd/tmio.h | 25 +++++++++++---- |
| 3 files changed, 49 insertions(+), 47 deletions(-) |
| |
| diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h |
| index 8531d8d..ede2f4e5 100644 |
| --- a/drivers/mmc/host/tmio_mmc.h |
| +++ b/drivers/mmc/host/tmio_mmc.h |
| @@ -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; |
| diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c |
| index 0a111f4..ce5f126 100644 |
| --- a/drivers/mmc/host/tmio_mmc_pio.c |
| +++ b/drivers/mmc/host/tmio_mmc_pio.c |
| @@ -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> |
| @@ -790,8 +791,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) { |
| @@ -915,7 +918,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); |
| |
| @@ -932,8 +935,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. |
| */ |
| @@ -970,6 +974,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; |
| @@ -987,14 +999,16 @@ 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); |
| |
| @@ -1007,7 +1021,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); |
| |
| @@ -1021,8 +1035,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); |
| @@ -1032,20 +1044,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); |
| @@ -1062,19 +1064,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); |
| diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h |
| index 0dc9804..5a197de 100644 |
| --- a/include/linux/mfd/tmio.h |
| +++ b/include/linux/mfd/tmio.h |
| @@ -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) |
| /* |
| @@ -73,6 +75,12 @@ |
| * 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)); |
| } |
| |
| /* |
| -- |
| 1.7.10.2.565.gbd578b5 |
| |