summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/at91_mci.c8
-rw-r--r--drivers/mmc/host/bfin_sdh.c12
-rw-r--r--drivers/mmc/host/cb710-mmc.c13
-rw-r--r--drivers/mmc/host/dw_mmc.c71
-rw-r--r--drivers/mmc/host/dw_mmc.h2
-rw-r--r--drivers/mmc/host/jz4740_mmc.c12
-rw-r--r--drivers/mmc/host/mmc_spi.c1
-rw-r--r--drivers/mmc/host/mmci.c1
-rw-r--r--drivers/mmc/host/msm_sdcc.c19
-rw-r--r--drivers/mmc/host/mxcmmc.c13
-rw-r--r--drivers/mmc/host/mxs-mmc.c13
-rw-r--r--drivers/mmc/host/omap_hsmmc.c43
-rw-r--r--drivers/mmc/host/pxamci.c13
-rw-r--r--drivers/mmc/host/s3cmci.c13
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c12
-rw-r--r--drivers/mmc/host/sdhci-dove.c12
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c12
-rw-r--r--drivers/mmc/host/sdhci-esdhc.h2
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c12
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c12
-rw-r--r--drivers/mmc/host/sdhci-pci-data.c5
-rw-r--r--drivers/mmc/host/sdhci-pci.c181
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c12
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c12
-rw-r--r--drivers/mmc/host/sdhci-s3c.c18
-rw-r--r--drivers/mmc/host/sdhci-spear.c51
-rw-r--r--drivers/mmc/host/sdhci-tegra.c12
-rw-r--r--drivers/mmc/host/sdhci.c150
-rw-r--r--drivers/mmc/host/sdhci.h1
-rw-r--r--drivers/mmc/host/sh_mmcif.c726
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c13
-rw-r--r--drivers/mmc/host/tifm_sd.c16
-rw-r--r--drivers/mmc/host/tmio_mmc.c14
-rw-r--r--drivers/mmc/host/tmio_mmc.h4
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c30
36 files changed, 839 insertions, 703 deletions
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index b4b83f3..745f8fc 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
+obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index f437c3e..947faa5 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -236,7 +236,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
sg = &data->sg[i];
- sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
amount = min(size, sg->length);
size -= amount;
@@ -252,7 +252,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
dmabuf = (unsigned *)tmpv;
}
- kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuffer);
if (size == 0)
break;
@@ -302,7 +302,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
sg = &data->sg[i];
- sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
amount = min(size, sg->length);
size -= amount;
@@ -318,7 +318,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
}
flush_kernel_dcache_page(sg_page(sg));
- kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
+ kunmap_atomic(sgbuffer);
data->bytes_xfered += amount;
if (size == 0)
break;
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index 0371bf5..0366617 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -627,17 +627,7 @@ static struct platform_driver sdh_driver = {
},
};
-static int __init sdh_init(void)
-{
- return platform_driver_register(&sdh_driver);
-}
-module_init(sdh_init);
-
-static void __exit sdh_exit(void)
-{
- platform_driver_unregister(&sdh_driver);
-}
-module_exit(sdh_exit);
+module_platform_driver(sdh_driver);
MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
MODULE_AUTHOR("Cliff Cai, Roy Huang");
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index ce2a47b..83693fd 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -780,18 +780,7 @@ static struct platform_driver cb710_mmc_driver = {
#endif
};
-static int __init cb710_mmc_init_module(void)
-{
- return platform_driver_register(&cb710_mmc_driver);
-}
-
-static void __exit cb710_mmc_cleanup_module(void)
-{
- platform_driver_unregister(&cb710_mmc_driver);
-}
-
-module_init(cb710_mmc_init_module);
-module_exit(cb710_mmc_cleanup_module);
+module_platform_driver(cb710_mmc_driver);
MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 3aaeb08..0e34279 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -588,11 +588,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
mci_writel(host, CTYPE, (slot->ctype << slot->id));
}
-static void dw_mci_start_request(struct dw_mci *host,
- struct dw_mci_slot *slot)
+static void __dw_mci_start_request(struct dw_mci *host,
+ struct dw_mci_slot *slot,
+ struct mmc_command *cmd)
{
struct mmc_request *mrq;
- struct mmc_command *cmd;
struct mmc_data *data;
u32 cmdflags;
@@ -610,14 +610,13 @@ static void dw_mci_start_request(struct dw_mci *host,
host->completed_events = 0;
host->data_status = 0;
- data = mrq->data;
+ data = cmd->data;
if (data) {
dw_mci_set_timeout(host);
mci_writel(host, BYTCNT, data->blksz*data->blocks);
mci_writel(host, BLKSIZ, data->blksz);
}
- cmd = mrq->cmd;
cmdflags = dw_mci_prepare_command(slot->mmc, cmd);
/* this is the first command, send the initialization clock */
@@ -635,6 +634,16 @@ static void dw_mci_start_request(struct dw_mci *host,
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
}
+static void dw_mci_start_request(struct dw_mci *host,
+ struct dw_mci_slot *slot)
+{
+ struct mmc_request *mrq = slot->mrq;
+ struct mmc_command *cmd;
+
+ cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
+ __dw_mci_start_request(host, slot, cmd);
+}
+
/* must be called with host->lock held */
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
struct mmc_request *mrq)
@@ -698,12 +707,15 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
}
+ regs = mci_readl(slot->host, UHS_REG);
+
/* DDR mode set */
- if (ios->timing == MMC_TIMING_UHS_DDR50) {
- regs = mci_readl(slot->host, UHS_REG);
+ if (ios->timing == MMC_TIMING_UHS_DDR50)
regs |= (0x1 << slot->id) << 16;
- mci_writel(slot->host, UHS_REG, regs);
- }
+ else
+ regs &= ~(0x1 << slot->id) << 16;
+
+ mci_writel(slot->host, UHS_REG, regs);
if (ios->clock) {
/*
@@ -889,7 +901,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
cmd = host->cmd;
host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
- dw_mci_command_complete(host, host->mrq->cmd);
+ dw_mci_command_complete(host, cmd);
+ if (cmd == host->mrq->sbc && !cmd->error) {
+ prev_state = state = STATE_SENDING_CMD;
+ __dw_mci_start_request(host, host->cur_slot,
+ host->mrq->cmd);
+ goto unlock;
+ }
+
if (!host->mrq->data || cmd->error) {
dw_mci_request_end(host, host->mrq);
goto unlock;
@@ -967,6 +986,12 @@ static void dw_mci_tasklet_func(unsigned long priv)
goto unlock;
}
+ if (host->mrq->sbc && !data->error) {
+ data->stop->error = 0;
+ dw_mci_request_end(host, host->mrq);
+ goto unlock;
+ }
+
prev_state = state = STATE_SENDING_STOP;
if (!data->error)
send_stop_cmd(host, data);
@@ -1678,8 +1703,9 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
- else
- mmc->caps = 0;
+
+ if (host->pdata->caps2)
+ mmc->caps2 = host->pdata->caps2;
if (host->pdata->get_bus_wd)
if (host->pdata->get_bus_wd(slot->id) >= 4)
@@ -1923,7 +1949,7 @@ static int dw_mci_probe(struct platform_device *pdev)
* should put it in the platform data.
*/
fifo_size = mci_readl(host, FIFOTH);
- fifo_size = 1 + ((fifo_size >> 16) & 0x7ff);
+ fifo_size = 1 + ((fifo_size >> 16) & 0xfff);
} else {
fifo_size = host->pdata->fifo_depth;
}
@@ -2062,14 +2088,14 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
*/
-static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int dw_mci_suspend(struct device *dev)
{
int i, ret;
- struct dw_mci *host = platform_get_drvdata(pdev);
+ struct dw_mci *host = dev_get_drvdata(dev);
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
@@ -2092,10 +2118,10 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
return 0;
}
-static int dw_mci_resume(struct platform_device *pdev)
+static int dw_mci_resume(struct device *dev)
{
int i, ret;
- struct dw_mci *host = platform_get_drvdata(pdev);
+ struct dw_mci *host = dev_get_drvdata(dev);
if (host->vmmc)
regulator_enable(host->vmmc);
@@ -2103,7 +2129,7 @@ static int dw_mci_resume(struct platform_device *pdev)
if (host->dma_ops->init)
host->dma_ops->init(host);
- if (!mci_wait_reset(&pdev->dev, host)) {
+ if (!mci_wait_reset(dev, host)) {
ret = -ENODEV;
return ret;
}
@@ -2131,14 +2157,15 @@ static int dw_mci_resume(struct platform_device *pdev)
#else
#define dw_mci_suspend NULL
#define dw_mci_resume NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume);
static struct platform_driver dw_mci_driver = {
.remove = __exit_p(dw_mci_remove),
- .suspend = dw_mci_suspend,
- .resume = dw_mci_resume,
.driver = {
.name = "dw_mmc",
+ .pm = &dw_mci_pmops,
},
};
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 72c071f..df392a1 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -126,7 +126,7 @@
#define SDMMC_CMD_RESP_EXP BIT(6)
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */
-#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FF)
+#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
/* Internal DMAC interrupt defines */
#define SDMMC_IDMAC_INT_AI BIT(9)
#define SDMMC_IDMAC_INT_NI BIT(8)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 74218ad..c8852a8 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -1012,17 +1012,7 @@ static struct platform_driver jz4740_mmc_driver = {
},
};
-static int __init jz4740_mmc_init(void)
-{
- return platform_driver_register(&jz4740_mmc_driver);
-}
-module_init(jz4740_mmc_init);
-
-static void __exit jz4740_mmc_exit(void)
-{
- platform_driver_unregister(&jz4740_mmc_driver);
-}
-module_exit(jz4740_mmc_exit);
+module_platform_driver(jz4740_mmc_driver);
MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 92946b8..273306c 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1525,7 +1525,6 @@ static struct of_device_id mmc_spi_of_match_table[] __devinitdata = {
static struct spi_driver mmc_spi_driver = {
.driver = {
.name = "mmc_spi",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
.of_match_table = mmc_spi_of_match_table,
},
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index fa8dd2f..ece03b4 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1245,6 +1245,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
if (host->vcc == NULL)
mmc->ocr_avail = plat->ocr_mask;
mmc->caps = plat->capabilities;
+ mmc->caps2 = plat->capabilities2;
/*
* We can do SGIO
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 80d8eb1..1d14cda 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -689,8 +689,8 @@ msmsdcc_pio_irq(int irq, void *dev_id)
/* Map the current scatter buffer */
local_irq_save(flags);
- buffer = kmap_atomic(sg_page(host->pio.sg),
- KM_BIO_SRC_IRQ) + host->pio.sg->offset;
+ buffer = kmap_atomic(sg_page(host->pio.sg))
+ + host->pio.sg->offset;
buffer += host->pio.sg_off;
remain = host->pio.sg->length - host->pio.sg_off;
len = 0;
@@ -700,7 +700,7 @@ msmsdcc_pio_irq(int irq, void *dev_id)
len = msmsdcc_pio_write(host, buffer, remain, status);
/* Unmap the buffer */
- kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ kunmap_atomic(buffer);
local_irq_restore(flags);
host->pio.sg_off += len;
@@ -1480,18 +1480,7 @@ static struct platform_driver msmsdcc_driver = {
},
};
-static int __init msmsdcc_init(void)
-{
- return platform_driver_register(&msmsdcc_driver);
-}
-
-static void __exit msmsdcc_exit(void)
-{
- platform_driver_unregister(&msmsdcc_driver);
-}
-
-module_init(msmsdcc_init);
-module_exit(msmsdcc_exit);
+module_platform_driver(msmsdcc_driver);
MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 8e0fbe9..7088b40 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -1047,18 +1047,7 @@ static struct platform_driver mxcmci_driver = {
}
};
-static int __init mxcmci_init(void)
-{
- return platform_driver_register(&mxcmci_driver);
-}
-
-static void __exit mxcmci_exit(void)
-{
- platform_driver_unregister(&mxcmci_driver);
-}
-
-module_init(mxcmci_init);
-module_exit(mxcmci_exit);
+module_platform_driver(mxcmci_driver);
MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 973011f..4e2e019 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -855,18 +855,7 @@ static struct platform_driver mxs_mmc_driver = {
},
};
-static int __init mxs_mmc_init(void)
-{
- return platform_driver_register(&mxs_mmc_driver);
-}
-
-static void __exit mxs_mmc_exit(void)
-{
- platform_driver_unregister(&mxs_mmc_driver);
-}
-
-module_init(mxs_mmc_init);
-module_exit(mxs_mmc_exit);
+module_platform_driver(mxs_mmc_driver);
MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral");
MODULE_AUTHOR("Freescale Semiconductor");
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index d1fb561..fd0c661 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -24,7 +24,6 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
-#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
@@ -120,7 +119,6 @@
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
-#define OMAP_MMC_MASTER_CLOCK 96000000
#define OMAP_MMC_MIN_CLOCK 400000
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
@@ -163,7 +161,6 @@ struct omap_hsmmc_host {
*/
struct regulator *vcc;
struct regulator *vcc_aux;
- struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
@@ -598,12 +595,12 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
}
/* Calculate divisor for the given clock frequency */
-static u16 calc_divisor(struct mmc_ios *ios)
+static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
{
u16 dsor = 0;
if (ios->clock) {
- dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
+ dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock);
if (dsor > 250)
dsor = 250;
}
@@ -623,7 +620,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
regval = regval & ~(CLKD_MASK | DTO_MASK);
- regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
+ regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16);
OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
@@ -1280,17 +1277,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
}
/*
- * Work Item to notify the core about card insertion/removal
+ * irq handler to notify the core about card insertion/removal
*/
-static void omap_hsmmc_detect(struct work_struct *work)
+static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
{
- struct omap_hsmmc_host *host =
- container_of(work, struct omap_hsmmc_host, mmc_carddetect_work);
+ struct omap_hsmmc_host *host = dev_id;
struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect;
if (host->suspended)
- return;
+ return IRQ_HANDLED;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
@@ -1305,19 +1301,6 @@ static void omap_hsmmc_detect(struct work_struct *work)
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
else
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
-}
-
-/*
- * ISR for handling card insertion and removal
- */
-static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id)
-{
- struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;
-
- if (host->suspended)
- return IRQ_HANDLED;
- schedule_work(&host->mmc_carddetect_work);
-
return IRQ_HANDLED;
}
@@ -1919,7 +1902,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
host->next_data.cookie = 1;
platform_set_drvdata(pdev, host);
- INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
mmc->ops = &omap_hsmmc_ops;
@@ -2049,10 +2031,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq)) {
- ret = request_irq(mmc_slot(host).card_detect_irq,
- omap_hsmmc_cd_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- mmc_hostname(mmc), host);
+ ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
+ NULL,
+ omap_hsmmc_detect,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc),
"Unable to grab MMC CD IRQ\n");
@@ -2131,7 +2114,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
free_irq(host->irq, host);
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
- flush_work_sync(&host->mmc_carddetect_work);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
@@ -2178,7 +2160,6 @@ static int omap_hsmmc_suspend(struct device *dev)
return ret;
}
}
- cancel_work_sync(&host->mmc_carddetect_work);
ret = mmc_suspend_host(host->mmc);
if (ret) {
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index fc4356e..cb2dc0e 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -872,18 +872,7 @@ static struct platform_driver pxamci_driver = {
},
};
-static int __init pxamci_init(void)
-{
- return platform_driver_register(&pxamci_driver);
-}
-
-static void __exit pxamci_exit(void)
-{
- platform_driver_unregister(&pxamci_driver);
-}
-
-module_init(pxamci_init);
-module_exit(pxamci_exit);
+module_platform_driver(pxamci_driver);
MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 720f993..1bcfd6d 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1914,18 +1914,7 @@ static struct platform_driver s3cmci_driver = {
.shutdown = s3cmci_shutdown,
};
-static int __init s3cmci_init(void)
-{
- return platform_driver_register(&s3cmci_driver);
-}
-
-static void __exit s3cmci_exit(void)
-{
- platform_driver_unregister(&s3cmci_driver);
-}
-
-module_init(s3cmci_init);
-module_exit(s3cmci_exit);
+module_platform_driver(s3cmci_driver);
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index b4257e7..28a8708 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -115,17 +115,7 @@ static struct platform_driver sdhci_cns3xxx_driver = {
.remove = __devexit_p(sdhci_cns3xxx_remove),
};
-static int __init sdhci_cns3xxx_init(void)
-{
- return platform_driver_register(&sdhci_cns3xxx_driver);
-}
-module_init(sdhci_cns3xxx_init);
-
-static void __exit sdhci_cns3xxx_exit(void)
-{
- platform_driver_unregister(&sdhci_cns3xxx_driver);
-}
-module_exit(sdhci_cns3xxx_exit);
+module_platform_driver(sdhci_cns3xxx_driver);
MODULE_DESCRIPTION("SDHCI driver for CNS3xxx");
MODULE_AUTHOR("Scott Shu, "
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index a81312c..46fd1fd 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -88,17 +88,7 @@ static struct platform_driver sdhci_dove_driver = {
.remove = __devexit_p(sdhci_dove_remove),
};
-static int __init sdhci_dove_init(void)
-{
- return platform_driver_register(&sdhci_dove_driver);
-}
-module_init(sdhci_dove_init);
-
-static void __exit sdhci_dove_exit(void)
-{
- platform_driver_unregister(&sdhci_dove_driver);
-}
-module_exit(sdhci_dove_exit);
+module_platform_driver(sdhci_dove_driver);
MODULE_DESCRIPTION("SDHCI driver for Dove");
MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, "
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 38ebc4e..d601e41 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -606,17 +606,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
.remove = __devexit_p(sdhci_esdhc_imx_remove),
};
-static int __init sdhci_esdhc_imx_init(void)
-{
- return platform_driver_register(&sdhci_esdhc_imx_driver);
-}
-module_init(sdhci_esdhc_imx_init);
-
-static void __exit sdhci_esdhc_imx_exit(void)
-{
- platform_driver_unregister(&sdhci_esdhc_imx_driver);
-}
-module_exit(sdhci_esdhc_imx_exit);
+module_platform_driver(sdhci_esdhc_imx_driver);
MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index c3b08f1..b97b2f5 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -73,7 +73,7 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- mdelay(100);
+ mdelay(1);
out:
host->clock = clock;
}
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 01e5f62..ff4adc0 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -131,17 +131,7 @@ static struct platform_driver sdhci_esdhc_driver = {
.remove = __devexit_p(sdhci_esdhc_remove),
};
-static int __init sdhci_esdhc_init(void)
-{
- return platform_driver_register(&sdhci_esdhc_driver);
-}
-module_init(sdhci_esdhc_init);
-
-static void __exit sdhci_esdhc_exit(void)
-{
- platform_driver_unregister(&sdhci_esdhc_driver);
-}
-module_exit(sdhci_esdhc_exit);
+module_platform_driver(sdhci_esdhc_driver);
MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC");
MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 3619adc..0ce088a 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -93,17 +93,7 @@ static struct platform_driver sdhci_hlwd_driver = {
.remove = __devexit_p(sdhci_hlwd_remove),
};
-static int __init sdhci_hlwd_init(void)
-{
- return platform_driver_register(&sdhci_hlwd_driver);
-}
-module_init(sdhci_hlwd_init);
-
-static void __exit sdhci_hlwd_exit(void)
-{
- platform_driver_unregister(&sdhci_hlwd_driver);
-}
-module_exit(sdhci_hlwd_exit);
+module_platform_driver(sdhci_hlwd_driver);
MODULE_DESCRIPTION("Nintendo Wii SDHCI OF driver");
MODULE_AUTHOR("The GameCube Linux Team, Albert Herranz");
diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c
new file mode 100644
index 0000000..a611217
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci-data.c
@@ -0,0 +1,5 @@
+#include <linux/module.h>
+#include <linux/mmc/sdhci-pci-data.h>
+
+struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
+EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 6878a946..7165e6a 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -23,8 +23,8 @@
#include <linux/scatterlist.h>
#include <linux/io.h>
#include <linux/gpio.h>
-#include <linux/sfi.h>
#include <linux/pm_runtime.h>
+#include <linux/mmc/sdhci-pci-data.h>
#include "sdhci.h"
@@ -61,6 +61,7 @@ struct sdhci_pci_fixes {
struct sdhci_pci_slot {
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
+ struct sdhci_pci_data *data;
int pci_bar;
int rst_n_gpio;
@@ -171,32 +172,9 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
return 0;
}
-/* Medfield eMMC hardware reset GPIOs */
-static int mfd_emmc0_rst_gpio = -EINVAL;
-static int mfd_emmc1_rst_gpio = -EINVAL;
-
-static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
-{
- struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
- struct sfi_gpio_table_entry *entry;
- int i, num;
-
- num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
- entry = (struct sfi_gpio_table_entry *)sb->pentry;
-
- for (i = 0; i < num; i++, entry++) {
- if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
- mfd_emmc0_rst_gpio = entry->pin_no;
- else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
- mfd_emmc1_rst_gpio = entry->pin_no;
- }
-
- return 0;
-}
-
#ifdef CONFIG_PM_RUNTIME
-static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
+static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id)
{
struct sdhci_pci_slot *slot = dev_id;
struct sdhci_host *host = slot->host;
@@ -205,15 +183,16 @@ static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
return IRQ_HANDLED;
}
-#define MFLD_SD_CD_PIN 69
-
-static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
+static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
{
- int err, irq, gpio = MFLD_SD_CD_PIN;
+ int err, irq, gpio = slot->cd_gpio;
slot->cd_gpio = -EINVAL;
slot->cd_irq = -EINVAL;
+ if (!gpio_is_valid(gpio))
+ return;
+
err = gpio_request(gpio, "sd_cd");
if (err < 0)
goto out;
@@ -226,72 +205,53 @@ static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
if (irq < 0)
goto out_free;
- err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
+ err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, "sd_cd", slot);
if (err)
goto out_free;
slot->cd_gpio = gpio;
slot->cd_irq = irq;
- slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;
- return 0;
+ return;
out_free:
gpio_free(gpio);
out:
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
- return 0;
}
-static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
+static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
{
if (slot->cd_irq >= 0)
free_irq(slot->cd_irq, slot);
- gpio_free(slot->cd_gpio);
+ if (gpio_is_valid(slot->cd_gpio))
+ gpio_free(slot->cd_gpio);
}
#else
-#define mfd_sd_probe_slot NULL
-#define mfd_sd_remove_slot NULL
+static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
+{
+}
+
+static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
+{
+}
#endif
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
- const char *name = NULL;
- int gpio = -EINVAL;
-
- sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
-
- switch (slot->chip->pdev->device) {
- case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
- gpio = mfd_emmc0_rst_gpio;
- name = "eMMC0_reset";
- break;
- case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
- gpio = mfd_emmc1_rst_gpio;
- name = "eMMC1_reset";
- break;
- }
-
- if (!gpio_request(gpio, name)) {
- gpio_direction_output(gpio, 1);
- slot->rst_n_gpio = gpio;
- slot->host->mmc->caps |= MMC_CAP_HW_RESET;
- }
-
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
-
slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
-
return 0;
}
-static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
+static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot)
{
- gpio_free(slot->rst_n_gpio);
+ slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD;
+ return 0;
}
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
@@ -307,20 +267,18 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
- .probe_slot = mfd_sd_probe_slot,
- .remove_slot = mfd_sd_remove_slot,
};
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
+ .probe_slot = mfd_sdio_probe_slot,
};
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
.probe_slot = mfd_emmc_probe_slot,
- .remove_slot = mfd_emmc_remove_slot,
};
/* O2Micro extra registers */
@@ -1012,11 +970,8 @@ static int sdhci_pci_suspend(struct device *dev)
ret = sdhci_suspend_host(slot->host);
- if (ret) {
- for (i--; i >= 0; i--)
- sdhci_resume_host(chip->slots[i]->host);
- return ret;
- }
+ if (ret)
+ goto err_pci_suspend;
slot_pm_flags = slot->host->mmc->pm_flags;
if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ)
@@ -1027,11 +982,8 @@ static int sdhci_pci_suspend(struct device *dev)
if (chip->fixes && chip->fixes->suspend) {
ret = chip->fixes->suspend(chip);
- if (ret) {
- for (i = chip->num_slots - 1; i >= 0; i--)
- sdhci_resume_host(chip->slots[i]->host);
- return ret;
- }
+ if (ret)
+ goto err_pci_suspend;
}
pci_save_state(pdev);
@@ -1048,6 +1000,11 @@ static int sdhci_pci_suspend(struct device *dev)
}
return 0;
+
+err_pci_suspend:
+ while (--i >= 0)
+ sdhci_resume_host(chip->slots[i]->host);
+ return ret;
}
static int sdhci_pci_resume(struct device *dev)
@@ -1113,23 +1070,22 @@ static int sdhci_pci_runtime_suspend(struct device *dev)
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 (ret)
+ goto err_pci_runtime_suspend;
}
if (chip->fixes && chip->fixes->suspend) {
ret = chip->fixes->suspend(chip);
- if (ret) {
- for (i = chip->num_slots - 1; i >= 0; i--)
- sdhci_runtime_resume_host(chip->slots[i]->host);
- return ret;
- }
+ if (ret)
+ goto err_pci_runtime_suspend;
}
return 0;
+
+err_pci_runtime_suspend:
+ while (--i >= 0)
+ sdhci_runtime_resume_host(chip->slots[i]->host);
+ return ret;
}
static int sdhci_pci_runtime_resume(struct device *dev)
@@ -1190,11 +1146,12 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
\*****************************************************************************/
static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
- struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
+ struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
+ int slotno)
{
struct sdhci_pci_slot *slot;
struct sdhci_host *host;
- int ret;
+ int ret, bar = first_bar + slotno;
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
@@ -1228,6 +1185,23 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
slot->host = host;
slot->pci_bar = bar;
slot->rst_n_gpio = -EINVAL;
+ slot->cd_gpio = -EINVAL;
+
+ /* Retrieve platform data if there is any */
+ if (*sdhci_pci_get_data)
+ slot->data = sdhci_pci_get_data(pdev, slotno);
+
+ if (slot->data) {
+ if (slot->data->setup) {
+ ret = slot->data->setup(slot->data);
+ if (ret) {
+ dev_err(&pdev->dev, "platform setup failed\n");
+ goto free;
+ }
+ }
+ slot->rst_n_gpio = slot->data->rst_n_gpio;
+ slot->cd_gpio = slot->data->cd_gpio;
+ }
host->hw_name = "PCI";
host->ops = &sdhci_pci_ops;
@@ -1238,7 +1212,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
if (ret) {
dev_err(&pdev->dev, "cannot request region\n");
- goto free;
+ goto cleanup;
}
host->ioaddr = pci_ioremap_bar(pdev, bar);
@@ -1254,15 +1228,30 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
goto unmap;
}
+ if (gpio_is_valid(slot->rst_n_gpio)) {
+ if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
+ gpio_direction_output(slot->rst_n_gpio, 1);
+ slot->host->mmc->caps |= MMC_CAP_HW_RESET;
+ } else {
+ dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
+ slot->rst_n_gpio = -EINVAL;
+ }
+ }
+
host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
ret = sdhci_add_host(host);
if (ret)
goto remove;
+ sdhci_pci_add_own_cd(slot);
+
return slot;
remove:
+ if (gpio_is_valid(slot->rst_n_gpio))
+ gpio_free(slot->rst_n_gpio);
+
if (chip->fixes && chip->fixes->remove_slot)
chip->fixes->remove_slot(slot, 0);
@@ -1272,6 +1261,10 @@ unmap:
release:
pci_release_region(pdev, bar);
+cleanup:
+ if (slot->data && slot->data->cleanup)
+ slot->data->cleanup(slot->data);
+
free:
sdhci_free_host(host);
@@ -1283,6 +1276,8 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
int dead;
u32 scratch;
+ sdhci_pci_remove_own_cd(slot);
+
dead = 0;
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1)
@@ -1290,9 +1285,15 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
sdhci_remove_host(slot->host, dead);
+ if (gpio_is_valid(slot->rst_n_gpio))
+ gpio_free(slot->rst_n_gpio);
+
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
slot->chip->fixes->remove_slot(slot, dead);
+ if (slot->data && slot->data->cleanup)
+ slot->data->cleanup(slot->data);
+
pci_release_region(slot->chip->pdev, slot->pci_bar);
sdhci_free_host(slot->host);
@@ -1379,7 +1380,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
slots = chip->num_slots; /* Quirk may have changed this */
for (i = 0; i < slots; i++) {
- slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
+ slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i);
if (IS_ERR(slot)) {
for (i--; i >= 0; i--)
sdhci_pci_remove_slot(chip->slots[i]);
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 7a039c3..dbb75bf 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -223,18 +223,8 @@ static struct platform_driver sdhci_pxav2_driver = {
.probe = sdhci_pxav2_probe,
.remove = __devexit_p(sdhci_pxav2_remove),
};
-static int __init sdhci_pxav2_init(void)
-{
- return platform_driver_register(&sdhci_pxav2_driver);
-}
-
-static void __exit sdhci_pxav2_exit(void)
-{
- platform_driver_unregister(&sdhci_pxav2_driver);
-}
-module_init(sdhci_pxav2_init);
-module_exit(sdhci_pxav2_exit);
+module_platform_driver(sdhci_pxav2_driver);
MODULE_DESCRIPTION("SDHCI driver for pxav2");
MODULE_AUTHOR("Marvell International Ltd.");
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 15673a7..f296956 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -269,18 +269,8 @@ static struct platform_driver sdhci_pxav3_driver = {
.probe = sdhci_pxav3_probe,
.remove = __devexit_p(sdhci_pxav3_remove),
};
-static int __init sdhci_pxav3_init(void)
-{
- return platform_driver_register(&sdhci_pxav3_driver);
-}
-
-static void __exit sdhci_pxav3_exit(void)
-{
- platform_driver_unregister(&sdhci_pxav3_driver);
-}
-module_init(sdhci_pxav3_init);
-module_exit(sdhci_pxav3_exit);
+module_platform_driver(sdhci_pxav3_driver);
MODULE_DESCRIPTION("SDHCI driver for pxav3");
MODULE_AUTHOR("Marvell International Ltd.");
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 9a20d1f..1af756e 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -80,7 +80,7 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
- writel(tmp, host->ioaddr + 0x80);
+ writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
}
}
@@ -521,6 +521,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (pdata->host_caps)
host->mmc->caps |= pdata->host_caps;
+ if (pdata->pm_caps)
+ host->mmc->pm_caps |= pdata->pm_caps;
+
host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE);
@@ -654,18 +657,7 @@ static struct platform_driver sdhci_s3c_driver = {
},
};
-static int __init sdhci_s3c_init(void)
-{
- return platform_driver_register(&sdhci_s3c_driver);
-}
-
-static void __exit sdhci_s3c_exit(void)
-{
- platform_driver_unregister(&sdhci_s3c_driver);
-}
-
-module_init(sdhci_s3c_init);
-module_exit(sdhci_s3c_exit);
+module_platform_driver(sdhci_s3c_driver);
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 63cc8b6..b7f8b33 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdhci-spear.h>
@@ -271,26 +272,54 @@ static int __devexit sdhci_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int sdhci_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ int ret;
+
+ ret = sdhci_suspend_host(host);
+ if (!ret)
+ clk_disable(sdhci->clk);
+
+ return ret;
+}
+
+static int sdhci_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ int ret;
+
+ ret = clk_enable(sdhci->clk);
+ if (ret) {
+ dev_dbg(dev, "Resume: Error enabling clock\n");
+ return ret;
+ }
+
+ return sdhci_resume_host(host);
+}
+
+const struct dev_pm_ops sdhci_pm_ops = {
+ .suspend = sdhci_suspend,
+ .resume = sdhci_resume,
+};
+#endif
+
static struct platform_driver sdhci_driver = {
.driver = {
.name = "sdhci",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &sdhci_pm_ops,
+#endif
},
.probe = sdhci_probe,
.remove = __devexit_p(sdhci_remove),
};
-static int __init sdhci_init(void)
-{
- return platform_driver_register(&sdhci_driver);
-}
-module_init(sdhci_init);
-
-static void __exit sdhci_exit(void)
-{
- platform_driver_unregister(&sdhci_driver);
-}
-module_exit(sdhci_exit);
+module_platform_driver(sdhci_driver);
MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index e2e18d3..78a36eb 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -324,17 +324,7 @@ static struct platform_driver sdhci_tegra_driver = {
.remove = __devexit_p(sdhci_tegra_remove),
};
-static int __init sdhci_tegra_init(void)
-{
- return platform_driver_register(&sdhci_tegra_driver);
-}
-module_init(sdhci_tegra_init);
-
-static void __exit sdhci_tegra_exit(void)
-{
- platform_driver_unregister(&sdhci_tegra_driver);
-}
-module_exit(sdhci_tegra_exit);
+module_platform_driver(sdhci_tegra_driver);
MODULE_DESCRIPTION("SDHCI driver for Tegra");
MODULE_AUTHOR(" Google, Inc.");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 19ed580..8d66706 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc);
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
#ifdef CONFIG_PM_RUNTIME
@@ -146,10 +146,8 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 present, irqs;
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
- return;
-
- if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
+ !mmc_card_is_removable(host->mmc))
return;
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
@@ -214,6 +212,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
+
+ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
+ if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
+ host->ops->enable_dma(host);
+ }
}
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
@@ -423,12 +426,12 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
{
local_irq_save(*flags);
- return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ return kmap_atomic(sg_page(sg)) + sg->offset;
}
static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
{
- kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ kunmap_atomic(buffer);
local_irq_restore(*flags);
}
@@ -1016,7 +1019,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
flags |= SDHCI_CMD_INDEX;
/* CMD19 is special in that the Data Present Select should be set */
- if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
+ if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
flags |= SDHCI_CMD_DATA;
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
@@ -1066,12 +1070,15 @@ static void sdhci_finish_command(struct sdhci_host *host)
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
int div = 0; /* Initialized for compiler warning */
+ int real_div = div, clk_mul = 1;
u16 clk = 0;
unsigned long timeout;
- if (clock == host->clock)
+ if (clock && clock == host->clock)
return;
+ host->mmc->actual_clock = 0;
+
if (host->ops->set_clock) {
host->ops->set_clock(host, clock);
if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
@@ -1109,6 +1116,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
* Control register.
*/
clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div;
+ clk_mul = host->clk_mul;
div--;
}
} else {
@@ -1122,6 +1131,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
break;
}
}
+ real_div = div;
div >>= 1;
}
} else {
@@ -1130,9 +1140,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if ((host->max_clk / div) <= clock)
break;
}
+ real_div = div;
div >>= 1;
}
+ if (real_div)
+ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
+
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT;
@@ -1160,7 +1174,7 @@ out:
host->clock = clock;
}
-static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
{
u8 pwr = 0;
@@ -1183,13 +1197,13 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
}
if (host->pwr == pwr)
- return;
+ return -1;
host->pwr = pwr;
if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
- return;
+ return 0;
}
/*
@@ -1216,6 +1230,8 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
*/
if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
mdelay(10);
+
+ return power;
}
/*****************************************************************************\
@@ -1277,7 +1293,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
spin_unlock_irqrestore(&host->lock, flags);
- sdhci_execute_tuning(mmc);
+ sdhci_execute_tuning(mmc, mrq->cmd->opcode);
spin_lock_irqsave(&host->lock, flags);
/* Restore original mmc_request structure */
@@ -1297,12 +1313,17 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
{
unsigned long flags;
+ int vdd_bit = -1;
u8 ctrl;
spin_lock_irqsave(&host->lock, flags);
- if (host->flags & SDHCI_DEVICE_DEAD)
- goto out;
+ if (host->flags & SDHCI_DEVICE_DEAD) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
+ mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
+ return;
+ }
/*
* Reset the chip on each power off.
@@ -1316,9 +1337,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
sdhci_set_clock(host, ios->clock);
if (ios->power_mode == MMC_POWER_OFF)
- sdhci_set_power(host, -1);
+ vdd_bit = sdhci_set_power(host, -1);
else
- sdhci_set_power(host, ios->vdd);
+ vdd_bit = sdhci_set_power(host, ios->vdd);
+
+ if (host->vmmc && vdd_bit != -1) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
+ spin_lock_irqsave(&host->lock, flags);
+ }
if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
@@ -1361,11 +1388,11 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
unsigned int clock;
/* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
+ if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ (ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
- (ios->timing == MMC_TIMING_UHS_SDR25) ||
- (ios->timing == MMC_TIMING_UHS_SDR12))
+ (ios->timing == MMC_TIMING_UHS_SDR25))
ctrl |= SDHCI_CTRL_HISPD;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@@ -1415,7 +1442,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
- if (ios->timing == MMC_TIMING_UHS_SDR12)
+ if (ios->timing == MMC_TIMING_MMC_HS200)
+ ctrl_2 |= SDHCI_CTRL_HS_SDR200;
+ else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
@@ -1443,7 +1472,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
-out:
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -1663,7 +1691,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return err;
}
-static int sdhci_execute_tuning(struct mmc_host *mmc)
+static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
u16 ctrl;
@@ -1671,6 +1699,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
int tuning_loop_counter = MAX_TUNING_LOOP;
unsigned long timeout;
int err = 0;
+ bool requires_tuning_nonuhs = false;
host = mmc_priv(mmc);
@@ -1681,13 +1710,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/*
- * Host Controller needs tuning only in case of SDR104 mode
- * and for SDR50 mode when Use Tuning for SDR50 is set in
+ * The Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in the
* Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
*/
+ if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
+ (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
+ host->flags & SDHCI_HS200_NEEDS_TUNING))
+ requires_tuning_nonuhs = true;
+
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
- (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
- (host->flags & SDHCI_SDR50_NEEDS_TUNING)))
+ requires_tuning_nonuhs)
ctrl |= SDHCI_CTRL_EXEC_TUNING;
else {
spin_unlock(&host->lock);
@@ -1723,7 +1758,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
if (!tuning_loop_counter && !timeout)
break;
- cmd.opcode = MMC_SEND_TUNING_BLOCK;
+ cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.retries = 0;
@@ -1738,7 +1773,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
* block to the Host Controller. So we set the block size
* to 64 here.
*/
- sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
+ if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
+ SDHCI_BLOCK_SIZE);
+ else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ } else {
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
+ SDHCI_BLOCK_SIZE);
+ }
/*
* The tuning block is sent by the card to the host controller.
@@ -2121,12 +2166,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
+ u32 command;
BUG_ON(intmask == 0);
/* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) {
- if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
- MMC_SEND_TUNING_BLOCK) {
+ command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
+ if (command == MMC_SEND_TUNING_BLOCK ||
+ command == MMC_SEND_TUNING_BLOCK_HS200) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
@@ -2330,26 +2377,33 @@ out:
int sdhci_suspend_host(struct sdhci_host *host)
{
int ret;
+ bool has_tuning_timer;
sdhci_disable_card_detection(host);
/* Disable tuning since we are suspending */
- if (host->version >= SDHCI_SPEC_300 && host->tuning_count &&
- host->tuning_mode == SDHCI_TUNING_MODE_1) {
+ has_tuning_timer = host->version >= SDHCI_SPEC_300 &&
+ host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1;
+ if (has_tuning_timer) {
+ del_timer_sync(&host->tuning_timer);
host->flags &= ~SDHCI_NEEDS_RETUNING;
- mod_timer(&host->tuning_timer, jiffies +
- host->tuning_count * HZ);
}
ret = mmc_suspend_host(host->mmc);
- if (ret)
+ if (ret) {
+ if (has_tuning_timer) {
+ host->flags |= SDHCI_NEEDS_RETUNING;
+ mod_timer(&host->tuning_timer, jiffies +
+ host->tuning_count * HZ);
+ }
+
+ sdhci_enable_card_detection(host);
+
return ret;
+ }
free_irq(host->irq, host);
- if (host->vmmc)
- ret = regulator_disable(host->vmmc);
-
return ret;
}
@@ -2359,12 +2413,6 @@ int sdhci_resume_host(struct sdhci_host *host)
{
int ret;
- if (host->vmmc) {
- int ret = regulator_enable(host->vmmc);
- if (ret)
- return ret;
- }
-
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
@@ -2727,10 +2775,14 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps[1] & SDHCI_SUPPORT_DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
- /* Does the host needs tuning for SDR50? */
+ /* Does the host need tuning for SDR50? */
if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+ /* Does the host need tuning for HS200? */
+ if (mmc->caps2 & MMC_CAP2_HS200)
+ host->flags |= SDHCI_HS200_NEEDS_TUNING;
+
/* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A)
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
@@ -2926,8 +2978,6 @@ int sdhci_add_host(struct sdhci_host *host)
if (IS_ERR(host->vmmc)) {
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
host->vmmc = NULL;
- } else {
- regulator_enable(host->vmmc);
}
sdhci_init(host, 0);
@@ -3016,10 +3066,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
- if (host->vmmc) {
- regulator_disable(host->vmmc);
+ if (host->vmmc)
regulator_put(host->vmmc);
- }
kfree(host->adma_desc);
kfree(host->align_buffer);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a04d4d0..ad265b9 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -158,6 +158,7 @@
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
+#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index d5505f3..4a2c5b2 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -16,6 +16,33 @@
*
*/
+/*
+ * The MMCIF driver is now processing MMC requests asynchronously, according
+ * to the Linux MMC API requirement.
+ *
+ * The MMCIF driver processes MMC requests in up to 3 stages: command, optional
+ * data, and optional stop. To achieve asynchronous processing each of these
+ * stages is split into two halves: a top and a bottom half. The top half
+ * initialises the hardware, installs a timeout handler to handle completion
+ * timeouts, and returns. In case of the command stage this immediately returns
+ * control to the caller, leaving all further processing to run asynchronously.
+ * All further request processing is performed by the bottom halves.
+ *
+ * The bottom half further consists of a "hard" IRQ handler, an IRQ handler
+ * thread, a DMA completion callback, if DMA is used, a timeout work, and
+ * request- and stage-specific handler methods.
+ *
+ * Each bottom half run begins with either a hardware interrupt, a DMA callback
+ * invocation, or a timeout work run. In case of an error or a successful
+ * processing completion, the MMC core is informed and the request processing is
+ * finished. In case processing has to continue, i.e., if data has to be read
+ * from or written to the card, or if a stop command has to be sent, the next
+ * top half is called, which performs the necessary hardware handling and
+ * reschedules the timeout work. This returns the driver state machine into the
+ * bottom half waiting state.
+ */
+
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -123,6 +150,11 @@
#define MASK_MRBSYTO (1 << 1)
#define MASK_MRSPTO (1 << 0)
+#define MASK_START_CMD (MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | \
+ MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | \
+ MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \
+ MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO)
+
/* CE_HOST_STS1 */
#define STS1_CMDSEQ (1 << 31)
@@ -162,9 +194,21 @@ enum mmcif_state {
STATE_IOS,
};
+enum mmcif_wait_for {
+ MMCIF_WAIT_FOR_REQUEST,
+ MMCIF_WAIT_FOR_CMD,
+ MMCIF_WAIT_FOR_MREAD,
+ MMCIF_WAIT_FOR_MWRITE,
+ MMCIF_WAIT_FOR_READ,
+ MMCIF_WAIT_FOR_WRITE,
+ MMCIF_WAIT_FOR_READ_END,
+ MMCIF_WAIT_FOR_WRITE_END,
+ MMCIF_WAIT_FOR_STOP,
+};
+
struct sh_mmcif_host {
struct mmc_host *mmc;
- struct mmc_data *data;
+ struct mmc_request *mrq;
struct platform_device *pd;
struct sh_dmae_slave dma_slave_tx;
struct sh_dmae_slave dma_slave_rx;
@@ -172,11 +216,17 @@ struct sh_mmcif_host {
unsigned int clk;
int bus_width;
bool sd_error;
+ bool dying;
long timeout;
void __iomem *addr;
- struct completion intr_wait;
+ u32 *pio_ptr;
+ spinlock_t lock; /* protect sh_mmcif_host::state */
enum mmcif_state state;
- spinlock_t lock;
+ enum mmcif_wait_for wait_for;
+ struct delayed_work timeout_work;
+ size_t blocksize;
+ int sg_idx;
+ int sg_blkidx;
bool power;
bool card_present;
@@ -202,19 +252,21 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
static void mmcif_dma_complete(void *arg)
{
struct sh_mmcif_host *host = arg;
+ struct mmc_data *data = host->mrq->data;
+
dev_dbg(&host->pd->dev, "Command completed\n");
- if (WARN(!host->data, "%s: NULL data in DMA completion!\n",
+ if (WARN(!data, "%s: NULL data in DMA completion!\n",
dev_name(&host->pd->dev)))
return;
- if (host->data->flags & MMC_DATA_READ)
+ if (data->flags & MMC_DATA_READ)
dma_unmap_sg(host->chan_rx->device->dev,
- host->data->sg, host->data->sg_len,
+ data->sg, data->sg_len,
DMA_FROM_DEVICE);
else
dma_unmap_sg(host->chan_tx->device->dev,
- host->data->sg, host->data->sg_len,
+ data->sg, data->sg_len,
DMA_TO_DEVICE);
complete(&host->dma_complete);
@@ -222,13 +274,14 @@ static void mmcif_dma_complete(void *arg)
static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
{
- struct scatterlist *sg = host->data->sg;
+ struct mmc_data *data = host->mrq->data;
+ struct scatterlist *sg = data->sg;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_rx;
dma_cookie_t cookie = -EINVAL;
int ret;
- ret = dma_map_sg(chan->device->dev, sg, host->data->sg_len,
+ ret = dma_map_sg(chan->device->dev, sg, data->sg_len,
DMA_FROM_DEVICE);
if (ret > 0) {
host->dma_active = true;
@@ -244,7 +297,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
dma_async_issue_pending(chan);
}
dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
- __func__, host->data->sg_len, ret, cookie);
+ __func__, data->sg_len, ret, cookie);
if (!desc) {
/* DMA failed, fall back to PIO */
@@ -265,18 +318,19 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
}
dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
- desc, cookie, host->data->sg_len);
+ desc, cookie, data->sg_len);
}
static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
{
- struct scatterlist *sg = host->data->sg;
+ struct mmc_data *data = host->mrq->data;
+ struct scatterlist *sg = data->sg;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_tx;
dma_cookie_t cookie = -EINVAL;
int ret;
- ret = dma_map_sg(chan->device->dev, sg, host->data->sg_len,
+ ret = dma_map_sg(chan->device->dev, sg, data->sg_len,
DMA_TO_DEVICE);
if (ret > 0) {
host->dma_active = true;
@@ -292,7 +346,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
dma_async_issue_pending(chan);
}
dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
- __func__, host->data->sg_len, ret, cookie);
+ __func__, data->sg_len, ret, cookie);
if (!desc) {
/* DMA failed, fall back to PIO */
@@ -399,7 +453,7 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
else
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
- (ilog2(__rounddown_pow_of_two(host->clk / clk)) << 16));
+ ((fls(host->clk / clk) - 1) << 16));
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
}
@@ -421,7 +475,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
{
u32 state1, state2;
- int ret, timeout = 10000000;
+ int ret, timeout;
host->sd_error = false;
@@ -433,155 +487,212 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
if (state1 & STS1_CMDSEQ) {
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK);
- while (1) {
- timeout--;
- if (timeout < 0) {
- dev_err(&host->pd->dev,
- "Forceed end of command sequence timeout err\n");
- return -EIO;
- }
+ for (timeout = 10000000; timeout; timeout--) {
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
- & STS1_CMDSEQ))
+ & STS1_CMDSEQ))
break;
mdelay(1);
}
+ if (!timeout) {
+ dev_err(&host->pd->dev,
+ "Forced end of command sequence timeout err\n");
+ return -EIO;
+ }
sh_mmcif_sync_reset(host);
dev_dbg(&host->pd->dev, "Forced end of command sequence\n");
return -EIO;
}
if (state2 & STS2_CRC_ERR) {
- dev_dbg(&host->pd->dev, ": Happened CRC error\n");
+ dev_dbg(&host->pd->dev, ": CRC error\n");
ret = -EIO;
} else if (state2 & STS2_TIMEOUT_ERR) {
- dev_dbg(&host->pd->dev, ": Happened Timeout error\n");
+ dev_dbg(&host->pd->dev, ": Timeout\n");
ret = -ETIMEDOUT;
} else {
- dev_dbg(&host->pd->dev, ": Happened End/Index error\n");
+ dev_dbg(&host->pd->dev, ": End/Index error\n");
ret = -EIO;
}
return ret;
}
-static int sh_mmcif_single_read(struct sh_mmcif_host *host,
- struct mmc_request *mrq)
+static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p)
{
- struct mmc_data *data = mrq->data;
- long time;
- u32 blocksize, i, *p = sg_virt(data->sg);
+ struct mmc_data *data = host->mrq->data;
+
+ host->sg_blkidx += host->blocksize;
+
+ /* data->sg->length must be a multiple of host->blocksize? */
+ BUG_ON(host->sg_blkidx > data->sg->length);
+
+ if (host->sg_blkidx == data->sg->length) {
+ host->sg_blkidx = 0;
+ if (++host->sg_idx < data->sg_len)
+ host->pio_ptr = sg_virt(++data->sg);
+ } else {
+ host->pio_ptr = p;
+ }
+
+ if (host->sg_idx == data->sg_len)
+ return false;
+
+ return true;
+}
+
+static void sh_mmcif_single_read(struct sh_mmcif_host *host,
+ struct mmc_request *mrq)
+{
+ host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
+ BLOCK_SIZE_MASK) + 3;
+
+ host->wait_for = MMCIF_WAIT_FOR_READ;
+ schedule_delayed_work(&host->timeout_work, host->timeout);
/* buf read enable */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
-
- blocksize = (BLOCK_SIZE_MASK &
- sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
- for (i = 0; i < blocksize / 4; i++)
+}
+
+static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
+{
+ struct mmc_data *data = host->mrq->data;
+ u32 *p = sg_virt(data->sg);
+ int i;
+
+ if (host->sd_error) {
+ data->error = sh_mmcif_error_manage(host);
+ return false;
+ }
+
+ for (i = 0; i < host->blocksize / 4; i++)
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
/* buffer read end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
+ host->wait_for = MMCIF_WAIT_FOR_READ_END;
- return 0;
+ return true;
}
-static int sh_mmcif_multi_read(struct sh_mmcif_host *host,
- struct mmc_request *mrq)
+static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
+ struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
- long time;
- u32 blocksize, i, j, sec, *p;
-
- blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr,
- MMCIF_CE_BLOCK_SET);
- for (j = 0; j < data->sg_len; j++) {
- p = sg_virt(data->sg);
- for (sec = 0; sec < data->sg->length / blocksize; sec++) {
- sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
- /* buf read enable */
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
-
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
-
- for (i = 0; i < blocksize / 4; i++)
- *p++ = sh_mmcif_readl(host->addr,
- MMCIF_CE_DATA);
- }
- if (j < data->sg_len - 1)
- data->sg++;
+
+ if (!data->sg_len || !data->sg->length)
+ return;
+
+ host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
+ BLOCK_SIZE_MASK;
+
+ host->wait_for = MMCIF_WAIT_FOR_MREAD;
+ host->sg_idx = 0;
+ host->sg_blkidx = 0;
+ host->pio_ptr = sg_virt(data->sg);
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
+}
+
+static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
+{
+ struct mmc_data *data = host->mrq->data;
+ u32 *p = host->pio_ptr;
+ int i;
+
+ if (host->sd_error) {
+ data->error = sh_mmcif_error_manage(host);
+ return false;
}
- return 0;
+
+ BUG_ON(!data->sg->length);
+
+ for (i = 0; i < host->blocksize / 4; i++)
+ *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
+
+ if (!sh_mmcif_next_block(host, p))
+ return false;
+
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
+
+ return true;
}
-static int sh_mmcif_single_write(struct sh_mmcif_host *host,
+static void sh_mmcif_single_write(struct sh_mmcif_host *host,
struct mmc_request *mrq)
{
- struct mmc_data *data = mrq->data;
- long time;
- u32 blocksize, i, *p = sg_virt(data->sg);
+ host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
+ BLOCK_SIZE_MASK) + 3;
- sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+ host->wait_for = MMCIF_WAIT_FOR_WRITE;
+ schedule_delayed_work(&host->timeout_work, host->timeout);
/* buf write enable */
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
-
- blocksize = (BLOCK_SIZE_MASK &
- sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
- for (i = 0; i < blocksize / 4; i++)
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+}
+
+static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
+{
+ struct mmc_data *data = host->mrq->data;
+ u32 *p = sg_virt(data->sg);
+ int i;
+
+ if (host->sd_error) {
+ data->error = sh_mmcif_error_manage(host);
+ return false;
+ }
+
+ for (i = 0; i < host->blocksize / 4; i++)
sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
/* buffer write end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
+ host->wait_for = MMCIF_WAIT_FOR_WRITE_END;
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
-
- return 0;
+ return true;
}
-static int sh_mmcif_multi_write(struct sh_mmcif_host *host,
- struct mmc_request *mrq)
+static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
+ struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
- long time;
- u32 i, sec, j, blocksize, *p;
- blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr,
- MMCIF_CE_BLOCK_SET);
+ if (!data->sg_len || !data->sg->length)
+ return;
- for (j = 0; j < data->sg_len; j++) {
- p = sg_virt(data->sg);
- for (sec = 0; sec < data->sg->length / blocksize; sec++) {
- sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
- /* buf write enable*/
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
+ host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
+ BLOCK_SIZE_MASK;
- if (time <= 0 || host->sd_error)
- return sh_mmcif_error_manage(host);
+ host->wait_for = MMCIF_WAIT_FOR_MWRITE;
+ host->sg_idx = 0;
+ host->sg_blkidx = 0;
+ host->pio_ptr = sg_virt(data->sg);
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+}
- for (i = 0; i < blocksize / 4; i++)
- sh_mmcif_writel(host->addr,
- MMCIF_CE_DATA, *p++);
- }
- if (j < data->sg_len - 1)
- data->sg++;
+static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
+{
+ struct mmc_data *data = host->mrq->data;
+ u32 *p = host->pio_ptr;
+ int i;
+
+ if (host->sd_error) {
+ data->error = sh_mmcif_error_manage(host);
+ return false;
}
- return 0;
+
+ BUG_ON(!data->sg->length);
+
+ for (i = 0; i < host->blocksize / 4; i++)
+ sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
+
+ if (!sh_mmcif_next_block(host, p))
+ return false;
+
+ schedule_delayed_work(&host->timeout_work, host->timeout);
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+
+ return true;
}
static void sh_mmcif_get_response(struct sh_mmcif_host *host,
@@ -603,8 +714,11 @@ static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
}
static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
- struct mmc_request *mrq, struct mmc_command *cmd, u32 opc)
+ struct mmc_request *mrq)
{
+ struct mmc_data *data = mrq->data;
+ struct mmc_command *cmd = mrq->cmd;
+ u32 opc = cmd->opcode;
u32 tmp = 0;
/* Response Type check */
@@ -636,7 +750,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
break;
}
/* WDAT / DATW */
- if (host->data) {
+ if (data) {
tmp |= CMD_SET_WDAT;
switch (host->bus_width) {
case MMC_BUS_WIDTH_1:
@@ -660,7 +774,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
if (opc == MMC_READ_MULTIPLE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) {
tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN;
sh_mmcif_bitset(host, MMCIF_CE_BLOCK_SET,
- mrq->data->blocks << 16);
+ data->blocks << 16);
}
/* RIDXC[1:0] check bits */
if (opc == MMC_SEND_OP_COND || opc == MMC_ALL_SEND_CID ||
@@ -674,68 +788,60 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
opc == MMC_SEND_CSD || opc == MMC_SEND_CID)
tmp |= CMD_SET_CRC7C_INTERNAL;
- return opc = ((opc << 24) | tmp);
+ return (opc << 24) | tmp;
}
static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
- struct mmc_request *mrq, u32 opc)
+ struct mmc_request *mrq, u32 opc)
{
- int ret;
-
switch (opc) {
case MMC_READ_MULTIPLE_BLOCK:
- ret = sh_mmcif_multi_read(host, mrq);
- break;
+ sh_mmcif_multi_read(host, mrq);
+ return 0;
case MMC_WRITE_MULTIPLE_BLOCK:
- ret = sh_mmcif_multi_write(host, mrq);
- break;
+ sh_mmcif_multi_write(host, mrq);
+ return 0;
case MMC_WRITE_BLOCK:
- ret = sh_mmcif_single_write(host, mrq);
- break;
+ sh_mmcif_single_write(host, mrq);
+ return 0;
case MMC_READ_SINGLE_BLOCK:
case MMC_SEND_EXT_CSD:
- ret = sh_mmcif_single_read(host, mrq);
- break;
+ sh_mmcif_single_read(host, mrq);
+ return 0;
default:
dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc);
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
- return ret;
}
static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
- struct mmc_request *mrq, struct mmc_command *cmd)
+ struct mmc_request *mrq)
{
- long time;
- int ret = 0, mask = 0;
+ struct mmc_command *cmd = mrq->cmd;
u32 opc = cmd->opcode;
+ u32 mask;
switch (opc) {
- /* respons busy check */
+ /* response busy check */
case MMC_SWITCH:
case MMC_STOP_TRANSMISSION:
case MMC_SET_WRITE_PROT:
case MMC_CLR_WRITE_PROT:
case MMC_ERASE:
case MMC_GEN_CMD:
- mask = MASK_MRBSYE;
+ mask = MASK_START_CMD | MASK_MRBSYE;
break;
default:
- mask = MASK_MCRSPE;
+ mask = MASK_START_CMD | MASK_MCRSPE;
break;
}
- mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR |
- MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR |
- MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO |
- MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO;
- if (host->data) {
+ if (mrq->data) {
sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0);
sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET,
mrq->data->blksz);
}
- opc = sh_mmcif_set_cmd(host, mrq, cmd, opc);
+ opc = sh_mmcif_set_cmd(host, mrq);
sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask);
@@ -744,80 +850,28 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
/* set cmd */
sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc);
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0) {
- cmd->error = sh_mmcif_error_manage(host);
- return;
- }
- if (host->sd_error) {
- switch (cmd->opcode) {
- case MMC_ALL_SEND_CID:
- case MMC_SELECT_CARD:
- case MMC_APP_CMD:
- cmd->error = -ETIMEDOUT;
- break;
- default:
- dev_dbg(&host->pd->dev, "Cmd(d'%d) err\n",
- cmd->opcode);
- cmd->error = sh_mmcif_error_manage(host);
- break;
- }
- host->sd_error = false;
- return;
- }
- if (!(cmd->flags & MMC_RSP_PRESENT)) {
- cmd->error = 0;
- return;
- }
- sh_mmcif_get_response(host, cmd);
- if (host->data) {
- if (!host->dma_active) {
- ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
- } else {
- long time =
- wait_for_completion_interruptible_timeout(&host->dma_complete,
- host->timeout);
- if (!time)
- ret = -ETIMEDOUT;
- else if (time < 0)
- ret = time;
- sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC,
- BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
- host->dma_active = false;
- }
- if (ret < 0)
- mrq->data->bytes_xfered = 0;
- else
- mrq->data->bytes_xfered =
- mrq->data->blocks * mrq->data->blksz;
- }
- cmd->error = ret;
+ host->wait_for = MMCIF_WAIT_FOR_CMD;
+ schedule_delayed_work(&host->timeout_work, host->timeout);
}
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
- struct mmc_request *mrq, struct mmc_command *cmd)
+ struct mmc_request *mrq)
{
- long time;
-
- if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK)
+ switch (mrq->cmd->opcode) {
+ case MMC_READ_MULTIPLE_BLOCK:
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
- else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)
+ break;
+ case MMC_WRITE_MULTIPLE_BLOCK:
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
- else {
+ break;
+ default:
dev_err(&host->pd->dev, "unsupported stop cmd\n");
- cmd->error = sh_mmcif_error_manage(host);
+ mrq->stop->error = sh_mmcif_error_manage(host);
return;
}
- time = wait_for_completion_interruptible_timeout(&host->intr_wait,
- host->timeout);
- if (time <= 0 || host->sd_error) {
- cmd->error = sh_mmcif_error_manage(host);
- return;
- }
- sh_mmcif_get_cmd12response(host, cmd);
- cmd->error = 0;
+ host->wait_for = MMCIF_WAIT_FOR_STOP;
+ schedule_delayed_work(&host->timeout_work, host->timeout);
}
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -856,23 +910,10 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
default:
break;
}
- host->data = mrq->data;
- if (mrq->data) {
- if (mrq->data->flags & MMC_DATA_READ) {
- if (host->chan_rx)
- sh_mmcif_start_dma_rx(host);
- } else {
- if (host->chan_tx)
- sh_mmcif_start_dma_tx(host);
- }
- }
- sh_mmcif_start_cmd(host, mrq, mrq->cmd);
- host->data = NULL;
- if (!mrq->cmd->error && mrq->stop)
- sh_mmcif_stop_cmd(host, mrq, mrq->stop);
- host->state = STATE_IDLE;
- mmc_request_done(mmc, mrq);
+ host->mrq = mrq;
+
+ sh_mmcif_start_cmd(host, mrq);
}
static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -947,9 +988,156 @@ static struct mmc_host_ops sh_mmcif_ops = {
.get_cd = sh_mmcif_get_cd,
};
-static void sh_mmcif_detect(struct mmc_host *mmc)
+static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
{
- mmc_detect_change(mmc, 0);
+ struct mmc_command *cmd = host->mrq->cmd;
+ struct mmc_data *data = host->mrq->data;
+ long time;
+
+ if (host->sd_error) {
+ switch (cmd->opcode) {
+ case MMC_ALL_SEND_CID:
+ case MMC_SELECT_CARD:
+ case MMC_APP_CMD:
+ cmd->error = -ETIMEDOUT;
+ host->sd_error = false;
+ break;
+ default:
+ cmd->error = sh_mmcif_error_manage(host);
+ dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n",
+ cmd->opcode, cmd->error);
+ break;
+ }
+ return false;
+ }
+ if (!(cmd->flags & MMC_RSP_PRESENT)) {
+ cmd->error = 0;
+ return false;
+ }
+
+ sh_mmcif_get_response(host, cmd);
+
+ if (!data)
+ return false;
+
+ if (data->flags & MMC_DATA_READ) {
+ if (host->chan_rx)
+ sh_mmcif_start_dma_rx(host);
+ } else {
+ if (host->chan_tx)
+ sh_mmcif_start_dma_tx(host);
+ }
+
+ if (!host->dma_active) {
+ data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode);
+ if (!data->error)
+ return true;
+ return false;
+ }
+
+ /* Running in the IRQ thread, can sleep */
+ time = wait_for_completion_interruptible_timeout(&host->dma_complete,
+ host->timeout);
+ if (host->sd_error) {
+ dev_err(host->mmc->parent,
+ "Error IRQ while waiting for DMA completion!\n");
+ /* Woken up by an error IRQ: abort DMA */
+ if (data->flags & MMC_DATA_READ)
+ dmaengine_terminate_all(host->chan_rx);
+ else
+ dmaengine_terminate_all(host->chan_tx);
+ data->error = sh_mmcif_error_manage(host);
+ } else if (!time) {
+ data->error = -ETIMEDOUT;
+ } else if (time < 0) {
+ data->error = time;
+ }
+ sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC,
+ BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
+ host->dma_active = false;
+
+ if (data->error)
+ data->bytes_xfered = 0;
+
+ return false;
+}
+
+static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
+{
+ struct sh_mmcif_host *host = dev_id;
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_data *data = mrq->data;
+
+ cancel_delayed_work_sync(&host->timeout_work);
+
+ /*
+ * All handlers return true, if processing continues, and false, if the
+ * request has to be completed - successfully or not
+ */
+ switch (host->wait_for) {
+ case MMCIF_WAIT_FOR_REQUEST:
+ /* We're too late, the timeout has already kicked in */
+ return IRQ_HANDLED;
+ case MMCIF_WAIT_FOR_CMD:
+ if (sh_mmcif_end_cmd(host))
+ /* Wait for data */
+ return IRQ_HANDLED;
+ break;
+ case MMCIF_WAIT_FOR_MREAD:
+ if (sh_mmcif_mread_block(host))
+ /* Wait for more data */
+ return IRQ_HANDLED;
+ break;
+ case MMCIF_WAIT_FOR_READ:
+ if (sh_mmcif_read_block(host))
+ /* Wait for data end */
+ return IRQ_HANDLED;
+ break;
+ case MMCIF_WAIT_FOR_MWRITE:
+ if (sh_mmcif_mwrite_block(host))
+ /* Wait data to write */
+ return IRQ_HANDLED;
+ break;
+ case MMCIF_WAIT_FOR_WRITE:
+ if (sh_mmcif_write_block(host))
+ /* Wait for data end */
+ return IRQ_HANDLED;
+ break;
+ case MMCIF_WAIT_FOR_STOP:
+ if (host->sd_error) {
+ mrq->stop->error = sh_mmcif_error_manage(host);
+ break;
+ }
+ sh_mmcif_get_cmd12response(host, mrq->stop);
+ mrq->stop->error = 0;
+ break;
+ case MMCIF_WAIT_FOR_READ_END:
+ case MMCIF_WAIT_FOR_WRITE_END:
+ if (host->sd_error)
+ data->error = sh_mmcif_error_manage(host);
+ break;
+ default:
+ BUG();
+ }
+
+ if (host->wait_for != MMCIF_WAIT_FOR_STOP) {
+ if (!mrq->cmd->error && data && !data->error)
+ data->bytes_xfered =
+ data->blocks * data->blksz;
+
+ if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) {
+ sh_mmcif_stop_cmd(host, mrq);
+ if (!mrq->stop->error)
+ return IRQ_HANDLED;
+ }
+ }
+
+ host->wait_for = MMCIF_WAIT_FOR_REQUEST;
+ host->state = STATE_IDLE;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+
+ return IRQ_HANDLED;
}
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
@@ -960,7 +1148,12 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
- if (state & INT_RBSYE) {
+ if (state & INT_ERR_STS) {
+ /* error interrupts - process first */
+ sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
+ err = 1;
+ } else if (state & INT_RBSYE) {
sh_mmcif_writel(host->addr, MMCIF_CE_INT,
~(INT_RBSYE | INT_CRSPE));
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE);
@@ -988,11 +1181,6 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
sh_mmcif_writel(host->addr, MMCIF_CE_INT,
~(INT_CMD12RBE | INT_CMD12CRE));
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
- } else if (state & INT_ERR_STS) {
- /* err interrupts */
- sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
- sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
- err = 1;
} else {
dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state);
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
@@ -1003,14 +1191,57 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
host->sd_error = true;
dev_dbg(&host->pd->dev, "int err state = %08x\n", state);
}
- if (state & ~(INT_CMD12RBE | INT_CMD12CRE))
- complete(&host->intr_wait);
- else
+ if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
+ if (!host->dma_active)
+ return IRQ_WAKE_THREAD;
+ else if (host->sd_error)
+ mmcif_dma_complete(host);
+ } else {
dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
+ }
return IRQ_HANDLED;
}
+static void mmcif_timeout_work(struct work_struct *work)
+{
+ struct delayed_work *d = container_of(work, struct delayed_work, work);
+ struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
+ struct mmc_request *mrq = host->mrq;
+
+ if (host->dying)
+ /* Don't run after mmc_remove_host() */
+ return;
+
+ /*
+ * Handle races with cancel_delayed_work(), unless
+ * cancel_delayed_work_sync() is used
+ */
+ switch (host->wait_for) {
+ case MMCIF_WAIT_FOR_CMD:
+ mrq->cmd->error = sh_mmcif_error_manage(host);
+ break;
+ case MMCIF_WAIT_FOR_STOP:
+ mrq->stop->error = sh_mmcif_error_manage(host);
+ break;
+ case MMCIF_WAIT_FOR_MREAD:
+ case MMCIF_WAIT_FOR_MWRITE:
+ case MMCIF_WAIT_FOR_READ:
+ case MMCIF_WAIT_FOR_WRITE:
+ case MMCIF_WAIT_FOR_READ_END:
+ case MMCIF_WAIT_FOR_WRITE_END:
+ mrq->data->error = sh_mmcif_error_manage(host);
+ break;
+ default:
+ BUG();
+ }
+
+ host->state = STATE_IDLE;
+ host->wait_for = MMCIF_WAIT_FOR_REQUEST;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
static int __devinit sh_mmcif_probe(struct platform_device *pdev)
{
int ret = 0, irq[2];
@@ -1064,7 +1295,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
host->clk = clk_get_rate(host->hclk);
host->pd = pdev;
- init_completion(&host->intr_wait);
spin_lock_init(&host->lock);
mmc->ops = &sh_mmcif_ops;
@@ -1101,19 +1331,21 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
- ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
+ ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host);
if (ret) {
dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
goto clean_up3;
}
- ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host);
+ ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host);
if (ret) {
free_irq(irq[0], host);
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
goto clean_up3;
}
- sh_mmcif_detect(host->mmc);
+ INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
+
+ mmc_detect_change(host->mmc, 0);
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
dev_dbg(&pdev->dev, "chip ver H'%04x\n",
@@ -1139,11 +1371,19 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
int irq[2];
+ host->dying = true;
pm_runtime_get_sync(&pdev->dev);
mmc_remove_host(host->mmc);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
+ /*
+ * FIXME: cancel_delayed_work(_sync)() and free_irq() race with the
+ * mmc_remove_host() call above. But swapping order doesn't help either
+ * (a query on the linux-mmc mailing list didn't bring any replies).
+ */
+ cancel_delayed_work_sync(&host->timeout_work);
+
if (host->addr)
iounmap(host->addr);
@@ -1206,19 +1446,7 @@ static struct platform_driver sh_mmcif_driver = {
},
};
-static int __init sh_mmcif_init(void)
-{
- return platform_driver_register(&sh_mmcif_driver);
-}
-
-static void __exit sh_mmcif_exit(void)
-{
- platform_driver_unregister(&sh_mmcif_driver);
-}
-
-module_init(sh_mmcif_init);
-module_exit(sh_mmcif_exit);
-
+module_platform_driver(sh_mmcif_driver);
MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 41ae646..58da3c4 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -282,18 +282,7 @@ static struct platform_driver sh_mobile_sdhi_driver = {
.remove = __devexit_p(sh_mobile_sdhi_remove),
};
-static int __init sh_mobile_sdhi_init(void)
-{
- return platform_driver_register(&sh_mobile_sdhi_driver);
-}
-
-static void __exit sh_mobile_sdhi_exit(void)
-{
- platform_driver_unregister(&sh_mobile_sdhi_driver);
-}
-
-module_init(sh_mobile_sdhi_init);
-module_exit(sh_mobile_sdhi_exit);
+module_platform_driver(sh_mobile_sdhi_driver);
MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
MODULE_AUTHOR("Magnus Damm");
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index f70d046..fc00081 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -118,7 +118,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
unsigned char *buf;
unsigned int pos = 0, val;
- buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
+ buf = kmap_atomic(pg) + off;
if (host->cmd_flags & DATA_CARRY) {
buf[pos++] = host->bounce_buf_data[0];
host->cmd_flags &= ~DATA_CARRY;
@@ -134,7 +134,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
}
buf[pos++] = (val >> 8) & 0xff;
}
- kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
+ kunmap_atomic(buf - off);
}
static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
@@ -144,7 +144,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
unsigned char *buf;
unsigned int pos = 0, val;
- buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off;
+ buf = kmap_atomic(pg) + off;
if (host->cmd_flags & DATA_CARRY) {
val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
writel(val, sock->addr + SOCK_MMCSD_DATA);
@@ -161,7 +161,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
val |= (buf[pos++] << 8) & 0xff00;
writel(val, sock->addr + SOCK_MMCSD_DATA);
}
- kunmap_atomic(buf - off, KM_BIO_SRC_IRQ);
+ kunmap_atomic(buf - off);
}
static void tifm_sd_transfer_data(struct tifm_sd *host)
@@ -212,13 +212,13 @@ static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
struct page *src, unsigned int src_off,
unsigned int count)
{
- unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off;
- unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off;
+ unsigned char *src_buf = kmap_atomic(src) + src_off;
+ unsigned char *dst_buf = kmap_atomic(dst) + dst_off;
memcpy(dst_buf, src_buf, count);
- kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ);
- kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ);
+ kunmap_atomic(dst_buf - dst_off);
+ kunmap_atomic(src_buf - src_off);
}
static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index a4ea102..113ce6c 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -138,19 +138,7 @@ static struct platform_driver tmio_mmc_driver = {
.resume = tmio_mmc_resume,
};
-
-static int __init tmio_mmc_init(void)
-{
- return platform_driver_register(&tmio_mmc_driver);
-}
-
-static void __exit tmio_mmc_exit(void)
-{
- platform_driver_unregister(&tmio_mmc_driver);
-}
-
-module_init(tmio_mmc_init);
-module_exit(tmio_mmc_exit);
+module_platform_driver(tmio_mmc_driver);
MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 3020f98..a95e6d9 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -105,13 +105,13 @@ static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
unsigned long *flags)
{
local_irq_save(*flags);
- return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ return kmap_atomic(sg_page(sg)) + sg->offset;
}
static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg,
unsigned long *flags, void *virt)
{
- kunmap_atomic(virt - sg->offset, KM_BIO_SRC_IRQ);
+ kunmap_atomic(virt - sg->offset);
local_irq_restore(*flags);
}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 4208b39..abad01b 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -800,8 +800,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} else if (ios->power_mode != MMC_POWER_UP) {
if (host->set_pwr && ios->power_mode == MMC_POWER_OFF)
host->set_pwr(host->pdev, 0);
- if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
- pdata->power) {
+ if (pdata->power) {
pdata->power = false;
pm_runtime_put(&host->pdev->dev);
}
@@ -915,6 +914,23 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
if (ret < 0)
goto pm_disable;
+ /*
+ * There are 4 different scenarios for the card detection:
+ * 1) an external gpio irq handles the cd (best for power savings)
+ * 2) internal sdhi irq handles the cd
+ * 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
+ * additionally ensure that in case 2) the tmio mmc hardware stays
+ * powered on during runtime for the card detection to work.
+ */
+ if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD
+ || mmc->caps & MMC_CAP_NEEDS_POLL
+ || mmc->caps & MMC_CAP_NONREMOVABLE))
+ pm_runtime_get_noresume(&pdev->dev);
+
tmio_mmc_clk_stop(_host);
tmio_mmc_reset(_host);
@@ -933,12 +949,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
/* See if we also get DMA */
tmio_mmc_request_dma(_host, pdata);
- /* We have to keep the device powered for its card detection to work */
- if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD)) {
- pdata->power = true;
- pm_runtime_get_noresume(&pdev->dev);
- }
-
mmc_add_host(mmc);
/* Unmask the IRQs we want to know about */
@@ -974,7 +984,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
* 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->pdata->flags & TMIO_MMC_HAS_COLD_CD)
+ if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD
+ || host->mmc->caps & MMC_CAP_NEEDS_POLL
+ || host->mmc->caps & MMC_CAP_NONREMOVABLE)
pm_runtime_get_sync(&pdev->dev);
mmc_remove_host(host->mmc);
OpenPOWER on IntegriCloud