summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2010-06-30 12:16:24 +0200
committerUwe Kleine-König <u.kleine-koenig@pengutronix.de>2010-07-26 14:27:25 +0200
commit2dcf78c0eeae3bd07082821557014f25f02ca2e9 (patch)
tree8ca5c4c7f35c9a9ab07fcd9732124c905e609aa1 /drivers/mmc
parent6b6322676add0fa2713d0ec89a28390fd4d907f5 (diff)
parent5109a4597f7e758b8d20694392d0361a0b4c43b1 (diff)
downloadop-kernel-dev-2dcf78c0eeae3bd07082821557014f25f02ca2e9.zip
op-kernel-dev-2dcf78c0eeae3bd07082821557014f25f02ca2e9.tar.gz
Merge branch 'imx/for-2.6.36' of git://git.pengutronix.de/git/ukl/linux-2.6 into HEAD
There are some more conflicts than detected by git, namely support for the newly added cpuimx machines needed to be converted to dynamic device registration. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Conflicts: arch/arm/mach-imx/Makefile arch/arm/mach-imx/devices.c arch/arm/mach-imx/devices.h arch/arm/mach-imx/eukrea_mbimx27-baseboard.c arch/arm/mach-mx2/Kconfig arch/arm/mach-mx25/Makefile arch/arm/mach-mx25/devices.c arch/arm/plat-mxc/include/mach/mx25.h arch/arm/plat-mxc/include/mach/mxc_nand.h
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/core.c3
-rw-r--r--drivers/mmc/core/sd_ops.c2
-rw-r--r--drivers/mmc/core/sdio_io.c30
-rw-r--r--drivers/mmc/host/Kconfig20
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/at91_mci.c2
-rw-r--r--drivers/mmc/host/atmel-mci.c64
-rw-r--r--drivers/mmc/host/au1xmmc.c2
-rw-r--r--drivers/mmc/host/bfin_sdh.c2
-rw-r--r--drivers/mmc/host/cb710-mmc.c2
-rw-r--r--drivers/mmc/host/davinci_mmc.c111
-rw-r--r--drivers/mmc/host/imxmmc.c2
-rw-r--r--drivers/mmc/host/mmci.c2
-rw-r--r--drivers/mmc/host/msm_sdcc.c2
-rw-r--r--drivers/mmc/host/mvsdio.c2
-rw-r--r--drivers/mmc/host/mxcmmc.c2
-rw-r--r--drivers/mmc/host/omap.c64
-rw-r--r--drivers/mmc/host/omap_hsmmc.c279
-rw-r--r--drivers/mmc/host/pxamci.c2
-rw-r--r--drivers/mmc/host/s3cmci.c3
-rw-r--r--drivers/mmc/host/sdhci-of-core.c2
-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.c2
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c26
-rw-r--r--drivers/mmc/host/sdhci-s3c.c10
-rw-r--r--drivers/mmc/host/sdhci-spear.c298
-rw-r--r--drivers/mmc/host/sdhci.c25
-rw-r--r--drivers/mmc/host/sdhci.h42
-rw-r--r--drivers/mmc/host/sdricoh_cs.c2
-rw-r--r--drivers/mmc/host/sh_mmcif.c965
-rw-r--r--drivers/mmc/host/tifm_sd.c2
-rw-r--r--drivers/mmc/host/tmio_mmc.c2
-rw-r--r--drivers/mmc/host/via-sdmmc.c2
-rw-r--r--drivers/mmc/host/wbsd.c2
35 files changed, 1721 insertions, 281 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3168ebd..569e94d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1252,9 +1252,8 @@ EXPORT_SYMBOL(mmc_card_can_sleep);
/**
* mmc_suspend_host - suspend a host
* @host: mmc host
- * @state: suspend mode (PM_SUSPEND_xxx)
*/
-int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
+int mmc_suspend_host(struct mmc_host *host)
{
int err = 0;
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
index 0d96080..63772e7 100644
--- a/drivers/mmc/core/sd_ops.c
+++ b/drivers/mmc/core/sd_ops.c
@@ -79,8 +79,6 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
- memset(&mrq, 0, sizeof(struct mmc_request));
-
err = mmc_app_cmd(host, card);
if (err) {
/* no point in retrying; no APP commands allowed */
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index ff27c8c..0f687cd 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -406,6 +406,36 @@ void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
EXPORT_SYMBOL_GPL(sdio_writeb);
/**
+ * sdio_writeb_readb - write and read a byte from SDIO function
+ * @func: SDIO function to access
+ * @write_byte: byte to write
+ * @addr: address to write to
+ * @err_ret: optional status value from transfer
+ *
+ * Performs a RAW (Read after Write) operation as defined by SDIO spec -
+ * single byte is written to address space of a given SDIO function and
+ * response is read back from the same address, both using single request.
+ * If there is a problem with the operation, 0xff is returned and
+ * @err_ret will contain the error code.
+ */
+u8 sdio_writeb_readb(struct sdio_func *func, u8 write_byte,
+ unsigned int addr, int *err_ret)
+{
+ int ret;
+ u8 val;
+
+ ret = mmc_io_rw_direct(func->card, 1, func->num, addr,
+ write_byte, &val);
+ if (err_ret)
+ *err_ret = ret;
+ if (ret)
+ val = 0xff;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sdio_writeb_readb);
+
+/**
* sdio_memcpy_fromio - read a chunk of memory from a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2e13b94..e171e77 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -136,6 +136,18 @@ config MMC_SDHCI_S3C
If unsure, say N.
+config MMC_SDHCI_SPEAR
+ tristate "SDHCI support on ST SPEAr platform"
+ depends on MMC_SDHCI && PLAT_SPEAR
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ often referrered to as the HSMMC block in some of the ST SPEAR range
+ of SoC
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_S3C_DMA
bool "DMA support on S3C SDHCI"
depends on MMC_SDHCI_S3C && EXPERIMENTAL
@@ -412,3 +424,11 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
depends on SDH_BFIN
help
If you say yes here SD-Cards may work on the EZkit.
+
+config MMC_SH_MMCIF
+ tristate "SuperH Internal MMCIF support"
+ depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
+ help
+ This selects the MMC Host Interface controler (MMCIF).
+
+ This driver supports MMCIF in sh7724/sh7757/sh7372.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f480397..e30c2ee 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
+obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_CB710) += cb710-mmc.o
obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o
obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
+obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 336d9f5..5f3a599 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -1157,7 +1157,7 @@ static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
enable_irq_wake(host->board->det_pin);
if (mmc)
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
return ret;
}
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index df0e8a8..95ef864 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -173,6 +173,7 @@ struct atmel_mci {
* @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using.
* @sdc_reg: Value of SDCR to be written before using this slot.
+ * @sdio_irq: SDIO irq mask for this slot.
* @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle.
* @queue_node: List node for placing this node in the @queue list of
@@ -191,6 +192,7 @@ struct atmel_mci_slot {
struct atmel_mci *host;
u32 sdc_reg;
+ u32 sdio_irq;
struct mmc_request *mrq;
struct list_head queue_node;
@@ -792,7 +794,7 @@ static void atmci_start_request(struct atmel_mci *host,
mci_writel(host, SDCR, slot->sdc_reg);
iflags = mci_readl(host, IMR);
- if (iflags)
+ if (iflags & ~(MCI_SDIOIRQA | MCI_SDIOIRQB))
dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
iflags);
@@ -952,10 +954,21 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (mci_has_rwproof())
host->mode_reg |= (MCI_MR_WRPROOF | MCI_MR_RDPROOF);
- if (list_empty(&host->queue))
+ if (atmci_is_mci2()) {
+ /* setup High Speed mode in relation with card capacity */
+ if (ios->timing == MMC_TIMING_SD_HS)
+ host->cfg_reg |= MCI_CFG_HSMODE;
+ else
+ host->cfg_reg &= ~MCI_CFG_HSMODE;
+ }
+
+ if (list_empty(&host->queue)) {
mci_writel(host, MR, host->mode_reg);
- else
+ if (atmci_is_mci2())
+ mci_writel(host, CFG, host->cfg_reg);
+ } else {
host->need_clock_update = true;
+ }
spin_unlock_bh(&host->lock);
} else {
@@ -1030,11 +1043,23 @@ static int atmci_get_cd(struct mmc_host *mmc)
return present;
}
+static void atmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+ struct atmel_mci *host = slot->host;
+
+ if (enable)
+ mci_writel(host, IER, slot->sdio_irq);
+ else
+ mci_writel(host, IDR, slot->sdio_irq);
+}
+
static const struct mmc_host_ops atmci_ops = {
.request = atmci_request,
.set_ios = atmci_set_ios,
.get_ro = atmci_get_ro,
.get_cd = atmci_get_cd,
+ .enable_sdio_irq = atmci_enable_sdio_irq,
};
/* Called with host->lock held */
@@ -1052,8 +1077,11 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
* necessary if set_ios() is called when a different slot is
* busy transfering data.
*/
- if (host->need_clock_update)
+ if (host->need_clock_update) {
mci_writel(host, MR, host->mode_reg);
+ if (atmci_is_mci2())
+ mci_writel(host, CFG, host->cfg_reg);
+ }
host->cur_slot->mrq = NULL;
host->mrq = NULL;
@@ -1483,6 +1511,19 @@ static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
tasklet_schedule(&host->tasklet);
}
+static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
+{
+ int i;
+
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ struct atmel_mci_slot *slot = host->slot[i];
+ if (slot && (status & slot->sdio_irq)) {
+ mmc_signal_sdio_irq(slot->mmc);
+ }
+ }
+}
+
+
static irqreturn_t atmci_interrupt(int irq, void *dev_id)
{
struct atmel_mci *host = dev_id;
@@ -1522,6 +1563,10 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
if (pending & MCI_CMDRDY)
atmci_cmd_interrupt(host, status);
+
+ if (pending & (MCI_SDIOIRQA | MCI_SDIOIRQB))
+ atmci_sdio_interrupt(host, status);
+
} while (pass_count++ < 5);
return pass_count ? IRQ_HANDLED : IRQ_NONE;
@@ -1544,7 +1589,7 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
static int __init atmci_init_slot(struct atmel_mci *host,
struct mci_slot_pdata *slot_data, unsigned int id,
- u32 sdc_reg)
+ u32 sdc_reg, u32 sdio_irq)
{
struct mmc_host *mmc;
struct atmel_mci_slot *slot;
@@ -1560,11 +1605,16 @@ static int __init atmci_init_slot(struct atmel_mci *host,
slot->wp_pin = slot_data->wp_pin;
slot->detect_is_active_high = slot_data->detect_is_active_high;
slot->sdc_reg = sdc_reg;
+ slot->sdio_irq = sdio_irq;
mmc->ops = &atmci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
mmc->f_max = host->bus_hz / 2;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (sdio_irq)
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
+ if (atmci_is_mci2())
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED;
if (slot_data->bus_width >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
@@ -1753,13 +1803,13 @@ static int __init atmci_probe(struct platform_device *pdev)
ret = -ENODEV;
if (pdata->slot[0].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[0],
- 0, MCI_SDCSEL_SLOT_A);
+ 0, MCI_SDCSEL_SLOT_A, MCI_SDIOIRQA);
if (!ret)
nr_slots++;
}
if (pdata->slot[1].bus_width) {
ret = atmci_init_slot(host, &pdata->slot[1],
- 1, MCI_SDCSEL_SLOT_B);
+ 1, MCI_SDCSEL_SLOT_B, MCI_SDIOIRQB);
if (!ret)
nr_slots++;
}
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index f583444..c8da5d3 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -1142,7 +1142,7 @@ static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
struct au1xmmc_host *host = platform_get_drvdata(pdev);
int ret;
- ret = mmc_suspend_host(host->mmc, state);
+ ret = mmc_suspend_host(host->mmc);
if (ret)
return ret;
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index 6919e84..4b0e677 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -576,7 +576,7 @@ static int sdh_suspend(struct platform_device *dev, pm_message_t state)
int ret = 0;
if (mmc)
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON);
peripheral_free_list(drv_data->pin_req);
diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 92a324f7..ca3bdc8 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -675,7 +675,7 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
int err;
- err = mmc_suspend_host(mmc, state);
+ err = mmc_suspend_host(mmc);
if (err)
return err;
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 3bd0ba2..33d9f1b 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -137,15 +137,15 @@
/*
* One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
- * and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
+ * and we handle up to MAX_NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
* for drivers with max_hw_segs == 1, making the segments bigger (64KB)
- * than the page or two that's otherwise typical. NR_SG == 16 gives at
- * least the same throughput boost, using EDMA transfer linkage instead
- * of spending CPU time copying pages.
+ * than the page or two that's otherwise typical. nr_sg (passed from
+ * platform data) == 16 gives at least the same throughput boost, using
+ * EDMA transfer linkage instead of spending CPU time copying pages.
*/
#define MAX_CCNT ((1 << 16) - 1)
-#define NR_SG 16
+#define MAX_NR_SG 16
static unsigned rw_threshold = 32;
module_param(rw_threshold, uint, S_IRUGO);
@@ -171,6 +171,7 @@ struct mmc_davinci_host {
#define DAVINCI_MMC_DATADIR_READ 1
#define DAVINCI_MMC_DATADIR_WRITE 2
unsigned char data_dir;
+ unsigned char suspended;
/* buffer is used during PIO of one scatterlist segment, and
* is updated along with buffer_bytes_left. bytes_left applies
@@ -192,7 +193,7 @@ struct mmc_davinci_host {
struct edmacc_param tx_template;
struct edmacc_param rx_template;
unsigned n_link;
- u32 links[NR_SG - 1];
+ u32 links[MAX_NR_SG - 1];
/* For PIO we walk scatterlists one segment at a time. */
unsigned int sg_len;
@@ -202,6 +203,8 @@ struct mmc_davinci_host {
u8 version;
/* for ns in one cycle calculation */
unsigned ns_in_one_cycle;
+ /* Number of sg segments */
+ u8 nr_sg;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
@@ -568,6 +571,7 @@ davinci_release_dma_channels(struct mmc_davinci_host *host)
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{
+ u32 link_size;
int r, i;
/* Acquire master DMA write channel */
@@ -593,7 +597,8 @@ static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
/* Allocate parameter RAM slots, which will later be bound to a
* channel as needed to handle a scatterlist.
*/
- for (i = 0; i < ARRAY_SIZE(host->links); i++) {
+ link_size = min_t(unsigned, host->nr_sg, ARRAY_SIZE(host->links));
+ for (i = 0; i < link_size; i++) {
r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
if (r < 0) {
dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
@@ -905,19 +910,26 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
}
}
-static void
-davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+static inline void mmc_davinci_reset_ctrl(struct mmc_davinci_host *host,
+ int val)
{
u32 temp;
- /* reset command and data state machines */
temp = readl(host->base + DAVINCI_MMCCTL);
- writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
- host->base + DAVINCI_MMCCTL);
+ if (val) /* reset */
+ temp |= MMCCTL_CMDRST | MMCCTL_DATRST;
+ else /* enable */
+ temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
- temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
- udelay(10);
writel(temp, host->base + DAVINCI_MMCCTL);
+ udelay(10);
+}
+
+static void
+davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
+{
+ mmc_davinci_reset_ctrl(host, 1);
+ mmc_davinci_reset_ctrl(host, 0);
}
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
@@ -1121,15 +1133,8 @@ static inline void mmc_davinci_cpufreq_deregister(struct mmc_davinci_host *host)
#endif
static void __init init_mmcsd_host(struct mmc_davinci_host *host)
{
- /* DAT line portion is diabled and in reset state */
- writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
- host->base + DAVINCI_MMCCTL);
-
- /* CMD line portion is diabled and in reset state */
- writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_CMDRST,
- host->base + DAVINCI_MMCCTL);
- udelay(10);
+ mmc_davinci_reset_ctrl(host, 1);
writel(0, host->base + DAVINCI_MMCCLK);
writel(MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
@@ -1137,12 +1142,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host)
writel(0x1FFF, host->base + DAVINCI_MMCTOR);
writel(0xFFFF, host->base + DAVINCI_MMCTOD);
- writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_DATRST,
- host->base + DAVINCI_MMCCTL);
- writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_CMDRST,
- host->base + DAVINCI_MMCCTL);
-
- udelay(10);
+ mmc_davinci_reset_ctrl(host, 0);
}
static int __init davinci_mmcsd_probe(struct platform_device *pdev)
@@ -1202,6 +1202,12 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
init_mmcsd_host(host);
+ if (pdata->nr_sg)
+ host->nr_sg = pdata->nr_sg - 1;
+
+ if (host->nr_sg > MAX_NR_SG || !host->nr_sg)
+ host->nr_sg = MAX_NR_SG;
+
host->use_dma = use_dma;
host->irq = irq;
@@ -1327,32 +1333,65 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int davinci_mmcsd_suspend(struct platform_device *pdev, pm_message_t msg)
+static int davinci_mmcsd_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+ int ret;
- return mmc_suspend_host(host->mmc, msg);
+ mmc_host_enable(host->mmc);
+ ret = mmc_suspend_host(host->mmc);
+ if (!ret) {
+ writel(0, host->base + DAVINCI_MMCIM);
+ mmc_davinci_reset_ctrl(host, 1);
+ mmc_host_disable(host->mmc);
+ clk_disable(host->clk);
+ host->suspended = 1;
+ } else {
+ host->suspended = 0;
+ mmc_host_disable(host->mmc);
+ }
+
+ return ret;
}
-static int davinci_mmcsd_resume(struct platform_device *pdev)
+static int davinci_mmcsd_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!host->suspended)
+ return 0;
- return mmc_resume_host(host->mmc);
+ clk_enable(host->clk);
+ mmc_host_enable(host->mmc);
+
+ mmc_davinci_reset_ctrl(host, 0);
+ ret = mmc_resume_host(host->mmc);
+ if (!ret)
+ host->suspended = 0;
+
+ return ret;
}
+
+static const struct dev_pm_ops davinci_mmcsd_pm = {
+ .suspend = davinci_mmcsd_suspend,
+ .resume = davinci_mmcsd_resume,
+};
+
+#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm)
#else
-#define davinci_mmcsd_suspend NULL
-#define davinci_mmcsd_resume NULL
+#define davinci_mmcsd_pm_ops NULL
#endif
static struct platform_driver davinci_mmcsd_driver = {
.driver = {
.name = "davinci_mmc",
.owner = THIS_MODULE,
+ .pm = davinci_mmcsd_pm_ops,
},
.remove = __exit_p(davinci_mmcsd_remove),
- .suspend = davinci_mmcsd_suspend,
- .resume = davinci_mmcsd_resume,
};
static int __init davinci_mmcsd_init(void)
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index bf98d7c..9a68ff4 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -1115,7 +1115,7 @@ static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
int ret = 0;
if (mmc)
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
return ret;
}
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index ff115d9..4917af9 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -824,7 +824,7 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
if (ret == 0)
writel(0, host->base + MMCIMASK0);
}
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 61f1d27..24e0945 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1327,7 +1327,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
disable_irq(host->stat_irq);
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
- rc = mmc_suspend_host(mmc, state);
+ rc = mmc_suspend_host(mmc);
if (!rc)
msmsdcc_writel(host, 0, MMCIMASK0);
if (host->clks_on)
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 34e2348..366eefa 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -865,7 +865,7 @@ static int mvsd_suspend(struct platform_device *dev, pm_message_t state)
int ret = 0;
if (mmc)
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
return ret;
}
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 74c87e0..fdf33e8 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -942,7 +942,7 @@ static int mxcmci_suspend(struct platform_device *dev, pm_message_t state)
int ret = 0;
if (mmc)
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
return ret;
}
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 84d2804..2b28168 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -39,30 +39,30 @@
#include <plat/fpga.h>
#define OMAP_MMC_REG_CMD 0x00
-#define OMAP_MMC_REG_ARGL 0x04
-#define OMAP_MMC_REG_ARGH 0x08
-#define OMAP_MMC_REG_CON 0x0c
-#define OMAP_MMC_REG_STAT 0x10
-#define OMAP_MMC_REG_IE 0x14
-#define OMAP_MMC_REG_CTO 0x18
-#define OMAP_MMC_REG_DTO 0x1c
-#define OMAP_MMC_REG_DATA 0x20
-#define OMAP_MMC_REG_BLEN 0x24
-#define OMAP_MMC_REG_NBLK 0x28
-#define OMAP_MMC_REG_BUF 0x2c
-#define OMAP_MMC_REG_SDIO 0x34
-#define OMAP_MMC_REG_REV 0x3c
-#define OMAP_MMC_REG_RSP0 0x40
-#define OMAP_MMC_REG_RSP1 0x44
-#define OMAP_MMC_REG_RSP2 0x48
-#define OMAP_MMC_REG_RSP3 0x4c
-#define OMAP_MMC_REG_RSP4 0x50
-#define OMAP_MMC_REG_RSP5 0x54
-#define OMAP_MMC_REG_RSP6 0x58
-#define OMAP_MMC_REG_RSP7 0x5c
-#define OMAP_MMC_REG_IOSR 0x60
-#define OMAP_MMC_REG_SYSC 0x64
-#define OMAP_MMC_REG_SYSS 0x68
+#define OMAP_MMC_REG_ARGL 0x01
+#define OMAP_MMC_REG_ARGH 0x02
+#define OMAP_MMC_REG_CON 0x03
+#define OMAP_MMC_REG_STAT 0x04
+#define OMAP_MMC_REG_IE 0x05
+#define OMAP_MMC_REG_CTO 0x06
+#define OMAP_MMC_REG_DTO 0x07
+#define OMAP_MMC_REG_DATA 0x08
+#define OMAP_MMC_REG_BLEN 0x09
+#define OMAP_MMC_REG_NBLK 0x0a
+#define OMAP_MMC_REG_BUF 0x0b
+#define OMAP_MMC_REG_SDIO 0x0d
+#define OMAP_MMC_REG_REV 0x0f
+#define OMAP_MMC_REG_RSP0 0x10
+#define OMAP_MMC_REG_RSP1 0x11
+#define OMAP_MMC_REG_RSP2 0x12
+#define OMAP_MMC_REG_RSP3 0x13
+#define OMAP_MMC_REG_RSP4 0x14
+#define OMAP_MMC_REG_RSP5 0x15
+#define OMAP_MMC_REG_RSP6 0x16
+#define OMAP_MMC_REG_RSP7 0x17
+#define OMAP_MMC_REG_IOSR 0x18
+#define OMAP_MMC_REG_SYSC 0x19
+#define OMAP_MMC_REG_SYSS 0x1a
#define OMAP_MMC_STAT_CARD_ERR (1 << 14)
#define OMAP_MMC_STAT_CARD_IRQ (1 << 13)
@@ -78,8 +78,9 @@
#define OMAP_MMC_STAT_CARD_BUSY (1 << 2)
#define OMAP_MMC_STAT_END_OF_CMD (1 << 0)
-#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg)
-#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg)
+#define OMAP_MMC_REG(host, reg) (OMAP_MMC_REG_##reg << (host)->reg_shift)
+#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg))
+#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
/*
* Command types
@@ -133,6 +134,7 @@ struct mmc_omap_host {
int irq;
unsigned char bus_mode;
unsigned char hw_bus_mode;
+ unsigned int reg_shift;
struct work_struct cmd_abort_work;
unsigned abort:1;
@@ -680,9 +682,9 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
host->data->bytes_xfered += n;
if (write) {
- __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+ __raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
} else {
- __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+ __raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
}
}
@@ -900,7 +902,7 @@ mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
int dst_port = 0;
int sync_dev = 0;
- data_addr = host->phys_base + OMAP_MMC_REG_DATA;
+ data_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
frame = data->blksz;
count = sg_dma_len(sg);
@@ -1493,6 +1495,8 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
}
}
+ host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
+
return 0;
err_plat_cleanup:
@@ -1557,7 +1561,7 @@ static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
struct mmc_omap_slot *slot;
slot = host->slots[i];
- ret = mmc_suspend_host(slot->mmc, mesg);
+ ret = mmc_suspend_host(slot->mmc);
if (ret < 0) {
while (--i >= 0) {
slot = host->slots[i];
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e9caf69..b032828 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -157,12 +157,10 @@ struct omap_hsmmc_host {
*/
struct regulator *vcc;
struct regulator *vcc_aux;
- struct semaphore sem;
struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
- unsigned long flags;
unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
@@ -183,6 +181,7 @@ struct omap_hsmmc_host {
int protect_card;
int reqs_blocked;
int use_reg;
+ int req_in_progress;
struct omap_mmc_platform_data *pdata;
};
@@ -524,6 +523,27 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+{
+ unsigned int irq_mask;
+
+ if (host->use_dma)
+ irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
+ else
+ irq_mask = INT_EN_MASK;
+
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
+ OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
+}
+
+static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
+{
+ OMAP_HSMMC_WRITE(host->base, ISE, 0);
+ OMAP_HSMMC_WRITE(host->base, IE, 0);
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+}
+
#ifdef CONFIG_PM
/*
@@ -592,9 +612,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
&& time_before(jiffies, timeout))
;
- OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
- OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
- OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+ omap_hsmmc_disable_irq(host);
/* Do not initialize card-specific things if the power is off */
if (host->power_mode == MMC_POWER_OFF)
@@ -697,6 +715,8 @@ static void send_init_stream(struct omap_hsmmc_host *host)
return;
disable_irq(host->irq);
+
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
@@ -762,17 +782,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
- /*
- * Clear status bits and enable interrupts
- */
- OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
- OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
-
- if (host->use_dma)
- OMAP_HSMMC_WRITE(host->base, IE,
- INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE));
- else
- OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+ omap_hsmmc_enable_irq(host);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -806,13 +816,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
if (host->use_dma)
cmdreg |= DMA_EN;
- /*
- * In an interrupt context (i.e. STOP command), the spinlock is unlocked
- * by the interrupt handler, otherwise (i.e. for a new request) it is
- * unlocked here.
- */
- if (!in_interrupt())
- spin_unlock_irqrestore(&host->irq_lock, host->flags);
+ host->req_in_progress = 1;
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
@@ -827,6 +831,23 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
return DMA_FROM_DEVICE;
}
+static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
+{
+ int dma_ch;
+
+ spin_lock(&host->irq_lock);
+ host->req_in_progress = 0;
+ dma_ch = host->dma_ch;
+ spin_unlock(&host->irq_lock);
+
+ omap_hsmmc_disable_irq(host);
+ /* Do not complete the request if DMA is still in progress */
+ if (mrq->data && host->use_dma && dma_ch != -1)
+ return;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
/*
* Notify the transfer complete to MMC core
*/
@@ -843,25 +864,19 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
return;
}
- host->mrq = NULL;
- mmc_request_done(host->mmc, mrq);
+ omap_hsmmc_request_done(host, mrq);
return;
}
host->data = NULL;
- if (host->use_dma && host->dma_ch != -1)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
- omap_hsmmc_get_dma_dir(host, data));
-
if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz);
else
data->bytes_xfered = 0;
if (!data->stop) {
- host->mrq = NULL;
- mmc_request_done(host->mmc, data->mrq);
+ omap_hsmmc_request_done(host, data->mrq);
return;
}
omap_hsmmc_start_command(host, data->stop, NULL);
@@ -887,10 +902,8 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
}
}
- if ((host->data == NULL && !host->response_busy) || cmd->error) {
- host->mrq = NULL;
- mmc_request_done(host->mmc, cmd->mrq);
- }
+ if ((host->data == NULL && !host->response_busy) || cmd->error)
+ omap_hsmmc_request_done(host, cmd->mrq);
}
/*
@@ -898,14 +911,19 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
*/
static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
{
+ int dma_ch;
+
host->data->error = errno;
- if (host->use_dma && host->dma_ch != -1) {
+ spin_lock(&host->irq_lock);
+ dma_ch = host->dma_ch;
+ host->dma_ch = -1;
+ spin_unlock(&host->irq_lock);
+
+ if (host->use_dma && dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
omap_hsmmc_get_dma_dir(host, host->data));
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
- up(&host->sem);
+ omap_free_dma(dma_ch);
}
host->data = NULL;
}
@@ -967,28 +985,21 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
__func__);
}
-/*
- * MMC controller IRQ handler
- */
-static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
+static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
{
- struct omap_hsmmc_host *host = dev_id;
struct mmc_data *data;
- int end_cmd = 0, end_trans = 0, status;
-
- spin_lock(&host->irq_lock);
-
- if (host->mrq == NULL) {
- OMAP_HSMMC_WRITE(host->base, STAT,
- OMAP_HSMMC_READ(host->base, STAT));
- /* Flush posted write */
- OMAP_HSMMC_READ(host->base, STAT);
- spin_unlock(&host->irq_lock);
- return IRQ_HANDLED;
+ int end_cmd = 0, end_trans = 0;
+
+ if (!host->req_in_progress) {
+ do {
+ OMAP_HSMMC_WRITE(host->base, STAT, status);
+ /* Flush posted write */
+ status = OMAP_HSMMC_READ(host->base, STAT);
+ } while (status & INT_EN_MASK);
+ return;
}
data = host->data;
- status = OMAP_HSMMC_READ(host->base, STAT);
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
if (status & ERR) {
@@ -1041,15 +1052,27 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
}
OMAP_HSMMC_WRITE(host->base, STAT, status);
- /* Flush posted write */
- OMAP_HSMMC_READ(host->base, STAT);
if (end_cmd || ((status & CC) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
+}
- spin_unlock(&host->irq_lock);
+/*
+ * MMC controller IRQ handler
+ */
+static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
+{
+ struct omap_hsmmc_host *host = dev_id;
+ int status;
+
+ status = OMAP_HSMMC_READ(host->base, STAT);
+ do {
+ omap_hsmmc_do_irq(host, status);
+ /* Flush posted write */
+ status = OMAP_HSMMC_READ(host->base, STAT);
+ } while (status & INT_EN_MASK);
return IRQ_HANDLED;
}
@@ -1244,31 +1267,47 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
/*
* DMA call back function
*/
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
{
- struct omap_hsmmc_host *host = data;
+ struct omap_hsmmc_host *host = cb_data;
+ struct mmc_data *data = host->mrq->data;
+ int dma_ch, req_in_progress;
if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
- if (host->dma_ch < 0)
+ spin_lock(&host->irq_lock);
+ if (host->dma_ch < 0) {
+ spin_unlock(&host->irq_lock);
return;
+ }
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
- omap_hsmmc_config_dma_params(host, host->data,
- host->data->sg + host->dma_sg_idx);
+ omap_hsmmc_config_dma_params(host, data,
+ data->sg + host->dma_sg_idx);
+ spin_unlock(&host->irq_lock);
return;
}
- omap_free_dma(host->dma_ch);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ omap_hsmmc_get_dma_dir(host, data));
+
+ req_in_progress = host->req_in_progress;
+ dma_ch = host->dma_ch;
host->dma_ch = -1;
- /*
- * DMA Callback: run in interrupt context.
- * mutex_unlock will throw a kernel warning if used.
- */
- up(&host->sem);
+ spin_unlock(&host->irq_lock);
+
+ omap_free_dma(dma_ch);
+
+ /* If DMA has finished after TC, complete the request */
+ if (!req_in_progress) {
+ struct mmc_request *mrq = host->mrq;
+
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+ }
}
/*
@@ -1277,7 +1316,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
- int dma_ch = 0, ret = 0, err = 1, i;
+ int dma_ch = 0, ret = 0, i;
struct mmc_data *data = req->data;
/* Sanity check: all the SG entries must be aligned by block size. */
@@ -1294,23 +1333,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
*/
return -EINVAL;
- /*
- * If for some reason the DMA transfer is still active,
- * we wait for timeout period and free the dma
- */
- if (host->dma_ch != -1) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(100);
- if (down_trylock(&host->sem)) {
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
- up(&host->sem);
- return err;
- }
- } else {
- if (down_trylock(&host->sem))
- return err;
- }
+ BUG_ON(host->dma_ch != -1);
ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
"MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
@@ -1410,37 +1433,27 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
struct omap_hsmmc_host *host = mmc_priv(mmc);
int err;
- /*
- * Prevent races with the interrupt handler because of unexpected
- * interrupts, but not if we are already in interrupt context i.e.
- * retries.
- */
- if (!in_interrupt()) {
- spin_lock_irqsave(&host->irq_lock, host->flags);
- /*
- * Protect the card from I/O if there is a possibility
- * it can be removed.
- */
- if (host->protect_card) {
- if (host->reqs_blocked < 3) {
- /*
- * Ensure the controller is left in a consistent
- * state by resetting the command and data state
- * machines.
- */
- omap_hsmmc_reset_controller_fsm(host, SRD);
- omap_hsmmc_reset_controller_fsm(host, SRC);
- host->reqs_blocked += 1;
- }
- req->cmd->error = -EBADF;
- if (req->data)
- req->data->error = -EBADF;
- spin_unlock_irqrestore(&host->irq_lock, host->flags);
- mmc_request_done(mmc, req);
- return;
- } else if (host->reqs_blocked)
- host->reqs_blocked = 0;
- }
+ BUG_ON(host->req_in_progress);
+ BUG_ON(host->dma_ch != -1);
+ if (host->protect_card) {
+ if (host->reqs_blocked < 3) {
+ /*
+ * Ensure the controller is left in a consistent
+ * state by resetting the command and data state
+ * machines.
+ */
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ host->reqs_blocked += 1;
+ }
+ req->cmd->error = -EBADF;
+ if (req->data)
+ req->data->error = -EBADF;
+ req->cmd->retries = 0;
+ mmc_request_done(mmc, req);
+ return;
+ } else if (host->reqs_blocked)
+ host->reqs_blocked = 0;
WARN_ON(host->mrq != NULL);
host->mrq = req;
err = omap_hsmmc_prepare_data(host, req);
@@ -1449,8 +1462,6 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
if (req->data)
req->data->error = err;
host->mrq = NULL;
- if (!in_interrupt())
- spin_unlock_irqrestore(&host->irq_lock, host->flags);
mmc_request_done(mmc, req);
return;
}
@@ -2019,7 +2030,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->f_min = 400000;
mmc->f_max = 52000000;
- sema_init(&host->sem, 1);
spin_lock_init(&host->irq_lock);
host->iclk = clk_get(&pdev->dev, "ick");
@@ -2162,8 +2172,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
}
}
- OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
- OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+ omap_hsmmc_disable_irq(host);
mmc_host_lazy_disable(host->mmc);
@@ -2258,10 +2267,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int omap_hsmmc_suspend(struct device *dev)
{
int ret = 0;
+ struct platform_device *pdev = to_platform_device(dev);
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
+ pm_message_t state = PMSG_SUSPEND; /* unused by MMC core */
if (host && host->suspended)
return 0;
@@ -2281,12 +2292,9 @@ static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
}
cancel_work_sync(&host->mmc_carddetect_work);
mmc_host_enable(host->mmc);
- ret = mmc_suspend_host(host->mmc, state);
+ ret = mmc_suspend_host(host->mmc);
if (ret == 0) {
- OMAP_HSMMC_WRITE(host->base, ISE, 0);
- OMAP_HSMMC_WRITE(host->base, IE, 0);
-
-
+ omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
@@ -2310,9 +2318,10 @@ static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
}
/* Routine to resume the MMC device */
-static int omap_hsmmc_resume(struct platform_device *pdev)
+static int omap_hsmmc_resume(struct device *dev)
{
int ret = 0;
+ struct platform_device *pdev = to_platform_device(dev);
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
if (host && !host->suspended)
@@ -2363,13 +2372,17 @@ clk_en_err:
#define omap_hsmmc_resume NULL
#endif
-static struct platform_driver omap_hsmmc_driver = {
- .remove = omap_hsmmc_remove,
+static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
+};
+
+static struct platform_driver omap_hsmmc_driver = {
+ .remove = omap_hsmmc_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &omap_hsmmc_dev_pm_ops,
},
};
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index e4f00e7..0a4e43f 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -813,7 +813,7 @@ static int pxamci_suspend(struct device *dev)
int ret = 0;
if (mmc)
- ret = mmc_suspend_host(mmc, PMSG_SUSPEND);
+ ret = mmc_suspend_host(mmc);
return ret;
}
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 2fdf768..2e16e0a 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1881,9 +1881,8 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
static int s3cmci_suspend(struct device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
- struct pm_message event = { PM_EVENT_SUSPEND };
- return mmc_suspend_host(mmc, event);
+ return mmc_suspend_host(mmc);
}
static int s3cmci_resume(struct device *dev)
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
index 7802a54..a2e9820 100644
--- a/drivers/mmc/host/sdhci-of-core.c
+++ b/drivers/mmc/host/sdhci-of-core.c
@@ -89,7 +89,7 @@ static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
{
struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
- return mmc_suspend_host(host->mmc, state);
+ return mmc_suspend_host(host->mmc);
}
static int sdhci_of_resume(struct of_device *ofdev)
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index d5b11a1..c8623de 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -129,12 +129,12 @@ struct sdhci_of_data sdhci_esdhc = {
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
SDHCI_QUIRK_NO_CARD_NO_RESET,
.ops = {
- .readl = sdhci_be32bs_readl,
- .readw = esdhc_readw,
- .readb = sdhci_be32bs_readb,
- .writel = sdhci_be32bs_writel,
- .writew = esdhc_writew,
- .writeb = esdhc_writeb,
+ .read_l = sdhci_be32bs_readl,
+ .read_w = esdhc_readw,
+ .read_b = sdhci_be32bs_readb,
+ .write_l = sdhci_be32bs_writel,
+ .write_w = esdhc_writew,
+ .write_b = esdhc_writeb,
.set_clock = esdhc_set_clock,
.enable_dma = esdhc_enable_dma,
.get_max_clock = esdhc_get_max_clock,
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 35117f3..68ddb75 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -55,11 +55,11 @@ struct sdhci_of_data sdhci_hlwd = {
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE,
.ops = {
- .readl = sdhci_be32bs_readl,
- .readw = sdhci_be32bs_readw,
- .readb = sdhci_be32bs_readb,
- .writel = sdhci_hlwd_writel,
- .writew = sdhci_hlwd_writew,
- .writeb = sdhci_hlwd_writeb,
+ .read_l = sdhci_be32bs_readl,
+ .read_w = sdhci_be32bs_readw,
+ .read_b = sdhci_be32bs_readb,
+ .write_l = sdhci_hlwd_writel,
+ .write_w = sdhci_hlwd_writew,
+ .write_b = sdhci_hlwd_writeb,
},
};
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 6701af6..65483fd 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -628,7 +628,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
if (IS_ERR(host)) {
dev_err(&pdev->dev, "cannot allocate host\n");
- return ERR_PTR(PTR_ERR(host));
+ return ERR_CAST(host);
}
slot = sdhci_priv(host);
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 297f40a..b6ee0d7 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -29,6 +29,7 @@
#include <linux/mmc/host.h>
#include <linux/io.h>
+#include <linux/sdhci-pltfm.h>
#include "sdhci.h"
@@ -49,19 +50,18 @@ static struct sdhci_ops sdhci_pltfm_ops = {
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
{
+ struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
struct sdhci_host *host;
struct resource *iomem;
int ret;
- BUG_ON(pdev == NULL);
-
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
goto err;
}
- if (resource_size(iomem) != 0x100)
+ if (resource_size(iomem) < 0x100)
dev_err(&pdev->dev, "Invalid iomem size. You may "
"experience problems.\n");
@@ -76,7 +76,12 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
}
host->hw_name = "platform";
- host->ops = &sdhci_pltfm_ops;
+ if (pdata && pdata->ops)
+ host->ops = pdata->ops;
+ else
+ host->ops = &sdhci_pltfm_ops;
+ if (pdata)
+ host->quirks = pdata->quirks;
host->irq = platform_get_irq(pdev, 0);
if (!request_mem_region(iomem->start, resource_size(iomem),
@@ -93,6 +98,12 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
goto err_remap;
}
+ if (pdata && pdata->init) {
+ ret = pdata->init(host);
+ if (ret)
+ goto err_plat_init;
+ }
+
ret = sdhci_add_host(host);
if (ret)
goto err_add_host;
@@ -102,6 +113,9 @@ static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
return 0;
err_add_host:
+ if (pdata && pdata->exit)
+ pdata->exit(host);
+err_plat_init:
iounmap(host->ioaddr);
err_remap:
release_mem_region(iomem->start, resource_size(iomem));
@@ -114,6 +128,7 @@ err:
static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
{
+ struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int dead;
@@ -125,6 +140,8 @@ static int __devexit sdhci_pltfm_remove(struct platform_device *pdev)
dead = 1;
sdhci_remove_host(host, dead);
+ if (pdata && pdata->exit)
+ pdata->exit(host);
iounmap(host->ioaddr);
release_mem_region(iomem->start, resource_size(iomem));
sdhci_free_host(host);
@@ -165,4 +182,3 @@ MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sdhci");
-
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 2136794..af21792 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -317,12 +317,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
host->irq = irq;
/* Setup quirks for the controller */
-
- /* Currently with ADMA enabled we are getting some length
- * interrupts that are not being dealt with, do disable
- * ADMA until this is sorted out. */
- host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
- host->quirks |= SDHCI_QUIRK_32BIT_ADMA_SIZE;
+ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
@@ -330,9 +325,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
* support as well. */
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
- /* PIO currently has problems with multi-block IO */
- host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
-
#endif /* CONFIG_MMC_SDHCI_S3C_DMA */
/* It seems we do not get an DATA transfer complete on non-busy
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
new file mode 100644
index 0000000..d70c54c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -0,0 +1,298 @@
+/*
+ * drivers/mmc/host/sdhci-spear.c
+ *
+ * Support of SDHCI platform devices for spear soc family
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * Inspired by sdhci-pltfm.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdhci-spear.h>
+#include <linux/io.h>
+#include "sdhci.h"
+
+struct spear_sdhci {
+ struct clk *clk;
+ struct sdhci_plat_data *data;
+};
+
+/* sdhci ops */
+static struct sdhci_ops sdhci_pltfm_ops = {
+ /* Nothing to do for now. */
+};
+
+/* gpio card detection interrupt handler */
+static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
+ unsigned long gpio_irq_type;
+ int val;
+
+ val = gpio_get_value(sdhci->data->card_int_gpio);
+
+ /* val == 1 -> card removed, val == 0 -> card inserted */
+ /* if card removed - set irq for low level, else vice versa */
+ gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
+ set_irq_type(irq, gpio_irq_type);
+
+ if (sdhci->data->card_power_gpio >= 0) {
+ if (!sdhci->data->power_always_enb) {
+ /* if card inserted, give power, otherwise remove it */
+ val = sdhci->data->power_active_high ? !val : val ;
+ gpio_set_value(sdhci->data->card_power_gpio, val);
+ }
+ }
+
+ /* inform sdhci driver about card insertion/removal */
+ tasklet_schedule(&host->card_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit sdhci_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct resource *iomem;
+ struct spear_sdhci *sdhci;
+ int ret;
+
+ BUG_ON(pdev == NULL);
+
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iomem) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "memory resource not defined\n");
+ goto err;
+ }
+
+ if (!request_mem_region(iomem->start, resource_size(iomem),
+ "spear-sdhci")) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "cannot request region\n");
+ goto err;
+ }
+
+ sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL);
+ if (!sdhci) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
+ goto err_kzalloc;
+ }
+
+ /* clk enable */
+ sdhci->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sdhci->clk)) {
+ ret = PTR_ERR(sdhci->clk);
+ dev_dbg(&pdev->dev, "Error getting clock\n");
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(sdhci->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Error enabling clock\n");
+ goto err_clk_enb;
+ }
+
+ /* overwrite platform_data */
+ sdhci->data = dev_get_platdata(&pdev->dev);
+ pdev->dev.platform_data = sdhci;
+
+ if (pdev->dev.parent)
+ host = sdhci_alloc_host(pdev->dev.parent, 0);
+ else
+ host = sdhci_alloc_host(&pdev->dev, 0);
+
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ dev_dbg(&pdev->dev, "error allocating host\n");
+ goto err_alloc_host;
+ }
+
+ host->hw_name = "sdhci";
+ host->ops = &sdhci_pltfm_ops;
+ host->irq = platform_get_irq(pdev, 0);
+ host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
+
+ host->ioaddr = ioremap(iomem->start, resource_size(iomem));
+ if (!host->ioaddr) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "failed to remap registers\n");
+ goto err_ioremap;
+ }
+
+ ret = sdhci_add_host(host);
+ if (ret) {
+ dev_dbg(&pdev->dev, "error adding host\n");
+ goto err_add_host;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ /*
+ * It is optional to use GPIOs for sdhci Power control & sdhci card
+ * interrupt detection. If sdhci->data is NULL, then use original sdhci
+ * lines otherwise GPIO lines.
+ * If GPIO is selected for power control, then power should be disabled
+ * after card removal and should be enabled when card insertion
+ * interrupt occurs
+ */
+ if (!sdhci->data)
+ return 0;
+
+ if (sdhci->data->card_power_gpio >= 0) {
+ int val = 0;
+
+ ret = gpio_request(sdhci->data->card_power_gpio, "sdhci");
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "gpio request fail: %d\n",
+ sdhci->data->card_power_gpio);
+ goto err_pgpio_request;
+ }
+
+ if (sdhci->data->power_always_enb)
+ val = sdhci->data->power_active_high;
+ else
+ val = !sdhci->data->power_active_high;
+
+ ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
+ if (ret) {
+ dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
+ sdhci->data->card_power_gpio);
+ goto err_pgpio_direction;
+ }
+
+ gpio_set_value(sdhci->data->card_power_gpio, 1);
+ }
+
+ if (sdhci->data->card_int_gpio >= 0) {
+ ret = gpio_request(sdhci->data->card_int_gpio, "sdhci");
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "gpio request fail: %d\n",
+ sdhci->data->card_int_gpio);
+ goto err_igpio_request;
+ }
+
+ ret = gpio_direction_input(sdhci->data->card_int_gpio);
+ if (ret) {
+ dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
+ sdhci->data->card_int_gpio);
+ goto err_igpio_direction;
+ }
+ ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio),
+ sdhci_gpio_irq, IRQF_TRIGGER_LOW,
+ mmc_hostname(host->mmc), pdev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
+ sdhci->data->card_int_gpio);
+ goto err_igpio_request_irq;
+ }
+
+ }
+
+ return 0;
+
+err_igpio_request_irq:
+err_igpio_direction:
+ if (sdhci->data->card_int_gpio >= 0)
+ gpio_free(sdhci->data->card_int_gpio);
+err_igpio_request:
+err_pgpio_direction:
+ if (sdhci->data->card_power_gpio >= 0)
+ gpio_free(sdhci->data->card_power_gpio);
+err_pgpio_request:
+ platform_set_drvdata(pdev, NULL);
+ sdhci_remove_host(host, 1);
+err_add_host:
+ iounmap(host->ioaddr);
+err_ioremap:
+ sdhci_free_host(host);
+err_alloc_host:
+ clk_disable(sdhci->clk);
+err_clk_enb:
+ clk_put(sdhci->clk);
+err_clk_get:
+ kfree(sdhci);
+err_kzalloc:
+ release_mem_region(iomem->start, resource_size(iomem));
+err:
+ dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
+ return ret;
+}
+
+static int __devexit sdhci_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
+ int dead;
+ u32 scratch;
+
+ if (sdhci->data) {
+ if (sdhci->data->card_int_gpio >= 0) {
+ free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev);
+ gpio_free(sdhci->data->card_int_gpio);
+ }
+
+ if (sdhci->data->card_power_gpio >= 0)
+ gpio_free(sdhci->data->card_power_gpio);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ dead = 0;
+ scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+
+ sdhci_remove_host(host, dead);
+ iounmap(host->ioaddr);
+ sdhci_free_host(host);
+ clk_disable(sdhci->clk);
+ clk_put(sdhci->clk);
+ kfree(sdhci);
+ if (iomem)
+ release_mem_region(iomem->start, resource_size(iomem));
+
+ return 0;
+}
+
+static struct platform_driver sdhci_driver = {
+ .driver = {
+ .name = "sdhci",
+ .owner = THIS_MODULE,
+ },
+ .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_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9d4fdfa..c6d1bd8 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -496,12 +496,22 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4);
}
- /*
- * Add a terminating entry.
- */
+ if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
+ /*
+ * Mark the last descriptor as the terminating descriptor
+ */
+ if (desc != host->adma_desc) {
+ desc -= 8;
+ desc[0] |= 0x2; /* end */
+ }
+ } else {
+ /*
+ * Add a terminating entry.
+ */
- /* nop, end, valid */
- sdhci_set_adma_desc(desc, 0, 0, 0x3);
+ /* nop, end, valid */
+ sdhci_set_adma_desc(desc, 0, 0, 0x3);
+ }
/*
* Resync align buffer as we might have changed it.
@@ -1587,7 +1597,7 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
sdhci_disable_card_detection(host);
- ret = mmc_suspend_host(host->mmc, state);
+ ret = mmc_suspend_host(host->mmc);
if (ret)
return ret;
@@ -1744,7 +1754,8 @@ int sdhci_add_host(struct sdhci_host *host)
host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk *= 1000000;
- if (host->max_clk == 0) {
+ if (host->max_clk == 0 || host->quirks &
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
if (!host->ops->get_max_clock) {
printk(KERN_ERR
"%s: Hardware doesn't specify base clock "
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 842f46f..c846813 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -127,7 +127,7 @@
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
- SDHCI_INT_DATA_END_BIT | SDHCI_ADMA_ERROR)
+ SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
@@ -236,6 +236,10 @@ struct sdhci_host {
#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
/* Controller uses SDCLK instead of TMCLK for data timeouts */
#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
+/* Controller reports wrong base clock capability */
+#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
+/* Controller cannot support End Attribute in NOP ADMA descriptor */
+#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
@@ -294,12 +298,12 @@ struct sdhci_host {
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
- u32 (*readl)(struct sdhci_host *host, int reg);
- u16 (*readw)(struct sdhci_host *host, int reg);
- u8 (*readb)(struct sdhci_host *host, int reg);
- void (*writel)(struct sdhci_host *host, u32 val, int reg);
- void (*writew)(struct sdhci_host *host, u16 val, int reg);
- void (*writeb)(struct sdhci_host *host, u8 val, int reg);
+ u32 (*read_l)(struct sdhci_host *host, int reg);
+ u16 (*read_w)(struct sdhci_host *host, int reg);
+ u8 (*read_b)(struct sdhci_host *host, int reg);
+ void (*write_l)(struct sdhci_host *host, u32 val, int reg);
+ void (*write_w)(struct sdhci_host *host, u16 val, int reg);
+ void (*write_b)(struct sdhci_host *host, u8 val, int reg);
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
@@ -314,48 +318,48 @@ struct sdhci_ops {
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
- if (unlikely(host->ops->writel))
- host->ops->writel(host, val, reg);
+ if (unlikely(host->ops->write_l))
+ host->ops->write_l(host, val, reg);
else
writel(val, host->ioaddr + reg);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
- if (unlikely(host->ops->writew))
- host->ops->writew(host, val, reg);
+ if (unlikely(host->ops->write_w))
+ host->ops->write_w(host, val, reg);
else
writew(val, host->ioaddr + reg);
}
static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
- if (unlikely(host->ops->writeb))
- host->ops->writeb(host, val, reg);
+ if (unlikely(host->ops->write_b))
+ host->ops->write_b(host, val, reg);
else
writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
{
- if (unlikely(host->ops->readl))
- return host->ops->readl(host, reg);
+ if (unlikely(host->ops->read_l))
+ return host->ops->read_l(host, reg);
else
return readl(host->ioaddr + reg);
}
static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
{
- if (unlikely(host->ops->readw))
- return host->ops->readw(host, reg);
+ if (unlikely(host->ops->read_w))
+ return host->ops->read_w(host, reg);
else
return readw(host->ioaddr + reg);
}
static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
{
- if (unlikely(host->ops->readb))
- return host->ops->readb(host, reg);
+ if (unlikely(host->ops->read_b))
+ return host->ops->read_b(host, reg);
else
return readb(host->ioaddr + reg);
}
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index cb41e9c..e7507af 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -519,7 +519,7 @@ static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
{
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "suspend\n");
- mmc_suspend_host(mmc, PMSG_SUSPEND);
+ mmc_suspend_host(mmc);
return 0;
}
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
new file mode 100644
index 0000000..eb97830
--- /dev/null
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -0,0 +1,965 @@
+/*
+ * MMCIF eMMC driver.
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ * Yusuke Goda <yusuke.goda.sx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ *
+ * TODO
+ * 1. DMA
+ * 2. Power management
+ * 3. Handle MMC errors better
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/sh_mmcif.h>
+
+#define DRIVER_NAME "sh_mmcif"
+#define DRIVER_VERSION "2010-04-28"
+
+#define MMCIF_CE_CMD_SET 0x00000000
+#define MMCIF_CE_ARG 0x00000008
+#define MMCIF_CE_ARG_CMD12 0x0000000C
+#define MMCIF_CE_CMD_CTRL 0x00000010
+#define MMCIF_CE_BLOCK_SET 0x00000014
+#define MMCIF_CE_CLK_CTRL 0x00000018
+#define MMCIF_CE_BUF_ACC 0x0000001C
+#define MMCIF_CE_RESP3 0x00000020
+#define MMCIF_CE_RESP2 0x00000024
+#define MMCIF_CE_RESP1 0x00000028
+#define MMCIF_CE_RESP0 0x0000002C
+#define MMCIF_CE_RESP_CMD12 0x00000030
+#define MMCIF_CE_DATA 0x00000034
+#define MMCIF_CE_INT 0x00000040
+#define MMCIF_CE_INT_MASK 0x00000044
+#define MMCIF_CE_HOST_STS1 0x00000048
+#define MMCIF_CE_HOST_STS2 0x0000004C
+#define MMCIF_CE_VERSION 0x0000007C
+
+/* CE_CMD_SET */
+#define CMD_MASK 0x3f000000
+#define CMD_SET_RTYP_NO ((0 << 23) | (0 << 22))
+#define CMD_SET_RTYP_6B ((0 << 23) | (1 << 22)) /* R1/R1b/R3/R4/R5 */
+#define CMD_SET_RTYP_17B ((1 << 23) | (0 << 22)) /* R2 */
+#define CMD_SET_RBSY (1 << 21) /* R1b */
+#define CMD_SET_CCSEN (1 << 20)
+#define CMD_SET_WDAT (1 << 19) /* 1: on data, 0: no data */
+#define CMD_SET_DWEN (1 << 18) /* 1: write, 0: read */
+#define CMD_SET_CMLTE (1 << 17) /* 1: multi block trans, 0: single */
+#define CMD_SET_CMD12EN (1 << 16) /* 1: CMD12 auto issue */
+#define CMD_SET_RIDXC_INDEX ((0 << 15) | (0 << 14)) /* index check */
+#define CMD_SET_RIDXC_BITS ((0 << 15) | (1 << 14)) /* check bits check */
+#define CMD_SET_RIDXC_NO ((1 << 15) | (0 << 14)) /* no check */
+#define CMD_SET_CRC7C ((0 << 13) | (0 << 12)) /* CRC7 check*/
+#define CMD_SET_CRC7C_BITS ((0 << 13) | (1 << 12)) /* check bits check*/
+#define CMD_SET_CRC7C_INTERNAL ((1 << 13) | (0 << 12)) /* internal CRC7 check*/
+#define CMD_SET_CRC16C (1 << 10) /* 0: CRC16 check*/
+#define CMD_SET_CRCSTE (1 << 8) /* 1: not receive CRC status */
+#define CMD_SET_TBIT (1 << 7) /* 1: tran mission bit "Low" */
+#define CMD_SET_OPDM (1 << 6) /* 1: open/drain */
+#define CMD_SET_CCSH (1 << 5)
+#define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) /* 1bit */
+#define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) /* 4bit */
+#define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) /* 8bit */
+
+/* CE_CMD_CTRL */
+#define CMD_CTRL_BREAK (1 << 0)
+
+/* CE_BLOCK_SET */
+#define BLOCK_SIZE_MASK 0x0000ffff
+
+/* CE_CLK_CTRL */
+#define CLK_ENABLE (1 << 24) /* 1: output mmc clock */
+#define CLK_CLEAR ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
+#define CLK_SUP_PCLK ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
+#define SRSPTO_256 ((1 << 13) | (0 << 12)) /* resp timeout */
+#define SRBSYTO_29 ((1 << 11) | (1 << 10) | \
+ (1 << 9) | (1 << 8)) /* resp busy timeout */
+#define SRWDTO_29 ((1 << 7) | (1 << 6) | \
+ (1 << 5) | (1 << 4)) /* read/write timeout */
+#define SCCSTO_29 ((1 << 3) | (1 << 2) | \
+ (1 << 1) | (1 << 0)) /* ccs timeout */
+
+/* CE_BUF_ACC */
+#define BUF_ACC_DMAWEN (1 << 25)
+#define BUF_ACC_DMAREN (1 << 24)
+#define BUF_ACC_BUSW_32 (0 << 17)
+#define BUF_ACC_BUSW_16 (1 << 17)
+#define BUF_ACC_ATYP (1 << 16)
+
+/* CE_INT */
+#define INT_CCSDE (1 << 29)
+#define INT_CMD12DRE (1 << 26)
+#define INT_CMD12RBE (1 << 25)
+#define INT_CMD12CRE (1 << 24)
+#define INT_DTRANE (1 << 23)
+#define INT_BUFRE (1 << 22)
+#define INT_BUFWEN (1 << 21)
+#define INT_BUFREN (1 << 20)
+#define INT_CCSRCV (1 << 19)
+#define INT_RBSYE (1 << 17)
+#define INT_CRSPE (1 << 16)
+#define INT_CMDVIO (1 << 15)
+#define INT_BUFVIO (1 << 14)
+#define INT_WDATERR (1 << 11)
+#define INT_RDATERR (1 << 10)
+#define INT_RIDXERR (1 << 9)
+#define INT_RSPERR (1 << 8)
+#define INT_CCSTO (1 << 5)
+#define INT_CRCSTO (1 << 4)
+#define INT_WDATTO (1 << 3)
+#define INT_RDATTO (1 << 2)
+#define INT_RBSYTO (1 << 1)
+#define INT_RSPTO (1 << 0)
+#define INT_ERR_STS (INT_CMDVIO | INT_BUFVIO | INT_WDATERR | \
+ INT_RDATERR | INT_RIDXERR | INT_RSPERR | \
+ INT_CCSTO | INT_CRCSTO | INT_WDATTO | \
+ INT_RDATTO | INT_RBSYTO | INT_RSPTO)
+
+/* CE_INT_MASK */
+#define MASK_ALL 0x00000000
+#define MASK_MCCSDE (1 << 29)
+#define MASK_MCMD12DRE (1 << 26)
+#define MASK_MCMD12RBE (1 << 25)
+#define MASK_MCMD12CRE (1 << 24)
+#define MASK_MDTRANE (1 << 23)
+#define MASK_MBUFRE (1 << 22)
+#define MASK_MBUFWEN (1 << 21)
+#define MASK_MBUFREN (1 << 20)
+#define MASK_MCCSRCV (1 << 19)
+#define MASK_MRBSYE (1 << 17)
+#define MASK_MCRSPE (1 << 16)
+#define MASK_MCMDVIO (1 << 15)
+#define MASK_MBUFVIO (1 << 14)
+#define MASK_MWDATERR (1 << 11)
+#define MASK_MRDATERR (1 << 10)
+#define MASK_MRIDXERR (1 << 9)
+#define MASK_MRSPERR (1 << 8)
+#define MASK_MCCSTO (1 << 5)
+#define MASK_MCRCSTO (1 << 4)
+#define MASK_MWDATTO (1 << 3)
+#define MASK_MRDATTO (1 << 2)
+#define MASK_MRBSYTO (1 << 1)
+#define MASK_MRSPTO (1 << 0)
+
+/* CE_HOST_STS1 */
+#define STS1_CMDSEQ (1 << 31)
+
+/* CE_HOST_STS2 */
+#define STS2_CRCSTE (1 << 31)
+#define STS2_CRC16E (1 << 30)
+#define STS2_AC12CRCE (1 << 29)
+#define STS2_RSPCRC7E (1 << 28)
+#define STS2_CRCSTEBE (1 << 27)
+#define STS2_RDATEBE (1 << 26)
+#define STS2_AC12REBE (1 << 25)
+#define STS2_RSPEBE (1 << 24)
+#define STS2_AC12IDXE (1 << 23)
+#define STS2_RSPIDXE (1 << 22)
+#define STS2_CCSTO (1 << 15)
+#define STS2_RDATTO (1 << 14)
+#define STS2_DATBSYTO (1 << 13)
+#define STS2_CRCSTTO (1 << 12)
+#define STS2_AC12BSYTO (1 << 11)
+#define STS2_RSPBSYTO (1 << 10)
+#define STS2_AC12RSPTO (1 << 9)
+#define STS2_RSPTO (1 << 8)
+#define STS2_CRC_ERR (STS2_CRCSTE | STS2_CRC16E | \
+ STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE)
+#define STS2_TIMEOUT_ERR (STS2_CCSTO | STS2_RDATTO | \
+ STS2_DATBSYTO | STS2_CRCSTTO | \
+ STS2_AC12BSYTO | STS2_RSPBSYTO | \
+ STS2_AC12RSPTO | STS2_RSPTO)
+
+/* CE_VERSION */
+#define SOFT_RST_ON (1 << 31)
+#define SOFT_RST_OFF (0 << 31)
+
+#define CLKDEV_EMMC_DATA 52000000 /* 52MHz */
+#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
+#define CLKDEV_INIT 400000 /* 400 KHz */
+
+struct sh_mmcif_host {
+ struct mmc_host *mmc;
+ struct mmc_data *data;
+ struct mmc_command *cmd;
+ struct platform_device *pd;
+ struct clk *hclk;
+ unsigned int clk;
+ int bus_width;
+ u16 wait_int;
+ u16 sd_error;
+ long timeout;
+ void __iomem *addr;
+ wait_queue_head_t intr_wait;
+};
+
+static inline u32 sh_mmcif_readl(struct sh_mmcif_host *host, unsigned int reg)
+{
+ return readl(host->addr + reg);
+}
+
+static inline void sh_mmcif_writel(struct sh_mmcif_host *host,
+ unsigned int reg, u32 val)
+{
+ writel(val, host->addr + reg);
+}
+
+static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
+ unsigned int reg, u32 val)
+{
+ writel(val | sh_mmcif_readl(host, reg), host->addr + reg);
+}
+
+static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
+ unsigned int reg, u32 val)
+{
+ writel(~val & sh_mmcif_readl(host, reg), host->addr + reg);
+}
+
+
+static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
+{
+ struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+
+ sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
+ sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
+
+ if (!clk)
+ return;
+ if (p->sup_pclk && clk == host->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));
+
+ sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
+}
+
+static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
+{
+ u32 tmp;
+
+ tmp = 0x010f0000 & sh_mmcif_readl(host, MMCIF_CE_CLK_CTRL);
+
+ sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_ON);
+ sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_OFF);
+ sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp |
+ SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29);
+ /* byte swap on */
+ sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_ATYP);
+}
+
+static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
+{
+ u32 state1, state2;
+ int ret, timeout = 10000000;
+
+ host->sd_error = 0;
+ host->wait_int = 0;
+
+ state1 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS1);
+ state2 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS2);
+ pr_debug("%s: ERR HOST_STS1 = %08x\n", \
+ DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS1));
+ pr_debug("%s: ERR HOST_STS2 = %08x\n", \
+ DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS2));
+
+ 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) {
+ pr_err(DRIVER_NAME": Forceed end of " \
+ "command sequence timeout err\n");
+ return -EIO;
+ }
+ if (!(sh_mmcif_readl(host, MMCIF_CE_HOST_STS1)
+ & STS1_CMDSEQ))
+ break;
+ mdelay(1);
+ }
+ sh_mmcif_sync_reset(host);
+ pr_debug(DRIVER_NAME": Forced end of command sequence\n");
+ return -EIO;
+ }
+
+ if (state2 & STS2_CRC_ERR) {
+ pr_debug(DRIVER_NAME": Happened CRC error\n");
+ ret = -EIO;
+ } else if (state2 & STS2_TIMEOUT_ERR) {
+ pr_debug(DRIVER_NAME": Happened Timeout error\n");
+ ret = -ETIMEDOUT;
+ } else {
+ pr_debug(DRIVER_NAME": Happened End/Index error\n");
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static int sh_mmcif_single_read(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->wait_int = 0;
+
+ /* buf read enable */
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = (BLOCK_SIZE_MASK &
+ sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3;
+ for (i = 0; i < blocksize / 4; i++)
+ *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA);
+
+ /* buffer read end */
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ return 0;
+}
+
+static int 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, MMCIF_CE_BLOCK_SET);
+ for (j = 0; j < data->sg_len; j++) {
+ p = sg_virt(data->sg);
+ host->wait_int = 0;
+ for (sec = 0; sec < data->sg->length / blocksize; sec++) {
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
+ /* buf read enable */
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+
+ if (host->wait_int != 1 &&
+ (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ for (i = 0; i < blocksize / 4; i++)
+ *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA);
+ }
+ if (j < data->sg_len - 1)
+ data->sg++;
+ }
+ return 0;
+}
+
+static int 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->wait_int = 0;
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+
+ /* buf write enable */
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = (BLOCK_SIZE_MASK &
+ sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3;
+ for (i = 0; i < blocksize / 4; i++)
+ sh_mmcif_writel(host, MMCIF_CE_DATA, *p++);
+
+ /* buffer write end */
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
+
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ return 0;
+}
+
+static int 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, MMCIF_CE_BLOCK_SET);
+
+ for (j = 0; j < data->sg_len; j++) {
+ p = sg_virt(data->sg);
+ host->wait_int = 0;
+ for (sec = 0; sec < data->sg->length / blocksize; sec++) {
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+ /* buf write enable*/
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+
+ if (host->wait_int != 1 &&
+ (time == 0 || host->sd_error != 0))
+ return sh_mmcif_error_manage(host);
+
+ host->wait_int = 0;
+ for (i = 0; i < blocksize / 4; i++)
+ sh_mmcif_writel(host, MMCIF_CE_DATA, *p++);
+ }
+ if (j < data->sg_len - 1)
+ data->sg++;
+ }
+ return 0;
+}
+
+static void sh_mmcif_get_response(struct sh_mmcif_host *host,
+ struct mmc_command *cmd)
+{
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP3);
+ cmd->resp[1] = sh_mmcif_readl(host, MMCIF_CE_RESP2);
+ cmd->resp[2] = sh_mmcif_readl(host, MMCIF_CE_RESP1);
+ cmd->resp[3] = sh_mmcif_readl(host, MMCIF_CE_RESP0);
+ } else
+ cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP0);
+}
+
+static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
+ struct mmc_command *cmd)
+{
+ cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP_CMD12);
+}
+
+static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd, u32 opc)
+{
+ u32 tmp = 0;
+
+ /* Response Type check */
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ tmp |= CMD_SET_RTYP_NO;
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ case MMC_RSP_R3:
+ tmp |= CMD_SET_RTYP_6B;
+ break;
+ case MMC_RSP_R2:
+ tmp |= CMD_SET_RTYP_17B;
+ break;
+ default:
+ pr_err(DRIVER_NAME": Not support type response.\n");
+ break;
+ }
+ switch (opc) {
+ /* RBSY */
+ case MMC_SWITCH:
+ case MMC_STOP_TRANSMISSION:
+ case MMC_SET_WRITE_PROT:
+ case MMC_CLR_WRITE_PROT:
+ case MMC_ERASE:
+ case MMC_GEN_CMD:
+ tmp |= CMD_SET_RBSY;
+ break;
+ }
+ /* WDAT / DATW */
+ if (host->data) {
+ tmp |= CMD_SET_WDAT;
+ switch (host->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ tmp |= CMD_SET_DATW_1;
+ break;
+ case MMC_BUS_WIDTH_4:
+ tmp |= CMD_SET_DATW_4;
+ break;
+ case MMC_BUS_WIDTH_8:
+ tmp |= CMD_SET_DATW_8;
+ break;
+ default:
+ pr_err(DRIVER_NAME": Not support bus width.\n");
+ break;
+ }
+ }
+ /* DWEN */
+ if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK)
+ tmp |= CMD_SET_DWEN;
+ /* CMLTE/CMD12EN */
+ 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);
+ }
+ /* RIDXC[1:0] check bits */
+ if (opc == MMC_SEND_OP_COND || opc == MMC_ALL_SEND_CID ||
+ opc == MMC_SEND_CSD || opc == MMC_SEND_CID)
+ tmp |= CMD_SET_RIDXC_BITS;
+ /* RCRC7C[1:0] check bits */
+ if (opc == MMC_SEND_OP_COND)
+ tmp |= CMD_SET_CRC7C_BITS;
+ /* RCRC7C[1:0] internal CRC7 */
+ if (opc == MMC_ALL_SEND_CID ||
+ opc == MMC_SEND_CSD || opc == MMC_SEND_CID)
+ tmp |= CMD_SET_CRC7C_INTERNAL;
+
+ return opc = ((opc << 24) | tmp);
+}
+
+static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host,
+ struct mmc_request *mrq, u32 opc)
+{
+ u32 ret;
+
+ switch (opc) {
+ case MMC_READ_MULTIPLE_BLOCK:
+ ret = sh_mmcif_multi_read(host, mrq);
+ break;
+ case MMC_WRITE_MULTIPLE_BLOCK:
+ ret = sh_mmcif_multi_write(host, mrq);
+ break;
+ case MMC_WRITE_BLOCK:
+ ret = sh_mmcif_single_write(host, mrq);
+ break;
+ case MMC_READ_SINGLE_BLOCK:
+ case MMC_SEND_EXT_CSD:
+ ret = sh_mmcif_single_read(host, mrq);
+ break;
+ default:
+ pr_err(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ long time;
+ int ret = 0, mask = 0;
+ u32 opc = cmd->opcode;
+
+ host->cmd = cmd;
+
+ switch (opc) {
+ /* respons 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;
+ break;
+ default:
+ mask = 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) {
+ sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, 0);
+ sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, mrq->data->blksz);
+ }
+ opc = sh_mmcif_set_cmd(host, mrq, cmd, opc);
+
+ sh_mmcif_writel(host, MMCIF_CE_INT, 0xD80430C0);
+ sh_mmcif_writel(host, MMCIF_CE_INT_MASK, mask);
+ /* set arg */
+ sh_mmcif_writel(host, MMCIF_CE_ARG, cmd->arg);
+ host->wait_int = 0;
+ /* set cmd */
+ sh_mmcif_writel(host, MMCIF_CE_CMD_SET, opc);
+
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 || host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && 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:
+ pr_debug("%s: Cmd(d'%d) err\n",
+ DRIVER_NAME, cmd->opcode);
+ cmd->error = sh_mmcif_error_manage(host);
+ break;
+ }
+ host->sd_error = 0;
+ host->wait_int = 0;
+ return;
+ }
+ if (!(cmd->flags & MMC_RSP_PRESENT)) {
+ cmd->error = ret;
+ host->wait_int = 0;
+ return;
+ }
+ if (host->wait_int == 1) {
+ sh_mmcif_get_response(host, cmd);
+ host->wait_int = 0;
+ }
+ if (host->data) {
+ ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
+ if (ret < 0)
+ mrq->data->bytes_xfered = 0;
+ else
+ mrq->data->bytes_xfered =
+ mrq->data->blocks * mrq->data->blksz;
+ }
+ cmd->error = ret;
+}
+
+static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
+ struct mmc_request *mrq, struct mmc_command *cmd)
+{
+ long time;
+
+ if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK)
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
+ else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)
+ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
+ else {
+ pr_err(DRIVER_NAME": not support stop cmd\n");
+ cmd->error = sh_mmcif_error_manage(host);
+ return;
+ }
+
+ time = wait_event_interruptible_timeout(host->intr_wait,
+ host->wait_int == 1 ||
+ host->sd_error == 1, host->timeout);
+ if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) {
+ cmd->error = sh_mmcif_error_manage(host);
+ return;
+ }
+ sh_mmcif_get_cmd12response(host, cmd);
+ host->wait_int = 0;
+ cmd->error = 0;
+}
+
+static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sh_mmcif_host *host = mmc_priv(mmc);
+
+ switch (mrq->cmd->opcode) {
+ /* MMCIF does not support SD/SDIO command */
+ case SD_IO_SEND_OP_COND:
+ case MMC_APP_CMD:
+ mrq->cmd->error = -ETIMEDOUT;
+ mmc_request_done(mmc, mrq);
+ return;
+ case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
+ if (!mrq->data) {
+ /* send_if_cond cmd (not support) */
+ mrq->cmd->error = -ETIMEDOUT;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ host->data = mrq->data;
+ sh_mmcif_start_cmd(host, mrq, mrq->cmd);
+ host->data = NULL;
+
+ if (mrq->cmd->error != 0) {
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+ if (mrq->stop)
+ sh_mmcif_stop_cmd(host, mrq, mrq->stop);
+ mmc_request_done(mmc, mrq);
+}
+
+static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sh_mmcif_host *host = mmc_priv(mmc);
+ struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+
+ if (ios->power_mode == MMC_POWER_OFF) {
+ /* clock stop */
+ sh_mmcif_clock_control(host, 0);
+ if (p->down_pwr)
+ p->down_pwr(host->pd);
+ return;
+ } else if (ios->power_mode == MMC_POWER_UP) {
+ if (p->set_pwr)
+ p->set_pwr(host->pd, ios->power_mode);
+ }
+
+ if (ios->clock)
+ sh_mmcif_clock_control(host, ios->clock);
+
+ host->bus_width = ios->bus_width;
+}
+
+static struct mmc_host_ops sh_mmcif_ops = {
+ .request = sh_mmcif_request,
+ .set_ios = sh_mmcif_set_ios,
+};
+
+static void sh_mmcif_detect(struct mmc_host *mmc)
+{
+ mmc_detect_change(mmc, 0);
+}
+
+static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
+{
+ struct sh_mmcif_host *host = dev_id;
+ u32 state = 0;
+ int err = 0;
+
+ state = sh_mmcif_readl(host, MMCIF_CE_INT);
+
+ if (state & INT_RBSYE) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE));
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE);
+ } else if (state & INT_CRSPE) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_CRSPE);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE);
+ } else if (state & INT_BUFREN) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFREN);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
+ } else if (state & INT_BUFWEN) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFWEN);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
+ } else if (state & INT_CMD12DRE) {
+ sh_mmcif_writel(host, MMCIF_CE_INT,
+ ~(INT_CMD12DRE | INT_CMD12RBE |
+ INT_CMD12CRE | INT_BUFRE));
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
+ } else if (state & INT_BUFRE) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFRE);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
+ } else if (state & INT_DTRANE) {
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_DTRANE);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
+ } else if (state & INT_CMD12RBE) {
+ sh_mmcif_writel(host, 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, MMCIF_CE_INT, ~state);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
+ err = 1;
+ } else {
+ pr_debug("%s: Not support int\n", DRIVER_NAME);
+ sh_mmcif_writel(host, MMCIF_CE_INT, ~state);
+ sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
+ err = 1;
+ }
+ if (err) {
+ host->sd_error = 1;
+ pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state);
+ }
+ host->wait_int = 1;
+ wake_up(&host->intr_wait);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit sh_mmcif_probe(struct platform_device *pdev)
+{
+ int ret = 0, irq[2];
+ struct mmc_host *mmc;
+ struct sh_mmcif_host *host = NULL;
+ struct sh_mmcif_plat_data *pd = NULL;
+ struct resource *res;
+ void __iomem *reg;
+ char clk_name[8];
+
+ irq[0] = platform_get_irq(pdev, 0);
+ irq[1] = platform_get_irq(pdev, 1);
+ if (irq[0] < 0 || irq[1] < 0) {
+ pr_err(DRIVER_NAME": Get irq error\n");
+ return -ENXIO;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "platform_get_resource error.\n");
+ return -ENXIO;
+ }
+ reg = ioremap(res->start, resource_size(res));
+ if (!reg) {
+ dev_err(&pdev->dev, "ioremap error.\n");
+ return -ENOMEM;
+ }
+ pd = (struct sh_mmcif_plat_data *)(pdev->dev.platform_data);
+ if (!pd) {
+ dev_err(&pdev->dev, "sh_mmcif plat data error.\n");
+ ret = -ENXIO;
+ goto clean_up;
+ }
+ mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto clean_up;
+ }
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->addr = reg;
+ host->timeout = 1000;
+
+ snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id);
+ host->hclk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(host->hclk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(host->hclk);
+ goto clean_up1;
+ }
+ clk_enable(host->hclk);
+ host->clk = clk_get_rate(host->hclk);
+ host->pd = pdev;
+
+ init_waitqueue_head(&host->intr_wait);
+
+ mmc->ops = &sh_mmcif_ops;
+ mmc->f_max = host->clk;
+ /* close to 400KHz */
+ if (mmc->f_max < 51200000)
+ mmc->f_min = mmc->f_max / 128;
+ else if (mmc->f_max < 102400000)
+ mmc->f_min = mmc->f_max / 256;
+ else
+ mmc->f_min = mmc->f_max / 512;
+ if (pd->ocr)
+ mmc->ocr_avail = pd->ocr;
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED;
+ if (pd->caps)
+ mmc->caps |= pd->caps;
+ mmc->max_phys_segs = 128;
+ mmc->max_hw_segs = 128;
+ mmc->max_blk_size = 512;
+ mmc->max_blk_count = 65535;
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ sh_mmcif_sync_reset(host);
+ platform_set_drvdata(pdev, host);
+ mmc_add_host(mmc);
+
+ ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
+ if (ret) {
+ pr_err(DRIVER_NAME": request_irq error (sh_mmc:error)\n");
+ goto clean_up2;
+ }
+ ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host);
+ if (ret) {
+ free_irq(irq[0], host);
+ pr_err(DRIVER_NAME": request_irq error (sh_mmc:int)\n");
+ goto clean_up2;
+ }
+
+ sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL);
+ sh_mmcif_detect(host->mmc);
+
+ pr_info("%s: driver version %s\n", DRIVER_NAME, DRIVER_VERSION);
+ pr_debug("%s: chip ver H'%04x\n", DRIVER_NAME,
+ sh_mmcif_readl(host, MMCIF_CE_VERSION) & 0x0000ffff);
+ return ret;
+
+clean_up2:
+ clk_disable(host->hclk);
+clean_up1:
+ mmc_free_host(mmc);
+clean_up:
+ if (reg)
+ iounmap(reg);
+ return ret;
+}
+
+static int __devexit sh_mmcif_remove(struct platform_device *pdev)
+{
+ struct sh_mmcif_host *host = platform_get_drvdata(pdev);
+ int irq[2];
+
+ sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL);
+
+ irq[0] = platform_get_irq(pdev, 0);
+ irq[1] = platform_get_irq(pdev, 1);
+
+ if (host->addr)
+ iounmap(host->addr);
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_remove_host(host->mmc);
+
+ free_irq(irq[0], host);
+ free_irq(irq[1], host);
+
+ clk_disable(host->hclk);
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+static struct platform_driver sh_mmcif_driver = {
+ .probe = sh_mmcif_probe,
+ .remove = sh_mmcif_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+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_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_AUTHOR("Yusuke Goda <yusuke.goda.sx@renesas.com>");
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index 82554dd..cec9995 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -1032,7 +1032,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
{
- return mmc_suspend_host(tifm_get_drvdata(sock), state);
+ return mmc_suspend_host(tifm_get_drvdata(sock));
}
static int tifm_sd_resume(struct tifm_dev *sock)
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 883fcac..ee7d0a5 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -768,7 +768,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret;
- ret = mmc_suspend_host(mmc, state);
+ ret = mmc_suspend_host(mmc);
/* Tell MFD core it can disable us now.*/
if (!ret && cell->disable)
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 632858a..19f2d72 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1280,7 +1280,7 @@ static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state)
via_save_pcictrlreg(host);
via_save_sdcreg(host);
- ret = mmc_suspend_host(host->mmc, state);
+ ret = mmc_suspend_host(host->mmc);
pci_save_state(pcidev);
pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 69efe01..0012f5d 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1819,7 +1819,7 @@ static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
{
BUG_ON(host == NULL);
- return mmc_suspend_host(host->mmc, state);
+ return mmc_suspend_host(host->mmc);
}
static int wbsd_resume(struct wbsd_host *host)
OpenPOWER on IntegriCloud