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>
This commit is contained in:
parent
2b1ac5c2ca
commit
c8be24c2af
3 changed files with 50 additions and 48 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue