LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RESEND PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
@ 2010-12-06 15:53 Will Newton
  2010-12-08 11:55 ` Matt Fleming
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-06 15:53 UTC (permalink / raw)
  To: Linux Kernel list, linux-mmc, Chris Ball

[-- Attachment #1: Type: text/plain, Size: 56323 bytes --]

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   15 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1810 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  155 ++++
 include/linux/mmc/dw_mmc.h |  149 ++++
 5 files changed, 2130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

Resending, hopefully this time the patch will make it to the linux-mmc list.

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..c99d2c6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.

+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	depends on MMC_DW
+	bool "Internal DMAC interface"
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..fa90b44
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1810 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&dw_mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+			&dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc,
+				  struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		/* expect the respond, need to set this bit */
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	/* write to CMDARG register */
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	/* write to CMD register */
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+		     ((data->flags & MMC_DATA_WRITE)
+		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host,
+				    struct mmc_data *data, unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+			SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+				   direction);
+
+	dev_vdbg(&host->pdev->dev,
+		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* disable irq of RX & TX, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the dvide
+			 * to prevent over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			" div = %d)\n",
+			slot->id, host->bus_hz, slot->clock,
+			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	/* no select the proper slot */
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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, lets send the initialization clock */
+	if (unlikely(
+		test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
+		cmdflags |= SDMMC_CMD_INIT;
+	}
+
+	/* we may need to move this code to mci_start_command */
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_ro != NULL) {
+		read_only = brd->get_ro(slot->id);
+	} else {
+		/* Try on board write protect */
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+	}
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_cd != NULL)
+		present = !brd->get_cd(slot->id);
+	else	/* try onboard card detect */
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+				== 0 ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host,
+				    struct mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pData++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pData++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pData++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (likely(offset + len <= sg->length)) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
+						lets read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (likely(offset + len <= sg->length)) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status,  pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error, lets report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+					present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+						mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01;	/* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled,
+	 * will be enabled when card is detected
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/*useful defaults*/
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	/* Assume card is present initially */
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
+				unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+			&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+				__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialise "
+					"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
+			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET |
+			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (((pdata->num_slots > 1) && !(pdata->select_slot))
+			|| !(pdata->init)) {
+		dev_err(&pdev->dev, "Platform data wrong\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Bus speed undefined in platform data!\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+			"HCON reports a reserved host data width!\n"
+			"Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	 /* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark = fifo_size/2-1,
+	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed ####pd####*/
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * enable interrupt for command done, data over, data empty,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+			"%d bit host data width\n",
+			irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..65bcff7
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,155 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define MCI_SLOT		0
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..d2f92bd
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,149 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned long bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2

[-- Attachment #2: 0001-dw_mmc-Add-Synopsys-DesignWare-mmc-host-driver.patch --]
[-- Type: text/x-patch, Size: 56466 bytes --]

From 199b9628e65de3d796062c9b722865cbf5e9b0b5 Mon Sep 17 00:00:00 2001
From: Will Newton <will.newton@imgtec.com>
Date: Wed, 24 Nov 2010 17:15:04 +0000
Subject: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   15 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1810 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  155 ++++
 include/linux/mmc/dw_mmc.h |  149 ++++
 5 files changed, 2130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..c99d2c6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.
 
+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	depends on MMC_DW
+	bool "Internal DMAC interface"
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..fa90b44
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1810 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&dw_mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+			&dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc,
+				  struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		/* expect the respond, need to set this bit */
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	/* write to CMDARG register */
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	/* write to CMD register */
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+		     ((data->flags & MMC_DATA_WRITE)
+		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host,
+				    struct mmc_data *data, unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+			SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+				   direction);
+
+	dev_vdbg(&host->pdev->dev,
+		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* disable irq of RX & TX, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the dvide
+			 * to prevent over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			" div = %d)\n",
+			slot->id, host->bus_hz, slot->clock,
+			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	/* no select the proper slot */
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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, lets send the initialization clock */
+	if (unlikely(
+		test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
+		cmdflags |= SDMMC_CMD_INIT;
+	}
+
+	/* we may need to move this code to mci_start_command */
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_ro != NULL) {
+		read_only = brd->get_ro(slot->id);
+	} else {
+		/* Try on board write protect */
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+	}
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_cd != NULL)
+		present = !brd->get_cd(slot->id);
+	else	/* try onboard card detect */
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+				== 0 ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host,
+				    struct mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pData++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pData++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pData++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (likely(offset + len <= sg->length)) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
+						lets read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (likely(offset + len <= sg->length)) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status,  pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error, lets report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+					present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+						mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01;	/* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled,
+	 * will be enabled when card is detected
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/*useful defaults*/
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	/* Assume card is present initially */
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
+				unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+			&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+				__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialise "
+					"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
+			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET |
+			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (((pdata->num_slots > 1) && !(pdata->select_slot))
+			|| !(pdata->init)) {
+		dev_err(&pdev->dev, "Platform data wrong\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Bus speed undefined in platform data!\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+			"HCON reports a reserved host data width!\n"
+			"Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	 /* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark = fifo_size/2-1,
+	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed ####pd####*/
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * enable interrupt for command done, data over, data empty,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+			"%d bit host data width\n",
+			irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..65bcff7
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,155 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define MCI_SLOT		0
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..d2f92bd
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,149 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned long bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RESEND PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-06 15:53 [RESEND PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver Will Newton
@ 2010-12-08 11:55 ` Matt Fleming
  2010-12-08 13:14   ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Matt Fleming @ 2010-12-08 11:55 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Chris Ball

On Mon, Dec 06, 2010 at 03:53:10PM +0000, Will Newton wrote:
> This adds the mmc host driver for the Synopsys DesignWare mmc
> host controller, found in a number of embedded SoC designs.
> 
> Signed-off-by: Will Newton <will.newton@imgtec.com>

Comments below..

> ---
>  drivers/mmc/host/Kconfig   |   15 +
>  drivers/mmc/host/Makefile  |    1 +
>  drivers/mmc/host/dw_mmc.c  | 1810 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/dw_mmc.h  |  155 ++++
>  include/linux/mmc/dw_mmc.h |  149 ++++
>  5 files changed, 2130 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/mmc/host/dw_mmc.c
>  create mode 100644 drivers/mmc/host/dw_mmc.h
>  create mode 100644 include/linux/mmc/dw_mmc.h
> 
> Resending, hopefully this time the patch will make it to the linux-mmc list.
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index f8fa9ef..c99d2c6 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
>  	help
>  	  If you say yes here SD-Cards may work on the EZkit.
> 
> +config MMC_DW
> +	tristate "Synopsys DesignWare Memory Card Interface"
> +	help
> +	  This selects support for the Synopsys DesignWare Mobile Storage IP
> +	  block, this provides host support for SD and MMC interfaces, in both
> +	  PIO and external DMA modes.
> +
> +config MMC_DW_IDMAC
> +	depends on MMC_DW
> +	bool "Internal DMAC interface"
> +	help
> +	  This selects support for the internal DMAC block within the Synopsys
> +	  Designware Mobile Storage IP block. This disables the external DMA
> +	  interface.
> +
>  config MMC_SH_MMCIF
>  	tristate "SuperH Internal MMCIF support"
>  	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index d91364d..a5d1cb2 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -31,6 +31,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_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
>  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
>  obj-$(CONFIG_MMC_USHC)		+= ushc.o
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> new file mode 100644
> index 0000000..fa90b44
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -0,0 +1,1810 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *
> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
> + *
> + * 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, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/blkdev.h>
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/scatterlist.h>
> +#include <linux/seq_file.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/dw_mmc.h>
> +#include <linux/bitops.h>
> +
> +#include "dw_mmc.h"
> +
> +/* Common flag combinations */
> +#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
> +				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
> +				 SDMMC_INT_EBE)
> +#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
> +				 SDMMC_INT_RESP_ERR)
> +#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
> +				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
> +#define DW_MCI_SEND_STATUS	1
> +#define DW_MCI_RECV_STATUS	2
> +#define DW_MCI_DMA_THRESHOLD	16
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +struct idmac_desc {
> +	u32		des0;	/* Control Descriptor */
> +#define IDMAC_DES0_DIC	BIT(1)
> +#define IDMAC_DES0_LD	BIT(2)
> +#define IDMAC_DES0_FD	BIT(3)
> +#define IDMAC_DES0_CH	BIT(4)
> +#define IDMAC_DES0_ER	BIT(5)
> +#define IDMAC_DES0_CES	BIT(30)
> +#define IDMAC_DES0_OWN	BIT(31)
> +
> +	u32		des1;	/* Buffer sizes */
> +#define IDMAC_SET_BUFFER1_SIZE(d, s) \
> +	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
> +
> +	u32		des2;	/* buffer 1 physical address */
> +
> +	u32		des3;	/* buffer 2 physical address */
> +};
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +struct dw_mci_slot {
> +	struct mmc_host		*mmc;
> +	struct dw_mci		*host;
> +
> +	u32			ctype;
> +
> +	struct mmc_request	*mrq;
> +	struct list_head	queue_node;
> +
> +	unsigned int		clock;
> +	unsigned long		flags;
> +#define DW_MMC_CARD_PRESENT	0
> +#define DW_MMC_CARD_NEED_INIT	1
> +	int			id;
> +	int			last_detect_state;
> +};
> +
> +#if defined(CONFIG_DEBUG_FS)
> +/*
> + * The debugfs stuff below is mostly optimized away when
> + * CONFIG_DEBUG_FS is not set.
> + */
> +static int dw_mci_req_show(struct seq_file *s, void *v)
> +{
> +	struct dw_mci_slot *slot = s->private;
> +	struct mmc_request *mrq;
> +	struct mmc_command *cmd;
> +	struct mmc_command *stop;
> +	struct mmc_data	*data;
> +
> +	/* Make sure we get a consistent snapshot */
> +	spin_lock_bh(&slot->host->lock);
> +	mrq = slot->mrq;
> +
> +	if (mrq) {
> +		cmd = mrq->cmd;
> +		data = mrq->data;
> +		stop = mrq->stop;
> +
> +		if (cmd)
> +			seq_printf(s,
> +				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				cmd->opcode, cmd->arg, cmd->flags,
> +				cmd->resp[0], cmd->resp[1], cmd->resp[2],
> +				cmd->resp[2], cmd->error);
> +		if (data)
> +			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
> +				data->bytes_xfered, data->blocks,
> +				data->blksz, data->flags, data->error);
> +		if (stop)
> +			seq_printf(s,
> +				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				stop->opcode, stop->arg, stop->flags,
> +				stop->resp[0], stop->resp[1], stop->resp[2],
> +				stop->resp[2], stop->error);
> +	}
> +
> +	spin_unlock_bh(&slot->host->lock);
> +
> +	return 0;
> +}
> +
> +static int dw_mci_req_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, dw_mci_req_show, inode->i_private);
> +}
> +
> +static const struct file_operations dw_mci_req_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= dw_mci_req_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static int dw_mci_regs_show(struct seq_file *s, void *v)
> +{
> +	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
> +	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
> +	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
> +	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
> +	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
> +	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
> +
> +	return 0;
> +}
> +
> +static int dw_mci_regs_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, dw_mci_regs_show, inode->i_private);
> +}
> +
> +static const struct file_operations dw_mci_regs_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= dw_mci_regs_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
> +{
> +	struct mmc_host	*mmc = slot->mmc;
> +	struct dw_mci *host = slot->host;
> +	struct dentry *root;
> +	struct dentry *node;
> +
> +	root = mmc->debugfs_root;
> +	if (!root)
> +		return;
> +
> +	node = debugfs_create_file("regs", S_IRUSR, root, host,
> +			&dw_mci_regs_fops);
> +	if (IS_ERR(node))
> +		return;

Should this be a goto err instead of return? I'm guessing this is
meant to catch -ENODEV being returned when debugfs is not enabled in
the kernel, but if we've compiled this code with CONFIG_DEBUG_FS then
we probably want to know when this call fails.

> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_file("req", S_IRUSR, root, slot,
> +			&dw_mci_req_fops);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_x32("pending_events", S_IRUSR, root,
> +				     (u32 *)&host->pending_events);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_x32("completed_events", S_IRUSR, root,
> +				     (u32 *)&host->completed_events);
> +	if (!node)
> +		goto err;
> +
> +	return;
> +
> +err:
> +	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
> +}
> +#endif /* defined(CONFIG_DEBUG_FS) */
> +
> +static void dw_mci_set_timeout(struct dw_mci *host)
> +{
> +	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
> +}
> +

Could do with an explicit 'inline'? Especially as it only has one
callsite.

> +static u32 dw_mci_prepare_command(struct mmc_host *mmc,
> +				  struct mmc_command *cmd)
> +{
> +	struct mmc_data	*data;
> +	u32 cmdr;
> +	cmd->error = -EINPROGRESS;
> +
> +	cmdr = cmd->opcode;
> +
> +	if (cmdr == MMC_STOP_TRANSMISSION)
> +		cmdr |= SDMMC_CMD_STOP;
> +	else
> +		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		cmdr |= SDMMC_CMD_RESP_EXP;
> +		/* expect the respond, need to set this bit */
> +		if (cmd->flags & MMC_RSP_136)
> +			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
> +	}
> +
> +	if (cmd->flags & MMC_RSP_CRC)
> +		cmdr |= SDMMC_CMD_RESP_CRC;
> +
> +	data = cmd->data;
> +	if (data) {
> +		cmdr |= SDMMC_CMD_DAT_EXP;
> +		if (data->flags & MMC_DATA_STREAM)
> +			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
> +		if (data->flags & MMC_DATA_WRITE)
> +			cmdr |= SDMMC_CMD_DAT_WR;
> +	}
> +
> +	return cmdr;
> +}
> +
> +static void dw_mci_start_command(struct dw_mci *host,
> +				 struct mmc_command *cmd, u32 cmd_flags)
> +{
> +	host->cmd = cmd;
> +	dev_vdbg(&host->pdev->dev,
> +			"start command: ARGR=0x%08x CMDR=0x%08x\n",
> +			cmd->arg, cmd_flags);
> +
> +	/* write to CMDARG register */
> +	mci_writel(host, CMDARG, cmd->arg);
> +	wmb();
> +
> +	/* write to CMD register */
> +	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
> +}
> +
> +static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
> +{
> +	dw_mci_start_command(host, data->stop, host->stop_cmdr);
> +}

Explicit 'inline'?

> +
> +/* DMA interface functions */
> +static void dw_mci_stop_dma(struct dw_mci *host)
> +{
> +	if (host->use_dma) {
> +		host->dma_ops->stop(host);
> +		host->dma_ops->cleanup(host);
> +
> +	} else {
> +		/* Data transfer was stopped by the interrupt handler */
> +		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +	}
> +}
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +static void dw_mci_dma_cleanup(struct dw_mci *host)
> +{
> +	struct mmc_data *data = host->data;
> +
> +	if (data)
> +		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
> +		     ((data->flags & MMC_DATA_WRITE)
> +		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
> +}
> +
> +static void dw_mci_idmac_stop_dma(struct dw_mci *host)
> +{
> +	u32 temp;
> +
> +	/* Disable and reset the IDMAC interface */
> +	temp = mci_readl(host, CTRL);
> +	temp &= ~SDMMC_CTRL_USE_IDMAC;
> +	temp |= SDMMC_CTRL_DMA_RESET;
> +	mci_writel(host, CTRL, temp);
> +
> +	/* Stop the IDMAC running */
> +	temp = mci_readl(host, BMOD);
> +	temp &= ~SDMMC_IDMAC_ENABLE;
> +	mci_writel(host, BMOD, temp);
> +}
> +
> +static void dw_mci_idmac_complete_dma(struct dw_mci *host)
> +{
> +	struct mmc_data *data = host->data;
> +
> +	dev_vdbg(&host->pdev->dev, "DMA complete\n");
> +
> +	host->dma_ops->cleanup(host);
> +
> +	/*
> +	 * If the card was removed, data will be NULL. No point in trying to
> +	 * send the stop command or waiting for NBUSY in this case.
> +	 */
> +	if (data) {
> +		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +		tasklet_schedule(&host->tasklet);
> +	}
> +}
> +
> +static void dw_mci_translate_sglist(struct dw_mci *host,
> +				    struct mmc_data *data, unsigned int sg_len)
> +{
> +	int i;
> +	struct idmac_desc *desc = host->sg_cpu;
> +
> +	for (i = 0; i < sg_len; i++, desc++) {
> +		unsigned int length = sg_dma_len(&data->sg[i]);
> +		u32 mem_addr = sg_dma_address(&data->sg[i]);
> +
> +		/* Set the OWN bit and disable interrupts for this descriptor */
> +		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
> +
> +		/* Buffer length */
> +		IDMAC_SET_BUFFER1_SIZE(desc, length);
> +
> +		/* Physical address to DMA to/from */
> +		desc->des2 = mem_addr;
> +	}
> +
> +	/* Set first descriptor */
> +	desc = host->sg_cpu;
> +	desc->des0 |= IDMAC_DES0_FD;
> +
> +	/* Set last descriptor */
> +	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
> +	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
> +	desc->des0 |= IDMAC_DES0_LD;
> +
> +	wmb();
> +}
> +
> +static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
> +{
> +	u32 temp;
> +
> +	dw_mci_translate_sglist(host, host->data, sg_len);
> +
> +	/* Select IDMAC interface */
> +	temp = mci_readl(host, CTRL);
> +	temp |= SDMMC_CTRL_USE_IDMAC;
> +	mci_writel(host, CTRL, temp);
> +
> +	wmb();
> +
> +	/* Enable the IDMAC */
> +	temp = mci_readl(host, BMOD);
> +	temp |= SDMMC_IDMAC_ENABLE;
> +	mci_writel(host, BMOD, temp);
> +
> +	/* Start it running */
> +	mci_writel(host, PLDMND, 1);
> +}
> +
> +static int dw_mci_idmac_init(struct dw_mci *host)
> +{
> +	struct idmac_desc *p;
> +	int i;
> +
> +	/* Number of descriptors in the ring buffer */
> +	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
> +
> +	/* Forward link the descriptor list */
> +	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
> +		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
> +
> +	/* Set the last descriptor as the end-of-ring descriptor */
> +	p->des3 = host->sg_dma;
> +	p->des0 = IDMAC_DES0_ER;
> +
> +	/* Mask out interrupts - get Tx & Rx complete only */
> +	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
> +			SDMMC_IDMAC_INT_TI);
> +
> +	/* Set the descriptor base address */
> +	mci_writel(host, DBADDR, host->sg_dma);
> +	return 0;
> +}
> +
> +static struct dw_mci_dma_ops dw_mci_idmac_ops = {
> +	.init = dw_mci_idmac_init,
> +	.start = dw_mci_idmac_start_dma,
> +	.stop = dw_mci_idmac_stop_dma,
> +	.complete = dw_mci_idmac_complete_dma,
> +	.cleanup = dw_mci_dma_cleanup,
> +};
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
> +{
> +	struct scatterlist *sg;
> +	unsigned int i, direction, sg_len;
> +	u32 temp;
> +
> +	/* If we don't have a channel, we can't do DMA */
> +	if (!host->use_dma)
> +		return -ENODEV;
> +
> +	/*
> +	 * We don't do DMA on "complex" transfers, i.e. with
> +	 * non-word-aligned buffers or lengths. Also, we don't bother
> +	 * with all the DMA setup overhead for short transfers.
> +	 */
> +	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
> +		return -EINVAL;
> +	if (data->blksz & 3)
> +		return -EINVAL;
> +
> +	for_each_sg(data->sg, sg, data->sg_len, i) {
> +		if (sg->offset & 3 || sg->length & 3)
> +			return -EINVAL;
> +	}
> +
> +	if (data->flags & MMC_DATA_READ)
> +		direction = DMA_FROM_DEVICE;
> +	else
> +		direction = DMA_TO_DEVICE;
> +
> +	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
> +				   direction);
> +
> +	dev_vdbg(&host->pdev->dev,
> +		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
> +		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
> +		 sg_len);
> +
> +	/* Enable the DMA interface */
> +	temp = mci_readl(host, CTRL);
> +	temp |= SDMMC_CTRL_DMA_ENABLE;
> +	mci_writel(host, CTRL, temp);
> +
> +	/* disable irq of RX & TX, let DMA handle it */
> +	temp = mci_readl(host, INTMASK);
> +	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
> +	mci_writel(host, INTMASK, temp);
> +
> +	host->dma_ops->start(host, sg_len);
> +
> +	return 0;
> +}
> +
> +static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
> +{
> +	u32 temp;
> +
> +	data->error = -EINPROGRESS;
> +
> +	WARN_ON(host->data);
> +	host->sg = NULL;
> +	host->data = data;
> +
> +	if (dw_mci_submit_data_dma(host, data)) {
> +		host->sg = data->sg;
> +		host->pio_offset = 0;
> +		if (data->flags & MMC_DATA_READ)
> +			host->dir_status = DW_MCI_RECV_STATUS;
> +		else
> +			host->dir_status = DW_MCI_SEND_STATUS;
> +
> +		temp = mci_readl(host, INTMASK);
> +		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
> +		mci_writel(host, INTMASK, temp);
> +
> +		temp = mci_readl(host, CTRL);
> +		temp &= ~SDMMC_CTRL_DMA_ENABLE;
> +		mci_writel(host, CTRL, temp);
> +	}
> +}
> +
> +static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
> +{
> +	struct dw_mci *host = slot->host;
> +	unsigned long timeout = jiffies + msecs_to_jiffies(500);
> +	unsigned int cmd_status = 0;
> +
> +	mci_writel(host, CMDARG, arg);
> +	wmb();
> +	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
> +
> +	while (time_before(jiffies, timeout)) {
> +		cmd_status = mci_readl(host, CMD);
> +		if (!(cmd_status & SDMMC_CMD_START))
> +			return;
> +	}
> +	dev_err(&slot->mmc->class_dev,
> +		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
> +		cmd, arg, cmd_status);
> +}
> +
> +static void dw_mci_setup_bus(struct dw_mci_slot *slot)
> +{
> +	struct dw_mci *host = slot->host;
> +	u32 div;
> +
> +	if (slot->clock != host->current_speed) {
> +		if (host->bus_hz % slot->clock)
> +			/*
> +			 * move the + 1 after the dvide
> +			 * to prevent over-clocking the card.
> +			 */
> +			div = ((host->bus_hz / slot->clock) >> 1) + 1;
> +		else
> +			div = (host->bus_hz  / slot->clock) >> 1;
> +
> +		dev_info(&slot->mmc->class_dev,
> +			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
> +			" div = %d)\n",
> +			slot->id, host->bus_hz, slot->clock,
> +			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
> +
> +		/* disable clock */
> +		mci_writel(host, CLKENA, 0);
> +		mci_writel(host, CLKSRC, 0);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		/* set clock to desired speed */
> +		mci_writel(host, CLKDIV, div);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		/* enable clock */
> +		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		host->current_speed = slot->clock;
> +	}
> +
> +	/* Set the current slot bus width */
> +	mci_writel(host, CTYPE, slot->ctype);
> +}
> +
> +static void dw_mci_start_request(struct dw_mci *host,
> +				 struct dw_mci_slot *slot)
> +{
> +	struct mmc_request *mrq;
> +	struct mmc_command *cmd;
> +	struct mmc_data	*data;
> +	u32 cmdflags;
> +
> +	mrq = slot->mrq;
> +	/* no select the proper slot */
> +	if (host->pdata->select_slot)
> +		host->pdata->select_slot(slot->id);
> +
> +	/* Slot specific timing and width adjustment */
> +	dw_mci_setup_bus(slot);
> +
> +	host->cur_slot = slot;
> +	host->mrq = mrq;
> +
> +	host->pending_events = 0;
> +	host->completed_events = 0;
> +	host->data_status = 0;
> +
> +	data = mrq->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, lets send the initialization clock */
> +	if (unlikely(
> +		test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
> +		cmdflags |= SDMMC_CMD_INIT;
> +	}

This unlikely seems a bit over the top and forces an unfortunate break
in the line to keep within 80 cols. Maybe delete it?

> +
> +	/* we may need to move this code to mci_start_command */
> +	if (data) {
> +		dw_mci_submit_data(host, data);
> +		wmb();
> +	}
> +
> +	dw_mci_start_command(host, cmd, cmdflags);
> +
> +	if (mrq->stop)
> +		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
> +}
> +
> +static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
> +				 struct mmc_request *mrq)
> +{
> +	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
> +			host->state);
> +
> +	spin_lock_bh(&host->lock);
> +	slot->mrq = mrq;
> +
> +	if (host->state == STATE_IDLE) {
> +		host->state = STATE_SENDING_CMD;
> +		dw_mci_start_request(host, slot);
> +	} else {
> +		list_add_tail(&slot->queue_node, &host->queue);
> +	}
> +
> +	spin_unlock_bh(&host->lock);
> +}
> +
> +static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +
> +	WARN_ON(slot->mrq);
> +
> +	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
> +		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_request_done(mmc, mrq);
> +		return;
> +	}
> +
> +	/* We don't support multiple blocks of weird lengths. */
> +	dw_mci_queue_request(host, slot, mrq);
> +}
> +
> +static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +
> +	/* set default 1 bit mode */
> +	slot->ctype = SDMMC_CTYPE_1BIT;
> +
> +	switch (ios->bus_width) {
> +	case MMC_BUS_WIDTH_1:
> +		slot->ctype = SDMMC_CTYPE_1BIT;
> +		break;
> +	case MMC_BUS_WIDTH_4:
> +		slot->ctype = SDMMC_CTYPE_4BIT;
> +		break;
> +	}
> +
> +	if (ios->clock) {
> +		/*
> +		 * Use mirror of ios->clock to prevent race with mmc
> +		 * core ios update when finding the minimum.
> +		 */
> +		slot->clock = ios->clock;
> +	}
> +
> +	switch (ios->power_mode) {
> +	case MMC_POWER_UP:
> +		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static int dw_mci_get_ro(struct mmc_host *mmc)
> +{
> +	int read_only;
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci_board *brd = slot->host->pdata;
> +
> +	if (brd->get_ro != NULL) {
> +		read_only = brd->get_ro(slot->id);
> +	} else {
> +		/* Try on board write protect */
> +		read_only =
> +			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
> +	}
> +
> +	dev_dbg(&mmc->class_dev, "card is %s\n",
> +				read_only ? "read-only" : "read-write");
> +
> +	return read_only;
> +}
> +
> +static int dw_mci_get_cd(struct mmc_host *mmc)
> +{
> +	int present;
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci_board *brd = slot->host->pdata;
> +
> +	if (brd->get_cd != NULL)
> +		present = !brd->get_cd(slot->id);
> +	else	/* try onboard card detect */
> +		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
> +				== 0 ? 1 : 0;
> +
> +	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
> +
> +	return present;
> +}
> +
> +static const struct mmc_host_ops dw_mci_ops = {
> +	.request	= dw_mci_request,
> +	.set_ios	= dw_mci_set_ios,
> +	.get_ro		= dw_mci_get_ro,
> +	.get_cd		= dw_mci_get_cd,
> +};
> +
> +static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
> +	__releases(&host->lock)
> +	__acquires(&host->lock)
> +{
> +	struct dw_mci_slot *slot;
> +	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
> +
> +	WARN_ON(host->cmd || host->data);
> +
> +	host->cur_slot->mrq = NULL;
> +	host->mrq = NULL;
> +	if (!list_empty(&host->queue)) {
> +		slot = list_entry(host->queue.next,
> +				struct dw_mci_slot, queue_node);
> +		list_del(&slot->queue_node);
> +		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
> +				mmc_hostname(slot->mmc));
> +		host->state = STATE_SENDING_CMD;
> +		dw_mci_start_request(host, slot);
> +	} else {
> +		dev_vdbg(&host->pdev->dev, "list empty\n");
> +		host->state = STATE_IDLE;
> +	}
> +
> +	spin_unlock(&host->lock);
> +	mmc_request_done(prev_mmc, mrq);
> +
> +	spin_lock(&host->lock);
> +}
> +
> +static void dw_mci_command_complete(struct dw_mci *host,
> +				    struct mmc_command *cmd)
> +{
> +	u32 status = host->cmd_status;
> +
> +	host->cmd_status = 0;
> +
> +	/* Read the response from the card (up to 16 bytes) */
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136) {
> +			cmd->resp[3] = mci_readl(host, RESP0);
> +			cmd->resp[2] = mci_readl(host, RESP1);
> +			cmd->resp[1] = mci_readl(host, RESP2);
> +			cmd->resp[0] = mci_readl(host, RESP3);
> +		} else {
> +			cmd->resp[0] = mci_readl(host, RESP0);
> +			cmd->resp[1] = 0;
> +			cmd->resp[2] = 0;
> +			cmd->resp[3] = 0;
> +		}
> +	}
> +
> +	if (status & SDMMC_INT_RTO)
> +		cmd->error = -ETIMEDOUT;
> +	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
> +		cmd->error = -EILSEQ;
> +	else if (status & SDMMC_INT_RESP_ERR)
> +		cmd->error = -EIO;
> +	else
> +		cmd->error = 0;
> +
> +	if (cmd->error) {
> +		/* newer ip versions need a delay between retries */
> +		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
> +			mdelay(20);
> +
> +		if (cmd->data) {
> +			host->data = NULL;
> +			dw_mci_stop_dma(host);
> +		}
> +	}
> +}
> +
> +static void dw_mci_tasklet_func(unsigned long priv)
> +{
> +	struct dw_mci *host = (struct dw_mci *)priv;
> +	struct mmc_data	*data;
> +	struct mmc_command *cmd;
> +	enum dw_mci_state state;
> +	enum dw_mci_state prev_state;
> +	u32 status;
> +
> +	spin_lock(&host->lock);
> +
> +	state = host->state;
> +	data = host->data;
> +
> +	do {
> +		prev_state = state;
> +
> +		switch (state) {
> +		case STATE_IDLE:
> +			break;
> +
> +		case STATE_SENDING_CMD:
> +			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			cmd = host->cmd;
> +			host->cmd = NULL;
> +			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
> +			dw_mci_command_complete(host, host->mrq->cmd);
> +			if (!host->mrq->data || cmd->error) {
> +				dw_mci_request_end(host, host->mrq);
> +				goto unlock;
> +			}
> +
> +			prev_state = state = STATE_SENDING_DATA;
> +			/* fall through */
> +
> +		case STATE_SENDING_DATA:
> +			if (test_and_clear_bit(EVENT_DATA_ERROR,
> +					       &host->pending_events)) {
> +				dw_mci_stop_dma(host);
> +				if (data->stop)
> +					send_stop_cmd(host, data);
> +				state = STATE_DATA_ERROR;
> +				break;
> +			}
> +
> +			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
> +			prev_state = state = STATE_DATA_BUSY;
> +			/* fall through */
> +
> +		case STATE_DATA_BUSY:
> +			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			host->data = NULL;
> +			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
> +			status = host->data_status;
> +
> +			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
> +				if (status & SDMMC_INT_DTO) {
> +					dev_err(&host->pdev->dev,
> +							"data timeout error\n");
> +					data->error = -ETIMEDOUT;
> +				} else if (status & SDMMC_INT_DCRC) {
> +					dev_err(&host->pdev->dev,
> +							"data CRC error\n");
> +					data->error = -EILSEQ;
> +				} else {
> +					dev_err(&host->pdev->dev,
> +						"data FIFO error "
> +						"(status=%08x)\n",
> +						status);
> +					data->error = -EIO;
> +				}
> +			} else {
> +				data->bytes_xfered = data->blocks * data->blksz;
> +				data->error = 0;
> +			}
> +
> +			if (!data->stop) {
> +				dw_mci_request_end(host, host->mrq);
> +				goto unlock;
> +			}
> +
> +			prev_state = state = STATE_SENDING_STOP;
> +			if (!data->error)
> +				send_stop_cmd(host, data);
> +			/* fall through */
> +
> +		case STATE_SENDING_STOP:
> +			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			host->cmd = NULL;
> +			dw_mci_command_complete(host, host->mrq->stop);
> +			dw_mci_request_end(host, host->mrq);
> +			goto unlock;
> +
> +		case STATE_DATA_ERROR:
> +			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			state = STATE_DATA_BUSY;
> +			break;
> +		}
> +	} while (state != prev_state);
> +
> +	host->state = state;
> +unlock:
> +	spin_unlock(&host->lock);
> +
> +}
> +
> +static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u16 *pData = (u16 *)buf;
> +
> +	WARN_ON(cnt % 2 != 0);
> +
> +	cnt = cnt >> 1;
> +	while (cnt > 0) {
> +		mci_writew(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u16 *pData = (u16 *)buf;
> +
> +	WARN_ON(cnt % 2 != 0);
> +
> +	cnt = cnt >> 1;
> +	while (cnt > 0) {
> +		*pData++ = mci_readw(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u32 *pData = (u32 *)buf;
> +
> +	WARN_ON(cnt % 4 != 0);
> +	WARN_ON((unsigned long)pData & 0x3);
> +
> +	cnt = cnt >> 2;
> +	while (cnt > 0) {
> +		mci_writel(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u32 *pData = (u32 *)buf;
> +
> +	WARN_ON(cnt % 4 != 0);
> +	WARN_ON((unsigned long)pData & 0x3);
> +
> +	cnt = cnt >> 2;
> +	while (cnt > 0) {
> +		*pData++ = mci_readl(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u64 *pData = (u64 *)buf;
> +
> +	WARN_ON(cnt % 8 != 0);
> +
> +	cnt = cnt >> 3;
> +	while (cnt > 0) {
> +		mci_writeq(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u64 *pData = (u64 *)buf;
> +
> +	WARN_ON(cnt % 8 != 0);
> +
> +	cnt = cnt >> 3;
> +	while (cnt > 0) {
> +		*pData++ = mci_readq(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_read_data_pio(struct dw_mci *host)
> +{
> +	struct scatterlist *sg = host->sg;
> +	void *buf = sg_virt(sg);
> +	unsigned int offset = host->pio_offset;
> +	struct mmc_data	*data = host->data;
> +	int shift = host->data_shift;
> +	u32 status;
> +	unsigned int nbytes = 0, len, old_len, count = 0;
> +
> +	do {
> +		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
> +		if (count == 0)
> +			old_len = len;
> +
> +		if (likely(offset + len <= sg->length)) {
> +			host->pull_data(host, (void *)(buf + offset), len);
> +
> +			offset += len;
> +			nbytes += len;
> +
> +			if (offset == sg->length) {
> +				flush_dcache_page(sg_page(sg));
> +				host->sg = sg = sg_next(sg);
> +				if (!sg)
> +					goto done;
> +
> +				offset = 0;
> +				buf = sg_virt(sg);
> +			}
> +		} else {
> +			unsigned int remaining = sg->length - offset;
> +			host->pull_data(host,
> +				(void *)(buf + offset), remaining);
> +			nbytes += remaining;
> +
> +			flush_dcache_page(sg_page(sg));
> +			host->sg = sg = sg_next(sg);
> +			if (!sg)
> +				goto done;
> +
> +			offset = len - remaining;
> +			buf = sg_virt(sg);
> +			host->pull_data(host, buf, offset);
> +			nbytes += offset;
> +		}
> +
> +		status = mci_readl(host, MINTSTS);
> +		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
> +		if (status & DW_MCI_DATA_ERROR_FLAGS) {
> +			host->data_status = status;
> +			data->bytes_xfered += nbytes;
> +			smp_wmb();
> +
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +
> +			tasklet_schedule(&host->tasklet);
> +			return;
> +		}
> +		count++;
> +	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
> +						lets read again*/
> +	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
> +	host->pio_offset = offset;
> +	data->bytes_xfered += nbytes;
> +	return;
> +
> +done:
> +	data->bytes_xfered += nbytes;
> +	smp_wmb();
> +	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +}
> +
> +static void dw_mci_write_data_pio(struct dw_mci *host)
> +{
> +	struct scatterlist *sg = host->sg;
> +	void *buf = sg_virt(sg);
> +	unsigned int offset = host->pio_offset;
> +	struct mmc_data	*data = host->data;
> +	int shift = host->data_shift;
> +	u32 status;
> +	unsigned int nbytes = 0, len;
> +
> +	do {
> +		len = SDMMC_FIFO_SZ -
> +			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
> +		if (likely(offset + len <= sg->length)) {
> +			host->push_data(host, (void *)(buf + offset), len);
> +
> +			offset += len;
> +			nbytes += len;
> +			if (offset == sg->length) {
> +				host->sg = sg = sg_next(sg);
> +				if (!sg)
> +					goto done;
> +
> +				offset = 0;
> +				buf = sg_virt(sg);
> +			}
> +		} else {
> +			unsigned int remaining = sg->length - offset;
> +
> +			host->push_data(host,
> +				(void *)(buf + offset), remaining);
> +			nbytes += remaining;
> +
> +			host->sg = sg = sg_next(sg);
> +			if (!sg)
> +				goto done;
> +
> +			offset = len - remaining;
> +			buf = sg_virt(sg);
> +			host->push_data(host, (void *)buf, offset);
> +			nbytes += offset;
> +		}
> +
> +		status = mci_readl(host, MINTSTS);
> +		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
> +		if (status & DW_MCI_DATA_ERROR_FLAGS) {
> +			host->data_status = status;
> +			data->bytes_xfered += nbytes;
> +
> +			smp_wmb();
> +
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +
> +			tasklet_schedule(&host->tasklet);
> +			return;
> +		}
> +	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
> +
> +	host->pio_offset = offset;
> +	data->bytes_xfered += nbytes;
> +
> +	return;
> +
> +done:
> +	data->bytes_xfered += nbytes;
> +	smp_wmb();
> +	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +}
> +
> +static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
> +{
> +	if (!host->cmd_status)
> +		host->cmd_status = status;
> +
> +	smp_wmb();
> +
> +	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> +	tasklet_schedule(&host->tasklet);
> +}
> +
> +static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
> +{
> +	struct dw_mci *host = dev_id;
> +	u32 status,  pending;
> +	unsigned int pass_count = 0;
> +
> +	do {
> +		status = mci_readl(host, RINTSTS);
> +		pending = mci_readl(host, MINTSTS);/* read only mask reg */
> +
> +		/*
> +		 * DTO fix - version 2.10a and below, and only if internal DMA
> +		 * is configured.
> +		 */
> +		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
> +			if (!pending &&
> +			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
> +				pending |= SDMMC_INT_DATA_OVER;
> +		}
> +
> +		if (!pending)
> +			break;
> +
> +		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
> +			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
> +			host->cmd_status = status;
> +			smp_wmb();
> +			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
> +			/* if there is an error, lets report DATA_ERROR */
> +			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
> +			host->data_status = status;
> +			smp_wmb();
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +
> +		if (pending & SDMMC_INT_DATA_OVER) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
> +			if (!host->data_status)
> +				host->data_status = status;
> +			smp_wmb();
> +			if (host->dir_status == DW_MCI_RECV_STATUS) {
> +				if (host->sg != NULL)
> +					dw_mci_read_data_pio(host);
> +			}
> +			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +		if (pending & SDMMC_INT_RXDR) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
> +			if (host->sg)
> +				dw_mci_read_data_pio(host);
> +		}
> +
> +		if (pending & SDMMC_INT_TXDR) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
> +			if (host->sg)
> +				dw_mci_write_data_pio(host);
> +		}
> +
> +		if (pending & SDMMC_INT_CMD_DONE) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
> +			dw_mci_cmd_interrupt(host, status);
> +		}
> +
> +		if (pending & SDMMC_INT_CD) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_CD);
> +			tasklet_schedule(&host->card_tasklet);
> +		}
> +
> +	} while (pass_count++ < 5);

This could do with a comment, explaining why it's necessary to loop 5
times.

> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	/* Handle DMA interrupts */
> +	pending = mci_readl(host, IDSTS);
> +	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
> +		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
> +				SDMMC_IDMAC_INT_RI);
> +		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
> +		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> +		host->dma_ops->complete(host);
> +	}
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void dw_mci_tasklet_card(unsigned long data)
> +{
> +	struct dw_mci *host = (struct dw_mci *)data;
> +	int i;
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		struct mmc_host *mmc = slot->mmc;
> +		struct mmc_request *mrq;
> +		int present;
> +		u32 ctrl;
> +
> +		present = dw_mci_get_cd(mmc);
> +		while (present != slot->last_detect_state) {
> +			spin_lock(&host->lock);
> +
> +			dev_dbg(&slot->mmc->class_dev, "card %s\n",
> +					present ? "inserted" : "removed");
> +
> +			/* Card change detected */
> +			slot->last_detect_state = present;
> +
> +			/* Power up slot */
> +			if (present != 0) {
> +				if (host->pdata->setpower)
> +					host->pdata->setpower(slot->id,
> +						mmc->ocr_avail);
> +
> +				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +			}
> +
> +			/* Clean up queue if present */
> +			mrq = slot->mrq;
> +			if (mrq) {
> +				if (mrq == host->mrq) {
> +					host->data = NULL;
> +					host->cmd = NULL;
> +
> +					switch (host->state) {
> +					case STATE_IDLE:
> +						break;
> +					case STATE_SENDING_CMD:
> +						mrq->cmd->error = -ENOMEDIUM;
> +						if (!mrq->data)
> +							break;
> +						/* fall through */
> +					case STATE_SENDING_DATA:
> +						mrq->data->error = -ENOMEDIUM;
> +						dw_mci_stop_dma(host);
> +						break;
> +					case STATE_DATA_BUSY:
> +					case STATE_DATA_ERROR:
> +						if (mrq->data->error == -EINPROGRESS)
> +							mrq->data->error = -ENOMEDIUM;
> +						if (!mrq->stop)
> +							break;
> +						/* fall through */
> +					case STATE_SENDING_STOP:
> +						mrq->stop->error = -ENOMEDIUM;
> +						break;
> +					}
> +
> +					dw_mci_request_end(host, mrq);
> +				} else {
> +					list_del(&slot->queue_node);
> +					mrq->cmd->error = -ENOMEDIUM;
> +					if (mrq->data)
> +						mrq->data->error = -ENOMEDIUM;
> +					if (mrq->stop)
> +						mrq->stop->error = -ENOMEDIUM;
> +
> +					spin_unlock(&host->lock);
> +					mmc_request_done(slot->mmc, mrq);
> +					spin_lock(&host->lock);
> +				}
> +			}
> +
> +			/* Power down slot */
> +			if (present == 0) {
> +				if (host->pdata->setpower)
> +					host->pdata->setpower(slot->id, 0);
> +				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +
> +				/*
> +				 * Clear down the FIFO - doing so generates a
> +				 * block interrupt, hence setting the
> +				 * scatter-gather pointer to NULL.
> +				 */
> +				host->sg = NULL;
> +
> +				ctrl = mci_readl(host, CTRL);
> +				ctrl |= SDMMC_CTRL_FIFO_RESET;
> +				mci_writel(host, CTRL, ctrl);
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +				ctrl = mci_readl(host, BMOD);
> +				ctrl |= 0x01;	/* Software reset of DMA */
> +				mci_writel(host, BMOD, ctrl);
> +#endif
> +
> +			}
> +
> +			spin_unlock(&host->lock);
> +			present = dw_mci_get_cd(mmc);
> +		}
> +
> +		mmc_detect_change(slot->mmc,
> +			msecs_to_jiffies(host->pdata->detect_delay_ms));
> +	}
> +}
> +
> +static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
> +{
> +	struct mmc_host *mmc;
> +	struct dw_mci_slot *slot;
> +
> +	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
> +	if (!mmc)
> +		return -ENOMEM;
> +
> +	slot = mmc_priv(mmc);
> +	slot->id = id;
> +	slot->mmc = mmc;
> +	slot->host = host;
> +
> +	mmc->ops = &dw_mci_ops;
> +	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);

Is that really supposed to be 510, and not 512?

> +	mmc->f_max = host->bus_hz;
> +
> +	if (host->pdata->get_ocr)
> +		mmc->ocr_avail = host->pdata->get_ocr(id);
> +	else
> +		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
> +
> +	/*
> +	 * Start with slot power disabled,
> +	 * will be enabled when card is detected
> +	 */
> +	if (host->pdata->setpower)
> +		host->pdata->setpower(id, 0);
> +
> +	mmc->caps = 0;
> +	if (host->pdata->get_bus_wd)
> +		if (host->pdata->get_bus_wd(slot->id) >= 4)
> +			mmc->caps |= MMC_CAP_4_BIT_DATA;
> +
> +	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
> +		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	mmc->max_segs = host->ring_size;
> +	mmc->max_blk_size = 65536;
> +	mmc->max_blk_count = host->ring_size;
> +	mmc->max_seg_size = 0x1000;
> +	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
> +#else
> +	if (host->pdata->blk_settings) {
> +		mmc->max_segs = host->pdata->blk_settings->max_segs;
> +		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
> +		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
> +		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
> +		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
> +	} else {
> +		/*useful defaults*/
> +		mmc->max_segs = 64;
> +		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
> +		mmc->max_blk_count = 512;
> +		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
> +		mmc->max_seg_size = mmc->max_req_size;
> +	}
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +	/* Assume card is present initially */
> +	if (dw_mci_get_cd(mmc))
> +		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +	else
> +		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +
> +	host->slot[id] = slot;
> +	mmc_add_host(mmc);
> +
> +#if defined(CONFIG_DEBUG_FS)
> +	dw_mci_init_debugfs(slot);
> +#endif
> +
> +	/* Card initially undetected */
> +	slot->last_detect_state = 0;
> +
> +	return 0;
> +}
> +
> +static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
> +				unsigned int id)
> +{
> +	/* Shutdown detect IRQ */
> +	if (slot->host->pdata->exit)
> +		slot->host->pdata->exit(id);
> +
> +	/* Debugfs stuff is cleaned up by mmc core */
> +	mmc_remove_host(slot->mmc);
> +	slot->host->slot[id] = NULL;
> +	mmc_free_host(slot->mmc);
> +}
> +
> +static void dw_mci_init_dma(struct dw_mci *host)
> +{
> +	/* Alloc memory for sg translation */
> +	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
> +			&host->sg_dma, GFP_KERNEL);
> +	if (!host->sg_cpu) {
> +		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
> +				__func__);
> +		goto no_dma;
> +	}
> +
> +	/* Determine which DMA interface to use */
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	host->dma_ops = &dw_mci_idmac_ops;
> +	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
> +#endif
> +
> +	if (!host->dma_ops)
> +		goto no_dma;
> +
> +	if (host->dma_ops->init) {
> +		if (host->dma_ops->init(host)) {
> +			dev_err(&host->pdev->dev, "%s: Unable to initialise "
> +					"DMA Controller.\n", __func__);
> +			goto no_dma;
> +		}
> +	} else {
> +		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
> +		goto no_dma;
> +	}
> +
> +	host->use_dma = 1;
> +	return;
> +
> +no_dma:
> +	dev_info(&host->pdev->dev, "Using PIO mode.\n");
> +	host->use_dma = 0;
> +	return;
> +}
> +
> +static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
> +{
> +	unsigned long timeout = jiffies + msecs_to_jiffies(500);
> +	unsigned int ctrl;
> +
> +	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
> +			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
> +
> +	/* wait till resets clear */
> +	do {
> +		ctrl = mci_readl(host, CTRL);
> +		if (!(ctrl & (SDMMC_CTRL_RESET |
> +			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
> +			return true;
> +	} while (time_before(jiffies, timeout));
> +
> +	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
> +
> +	return false;
> +}
> +
> +static int dw_mci_probe(struct platform_device *pdev)
> +{
> +	struct dw_mci *host;
> +	struct resource	*regs;
> +	struct dw_mci_board *pdata;
> +	int irq, ret, i, width;
> +	u32 fifo_size;
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!regs)
> +		return -ENXIO;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
> +	if (!host)
> +		return -ENOMEM;
> +
> +	host->pdev = pdev;
> +	host->pdata = pdata = pdev->dev.platform_data;
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "Platform data missing\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	if (((pdata->num_slots > 1) && !(pdata->select_slot))
> +			|| !(pdata->init)) {
> +		dev_err(&pdev->dev, "Platform data wrong\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	if (!pdata->bus_hz) {
> +		dev_err(&pdev->dev,
> +			"Bus speed undefined in platform data!\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	host->bus_hz = pdata->bus_hz;
> +	host->quirks = pdata->quirks;
> +
> +	spin_lock_init(&host->lock);
> +	INIT_LIST_HEAD(&host->queue);
> +
> +	ret = -ENOMEM;
> +	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
> +	if (!host->regs)
> +		goto err_freehost;
> +
> +	host->dma_ops = pdata->dma_ops;
> +	dw_mci_init_dma(host);
> +
> +	/*
> +	 * Get the host data width - this assumes that HCON has been set with
> +	 * the correct values.
> +	 */
> +	i = (mci_readl(host, HCON) >> 7) & 0x7;
> +	if (!i) {
> +		host->push_data = dw_mci_push_data16;
> +		host->pull_data = dw_mci_pull_data16;
> +		width = 16;
> +		host->data_shift = 1;
> +	} else if (i == 2) {
> +		host->push_data = dw_mci_push_data64;
> +		host->pull_data = dw_mci_pull_data64;
> +		width = 64;
> +		host->data_shift = 3;
> +	} else {
> +		/* Check for a reserved value, and warn if it is */
> +		WARN((i != 1),
> +			"HCON reports a reserved host data width!\n"
> +			"Defaulting to 32-bit access.\n");
> +		host->push_data = dw_mci_push_data32;
> +		host->pull_data = dw_mci_pull_data32;
> +		width = 32;
> +		host->data_shift = 2;
> +	}
> +
> +	/* reset all blocks */
> +	if (!mci_wait_reset(&pdev->dev, host)) {
> +		ret = -ENODEV;
> +		goto err_dmaunmap;
> +	}
> +
> +	 /* Clear the interrupts for the host controller */
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
> +
> +	/* Put in max timeout */
> +	mci_writel(host, TMOUT, 0xFFFFFFFF);
> +
> +	/*
> +	 * FIFO threshold settings  RxMark = fifo_size/2-1,
> +	 *                          Tx Mark =fifo_size/2 DMA Size = 8
> +	 */
> +	fifo_size = mci_readl(host, FIFOTH);
> +	fifo_size = (fifo_size >> 16) & 0x7ff;
> +	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
> +				((fifo_size/2) << 0)));
> +
> +	/* disable clock to CIU */
> +	mci_writel(host, CLKENA, 0);
> +	mci_writel(host, CLKSRC, 0);
> +
> +	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
> +	tasklet_init(&host->card_tasklet,
> +		dw_mci_tasklet_card, (unsigned long)host);
> +
> +	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
> +	if (ret)
> +		goto err_dmaunmap;
> +
> +	platform_set_drvdata(pdev, host);
> +
> +	if (host->pdata->num_slots)
> +		host->num_slots = host->pdata->num_slots;
> +	else
> +		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
> +
> +	/* We need at least one slot to succeed ####pd####*/
> +	for (i = 0; i < host->num_slots; i++) {
> +		ret = dw_mci_init_slot(host, i);
> +		if (ret) {
> +			ret = -ENODEV;
> +			goto err_init_slot;
> +		}
> +	}
> +
> +	/*
> +	 * enable interrupt for command done, data over, data empty,
> +	 * receive ready and error such as transmit, receive timeout, crc error
> +	 */
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
> +				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
> +				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
> +	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
> +
> +	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
> +			"%d bit host data width\n",
> +			irq, width);
> +	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
> +		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
> +
> +	return 0;
> +
> +err_init_slot:
> +	/* De-init any initialized slots */
> +	while (i > 0) {
> +		if (host->slot[i])
> +			dw_mci_cleanup_slot(host->slot[i], i);
> +		i--;
> +	}
> +	free_irq(irq, host);
> +
> +err_dmaunmap:
> +	if (host->use_dma && host->dma_ops->exit)
> +		host->dma_ops->exit(host);
> +	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
> +			host->sg_cpu, host->sg_dma);
> +	iounmap(host->regs);
> +
> +err_freehost:
> +	kfree(host);
> +	return ret;
> +}
> +
> +static int __exit dw_mci_remove(struct platform_device *pdev)
> +{
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +	int i;
> +
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		dev_dbg(&pdev->dev, "remove slot %d\n", i);
> +		if (host->slot[i])
> +			dw_mci_cleanup_slot(host->slot[i], i);
> +	}
> +
> +	/* disable clock to CIU */
> +	mci_writel(host, CLKENA, 0);
> +	mci_writel(host, CLKSRC, 0);
> +
> +	free_irq(platform_get_irq(pdev, 0), host);
> +	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> +
> +	if (host->use_dma && host->dma_ops->exit)
> +		host->dma_ops->exit(host);
> +
> +	iounmap(host->regs);
> +
> +	kfree(host);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +/*
> + * 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)
> +{
> +	int i, ret;
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		if (!slot)
> +			continue;
> +		ret = mmc_suspend_host(slot->mmc);
> +		if (ret < 0) {
> +			while (--i >= 0) {
> +				slot = host->slot[i];
> +				if (slot)
> +					mmc_resume_host(host->slot[i]->mmc);
> +			}
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int dw_mci_resume(struct platform_device *pdev)
> +{
> +	int i, ret;
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		if (!slot)
> +			continue;
> +		ret = mmc_resume_host(host->slot[i]->mmc);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +#else
> +#define dw_mci_suspend	NULL
> +#define dw_mci_resume	NULL
> +#endif /* CONFIG_PM */
> +
> +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",
> +	},
> +};
> +
> +static int __init dw_mci_init(void)
> +{
> +	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
> +}
> +
> +static void __exit dw_mci_exit(void)
> +{
> +	platform_driver_unregister(&dw_mci_driver);
> +}
> +
> +module_init(dw_mci_init);
> +module_exit(dw_mci_exit);
> +
> +MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
> +MODULE_AUTHOR("NXP Semiconductor VietNam");
> +MODULE_AUTHOR("Imagination Technologies Ltd");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> new file mode 100644
> index 0000000..65bcff7
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -0,0 +1,155 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *
> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
> + *
> + * 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, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _DW_MMC_H_
> +#define _DW_MMC_H_
> +
> +#define MCI_SLOT		0

This appears to be unused.

> +
> +#define SDMMC_CTRL		0x000
> +#define SDMMC_PWREN		0x004
> +#define SDMMC_CLKDIV		0x008
> +#define SDMMC_CLKSRC		0x00c
> +#define SDMMC_CLKENA		0x010
> +#define SDMMC_TMOUT		0x014
> +#define SDMMC_CTYPE		0x018
> +#define SDMMC_BLKSIZ		0x01c
> +#define SDMMC_BYTCNT		0x020
> +#define SDMMC_INTMASK		0x024
> +#define SDMMC_CMDARG		0x028
> +#define SDMMC_CMD		0x02c
> +#define SDMMC_RESP0		0x030
> +#define SDMMC_RESP1		0x034
> +#define SDMMC_RESP2		0x038
> +#define SDMMC_RESP3		0x03c
> +#define SDMMC_MINTSTS		0x040
> +#define SDMMC_RINTSTS		0x044
> +#define SDMMC_STATUS		0x048
> +#define SDMMC_FIFOTH		0x04c
> +#define SDMMC_CDETECT		0x050
> +#define SDMMC_WRTPRT		0x054
> +#define SDMMC_GPIO		0x058
> +#define SDMMC_TCBCNT		0x05c
> +#define SDMMC_TBBCNT		0x060
> +#define SDMMC_DEBNCE		0x064
> +#define SDMMC_USRID		0x068
> +#define SDMMC_VERID		0x06c
> +#define SDMMC_HCON		0x070
> +#define SDMMC_BMOD		0x080
> +#define SDMMC_PLDMND		0x084
> +#define SDMMC_DBADDR		0x088
> +#define SDMMC_IDSTS		0x08c
> +#define SDMMC_IDINTEN		0x090
> +#define SDMMC_DSCADDR		0x094
> +#define SDMMC_BUFADDR		0x098
> +#define SDMMC_DATA		0x100
> +#define SDMMC_DATA_ADR		0x100
> +
> +/* shift bit field */
> +#define _SBF(f, v)		((v) << (f))
> +
> +/* Control register defines */
> +#define SDMMC_CTRL_USE_IDMAC		BIT(25)
> +#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
> +#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
> +#define SDMMC_CTRL_SEND_CCSD		BIT(9)
> +#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
> +#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
> +#define SDMMC_CTRL_READ_WAIT		BIT(6)
> +#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
> +#define SDMMC_CTRL_INT_ENABLE		BIT(4)
> +#define SDMMC_CTRL_DMA_RESET		BIT(2)
> +#define SDMMC_CTRL_FIFO_RESET		BIT(1)
> +#define SDMMC_CTRL_RESET		BIT(0)
> +/* Clock Enable register defines */
> +#define SDMMC_CLKEN_LOW_PWR		BIT(16)
> +#define SDMMC_CLKEN_ENABLE		BIT(0)
> +/* time-out register defines */
> +#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
> +#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
> +#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
> +#define SDMMC_TMOUT_RESP_MSK		0xFF
> +/* card-type register defines */
> +#define SDMMC_CTYPE_8BIT		BIT(16)
> +#define SDMMC_CTYPE_4BIT		BIT(0)
> +#define SDMMC_CTYPE_1BIT		0
> +/* Interrupt status & mask register defines */
> +#define SDMMC_INT_SDIO			BIT(16)
> +#define SDMMC_INT_EBE			BIT(15)
> +#define SDMMC_INT_ACD			BIT(14)
> +#define SDMMC_INT_SBE			BIT(13)
> +#define SDMMC_INT_HLE			BIT(12)
> +#define SDMMC_INT_FRUN			BIT(11)
> +#define SDMMC_INT_HTO			BIT(10)
> +#define SDMMC_INT_DTO			BIT(9)
> +#define SDMMC_INT_RTO			BIT(8)
> +#define SDMMC_INT_DCRC			BIT(7)
> +#define SDMMC_INT_RCRC			BIT(6)
> +#define SDMMC_INT_RXDR			BIT(5)
> +#define SDMMC_INT_TXDR			BIT(4)
> +#define SDMMC_INT_DATA_OVER		BIT(3)
> +#define SDMMC_INT_CMD_DONE		BIT(2)
> +#define SDMMC_INT_RESP_ERR		BIT(1)
> +#define SDMMC_INT_CD			BIT(0)
> +#define SDMMC_INT_ERROR			0xbfc2
> +/* Command register defines */
> +#define SDMMC_CMD_START			BIT(31)
> +#define SDMMC_CMD_CCS_EXP		BIT(23)
> +#define SDMMC_CMD_CEATA_RD		BIT(22)
> +#define SDMMC_CMD_UPD_CLK		BIT(21)
> +#define SDMMC_CMD_INIT			BIT(15)
> +#define SDMMC_CMD_STOP			BIT(14)
> +#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
> +#define SDMMC_CMD_SEND_STOP		BIT(12)
> +#define SDMMC_CMD_STRM_MODE		BIT(11)
> +#define SDMMC_CMD_DAT_WR		BIT(10)
> +#define SDMMC_CMD_DAT_EXP		BIT(9)
> +#define SDMMC_CMD_RESP_CRC		BIT(8)
> +#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
> +/* Internal DMAC interrupt defines */
> +#define SDMMC_IDMAC_INT_AI		BIT(9)
> +#define SDMMC_IDMAC_INT_NI		BIT(8)
> +#define SDMMC_IDMAC_INT_CES		BIT(5)
> +#define SDMMC_IDMAC_INT_DU		BIT(4)
> +#define SDMMC_IDMAC_INT_FBE		BIT(2)
> +#define SDMMC_IDMAC_INT_RI		BIT(1)
> +#define SDMMC_IDMAC_INT_TI		BIT(0)
> +/* Internal DMAC bus mode bits */
> +#define SDMMC_IDMAC_ENABLE		BIT(7)
> +#define SDMMC_IDMAC_FB			BIT(1)
> +#define SDMMC_IDMAC_SWRESET		BIT(0)
> +
> +/* Register access macros */
> +#define mci_readl(dev, reg)			\
> +	__raw_readl(dev->regs + SDMMC_##reg)
> +#define mci_writel(dev, reg, value)			\
> +	__raw_writel((value), dev->regs + SDMMC_##reg)
> +
> +/* 16-bit FIFO access macros */
> +#define mci_readw(dev, reg)			\
> +	__raw_readw(dev->regs + SDMMC_##reg)
> +#define mci_writew(dev, reg, value)			\
> +	__raw_writew((value), dev->regs + SDMMC_##reg)
> +
> +/* 64-bit FIFO access macros */
> +#define mci_readq(dev, reg)			\
> +	__raw_readq(dev->regs + SDMMC_##reg)
> +#define mci_writeq(dev, reg, value)			\
> +	__raw_writeq((value), dev->regs + SDMMC_##reg)
> +
> +#endif /* _DW_MMC_H_ */
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> new file mode 100644
> index 0000000..d2f92bd
> --- /dev/null
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -0,0 +1,149 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *
> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
> + *
> + * 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, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _LINUX_MMC_DW_MMC_H_
> +#define _LINUX_MMC_DW_MMC_H_
> +
> +#define MAX_MCI_SLOTS	2
> +
> +enum dw_mci_state {
> +	STATE_IDLE = 0,
> +	STATE_SENDING_CMD,
> +	STATE_SENDING_DATA,
> +	STATE_DATA_BUSY,
> +	STATE_SENDING_STOP,
> +	STATE_DATA_ERROR,
> +};
> +
> +enum {
> +	EVENT_CMD_COMPLETE = 0,
> +	EVENT_XFER_COMPLETE,
> +	EVENT_DATA_COMPLETE,
> +	EVENT_DATA_ERROR,
> +	EVENT_XFER_ERROR
> +};
> +
> +struct mmc_data;
> +
> +struct dw_mci {
> +	spinlock_t		lock;
> +	void __iomem		*regs;
> +
> +	struct scatterlist	*sg;
> +	unsigned int		pio_offset;
> +
> +	struct dw_mci_slot	*cur_slot;
> +	struct mmc_request	*mrq;
> +	struct mmc_command	*cmd;
> +	struct mmc_data		*data;
> +
> +	/* DMA interface members*/
> +	int			use_dma;
> +
> +	dma_addr_t		sg_dma;
> +	void			*sg_cpu;
> +	struct dw_mci_dma_ops	*dma_ops;
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	unsigned int		ring_size;
> +#else
> +	struct dw_mci_dma_data	*dma_data;
> +#endif
> +	u32			cmd_status;
> +	u32			data_status;
> +	u32			stop_cmdr;
> +	u32			dir_status;
> +	struct tasklet_struct	tasklet;
> +	struct tasklet_struct	card_tasklet;
> +	unsigned long		pending_events;
> +	unsigned long		completed_events;
> +	enum dw_mci_state	state;
> +	struct list_head	queue;
> +
> +	u32			bus_hz;
> +	u32			current_speed;
> +	u32			num_slots;
> +	struct platform_device	*pdev;
> +	struct dw_mci_board	*pdata;
> +	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
> +
> +	/* FIFO push and pull */
> +	int			data_shift;
> +	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
> +	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
> +
> +	/* Workaround flags */
> +	u32			quirks;
> +};
> +
> +/* DMA ops for Internal/External DMAC interface */
> +struct dw_mci_dma_ops {
> +	/* DMA Ops */
> +	int (*init)(struct dw_mci *host);
> +	void (*start)(struct dw_mci *host, unsigned int sg_len);
> +	void (*complete)(struct dw_mci *host);
> +	void (*stop)(struct dw_mci *host);
> +	void (*cleanup)(struct dw_mci *host);
> +	void (*exit)(struct dw_mci *host);
> +};
> +
> +/* IP Quirks/flags. */
> +/* No special quirks or flags to cater for */
> +#define DW_MCI_QUIRK_NONE		0

Is this needed?

> +/* DTO fix for command transmission with IDMAC configured */
> +#define DW_MCI_QUIRK_IDMAC_DTO		1
> +/* delay needed between retries on some 2.11a implementations */
> +#define DW_MCI_QUIRK_RETRY_DELAY	2
> +/* High Speed Capable - Supports HS cards (upto 50MHz) */
> +#define DW_MCI_QUIRK_HIGHSPEED		4
> +
> +
> +struct dma_pdata;
> +
> +struct block_settings {
> +	unsigned short	max_segs;	/* see blk_queue_max_segments */
> +	unsigned int	max_blk_size;	/* maximum size of one mmc block */
> +	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
> +	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
> +	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
> +};
> +
> +/* Board platform data */
> +struct dw_mci_board {
> +	u32 num_slots;
> +
> +	u32 quirks; /* Workaround / Quirk flags */
> +	unsigned long bus_hz; /* Bus speed */
> +
> +	/* delay in mS before detecting cards after interrupt */
> +	u32 detect_delay_ms;
> +
> +	int (*init)(u32 slot_id, irq_handler_t , void *);
> +	int (*get_ro)(u32 slot_id);
> +	int (*get_cd)(u32 slot_id);
> +	int (*get_ocr)(u32 slot_id);
> +	int (*get_bus_wd)(u32 slot_id);
> +	/*
> +	 * Enable power to selected slot and set voltage to desired level.
> +	 * Voltage levels are specified using MMC_VDD_xxx defines defined
> +	 * in linux/mmc/host.h file.
> +	 */
> +	void (*setpower)(u32 slot_id, u32 volt);
> +	void (*exit)(u32 slot_id);
> +	void (*select_slot)(u32 slot_id);
> +
> +	struct dw_mci_dma_ops *dma_ops;
> +	struct dma_pdata *data;
> +	struct block_settings *blk_settings;
> +};
> +
> +#endif /* _LINUX_MMC_DW_MMC_H_ */
> -- 
> 1.7.2.2

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [RESEND PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-08 11:55 ` Matt Fleming
@ 2010-12-08 13:14   ` Will Newton
  2010-12-08 14:21     ` [PATCH] " Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-08 13:14 UTC (permalink / raw)
  To: Matt Fleming; +Cc: Linux Kernel list, linux-mmc, Chris Ball

On Wed, Dec 8, 2010 at 11:55 AM, Matt Fleming <matt@console-pimps.org> wrote:
> On Mon, Dec 06, 2010 at 03:53:10PM +0000, Will Newton wrote:
>> This adds the mmc host driver for the Synopsys DesignWare mmc
>> host controller, found in a number of embedded SoC designs.
>>
>> Signed-off-by: Will Newton <will.newton@imgtec.com>
>
> Comments below..

Thanks for the review!

>> +static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
>> +{
>> +     struct mmc_host *mmc = slot->mmc;
>> +     struct dw_mci *host = slot->host;
>> +     struct dentry *root;
>> +     struct dentry *node;
>> +
>> +     root = mmc->debugfs_root;
>> +     if (!root)
>> +             return;
>> +
>> +     node = debugfs_create_file("regs", S_IRUSR, root, host,
>> +                     &dw_mci_regs_fops);
>> +     if (IS_ERR(node))
>> +             return;
>
> Should this be a goto err instead of return? I'm guessing this is
> meant to catch -ENODEV being returned when debugfs is not enabled in
> the kernel, but if we've compiled this code with CONFIG_DEBUG_FS then
> we probably want to know when this call fails.

I've removed that check, I don't think debugfs_create_file will return an error
other than NULL with CONFIG_DEBUG_FS enabled.

>> +static void dw_mci_set_timeout(struct dw_mci *host)
>> +{
>> +     mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
>> +}
>> +
>
> Could do with an explicit 'inline'? Especially as it only has one
> callsite.

gcc is always going to inline it so I'd rather leave out the explicit
inline. That way if the code gets changed in future the inline hint
isn't going to become out of date.

>> +static void dw_mci_start_request(struct dw_mci *host,
>> +                              struct dw_mci_slot *slot)
>> +{
>> +     struct mmc_request *mrq;
>> +     struct mmc_command *cmd;
>> +     struct mmc_data *data;
>> +     u32 cmdflags;
>> +
>> +     mrq = slot->mrq;
>> +     /* no select the proper slot */
>> +     if (host->pdata->select_slot)
>> +             host->pdata->select_slot(slot->id);
>> +
>> +     /* Slot specific timing and width adjustment */
>> +     dw_mci_setup_bus(slot);
>> +
>> +     host->cur_slot = slot;
>> +     host->mrq = mrq;
>> +
>> +     host->pending_events = 0;
>> +     host->completed_events = 0;
>> +     host->data_status = 0;
>> +
>> +     data = mrq->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, lets send the initialization clock */
>> +     if (unlikely(
>> +             test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
>> +             cmdflags |= SDMMC_CMD_INIT;
>> +     }
>
> This unlikely seems a bit over the top and forces an unfortunate break
> in the line to keep within 80 cols. Maybe delete it?

Removed.

>> +static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
>> +{
>> +     struct dw_mci *host = dev_id;
>> +     u32 status,  pending;
>> +     unsigned int pass_count = 0;
>> +
>> +     do {
>> +             status = mci_readl(host, RINTSTS);
>> +             pending = mci_readl(host, MINTSTS);/* read only mask reg */
>> +
>> +             /*
>> +              * DTO fix - version 2.10a and below, and only if internal DMA
>> +              * is configured.
>> +              */
>> +             if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
>> +                     if (!pending &&
>> +                         ((mci_readl(host, STATUS) >> 17) & 0x1fff))
>> +                             pending |= SDMMC_INT_DATA_OVER;
>> +             }
>> +
>> +             if (!pending)
>> +                     break;
>> +
>> +             if (pending & DW_MCI_CMD_ERROR_FLAGS) {
>> +                     mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
>> +                     host->cmd_status = status;
>> +                     smp_wmb();
>> +                     set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
>> +                     tasklet_schedule(&host->tasklet);
>> +             }
>> +
>> +             if (pending & DW_MCI_DATA_ERROR_FLAGS) {
>> +                     /* if there is an error, lets report DATA_ERROR */
>> +                     mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
>> +                     host->data_status = status;
>> +                     smp_wmb();
>> +                     set_bit(EVENT_DATA_ERROR, &host->pending_events);
>> +                     tasklet_schedule(&host->tasklet);
>> +             }
>> +
>> +
>> +             if (pending & SDMMC_INT_DATA_OVER) {
>> +                     mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
>> +                     if (!host->data_status)
>> +                             host->data_status = status;
>> +                     smp_wmb();
>> +                     if (host->dir_status == DW_MCI_RECV_STATUS) {
>> +                             if (host->sg != NULL)
>> +                                     dw_mci_read_data_pio(host);
>> +                     }
>> +                     set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
>> +                     tasklet_schedule(&host->tasklet);
>> +             }
>> +
>> +             if (pending & SDMMC_INT_RXDR) {
>> +                     mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
>> +                     if (host->sg)
>> +                             dw_mci_read_data_pio(host);
>> +             }
>> +
>> +             if (pending & SDMMC_INT_TXDR) {
>> +                     mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
>> +                     if (host->sg)
>> +                             dw_mci_write_data_pio(host);
>> +             }
>> +
>> +             if (pending & SDMMC_INT_CMD_DONE) {
>> +                     mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
>> +                     dw_mci_cmd_interrupt(host, status);
>> +             }
>> +
>> +             if (pending & SDMMC_INT_CD) {
>> +                     mci_writel(host, RINTSTS, SDMMC_INT_CD);
>> +                     tasklet_schedule(&host->card_tasklet);
>> +             }
>> +
>> +     } while (pass_count++ < 5);
>
> This could do with a comment, explaining why it's necessary to loop 5
> times.

I'm not sure where that number comes from so I can't really add much
unfortunately. :-/

>> +static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
>> +{
>> +     struct mmc_host *mmc;
>> +     struct dw_mci_slot *slot;
>> +
>> +     mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
>> +     if (!mmc)
>> +             return -ENOMEM;
>> +
>> +     slot = mmc_priv(mmc);
>> +     slot->id = id;
>> +     slot->mmc = mmc;
>> +     slot->host = host;
>> +
>> +     mmc->ops = &dw_mci_ops;
>> +     mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
>
> Is that really supposed to be 510, and not 512?

The maximum divider is 255*2 so 510 is the correct number. Strange but true.

>> +#ifndef _DW_MMC_H_
>> +#define _DW_MMC_H_
>> +
>> +#define MCI_SLOT             0
>
> This appears to be unused.

Yep, removed.

I'll respin the patch and post a v2.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-08 13:14   ` Will Newton
@ 2010-12-08 14:21     ` Will Newton
  2010-12-08 16:07       ` Matt Fleming
  2010-12-09  6:47       ` Chris Ball
  0 siblings, 2 replies; 29+ messages in thread
From: Will Newton @ 2010-12-08 14:21 UTC (permalink / raw)
  To: Linux Kernel list, linux-mmc, Chris Ball, Matt Fleming

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   15 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1806 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  153 ++++
 include/linux/mmc/dw_mmc.h |  149 ++++
 5 files changed, 2124 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

 - patch v2: make changes based on review by Matt Fleming.

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..c99d2c6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.

+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	depends on MMC_DW
+	bool "Internal DMAC interface"
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..7de6b42
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1806 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&dw_mci_regs_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+			&dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc,
+				  struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		/* expect the respond, need to set this bit */
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	/* write to CMDARG register */
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	/* write to CMD register */
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+		     ((data->flags & MMC_DATA_WRITE)
+		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host,
+				    struct mmc_data *data, unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+			SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+				   direction);
+
+	dev_vdbg(&host->pdev->dev,
+		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* disable irq of RX & TX, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the dvide
+			 * to prevent over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			" div = %d)\n",
+			slot->id, host->bus_hz, slot->clock,
+			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	/* no select the proper slot */
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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, lets send the initialization clock */
+	if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
+		cmdflags |= SDMMC_CMD_INIT;
+
+	/* we may need to move this code to mci_start_command */
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_ro != NULL) {
+		read_only = brd->get_ro(slot->id);
+	} else {
+		/* Try on board write protect */
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+	}
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_cd != NULL)
+		present = !brd->get_cd(slot->id);
+	else	/* try onboard card detect */
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+				== 0 ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host,
+				    struct mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pData++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pData++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pData++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (likely(offset + len <= sg->length)) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
+						lets read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (likely(offset + len <= sg->length)) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status,  pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error, lets report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+					present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+						mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01;	/* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled,
+	 * will be enabled when card is detected
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/*useful defaults*/
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	/* Assume card is present initially */
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
+				unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+			&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+				__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialise "
+					"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
+			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET |
+			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (((pdata->num_slots > 1) && !(pdata->select_slot))
+			|| !(pdata->init)) {
+		dev_err(&pdev->dev, "Platform data wrong\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Bus speed undefined in platform data!\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+			"HCON reports a reserved host data width!\n"
+			"Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	 /* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark = fifo_size/2-1,
+	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed ####pd####*/
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * enable interrupt for command done, data over, data empty,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+			"%d bit host data width\n",
+			irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..0ec6856
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,153 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..a037b56
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,149 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned int bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-08 14:21     ` [PATCH] " Will Newton
@ 2010-12-08 16:07       ` Matt Fleming
  2010-12-09  6:47       ` Chris Ball
  1 sibling, 0 replies; 29+ messages in thread
From: Matt Fleming @ 2010-12-08 16:07 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Chris Ball

On Wed, Dec 08, 2010 at 02:21:05PM +0000, Will Newton wrote:
> This adds the mmc host driver for the Synopsys DesignWare mmc
> host controller, found in a number of embedded SoC designs.
> 
> Signed-off-by: Will Newton <will.newton@imgtec.com>
> ---
>  drivers/mmc/host/Kconfig   |   15 +
>  drivers/mmc/host/Makefile  |    1 +
>  drivers/mmc/host/dw_mmc.c  | 1806 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/dw_mmc.h  |  153 ++++
>  include/linux/mmc/dw_mmc.h |  149 ++++
>  5 files changed, 2124 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/mmc/host/dw_mmc.c
>  create mode 100644 drivers/mmc/host/dw_mmc.h
>  create mode 100644 include/linux/mmc/dw_mmc.h
> 
>  - patch v2: make changes based on review by Matt Fleming.

Reviewed-by: Matt Fleming <matt@console-pimps.org>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-08 14:21     ` [PATCH] " Will Newton
  2010-12-08 16:07       ` Matt Fleming
@ 2010-12-09  6:47       ` Chris Ball
  2010-12-09 12:11         ` Will Newton
  1 sibling, 1 reply; 29+ messages in thread
From: Chris Ball @ 2010-12-09  6:47 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Matt Fleming

Hi Will,

Thanks for the submission (and thanks for the review, Matt!).

Some more comments below, mainly stylistic, and at the bottom I've
attached an indentation and cleanup patch.  It's okay if there are
some changes in it that you'd rather not make.

On Wed, Dec 08, 2010 at 02:21:05PM +0000, Will Newton wrote:
> This adds the mmc host driver for the Synopsys DesignWare mmc
> host controller, found in a number of embedded SoC designs.
> 
> Signed-off-by: Will Newton <will.newton@imgtec.com>
> ---
>  drivers/mmc/host/Kconfig   |   15 +
>  drivers/mmc/host/Makefile  |    1 +
>  drivers/mmc/host/dw_mmc.c  | 1806 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/dw_mmc.h  |  153 ++++
>  include/linux/mmc/dw_mmc.h |  149 ++++
>  5 files changed, 2124 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/mmc/host/dw_mmc.c
>  create mode 100644 drivers/mmc/host/dw_mmc.h
>  create mode 100644 include/linux/mmc/dw_mmc.h
> 
>  - patch v2: make changes based on review by Matt Fleming.
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index f8fa9ef..c99d2c6 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
>  	help
>  	  If you say yes here SD-Cards may work on the EZkit.
> 
> +config MMC_DW
> +	tristate "Synopsys DesignWare Memory Card Interface"
> +	help
> +	  This selects support for the Synopsys DesignWare Mobile Storage IP
> +	  block, this provides host support for SD and MMC interfaces, in both
> +	  PIO and external DMA modes.
> +
> +config MMC_DW_IDMAC
> +	depends on MMC_DW
> +	bool "Internal DMAC interface"
> +	help
> +	  This selects support for the internal DMAC block within the Synopsys
> +	  Designware Mobile Storage IP block. This disables the external DMA
> +	  interface.
> +

Is there something we could depend on that would stop this driver being
presented to everyone, without being far too specific?  At the moment
we'd be making x86 desktop users say whether they have this IP, which
isn't good.  Are the architectures that use this IP already upstream?
Are they all ARM architectures, for instance?

>  config MMC_SH_MMCIF
>  	tristate "SuperH Internal MMCIF support"
>  	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index d91364d..a5d1cb2 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -31,6 +31,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_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
>  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
>  obj-$(CONFIG_MMC_USHC)		+= ushc.o
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> new file mode 100644
> index 0000000..7de6b42
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -0,0 +1,1806 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *

Is the NXP driver publicly available?  If not, we can remove this line.
The driver looks like it's based on our atmel-mci.c to me.

It'd be nice if you kerneldoc'd your data structures in the same style as
atmel-mci does, and many of the locking comments in there apply directly
to this driver.

> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.

Hm, what's the relationship between NXP and Imagination here?  If
you're submitting a driver with NXP copyright, I think we should have
a Signed-off-by line from whomever owns the copyright/wrote that code.
If that's you, that's fine, but it's unclear to me.

> + *
> + * 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, or
> + * (at your option) any later version.

My patch snips from here until the end of this comment, to align with the
rest of MMC.  One reason not to include the FSF address in every source
file is that it becomes wrong occasionally, like the address below.  :)

> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/blkdev.h>
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/ioport.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/scatterlist.h>
> +#include <linux/seq_file.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/dw_mmc.h>
> +#include <linux/bitops.h>
> +
> +#include "dw_mmc.h"
> +
> +/* Common flag combinations */
> +#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
> +				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
> +				 SDMMC_INT_EBE)
> +#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
> +				 SDMMC_INT_RESP_ERR)
> +#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
> +				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
> +#define DW_MCI_SEND_STATUS	1
> +#define DW_MCI_RECV_STATUS	2
> +#define DW_MCI_DMA_THRESHOLD	16
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +struct idmac_desc {
> +	u32		des0;	/* Control Descriptor */
> +#define IDMAC_DES0_DIC	BIT(1)
> +#define IDMAC_DES0_LD	BIT(2)
> +#define IDMAC_DES0_FD	BIT(3)
> +#define IDMAC_DES0_CH	BIT(4)
> +#define IDMAC_DES0_ER	BIT(5)
> +#define IDMAC_DES0_CES	BIT(30)
> +#define IDMAC_DES0_OWN	BIT(31)
> +
> +	u32		des1;	/* Buffer sizes */
> +#define IDMAC_SET_BUFFER1_SIZE(d, s) \
> +	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
> +
> +	u32		des2;	/* buffer 1 physical address */
> +
> +	u32		des3;	/* buffer 2 physical address */
> +};
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +struct dw_mci_slot {
> +	struct mmc_host		*mmc;
> +	struct dw_mci		*host;
> +
> +	u32			ctype;
> +
> +	struct mmc_request	*mrq;
> +	struct list_head	queue_node;
> +
> +	unsigned int		clock;
> +	unsigned long		flags;
> +#define DW_MMC_CARD_PRESENT	0
> +#define DW_MMC_CARD_NEED_INIT	1
> +	int			id;
> +	int			last_detect_state;
> +};
> +
> +#if defined(CONFIG_DEBUG_FS)
> +/*
> + * The debugfs stuff below is mostly optimized away when
> + * CONFIG_DEBUG_FS is not set.
> + */

Don't think this comment needs to be here.

> +static int dw_mci_req_show(struct seq_file *s, void *v)
> +{
> +	struct dw_mci_slot *slot = s->private;
> +	struct mmc_request *mrq;
> +	struct mmc_command *cmd;
> +	struct mmc_command *stop;
> +	struct mmc_data	*data;
> +
> +	/* Make sure we get a consistent snapshot */
> +	spin_lock_bh(&slot->host->lock);
> +	mrq = slot->mrq;
> +
> +	if (mrq) {
> +		cmd = mrq->cmd;
> +		data = mrq->data;
> +		stop = mrq->stop;
> +
> +		if (cmd)
> +			seq_printf(s,
> +				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				cmd->opcode, cmd->arg, cmd->flags,
> +				cmd->resp[0], cmd->resp[1], cmd->resp[2],
> +				cmd->resp[2], cmd->error);
> +		if (data)
> +			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
> +				data->bytes_xfered, data->blocks,
> +				data->blksz, data->flags, data->error);
> +		if (stop)
> +			seq_printf(s,
> +				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
> +				stop->opcode, stop->arg, stop->flags,
> +				stop->resp[0], stop->resp[1], stop->resp[2],
> +				stop->resp[2], stop->error);
> +	}
> +
> +	spin_unlock_bh(&slot->host->lock);
> +
> +	return 0;
> +}
> +
> +static int dw_mci_req_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, dw_mci_req_show, inode->i_private);
> +}
> +
> +static const struct file_operations dw_mci_req_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= dw_mci_req_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static int dw_mci_regs_show(struct seq_file *s, void *v)
> +{
> +	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
> +	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
> +	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
> +	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
> +	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
> +	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
> +
> +	return 0;
> +}
> +
> +static int dw_mci_regs_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, dw_mci_regs_show, inode->i_private);
> +}
> +
> +static const struct file_operations dw_mci_regs_fops = {
> +	.owner		= THIS_MODULE,
> +	.open		= dw_mci_regs_open,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= single_release,
> +};
> +
> +static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
> +{
> +	struct mmc_host	*mmc = slot->mmc;
> +	struct dw_mci *host = slot->host;
> +	struct dentry *root;
> +	struct dentry *node;
> +
> +	root = mmc->debugfs_root;
> +	if (!root)
> +		return;
> +
> +	node = debugfs_create_file("regs", S_IRUSR, root, host,
> +			&dw_mci_regs_fops);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_file("req", S_IRUSR, root, slot,
> +			&dw_mci_req_fops);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_x32("pending_events", S_IRUSR, root,
> +				     (u32 *)&host->pending_events);
> +	if (!node)
> +		goto err;
> +
> +	node = debugfs_create_x32("completed_events", S_IRUSR, root,
> +				     (u32 *)&host->completed_events);
> +	if (!node)
> +		goto err;
> +
> +	return;
> +
> +err:
> +	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
> +}
> +#endif /* defined(CONFIG_DEBUG_FS) */
> +
> +static void dw_mci_set_timeout(struct dw_mci *host)
> +{
> +	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
> +}
> +
> +static u32 dw_mci_prepare_command(struct mmc_host *mmc,
> +				  struct mmc_command *cmd)
> +{
> +	struct mmc_data	*data;
> +	u32 cmdr;
> +	cmd->error = -EINPROGRESS;
> +
> +	cmdr = cmd->opcode;
> +
> +	if (cmdr == MMC_STOP_TRANSMISSION)
> +		cmdr |= SDMMC_CMD_STOP;
> +	else
> +		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
> +
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		cmdr |= SDMMC_CMD_RESP_EXP;
> +		/* expect the respond, need to set this bit */

I think the comment's referring to the line above, so it should be
before it.  Also, s/respond/response/, and maybe it could be reworded.

> +		if (cmd->flags & MMC_RSP_136)
> +			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
> +	}
> +
> +	if (cmd->flags & MMC_RSP_CRC)
> +		cmdr |= SDMMC_CMD_RESP_CRC;
> +
> +	data = cmd->data;
> +	if (data) {
> +		cmdr |= SDMMC_CMD_DAT_EXP;
> +		if (data->flags & MMC_DATA_STREAM)
> +			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */

I think both of these comments can be removed as obvious.

> +		if (data->flags & MMC_DATA_WRITE)
> +			cmdr |= SDMMC_CMD_DAT_WR;
> +	}
> +
> +	return cmdr;
> +}
> +
> +static void dw_mci_start_command(struct dw_mci *host,
> +				 struct mmc_command *cmd, u32 cmd_flags)
> +{
> +	host->cmd = cmd;
> +	dev_vdbg(&host->pdev->dev,
> +			"start command: ARGR=0x%08x CMDR=0x%08x\n",
> +			cmd->arg, cmd_flags);
> +
> +	/* write to CMDARG register */
> +	mci_writel(host, CMDARG, cmd->arg);
> +	wmb();
> +
> +	/* write to CMD register */
> +	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);

Again, these comments aren't very useful.  I do approve of comments in
general, honest, but they're supposed to say something that isn't
apparent by just looking at the code.

> +}
> +
> +static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
> +{
> +	dw_mci_start_command(host, data->stop, host->stop_cmdr);
> +}
> +
> +/* DMA interface functions */
> +static void dw_mci_stop_dma(struct dw_mci *host)
> +{
> +	if (host->use_dma) {
> +		host->dma_ops->stop(host);
> +		host->dma_ops->cleanup(host);
> +
> +	} else {
> +		/* Data transfer was stopped by the interrupt handler */
> +		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +	}
> +}
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +static void dw_mci_dma_cleanup(struct dw_mci *host)
> +{
> +	struct mmc_data *data = host->data;
> +
> +	if (data)
> +		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
> +		     ((data->flags & MMC_DATA_WRITE)
> +		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
> +}
> +
> +static void dw_mci_idmac_stop_dma(struct dw_mci *host)
> +{
> +	u32 temp;
> +
> +	/* Disable and reset the IDMAC interface */
> +	temp = mci_readl(host, CTRL);
> +	temp &= ~SDMMC_CTRL_USE_IDMAC;
> +	temp |= SDMMC_CTRL_DMA_RESET;
> +	mci_writel(host, CTRL, temp);
> +
> +	/* Stop the IDMAC running */
> +	temp = mci_readl(host, BMOD);
> +	temp &= ~SDMMC_IDMAC_ENABLE;
> +	mci_writel(host, BMOD, temp);
> +}
> +
> +static void dw_mci_idmac_complete_dma(struct dw_mci *host)
> +{
> +	struct mmc_data *data = host->data;
> +
> +	dev_vdbg(&host->pdev->dev, "DMA complete\n");
> +
> +	host->dma_ops->cleanup(host);
> +
> +	/*
> +	 * If the card was removed, data will be NULL. No point in trying to
> +	 * send the stop command or waiting for NBUSY in this case.
> +	 */
> +	if (data) {
> +		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +		tasklet_schedule(&host->tasklet);
> +	}
> +}
> +
> +static void dw_mci_translate_sglist(struct dw_mci *host,
> +				    struct mmc_data *data, unsigned int sg_len)
> +{
> +	int i;
> +	struct idmac_desc *desc = host->sg_cpu;
> +
> +	for (i = 0; i < sg_len; i++, desc++) {
> +		unsigned int length = sg_dma_len(&data->sg[i]);
> +		u32 mem_addr = sg_dma_address(&data->sg[i]);
> +
> +		/* Set the OWN bit and disable interrupts for this descriptor */
> +		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
> +
> +		/* Buffer length */
> +		IDMAC_SET_BUFFER1_SIZE(desc, length);
> +
> +		/* Physical address to DMA to/from */
> +		desc->des2 = mem_addr;
> +	}
> +
> +	/* Set first descriptor */
> +	desc = host->sg_cpu;
> +	desc->des0 |= IDMAC_DES0_FD;
> +
> +	/* Set last descriptor */
> +	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
> +	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
> +	desc->des0 |= IDMAC_DES0_LD;
> +
> +	wmb();
> +}
> +
> +static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
> +{
> +	u32 temp;
> +
> +	dw_mci_translate_sglist(host, host->data, sg_len);
> +
> +	/* Select IDMAC interface */
> +	temp = mci_readl(host, CTRL);
> +	temp |= SDMMC_CTRL_USE_IDMAC;
> +	mci_writel(host, CTRL, temp);
> +
> +	wmb();
> +
> +	/* Enable the IDMAC */
> +	temp = mci_readl(host, BMOD);
> +	temp |= SDMMC_IDMAC_ENABLE;
> +	mci_writel(host, BMOD, temp);
> +
> +	/* Start it running */
> +	mci_writel(host, PLDMND, 1);
> +}
> +
> +static int dw_mci_idmac_init(struct dw_mci *host)
> +{
> +	struct idmac_desc *p;
> +	int i;
> +
> +	/* Number of descriptors in the ring buffer */
> +	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
> +
> +	/* Forward link the descriptor list */
> +	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
> +		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
> +
> +	/* Set the last descriptor as the end-of-ring descriptor */
> +	p->des3 = host->sg_dma;
> +	p->des0 = IDMAC_DES0_ER;
> +
> +	/* Mask out interrupts - get Tx & Rx complete only */
> +	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
> +			SDMMC_IDMAC_INT_TI);
> +
> +	/* Set the descriptor base address */
> +	mci_writel(host, DBADDR, host->sg_dma);
> +	return 0;
> +}
> +
> +static struct dw_mci_dma_ops dw_mci_idmac_ops = {
> +	.init = dw_mci_idmac_init,
> +	.start = dw_mci_idmac_start_dma,
> +	.stop = dw_mci_idmac_stop_dma,
> +	.complete = dw_mci_idmac_complete_dma,
> +	.cleanup = dw_mci_dma_cleanup,
> +};
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
> +{
> +	struct scatterlist *sg;
> +	unsigned int i, direction, sg_len;
> +	u32 temp;
> +
> +	/* If we don't have a channel, we can't do DMA */
> +	if (!host->use_dma)
> +		return -ENODEV;
> +
> +	/*
> +	 * We don't do DMA on "complex" transfers, i.e. with
> +	 * non-word-aligned buffers or lengths. Also, we don't bother
> +	 * with all the DMA setup overhead for short transfers.
> +	 */
> +	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
> +		return -EINVAL;
> +	if (data->blksz & 3)
> +		return -EINVAL;
> +
> +	for_each_sg(data->sg, sg, data->sg_len, i) {
> +		if (sg->offset & 3 || sg->length & 3)
> +			return -EINVAL;
> +	}
> +
> +	if (data->flags & MMC_DATA_READ)
> +		direction = DMA_FROM_DEVICE;
> +	else
> +		direction = DMA_TO_DEVICE;
> +
> +	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
> +				   direction);
> +
> +	dev_vdbg(&host->pdev->dev,
> +		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
> +		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
> +		 sg_len);
> +
> +	/* Enable the DMA interface */
> +	temp = mci_readl(host, CTRL);
> +	temp |= SDMMC_CTRL_DMA_ENABLE;
> +	mci_writel(host, CTRL, temp);
> +
> +	/* disable irq of RX & TX, let DMA handle it */
> +	temp = mci_readl(host, INTMASK);
> +	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
> +	mci_writel(host, INTMASK, temp);
> +
> +	host->dma_ops->start(host, sg_len);
> +
> +	return 0;
> +}
> +
> +static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
> +{
> +	u32 temp;
> +
> +	data->error = -EINPROGRESS;
> +
> +	WARN_ON(host->data);
> +	host->sg = NULL;
> +	host->data = data;
> +
> +	if (dw_mci_submit_data_dma(host, data)) {
> +		host->sg = data->sg;
> +		host->pio_offset = 0;
> +		if (data->flags & MMC_DATA_READ)
> +			host->dir_status = DW_MCI_RECV_STATUS;
> +		else
> +			host->dir_status = DW_MCI_SEND_STATUS;
> +
> +		temp = mci_readl(host, INTMASK);
> +		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
> +		mci_writel(host, INTMASK, temp);
> +
> +		temp = mci_readl(host, CTRL);
> +		temp &= ~SDMMC_CTRL_DMA_ENABLE;
> +		mci_writel(host, CTRL, temp);
> +	}
> +}
> +
> +static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
> +{
> +	struct dw_mci *host = slot->host;
> +	unsigned long timeout = jiffies + msecs_to_jiffies(500);
> +	unsigned int cmd_status = 0;
> +
> +	mci_writel(host, CMDARG, arg);
> +	wmb();
> +	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
> +
> +	while (time_before(jiffies, timeout)) {
> +		cmd_status = mci_readl(host, CMD);
> +		if (!(cmd_status & SDMMC_CMD_START))
> +			return;
> +	}
> +	dev_err(&slot->mmc->class_dev,
> +		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
> +		cmd, arg, cmd_status);
> +}
> +
> +static void dw_mci_setup_bus(struct dw_mci_slot *slot)
> +{
> +	struct dw_mci *host = slot->host;
> +	u32 div;
> +
> +	if (slot->clock != host->current_speed) {
> +		if (host->bus_hz % slot->clock)
> +			/*
> +			 * move the + 1 after the dvide
> +			 * to prevent over-clocking the card.
> +			 */
> +			div = ((host->bus_hz / slot->clock) >> 1) + 1;
> +		else
> +			div = (host->bus_hz  / slot->clock) >> 1;
> +
> +		dev_info(&slot->mmc->class_dev,
> +			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
> +			" div = %d)\n",
> +			slot->id, host->bus_hz, slot->clock,
> +			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
> +
> +		/* disable clock */
> +		mci_writel(host, CLKENA, 0);
> +		mci_writel(host, CLKSRC, 0);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		/* set clock to desired speed */
> +		mci_writel(host, CLKDIV, div);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		/* enable clock */
> +		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
> +
> +		/* inform CIU */
> +		mci_send_cmd(slot,
> +			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
> +
> +		host->current_speed = slot->clock;
> +	}
> +
> +	/* Set the current slot bus width */
> +	mci_writel(host, CTYPE, slot->ctype);
> +}
> +
> +static void dw_mci_start_request(struct dw_mci *host,
> +				 struct dw_mci_slot *slot)
> +{
> +	struct mmc_request *mrq;
> +	struct mmc_command *cmd;
> +	struct mmc_data	*data;
> +	u32 cmdflags;
> +
> +	mrq = slot->mrq;
> +	/* no select the proper slot */
> +	if (host->pdata->select_slot)
> +		host->pdata->select_slot(slot->id);

Don't understand this comment.

> +
> +	/* Slot specific timing and width adjustment */
> +	dw_mci_setup_bus(slot);
> +
> +	host->cur_slot = slot;
> +	host->mrq = mrq;
> +
> +	host->pending_events = 0;
> +	host->completed_events = 0;
> +	host->data_status = 0;
> +
> +	data = mrq->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, lets send the initialization clock */

I think you mean:
	/* If this is the first command, send the initialization clock /*

> +	if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
> +		cmdflags |= SDMMC_CMD_INIT;
> +
> +	/* we may need to move this code to mci_start_command */

Has this question been resolved yet?

> +	if (data) {
> +		dw_mci_submit_data(host, data);
> +		wmb();
> +	}
> +
> +	dw_mci_start_command(host, cmd, cmdflags);
> +
> +	if (mrq->stop)
> +		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
> +}
> +
> +static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
> +				 struct mmc_request *mrq)
> +{
> +	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
> +			host->state);
> +
> +	spin_lock_bh(&host->lock);
> +	slot->mrq = mrq;
> +
> +	if (host->state == STATE_IDLE) {
> +		host->state = STATE_SENDING_CMD;
> +		dw_mci_start_request(host, slot);
> +	} else {
> +		list_add_tail(&slot->queue_node, &host->queue);
> +	}
> +
> +	spin_unlock_bh(&host->lock);
> +}
> +
> +static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci *host = slot->host;
> +
> +	WARN_ON(slot->mrq);
> +
> +	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
> +		mrq->cmd->error = -ENOMEDIUM;
> +		mmc_request_done(mmc, mrq);
> +		return;
> +	}
> +
> +	/* We don't support multiple blocks of weird lengths. */
> +	dw_mci_queue_request(host, slot, mrq);
> +}
> +
> +static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +
> +	/* set default 1 bit mode */
> +	slot->ctype = SDMMC_CTYPE_1BIT;
> +
> +	switch (ios->bus_width) {
> +	case MMC_BUS_WIDTH_1:
> +		slot->ctype = SDMMC_CTYPE_1BIT;
> +		break;
> +	case MMC_BUS_WIDTH_4:
> +		slot->ctype = SDMMC_CTYPE_4BIT;
> +		break;
> +	}
> +
> +	if (ios->clock) {
> +		/*
> +		 * Use mirror of ios->clock to prevent race with mmc
> +		 * core ios update when finding the minimum.
> +		 */
> +		slot->clock = ios->clock;
> +	}
> +
> +	switch (ios->power_mode) {
> +	case MMC_POWER_UP:
> +		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +static int dw_mci_get_ro(struct mmc_host *mmc)
> +{
> +	int read_only;
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci_board *brd = slot->host->pdata;
> +
> +	if (brd->get_ro != NULL) {

!= NULL isn't necessary here.

> +		read_only = brd->get_ro(slot->id);
> +	} else {
> +		/* Try on board write protect */
> +		read_only =
> +			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
> +	}
> +
> +	dev_dbg(&mmc->class_dev, "card is %s\n",
> +				read_only ? "read-only" : "read-write");
> +
> +	return read_only;
> +}
> +
> +static int dw_mci_get_cd(struct mmc_host *mmc)
> +{
> +	int present;
> +	struct dw_mci_slot *slot = mmc_priv(mmc);
> +	struct dw_mci_board *brd = slot->host->pdata;
> +
> +	if (brd->get_cd != NULL)

(Same.)

> +		present = !brd->get_cd(slot->id);
> +	else	/* try onboard card detect */
> +		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
> +				== 0 ? 1 : 0;
> +
> +	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");

I expanded this, since being able to grep for "not present" has some value.

> +
> +	return present;
> +}
> +
> +static const struct mmc_host_ops dw_mci_ops = {
> +	.request	= dw_mci_request,
> +	.set_ios	= dw_mci_set_ios,
> +	.get_ro		= dw_mci_get_ro,
> +	.get_cd		= dw_mci_get_cd,
> +};
> +
> +static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
> +	__releases(&host->lock)
> +	__acquires(&host->lock)
> +{
> +	struct dw_mci_slot *slot;
> +	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
> +
> +	WARN_ON(host->cmd || host->data);
> +
> +	host->cur_slot->mrq = NULL;
> +	host->mrq = NULL;
> +	if (!list_empty(&host->queue)) {
> +		slot = list_entry(host->queue.next,
> +				struct dw_mci_slot, queue_node);
> +		list_del(&slot->queue_node);
> +		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
> +				mmc_hostname(slot->mmc));
> +		host->state = STATE_SENDING_CMD;
> +		dw_mci_start_request(host, slot);
> +	} else {
> +		dev_vdbg(&host->pdev->dev, "list empty\n");
> +		host->state = STATE_IDLE;
> +	}
> +
> +	spin_unlock(&host->lock);
> +	mmc_request_done(prev_mmc, mrq);
> +
> +	spin_lock(&host->lock);
> +}
> +
> +static void dw_mci_command_complete(struct dw_mci *host,
> +				    struct mmc_command *cmd)
> +{
> +	u32 status = host->cmd_status;
> +
> +	host->cmd_status = 0;
> +
> +	/* Read the response from the card (up to 16 bytes) */
> +	if (cmd->flags & MMC_RSP_PRESENT) {
> +		if (cmd->flags & MMC_RSP_136) {
> +			cmd->resp[3] = mci_readl(host, RESP0);
> +			cmd->resp[2] = mci_readl(host, RESP1);
> +			cmd->resp[1] = mci_readl(host, RESP2);
> +			cmd->resp[0] = mci_readl(host, RESP3);

It'd be nice to have a comment explaining why this is in reverse
order, yet if RSP_136 is set we use RESP0 instead of RESP3.

> +		} else {
> +			cmd->resp[0] = mci_readl(host, RESP0);
> +			cmd->resp[1] = 0;
> +			cmd->resp[2] = 0;
> +			cmd->resp[3] = 0;
> +		}
> +	}
> +
> +	if (status & SDMMC_INT_RTO)
> +		cmd->error = -ETIMEDOUT;
> +	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
> +		cmd->error = -EILSEQ;
> +	else if (status & SDMMC_INT_RESP_ERR)
> +		cmd->error = -EIO;
> +	else
> +		cmd->error = 0;
> +
> +	if (cmd->error) {
> +		/* newer ip versions need a delay between retries */
> +		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
> +			mdelay(20);
> +
> +		if (cmd->data) {
> +			host->data = NULL;
> +			dw_mci_stop_dma(host);
> +		}
> +	}
> +}
> +
> +static void dw_mci_tasklet_func(unsigned long priv)
> +{
> +	struct dw_mci *host = (struct dw_mci *)priv;
> +	struct mmc_data	*data;
> +	struct mmc_command *cmd;
> +	enum dw_mci_state state;
> +	enum dw_mci_state prev_state;
> +	u32 status;
> +
> +	spin_lock(&host->lock);
> +
> +	state = host->state;
> +	data = host->data;
> +
> +	do {
> +		prev_state = state;
> +
> +		switch (state) {
> +		case STATE_IDLE:
> +			break;
> +
> +		case STATE_SENDING_CMD:
> +			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			cmd = host->cmd;
> +			host->cmd = NULL;
> +			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
> +			dw_mci_command_complete(host, host->mrq->cmd);
> +			if (!host->mrq->data || cmd->error) {
> +				dw_mci_request_end(host, host->mrq);
> +				goto unlock;
> +			}
> +
> +			prev_state = state = STATE_SENDING_DATA;
> +			/* fall through */
> +
> +		case STATE_SENDING_DATA:
> +			if (test_and_clear_bit(EVENT_DATA_ERROR,
> +					       &host->pending_events)) {
> +				dw_mci_stop_dma(host);
> +				if (data->stop)
> +					send_stop_cmd(host, data);
> +				state = STATE_DATA_ERROR;
> +				break;
> +			}
> +
> +			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
> +			prev_state = state = STATE_DATA_BUSY;
> +			/* fall through */
> +
> +		case STATE_DATA_BUSY:
> +			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			host->data = NULL;
> +			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
> +			status = host->data_status;
> +
> +			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
> +				if (status & SDMMC_INT_DTO) {
> +					dev_err(&host->pdev->dev,
> +							"data timeout error\n");
> +					data->error = -ETIMEDOUT;
> +				} else if (status & SDMMC_INT_DCRC) {
> +					dev_err(&host->pdev->dev,
> +							"data CRC error\n");
> +					data->error = -EILSEQ;
> +				} else {
> +					dev_err(&host->pdev->dev,
> +						"data FIFO error "
> +						"(status=%08x)\n",
> +						status);
> +					data->error = -EIO;
> +				}
> +			} else {
> +				data->bytes_xfered = data->blocks * data->blksz;
> +				data->error = 0;
> +			}
> +
> +			if (!data->stop) {
> +				dw_mci_request_end(host, host->mrq);
> +				goto unlock;
> +			}
> +
> +			prev_state = state = STATE_SENDING_STOP;
> +			if (!data->error)
> +				send_stop_cmd(host, data);
> +			/* fall through */
> +
> +		case STATE_SENDING_STOP:
> +			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			host->cmd = NULL;
> +			dw_mci_command_complete(host, host->mrq->stop);
> +			dw_mci_request_end(host, host->mrq);
> +			goto unlock;
> +
> +		case STATE_DATA_ERROR:
> +			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
> +						&host->pending_events))
> +				break;
> +
> +			state = STATE_DATA_BUSY;
> +			break;
> +		}
> +	} while (state != prev_state);
> +
> +	host->state = state;
> +unlock:
> +	spin_unlock(&host->lock);
> +
> +}
> +
> +static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u16 *pData = (u16 *)buf;

Changed to pdata.

> +
> +	WARN_ON(cnt % 2 != 0);
> +
> +	cnt = cnt >> 1;
> +	while (cnt > 0) {
> +		mci_writew(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u16 *pData = (u16 *)buf;
> +
> +	WARN_ON(cnt % 2 != 0);
> +
> +	cnt = cnt >> 1;
> +	while (cnt > 0) {
> +		*pData++ = mci_readw(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u32 *pData = (u32 *)buf;
> +
> +	WARN_ON(cnt % 4 != 0);
> +	WARN_ON((unsigned long)pData & 0x3);
> +
> +	cnt = cnt >> 2;
> +	while (cnt > 0) {
> +		mci_writel(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u32 *pData = (u32 *)buf;
> +
> +	WARN_ON(cnt % 4 != 0);
> +	WARN_ON((unsigned long)pData & 0x3);
> +
> +	cnt = cnt >> 2;
> +	while (cnt > 0) {
> +		*pData++ = mci_readl(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u64 *pData = (u64 *)buf;
> +
> +	WARN_ON(cnt % 8 != 0);
> +
> +	cnt = cnt >> 3;
> +	while (cnt > 0) {
> +		mci_writeq(host, DATA, *pData++);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
> +{
> +	u64 *pData = (u64 *)buf;
> +
> +	WARN_ON(cnt % 8 != 0);
> +
> +	cnt = cnt >> 3;
> +	while (cnt > 0) {
> +		*pData++ = mci_readq(host, DATA);
> +		cnt--;
> +	}
> +}
> +
> +static void dw_mci_read_data_pio(struct dw_mci *host)
> +{
> +	struct scatterlist *sg = host->sg;
> +	void *buf = sg_virt(sg);
> +	unsigned int offset = host->pio_offset;
> +	struct mmc_data	*data = host->data;
> +	int shift = host->data_shift;
> +	u32 status;
> +	unsigned int nbytes = 0, len, old_len, count = 0;
> +
> +	do {
> +		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
> +		if (count == 0)
> +			old_len = len;
> +
> +		if (likely(offset + len <= sg->length)) {

I agree with Matt, the unlikely/likely aren't necessary.

> +			host->pull_data(host, (void *)(buf + offset), len);
> +
> +			offset += len;
> +			nbytes += len;
> +
> +			if (offset == sg->length) {
> +				flush_dcache_page(sg_page(sg));
> +				host->sg = sg = sg_next(sg);
> +				if (!sg)
> +					goto done;
> +
> +				offset = 0;
> +				buf = sg_virt(sg);
> +			}
> +		} else {
> +			unsigned int remaining = sg->length - offset;
> +			host->pull_data(host,
> +				(void *)(buf + offset), remaining);
> +			nbytes += remaining;
> +
> +			flush_dcache_page(sg_page(sg));
> +			host->sg = sg = sg_next(sg);
> +			if (!sg)
> +				goto done;
> +
> +			offset = len - remaining;
> +			buf = sg_virt(sg);
> +			host->pull_data(host, buf, offset);
> +			nbytes += offset;
> +		}
> +
> +		status = mci_readl(host, MINTSTS);
> +		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
> +		if (status & DW_MCI_DATA_ERROR_FLAGS) {
> +			host->data_status = status;
> +			data->bytes_xfered += nbytes;
> +			smp_wmb();
> +
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +
> +			tasklet_schedule(&host->tasklet);
> +			return;
> +		}
> +		count++;
> +	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
> +						lets read again*/
> +	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
> +	host->pio_offset = offset;
> +	data->bytes_xfered += nbytes;
> +	return;
> +
> +done:
> +	data->bytes_xfered += nbytes;
> +	smp_wmb();
> +	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +}
> +
> +static void dw_mci_write_data_pio(struct dw_mci *host)
> +{
> +	struct scatterlist *sg = host->sg;
> +	void *buf = sg_virt(sg);
> +	unsigned int offset = host->pio_offset;
> +	struct mmc_data	*data = host->data;
> +	int shift = host->data_shift;
> +	u32 status;
> +	unsigned int nbytes = 0, len;
> +
> +	do {
> +		len = SDMMC_FIFO_SZ -
> +			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
> +		if (likely(offset + len <= sg->length)) {
> +			host->push_data(host, (void *)(buf + offset), len);
> +
> +			offset += len;
> +			nbytes += len;
> +			if (offset == sg->length) {
> +				host->sg = sg = sg_next(sg);
> +				if (!sg)
> +					goto done;
> +
> +				offset = 0;
> +				buf = sg_virt(sg);
> +			}
> +		} else {
> +			unsigned int remaining = sg->length - offset;
> +
> +			host->push_data(host,
> +				(void *)(buf + offset), remaining);
> +			nbytes += remaining;
> +
> +			host->sg = sg = sg_next(sg);
> +			if (!sg)
> +				goto done;
> +
> +			offset = len - remaining;
> +			buf = sg_virt(sg);
> +			host->push_data(host, (void *)buf, offset);
> +			nbytes += offset;
> +		}
> +
> +		status = mci_readl(host, MINTSTS);
> +		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
> +		if (status & DW_MCI_DATA_ERROR_FLAGS) {
> +			host->data_status = status;
> +			data->bytes_xfered += nbytes;
> +
> +			smp_wmb();
> +
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +
> +			tasklet_schedule(&host->tasklet);
> +			return;
> +		}
> +	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
> +
> +	host->pio_offset = offset;
> +	data->bytes_xfered += nbytes;
> +
> +	return;
> +
> +done:
> +	data->bytes_xfered += nbytes;
> +	smp_wmb();
> +	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
> +}
> +
> +static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
> +{
> +	if (!host->cmd_status)
> +		host->cmd_status = status;
> +
> +	smp_wmb();
> +
> +	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> +	tasklet_schedule(&host->tasklet);
> +}
> +
> +static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
> +{
> +	struct dw_mci *host = dev_id;
> +	u32 status,  pending;
> +	unsigned int pass_count = 0;
> +
> +	do {
> +		status = mci_readl(host, RINTSTS);
> +		pending = mci_readl(host, MINTSTS);/* read only mask reg */
> +
> +		/*
> +		 * DTO fix - version 2.10a and below, and only if internal DMA
> +		 * is configured.
> +		 */
> +		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
> +			if (!pending &&
> +			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
> +				pending |= SDMMC_INT_DATA_OVER;
> +		}
> +
> +		if (!pending)
> +			break;
> +
> +		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
> +			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
> +			host->cmd_status = status;
> +			smp_wmb();
> +			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
> +			/* if there is an error, lets report DATA_ERROR */

The "lets" were ungrammatical and didn't add much, so I removed them.

> +			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
> +			host->data_status = status;
> +			smp_wmb();
> +			set_bit(EVENT_DATA_ERROR, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +
> +		if (pending & SDMMC_INT_DATA_OVER) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
> +			if (!host->data_status)
> +				host->data_status = status;
> +			smp_wmb();
> +			if (host->dir_status == DW_MCI_RECV_STATUS) {
> +				if (host->sg != NULL)
> +					dw_mci_read_data_pio(host);
> +			}
> +			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> +			tasklet_schedule(&host->tasklet);
> +		}
> +
> +		if (pending & SDMMC_INT_RXDR) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
> +			if (host->sg)
> +				dw_mci_read_data_pio(host);
> +		}
> +
> +		if (pending & SDMMC_INT_TXDR) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
> +			if (host->sg)
> +				dw_mci_write_data_pio(host);
> +		}
> +
> +		if (pending & SDMMC_INT_CMD_DONE) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
> +			dw_mci_cmd_interrupt(host, status);
> +		}
> +
> +		if (pending & SDMMC_INT_CD) {
> +			mci_writel(host, RINTSTS, SDMMC_INT_CD);
> +			tasklet_schedule(&host->card_tasklet);
> +		}
> +
> +	} while (pass_count++ < 5);
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	/* Handle DMA interrupts */
> +	pending = mci_readl(host, IDSTS);
> +	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
> +		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
> +				SDMMC_IDMAC_INT_RI);
> +		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
> +		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
> +		host->dma_ops->complete(host);
> +	}
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void dw_mci_tasklet_card(unsigned long data)
> +{
> +	struct dw_mci *host = (struct dw_mci *)data;
> +	int i;
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		struct mmc_host *mmc = slot->mmc;
> +		struct mmc_request *mrq;
> +		int present;
> +		u32 ctrl;
> +
> +		present = dw_mci_get_cd(mmc);
> +		while (present != slot->last_detect_state) {
> +			spin_lock(&host->lock);
> +
> +			dev_dbg(&slot->mmc->class_dev, "card %s\n",
> +					present ? "inserted" : "removed");
> +
> +			/* Card change detected */
> +			slot->last_detect_state = present;
> +
> +			/* Power up slot */
> +			if (present != 0) {
> +				if (host->pdata->setpower)
> +					host->pdata->setpower(slot->id,
> +						mmc->ocr_avail);
> +
> +				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +			}
> +
> +			/* Clean up queue if present */
> +			mrq = slot->mrq;
> +			if (mrq) {
> +				if (mrq == host->mrq) {
> +					host->data = NULL;
> +					host->cmd = NULL;
> +
> +					switch (host->state) {
> +					case STATE_IDLE:
> +						break;
> +					case STATE_SENDING_CMD:
> +						mrq->cmd->error = -ENOMEDIUM;
> +						if (!mrq->data)
> +							break;
> +						/* fall through */
> +					case STATE_SENDING_DATA:
> +						mrq->data->error = -ENOMEDIUM;
> +						dw_mci_stop_dma(host);
> +						break;
> +					case STATE_DATA_BUSY:
> +					case STATE_DATA_ERROR:
> +						if (mrq->data->error == -EINPROGRESS)
> +							mrq->data->error = -ENOMEDIUM;
> +						if (!mrq->stop)
> +							break;
> +						/* fall through */
> +					case STATE_SENDING_STOP:
> +						mrq->stop->error = -ENOMEDIUM;
> +						break;
> +					}
> +
> +					dw_mci_request_end(host, mrq);
> +				} else {
> +					list_del(&slot->queue_node);
> +					mrq->cmd->error = -ENOMEDIUM;
> +					if (mrq->data)
> +						mrq->data->error = -ENOMEDIUM;
> +					if (mrq->stop)
> +						mrq->stop->error = -ENOMEDIUM;
> +
> +					spin_unlock(&host->lock);
> +					mmc_request_done(slot->mmc, mrq);
> +					spin_lock(&host->lock);
> +				}
> +			}
> +
> +			/* Power down slot */
> +			if (present == 0) {
> +				if (host->pdata->setpower)
> +					host->pdata->setpower(slot->id, 0);
> +				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +
> +				/*
> +				 * Clear down the FIFO - doing so generates a
> +				 * block interrupt, hence setting the
> +				 * scatter-gather pointer to NULL.
> +				 */
> +				host->sg = NULL;
> +
> +				ctrl = mci_readl(host, CTRL);
> +				ctrl |= SDMMC_CTRL_FIFO_RESET;
> +				mci_writel(host, CTRL, ctrl);
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +				ctrl = mci_readl(host, BMOD);
> +				ctrl |= 0x01;	/* Software reset of DMA */
> +				mci_writel(host, BMOD, ctrl);
> +#endif
> +
> +			}
> +
> +			spin_unlock(&host->lock);
> +			present = dw_mci_get_cd(mmc);
> +		}
> +
> +		mmc_detect_change(slot->mmc,
> +			msecs_to_jiffies(host->pdata->detect_delay_ms));
> +	}
> +}
> +
> +static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
> +{
> +	struct mmc_host *mmc;
> +	struct dw_mci_slot *slot;
> +
> +	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
> +	if (!mmc)
> +		return -ENOMEM;
> +
> +	slot = mmc_priv(mmc);
> +	slot->id = id;
> +	slot->mmc = mmc;
> +	slot->host = host;
> +
> +	mmc->ops = &dw_mci_ops;
> +	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
> +	mmc->f_max = host->bus_hz;
> +
> +	if (host->pdata->get_ocr)
> +		mmc->ocr_avail = host->pdata->get_ocr(id);
> +	else
> +		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
> +
> +	/*
> +	 * Start with slot power disabled,
> +	 * will be enabled when card is detected
> +	 */
> +	if (host->pdata->setpower)
> +		host->pdata->setpower(id, 0);
> +
> +	mmc->caps = 0;
> +	if (host->pdata->get_bus_wd)
> +		if (host->pdata->get_bus_wd(slot->id) >= 4)
> +			mmc->caps |= MMC_CAP_4_BIT_DATA;
> +
> +	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
> +		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
> +
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	mmc->max_segs = host->ring_size;
> +	mmc->max_blk_size = 65536;
> +	mmc->max_blk_count = host->ring_size;
> +	mmc->max_seg_size = 0x1000;
> +	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
> +#else
> +	if (host->pdata->blk_settings) {
> +		mmc->max_segs = host->pdata->blk_settings->max_segs;
> +		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
> +		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
> +		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
> +		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
> +	} else {
> +		/*useful defaults*/
> +		mmc->max_segs = 64;
> +		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
> +		mmc->max_blk_count = 512;
> +		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
> +		mmc->max_seg_size = mmc->max_req_size;
> +	}
> +#endif /* CONFIG_MMC_DW_IDMAC */
> +
> +	/* Assume card is present initially */
> +	if (dw_mci_get_cd(mmc))
> +		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
> +	else
> +		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);

Hm, does the code actually make that assumption?  Won't dw_mci_get_cd()
return an appropriate value, and have the present bit cleared if so?

> +
> +	host->slot[id] = slot;
> +	mmc_add_host(mmc);
> +
> +#if defined(CONFIG_DEBUG_FS)
> +	dw_mci_init_debugfs(slot);
> +#endif
> +
> +	/* Card initially undetected */
> +	slot->last_detect_state = 0;
> +
> +	return 0;
> +}
> +
> +static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
> +				unsigned int id)
> +{
> +	/* Shutdown detect IRQ */
> +	if (slot->host->pdata->exit)
> +		slot->host->pdata->exit(id);
> +
> +	/* Debugfs stuff is cleaned up by mmc core */
> +	mmc_remove_host(slot->mmc);
> +	slot->host->slot[id] = NULL;
> +	mmc_free_host(slot->mmc);
> +}
> +
> +static void dw_mci_init_dma(struct dw_mci *host)
> +{
> +	/* Alloc memory for sg translation */
> +	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
> +			&host->sg_dma, GFP_KERNEL);
> +	if (!host->sg_cpu) {
> +		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
> +				__func__);
> +		goto no_dma;
> +	}
> +
> +	/* Determine which DMA interface to use */
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	host->dma_ops = &dw_mci_idmac_ops;
> +	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
> +#endif
> +
> +	if (!host->dma_ops)
> +		goto no_dma;
> +
> +	if (host->dma_ops->init) {
> +		if (host->dma_ops->init(host)) {
> +			dev_err(&host->pdev->dev, "%s: Unable to initialise "
> +					"DMA Controller.\n", __func__);
> +			goto no_dma;
> +		}
> +	} else {
> +		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
> +		goto no_dma;
> +	}
> +
> +	host->use_dma = 1;
> +	return;
> +
> +no_dma:
> +	dev_info(&host->pdev->dev, "Using PIO mode.\n");
> +	host->use_dma = 0;
> +	return;
> +}
> +
> +static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
> +{
> +	unsigned long timeout = jiffies + msecs_to_jiffies(500);
> +	unsigned int ctrl;
> +
> +	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
> +			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
> +
> +	/* wait till resets clear */
> +	do {
> +		ctrl = mci_readl(host, CTRL);
> +		if (!(ctrl & (SDMMC_CTRL_RESET |
> +			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
> +			return true;
> +	} while (time_before(jiffies, timeout));
> +
> +	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
> +
> +	return false;
> +}
> +
> +static int dw_mci_probe(struct platform_device *pdev)
> +{
> +	struct dw_mci *host;
> +	struct resource	*regs;
> +	struct dw_mci_board *pdata;
> +	int irq, ret, i, width;
> +	u32 fifo_size;
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!regs)
> +		return -ENXIO;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		return irq;
> +
> +	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
> +	if (!host)
> +		return -ENOMEM;
> +
> +	host->pdev = pdev;
> +	host->pdata = pdata = pdev->dev.platform_data;
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "Platform data missing\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	if (((pdata->num_slots > 1) && !(pdata->select_slot))
> +			|| !(pdata->init)) {
> +		dev_err(&pdev->dev, "Platform data wrong\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}

"Platform data wrong" is pretty unhelpful -- perhaps you could have the
first test be (!pdata || !pdata->init), and then the second test can
explain that num_slots is > 1 without a slot selected.

> +
> +	if (!pdata->bus_hz) {
> +		dev_err(&pdev->dev,
> +			"Bus speed undefined in platform data!\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	host->bus_hz = pdata->bus_hz;
> +	host->quirks = pdata->quirks;
> +
> +	spin_lock_init(&host->lock);
> +	INIT_LIST_HEAD(&host->queue);
> +
> +	ret = -ENOMEM;
> +	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
> +	if (!host->regs)
> +		goto err_freehost;
> +
> +	host->dma_ops = pdata->dma_ops;
> +	dw_mci_init_dma(host);
> +
> +	/*
> +	 * Get the host data width - this assumes that HCON has been set with
> +	 * the correct values.
> +	 */
> +	i = (mci_readl(host, HCON) >> 7) & 0x7;
> +	if (!i) {
> +		host->push_data = dw_mci_push_data16;
> +		host->pull_data = dw_mci_pull_data16;
> +		width = 16;
> +		host->data_shift = 1;
> +	} else if (i == 2) {
> +		host->push_data = dw_mci_push_data64;
> +		host->pull_data = dw_mci_pull_data64;
> +		width = 64;
> +		host->data_shift = 3;
> +	} else {
> +		/* Check for a reserved value, and warn if it is */
> +		WARN((i != 1),
> +			"HCON reports a reserved host data width!\n"
> +			"Defaulting to 32-bit access.\n");
> +		host->push_data = dw_mci_push_data32;
> +		host->pull_data = dw_mci_pull_data32;
> +		width = 32;
> +		host->data_shift = 2;
> +	}
> +
> +	/* reset all blocks */
> +	if (!mci_wait_reset(&pdev->dev, host)) {
> +		ret = -ENODEV;
> +		goto err_dmaunmap;
> +	}
> +
> +	 /* Clear the interrupts for the host controller */
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
> +
> +	/* Put in max timeout */
> +	mci_writel(host, TMOUT, 0xFFFFFFFF);
> +
> +	/*
> +	 * FIFO threshold settings  RxMark = fifo_size/2-1,
> +	 *                          Tx Mark =fifo_size/2 DMA Size = 8
> +	 */
> +	fifo_size = mci_readl(host, FIFOTH);
> +	fifo_size = (fifo_size >> 16) & 0x7ff;
> +	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
> +				((fifo_size/2) << 0)));
> +
> +	/* disable clock to CIU */
> +	mci_writel(host, CLKENA, 0);
> +	mci_writel(host, CLKSRC, 0);
> +
> +	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
> +	tasklet_init(&host->card_tasklet,
> +		dw_mci_tasklet_card, (unsigned long)host);
> +
> +	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
> +	if (ret)
> +		goto err_dmaunmap;
> +
> +	platform_set_drvdata(pdev, host);
> +
> +	if (host->pdata->num_slots)
> +		host->num_slots = host->pdata->num_slots;
> +	else
> +		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
> +
> +	/* We need at least one slot to succeed ####pd####*/

What does ####pd#### mean?

> +	for (i = 0; i < host->num_slots; i++) {
> +		ret = dw_mci_init_slot(host, i);
> +		if (ret) {
> +			ret = -ENODEV;
> +			goto err_init_slot;
> +		}
> +	}
> +
> +	/*
> +	 * enable interrupt for command done, data over, data empty,
> +	 * receive ready and error such as transmit, receive timeout, crc error
> +	 */
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
> +				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
> +				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
> +	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
> +
> +	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
> +			"%d bit host data width\n",
> +			irq, width);
> +	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
> +		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
> +
> +	return 0;
> +
> +err_init_slot:
> +	/* De-init any initialized slots */
> +	while (i > 0) {
> +		if (host->slot[i])
> +			dw_mci_cleanup_slot(host->slot[i], i);
> +		i--;
> +	}
> +	free_irq(irq, host);
> +
> +err_dmaunmap:
> +	if (host->use_dma && host->dma_ops->exit)
> +		host->dma_ops->exit(host);
> +	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
> +			host->sg_cpu, host->sg_dma);
> +	iounmap(host->regs);
> +
> +err_freehost:
> +	kfree(host);
> +	return ret;
> +}
> +
> +static int __exit dw_mci_remove(struct platform_device *pdev)
> +{
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +	int i;
> +
> +	mci_writel(host, RINTSTS, 0xFFFFFFFF);
> +	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		dev_dbg(&pdev->dev, "remove slot %d\n", i);
> +		if (host->slot[i])
> +			dw_mci_cleanup_slot(host->slot[i], i);
> +	}
> +
> +	/* disable clock to CIU */
> +	mci_writel(host, CLKENA, 0);
> +	mci_writel(host, CLKSRC, 0);
> +
> +	free_irq(platform_get_irq(pdev, 0), host);
> +	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> +
> +	if (host->use_dma && host->dma_ops->exit)
> +		host->dma_ops->exit(host);
> +
> +	iounmap(host->regs);
> +
> +	kfree(host);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +/*
> + * 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)
> +{
> +	int i, ret;
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		if (!slot)
> +			continue;
> +		ret = mmc_suspend_host(slot->mmc);
> +		if (ret < 0) {
> +			while (--i >= 0) {
> +				slot = host->slot[i];
> +				if (slot)
> +					mmc_resume_host(host->slot[i]->mmc);
> +			}
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int dw_mci_resume(struct platform_device *pdev)
> +{
> +	int i, ret;
> +	struct dw_mci *host = platform_get_drvdata(pdev);
> +
> +	for (i = 0; i < host->num_slots; i++) {
> +		struct dw_mci_slot *slot = host->slot[i];
> +		if (!slot)
> +			continue;
> +		ret = mmc_resume_host(host->slot[i]->mmc);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +#else
> +#define dw_mci_suspend	NULL
> +#define dw_mci_resume	NULL
> +#endif /* CONFIG_PM */
> +
> +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",
> +	},
> +};
> +
> +static int __init dw_mci_init(void)
> +{
> +	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
> +}
> +
> +static void __exit dw_mci_exit(void)
> +{
> +	platform_driver_unregister(&dw_mci_driver);
> +}
> +
> +module_init(dw_mci_init);
> +module_exit(dw_mci_exit);
> +
> +MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
> +MODULE_AUTHOR("NXP Semiconductor VietNam");
> +MODULE_AUTHOR("Imagination Technologies Ltd");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> new file mode 100644
> index 0000000..0ec6856
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -0,0 +1,153 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *
> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
> + *
> + * 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, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _DW_MMC_H_
> +#define _DW_MMC_H_
> +
> +#define SDMMC_CTRL		0x000
> +#define SDMMC_PWREN		0x004
> +#define SDMMC_CLKDIV		0x008
> +#define SDMMC_CLKSRC		0x00c
> +#define SDMMC_CLKENA		0x010
> +#define SDMMC_TMOUT		0x014
> +#define SDMMC_CTYPE		0x018
> +#define SDMMC_BLKSIZ		0x01c
> +#define SDMMC_BYTCNT		0x020
> +#define SDMMC_INTMASK		0x024
> +#define SDMMC_CMDARG		0x028
> +#define SDMMC_CMD		0x02c
> +#define SDMMC_RESP0		0x030
> +#define SDMMC_RESP1		0x034
> +#define SDMMC_RESP2		0x038
> +#define SDMMC_RESP3		0x03c
> +#define SDMMC_MINTSTS		0x040
> +#define SDMMC_RINTSTS		0x044
> +#define SDMMC_STATUS		0x048
> +#define SDMMC_FIFOTH		0x04c
> +#define SDMMC_CDETECT		0x050
> +#define SDMMC_WRTPRT		0x054
> +#define SDMMC_GPIO		0x058
> +#define SDMMC_TCBCNT		0x05c
> +#define SDMMC_TBBCNT		0x060
> +#define SDMMC_DEBNCE		0x064
> +#define SDMMC_USRID		0x068
> +#define SDMMC_VERID		0x06c
> +#define SDMMC_HCON		0x070
> +#define SDMMC_BMOD		0x080
> +#define SDMMC_PLDMND		0x084
> +#define SDMMC_DBADDR		0x088
> +#define SDMMC_IDSTS		0x08c
> +#define SDMMC_IDINTEN		0x090
> +#define SDMMC_DSCADDR		0x094
> +#define SDMMC_BUFADDR		0x098
> +#define SDMMC_DATA		0x100
> +#define SDMMC_DATA_ADR		0x100
> +
> +/* shift bit field */
> +#define _SBF(f, v)		((v) << (f))
> +
> +/* Control register defines */
> +#define SDMMC_CTRL_USE_IDMAC		BIT(25)
> +#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
> +#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
> +#define SDMMC_CTRL_SEND_CCSD		BIT(9)
> +#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
> +#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
> +#define SDMMC_CTRL_READ_WAIT		BIT(6)
> +#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
> +#define SDMMC_CTRL_INT_ENABLE		BIT(4)
> +#define SDMMC_CTRL_DMA_RESET		BIT(2)
> +#define SDMMC_CTRL_FIFO_RESET		BIT(1)
> +#define SDMMC_CTRL_RESET		BIT(0)
> +/* Clock Enable register defines */
> +#define SDMMC_CLKEN_LOW_PWR		BIT(16)
> +#define SDMMC_CLKEN_ENABLE		BIT(0)
> +/* time-out register defines */
> +#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
> +#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
> +#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
> +#define SDMMC_TMOUT_RESP_MSK		0xFF
> +/* card-type register defines */
> +#define SDMMC_CTYPE_8BIT		BIT(16)
> +#define SDMMC_CTYPE_4BIT		BIT(0)
> +#define SDMMC_CTYPE_1BIT		0
> +/* Interrupt status & mask register defines */
> +#define SDMMC_INT_SDIO			BIT(16)
> +#define SDMMC_INT_EBE			BIT(15)
> +#define SDMMC_INT_ACD			BIT(14)
> +#define SDMMC_INT_SBE			BIT(13)
> +#define SDMMC_INT_HLE			BIT(12)
> +#define SDMMC_INT_FRUN			BIT(11)
> +#define SDMMC_INT_HTO			BIT(10)
> +#define SDMMC_INT_DTO			BIT(9)
> +#define SDMMC_INT_RTO			BIT(8)
> +#define SDMMC_INT_DCRC			BIT(7)
> +#define SDMMC_INT_RCRC			BIT(6)
> +#define SDMMC_INT_RXDR			BIT(5)
> +#define SDMMC_INT_TXDR			BIT(4)
> +#define SDMMC_INT_DATA_OVER		BIT(3)
> +#define SDMMC_INT_CMD_DONE		BIT(2)
> +#define SDMMC_INT_RESP_ERR		BIT(1)
> +#define SDMMC_INT_CD			BIT(0)
> +#define SDMMC_INT_ERROR			0xbfc2
> +/* Command register defines */
> +#define SDMMC_CMD_START			BIT(31)
> +#define SDMMC_CMD_CCS_EXP		BIT(23)
> +#define SDMMC_CMD_CEATA_RD		BIT(22)
> +#define SDMMC_CMD_UPD_CLK		BIT(21)
> +#define SDMMC_CMD_INIT			BIT(15)
> +#define SDMMC_CMD_STOP			BIT(14)
> +#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
> +#define SDMMC_CMD_SEND_STOP		BIT(12)
> +#define SDMMC_CMD_STRM_MODE		BIT(11)
> +#define SDMMC_CMD_DAT_WR		BIT(10)
> +#define SDMMC_CMD_DAT_EXP		BIT(9)
> +#define SDMMC_CMD_RESP_CRC		BIT(8)
> +#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
> +/* Internal DMAC interrupt defines */
> +#define SDMMC_IDMAC_INT_AI		BIT(9)
> +#define SDMMC_IDMAC_INT_NI		BIT(8)
> +#define SDMMC_IDMAC_INT_CES		BIT(5)
> +#define SDMMC_IDMAC_INT_DU		BIT(4)
> +#define SDMMC_IDMAC_INT_FBE		BIT(2)
> +#define SDMMC_IDMAC_INT_RI		BIT(1)
> +#define SDMMC_IDMAC_INT_TI		BIT(0)
> +/* Internal DMAC bus mode bits */
> +#define SDMMC_IDMAC_ENABLE		BIT(7)
> +#define SDMMC_IDMAC_FB			BIT(1)
> +#define SDMMC_IDMAC_SWRESET		BIT(0)
> +
> +/* Register access macros */
> +#define mci_readl(dev, reg)			\
> +	__raw_readl(dev->regs + SDMMC_##reg)
> +#define mci_writel(dev, reg, value)			\
> +	__raw_writel((value), dev->regs + SDMMC_##reg)
> +
> +/* 16-bit FIFO access macros */
> +#define mci_readw(dev, reg)			\
> +	__raw_readw(dev->regs + SDMMC_##reg)
> +#define mci_writew(dev, reg, value)			\
> +	__raw_writew((value), dev->regs + SDMMC_##reg)
> +
> +/* 64-bit FIFO access macros */
> +#define mci_readq(dev, reg)			\
> +	__raw_readq(dev->regs + SDMMC_##reg)
> +#define mci_writeq(dev, reg, value)			\
> +	__raw_writeq((value), dev->regs + SDMMC_##reg)
> +
> +#endif /* _DW_MMC_H_ */
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> new file mode 100644
> index 0000000..a037b56
> --- /dev/null
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -0,0 +1,149 @@
> +/*
> + * Synopsys DesignWare Multimedia Card Interface driver
> + *  (Based on NXP driver for lpc 31xx)
> + *
> + * Copyright (C) 2009 NXP Semiconductors
> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
> + *
> + * 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, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef _LINUX_MMC_DW_MMC_H_
> +#define _LINUX_MMC_DW_MMC_H_
> +
> +#define MAX_MCI_SLOTS	2
> +
> +enum dw_mci_state {
> +	STATE_IDLE = 0,
> +	STATE_SENDING_CMD,
> +	STATE_SENDING_DATA,
> +	STATE_DATA_BUSY,
> +	STATE_SENDING_STOP,
> +	STATE_DATA_ERROR,
> +};
> +
> +enum {
> +	EVENT_CMD_COMPLETE = 0,
> +	EVENT_XFER_COMPLETE,
> +	EVENT_DATA_COMPLETE,
> +	EVENT_DATA_ERROR,
> +	EVENT_XFER_ERROR
> +};
> +
> +struct mmc_data;
> +
> +struct dw_mci {
> +	spinlock_t		lock;
> +	void __iomem		*regs;
> +
> +	struct scatterlist	*sg;
> +	unsigned int		pio_offset;
> +
> +	struct dw_mci_slot	*cur_slot;
> +	struct mmc_request	*mrq;
> +	struct mmc_command	*cmd;
> +	struct mmc_data		*data;
> +
> +	/* DMA interface members*/
> +	int			use_dma;
> +
> +	dma_addr_t		sg_dma;
> +	void			*sg_cpu;
> +	struct dw_mci_dma_ops	*dma_ops;
> +#ifdef CONFIG_MMC_DW_IDMAC
> +	unsigned int		ring_size;
> +#else
> +	struct dw_mci_dma_data	*dma_data;
> +#endif
> +	u32			cmd_status;
> +	u32			data_status;
> +	u32			stop_cmdr;
> +	u32			dir_status;
> +	struct tasklet_struct	tasklet;
> +	struct tasklet_struct	card_tasklet;
> +	unsigned long		pending_events;
> +	unsigned long		completed_events;
> +	enum dw_mci_state	state;
> +	struct list_head	queue;
> +
> +	u32			bus_hz;
> +	u32			current_speed;
> +	u32			num_slots;
> +	struct platform_device	*pdev;
> +	struct dw_mci_board	*pdata;
> +	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
> +
> +	/* FIFO push and pull */
> +	int			data_shift;
> +	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
> +	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
> +
> +	/* Workaround flags */
> +	u32			quirks;
> +};
> +
> +/* DMA ops for Internal/External DMAC interface */
> +struct dw_mci_dma_ops {
> +	/* DMA Ops */
> +	int (*init)(struct dw_mci *host);
> +	void (*start)(struct dw_mci *host, unsigned int sg_len);
> +	void (*complete)(struct dw_mci *host);
> +	void (*stop)(struct dw_mci *host);
> +	void (*cleanup)(struct dw_mci *host);
> +	void (*exit)(struct dw_mci *host);
> +};
> +
> +/* IP Quirks/flags. */
> +/* No special quirks or flags to cater for */
> +#define DW_MCI_QUIRK_NONE		0
> +/* DTO fix for command transmission with IDMAC configured */
> +#define DW_MCI_QUIRK_IDMAC_DTO		1
> +/* delay needed between retries on some 2.11a implementations */
> +#define DW_MCI_QUIRK_RETRY_DELAY	2
> +/* High Speed Capable - Supports HS cards (upto 50MHz) */
> +#define DW_MCI_QUIRK_HIGHSPEED		4
> +
> +
> +struct dma_pdata;
> +
> +struct block_settings {
> +	unsigned short	max_segs;	/* see blk_queue_max_segments */
> +	unsigned int	max_blk_size;	/* maximum size of one mmc block */
> +	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
> +	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
> +	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
> +};
> +
> +/* Board platform data */
> +struct dw_mci_board {
> +	u32 num_slots;
> +
> +	u32 quirks; /* Workaround / Quirk flags */
> +	unsigned int bus_hz; /* Bus speed */
> +
> +	/* delay in mS before detecting cards after interrupt */
> +	u32 detect_delay_ms;
> +
> +	int (*init)(u32 slot_id, irq_handler_t , void *);
> +	int (*get_ro)(u32 slot_id);
> +	int (*get_cd)(u32 slot_id);
> +	int (*get_ocr)(u32 slot_id);
> +	int (*get_bus_wd)(u32 slot_id);
> +	/*
> +	 * Enable power to selected slot and set voltage to desired level.
> +	 * Voltage levels are specified using MMC_VDD_xxx defines defined
> +	 * in linux/mmc/host.h file.
> +	 */
> +	void (*setpower)(u32 slot_id, u32 volt);
> +	void (*exit)(u32 slot_id);
> +	void (*select_slot)(u32 slot_id);
> +
> +	struct dw_mci_dma_ops *dma_ops;
> +	struct dma_pdata *data;
> +	struct block_settings *blk_settings;
> +};
> +
> +#endif /* _LINUX_MMC_DW_MMC_H_ */


Cleanup patch:

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7de6b42..9c5c33a 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -9,16 +9,6 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
 
 #include <linux/blkdev.h>
@@ -119,20 +109,20 @@ static int dw_mci_req_show(struct seq_file *s, void *v)
 
 		if (cmd)
 			seq_printf(s,
-				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
-				cmd->opcode, cmd->arg, cmd->flags,
-				cmd->resp[0], cmd->resp[1], cmd->resp[2],
-				cmd->resp[2], cmd->error);
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   cmd->opcode, cmd->arg, cmd->flags,
+				   cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				   cmd->resp[2], cmd->error);
 		if (data)
 			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
-				data->bytes_xfered, data->blocks,
-				data->blksz, data->flags, data->error);
+				   data->bytes_xfered, data->blocks,
+				   data->blksz, data->flags, data->error);
 		if (stop)
 			seq_printf(s,
-				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
-				stop->opcode, stop->arg, stop->flags,
-				stop->resp[0], stop->resp[1], stop->resp[2],
-				stop->resp[2], stop->error);
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   stop->opcode, stop->arg, stop->flags,
+				   stop->resp[0], stop->resp[1], stop->resp[2],
+				   stop->resp[2], stop->error);
 	}
 
 	spin_unlock_bh(&slot->host->lock);
@@ -190,12 +180,11 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
 		return;
 
 	node = debugfs_create_file("regs", S_IRUSR, root, host,
-			&dw_mci_regs_fops);
+				   &dw_mci_regs_fops);
 	if (!node)
 		goto err;
 
-	node = debugfs_create_file("req", S_IRUSR, root, slot,
-			&dw_mci_req_fops);
+	node = debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
 	if (!node)
 		goto err;
 
@@ -204,12 +193,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
 		goto err;
 
 	node = debugfs_create_x32("pending_events", S_IRUSR, root,
-				     (u32 *)&host->pending_events);
+				  (u32 *)&host->pending_events);
 	if (!node)
 		goto err;
 
 	node = debugfs_create_x32("completed_events", S_IRUSR, root,
-				     (u32 *)&host->completed_events);
+				  (u32 *)&host->completed_events);
 	if (!node)
 		goto err;
 
@@ -225,8 +214,7 @@ static void dw_mci_set_timeout(struct dw_mci *host)
 	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
 }
 
-static u32 dw_mci_prepare_command(struct mmc_host *mmc,
-				  struct mmc_command *cmd)
+static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 {
 	struct mmc_data	*data;
 	u32 cmdr;
@@ -243,7 +231,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc,
 		cmdr |= SDMMC_CMD_RESP_EXP;
 		/* expect the respond, need to set this bit */
 		if (cmd->flags & MMC_RSP_136)
-			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+			cmdr |= SDMMC_CMD_RESP_LONG;
 	}
 
 	if (cmd->flags & MMC_RSP_CRC)
@@ -253,7 +241,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc,
 	if (data) {
 		cmdr |= SDMMC_CMD_DAT_EXP;
 		if (data->flags & MMC_DATA_STREAM)
-			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+			cmdr |= SDMMC_CMD_STRM_MODE;
 		if (data->flags & MMC_DATA_WRITE)
 			cmdr |= SDMMC_CMD_DAT_WR;
 	}
@@ -265,9 +253,8 @@ static void dw_mci_start_command(struct dw_mci *host,
 				 struct mmc_command *cmd, u32 cmd_flags)
 {
 	host->cmd = cmd;
-	dev_vdbg(&host->pdev->dev,
-			"start command: ARGR=0x%08x CMDR=0x%08x\n",
-			cmd->arg, cmd_flags);
+	dev_vdbg(&host->pdev->dev, "start command: ARGR=0x%08x CMDR=0x%08x\n",
+		 cmd->arg, cmd_flags);
 
 	/* write to CMDARG register */
 	mci_writel(host, CMDARG, cmd->arg);
@@ -288,7 +275,6 @@ static void dw_mci_stop_dma(struct dw_mci *host)
 	if (host->use_dma) {
 		host->dma_ops->stop(host);
 		host->dma_ops->cleanup(host);
-
 	} else {
 		/* Data transfer was stopped by the interrupt handler */
 		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
@@ -302,8 +288,8 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
 
 	if (data)
 		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
-		     ((data->flags & MMC_DATA_WRITE)
-		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+			     ((data->flags & MMC_DATA_WRITE)
+			      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
 }
 
 static void dw_mci_idmac_stop_dma(struct dw_mci *host)
@@ -340,8 +326,8 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
 	}
 }
 
-static void dw_mci_translate_sglist(struct dw_mci *host,
-				    struct mmc_data *data, unsigned int sg_len)
+static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
+				    unsigned int sg_len)
 {
 	int i;
 	struct idmac_desc *desc = host->sg_cpu;
@@ -412,7 +398,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
 
 	/* Mask out interrupts - get Tx & Rx complete only */
 	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
-			SDMMC_IDMAC_INT_TI);
+		   SDMMC_IDMAC_INT_TI);
 
 	/* Set the descriptor base address */
 	mci_writel(host, DBADDR, host->sg_dma);
@@ -458,12 +444,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 	else
 		direction = DMA_TO_DEVICE;
 
-	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
-				   direction);
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, direction);
 
 	dev_vdbg(&host->pdev->dev,
-		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
-		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
 		 sg_len);
 
 	/* Enable the DMA interface */
@@ -471,7 +456,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
 	temp |= SDMMC_CTRL_DMA_ENABLE;
 	mci_writel(host, CTRL, temp);
 
-	/* disable irq of RX & TX, let DMA handle it */
+	/* Disable RX/TX IRQs, let DMA handle it */
 	temp = mci_readl(host, INTMASK);
 	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
 	mci_writel(host, INTMASK, temp);
@@ -537,18 +522,17 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
 	if (slot->clock != host->current_speed) {
 		if (host->bus_hz % slot->clock)
 			/*
-			 * move the + 1 after the dvide
-			 * to prevent over-clocking the card.
+			 * move the "+ 1" after the divide to prevent
+			 * over-clocking the card.
 			 */
 			div = ((host->bus_hz / slot->clock) >> 1) + 1;
 		else
 			div = (host->bus_hz  / slot->clock) >> 1;
 
 		dev_info(&slot->mmc->class_dev,
-			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
-			" div = %d)\n",
-			slot->id, host->bus_hz, slot->clock,
-			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+			 "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			 " div = %d)\n", slot->id, host->bus_hz, slot->clock,
+			 div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
 
 		/* disable clock */
 		mci_writel(host, CLKENA, 0);
@@ -556,21 +540,21 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
 
 		/* inform CIU */
 		mci_send_cmd(slot,
-			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
 		/* set clock to desired speed */
 		mci_writel(host, CLKDIV, div);
 
 		/* inform CIU */
 		mci_send_cmd(slot,
-			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
 		/* enable clock */
 		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
 
 		/* inform CIU */
 		mci_send_cmd(slot,
-			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
 		host->current_speed = slot->clock;
 	}
@@ -579,8 +563,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
 	mci_writel(host, CTYPE, slot->ctype);
 }
 
-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_request *mrq;
 	struct mmc_command *cmd;
@@ -632,7 +615,7 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
 				 struct mmc_request *mrq)
 {
 	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
-			host->state);
+		 host->state);
 
 	spin_lock_bh(&host->lock);
 	slot->mrq = mrq;
@@ -703,16 +686,15 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
 	struct dw_mci_slot *slot = mmc_priv(mmc);
 	struct dw_mci_board *brd = slot->host->pdata;
 
-	if (brd->get_ro != NULL) {
+	/* Use platform get_ro function, else try on board write protect */
+	if (brd->get_ro)
 		read_only = brd->get_ro(slot->id);
-	} else {
-		/* Try on board write protect */
-		read_only =
+	else
+		read_only = 
 			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
-	}
 
 	dev_dbg(&mmc->class_dev, "card is %s\n",
-				read_only ? "read-only" : "read-write");
+		read_only ? "read-only" : "read-write");
 
 	return read_only;
 }
@@ -723,13 +705,17 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
 	struct dw_mci_slot *slot = mmc_priv(mmc);
 	struct dw_mci_board *brd = slot->host->pdata;
 
-	if (brd->get_cd != NULL)
+	/* Use platform get_cd function, else try onboard card detect */
+	if (brd->get_cd)
 		present = !brd->get_cd(slot->id);
-	else	/* try onboard card detect */
+	else 
 		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
-				== 0 ? 1 : 0;
+			== 0 ? 1 : 0;
 
-	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+	if (present)
+		dev_dbg("card is present\n");
+	else
+		dev_dbg("card is not present\n");
 
 	return present;
 }
@@ -754,10 +740,10 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
 	host->mrq = NULL;
 	if (!list_empty(&host->queue)) {
 		slot = list_entry(host->queue.next,
-				struct dw_mci_slot, queue_node);
+				  struct dw_mci_slot, queue_node);
 		list_del(&slot->queue_node);
 		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
-				mmc_hostname(slot->mmc));
+			 mmc_hostname(slot->mmc));
 		host->state = STATE_SENDING_CMD;
 		dw_mci_start_request(host, slot);
 	} else {
@@ -767,12 +753,10 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
 
 	spin_unlock(&host->lock);
 	mmc_request_done(prev_mmc, mrq);
-
 	spin_lock(&host->lock);
 }
 
-static void dw_mci_command_complete(struct dw_mci *host,
-				    struct mmc_command *cmd)
+static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
 {
 	u32 status = host->cmd_status;
 
@@ -879,14 +863,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
 			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
 			status = host->data_status;
 
-			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+			if (status & DW_MCI_DATA_ERROR_FLAGS)) {
 				if (status & SDMMC_INT_DTO) {
 					dev_err(&host->pdev->dev,
-							"data timeout error\n");
+						"data timeout error\n");
 					data->error = -ETIMEDOUT;
 				} else if (status & SDMMC_INT_DCRC) {
 					dev_err(&host->pdev->dev,
-							"data CRC error\n");
+						"data CRC error\n");
 					data->error = -EILSEQ;
 				} else {
 					dev_err(&host->pdev->dev,
@@ -938,80 +922,80 @@ unlock:
 
 static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
 {
-	u16 *pData = (u16 *)buf;
+	u16 *pdata = (u16 *)buf;
 
 	WARN_ON(cnt % 2 != 0);
 
 	cnt = cnt >> 1;
 	while (cnt > 0) {
-		mci_writew(host, DATA, *pData++);
+		mci_writew(host, DATA, *pdata++);
 		cnt--;
 	}
 }
 
 static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
 {
-	u16 *pData = (u16 *)buf;
+	u16 *pdata = (u16 *)buf;
 
 	WARN_ON(cnt % 2 != 0);
 
 	cnt = cnt >> 1;
 	while (cnt > 0) {
-		*pData++ = mci_readw(host, DATA);
+		*pdata++ = mci_readw(host, DATA);
 		cnt--;
 	}
 }
 
 static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
 {
-	u32 *pData = (u32 *)buf;
+	u32 *pdata = (u32 *)buf;
 
 	WARN_ON(cnt % 4 != 0);
-	WARN_ON((unsigned long)pData & 0x3);
+	WARN_ON((unsigned long)pdata & 0x3);
 
 	cnt = cnt >> 2;
 	while (cnt > 0) {
-		mci_writel(host, DATA, *pData++);
+		mci_writel(host, DATA, *pdata++);
 		cnt--;
 	}
 }
 
 static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
 {
-	u32 *pData = (u32 *)buf;
+	u32 *pdata = (u32 *)buf;
 
 	WARN_ON(cnt % 4 != 0);
-	WARN_ON((unsigned long)pData & 0x3);
+	WARN_ON((unsigned long)pdata & 0x3);
 
 	cnt = cnt >> 2;
 	while (cnt > 0) {
-		*pData++ = mci_readl(host, DATA);
+		*pdata++ = mci_readl(host, DATA);
 		cnt--;
 	}
 }
 
 static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
 {
-	u64 *pData = (u64 *)buf;
+	u64 *pdata = (u64 *)buf;
 
 	WARN_ON(cnt % 8 != 0);
 
 	cnt = cnt >> 3;
 	while (cnt > 0) {
-		mci_writeq(host, DATA, *pData++);
+		mci_writeq(host, DATA, *pdata++);
 		cnt--;
 	}
 }
 
 static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
 {
-	u64 *pData = (u64 *)buf;
+	u64 *pdata = (u64 *)buf;
 
 	WARN_ON(cnt % 8 != 0);
 
 	cnt = cnt >> 3;
 	while (cnt > 0) {
-		*pData++ = mci_readq(host, DATA);
+		*pdata++ = mci_readq(host, DATA);
 		cnt--;
 	}
 }
@@ -1031,7 +1015,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
 		if (count == 0)
 			old_len = len;
 
-		if (likely(offset + len <= sg->length)) {
+		if (offset + len <= sg->length)) {
 			host->pull_data(host, (void *)(buf + offset), len);
 
 			offset += len;
@@ -1049,7 +1033,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
 		} else {
 			unsigned int remaining = sg->length - offset;
 			host->pull_data(host,
-				(void *)(buf + offset), remaining);
+					(void *)(buf + offset), remaining);
 			nbytes += remaining;
 
 			flush_dcache_page(sg_page(sg));
@@ -1076,8 +1060,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
 			return;
 		}
 		count++;
-	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
-						lets read again*/
+	} while (status & SDMMC_INT_RXDR); /* if the RXDR is ready, read again */
 	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
 	host->pio_offset = offset;
 	data->bytes_xfered += nbytes;
@@ -1102,7 +1085,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
 	do {
 		len = SDMMC_FIFO_SZ -
 			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
-		if (likely(offset + len <= sg->length)) {
+		if (offset + len <= sg->length)) {
 			host->push_data(host, (void *)(buf + offset), len);
 
 			offset += len;
@@ -1119,7 +1102,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
 			unsigned int remaining = sg->length - offset;
 
 			host->push_data(host,
-				(void *)(buf + offset), remaining);
+					(void *)(buf + offset), remaining);
 			nbytes += remaining;
 
 			host->sg = sg = sg_next(sg);
@@ -1145,7 +1128,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
 			tasklet_schedule(&host->tasklet);
 			return;
 		}
-	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, write again */
 
 	host->pio_offset = offset;
 	data->bytes_xfered += nbytes;
@@ -1172,12 +1155,12 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
 static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 {
 	struct dw_mci *host = dev_id;
-	u32 status,  pending;
+	u32 status, pending;
 	unsigned int pass_count = 0;
 
 	do {
 		status = mci_readl(host, RINTSTS);
-		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+		pending = mci_readl(host, MINTSTS); /* read-only mask reg */
 
 		/*
 		 * DTO fix - version 2.10a and below, and only if internal DMA
@@ -1201,7 +1184,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 		}
 
 		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
-			/* if there is an error, lets report DATA_ERROR */
+			/* if there is an error, report DATA_ERROR */
 			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
 			host->data_status = status;
 			smp_wmb();
@@ -1209,7 +1192,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 			tasklet_schedule(&host->tasklet);
 		}
 
-
 		if (pending & SDMMC_INT_DATA_OVER) {
 			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
 			if (!host->data_status)
@@ -1251,8 +1233,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 	/* Handle DMA interrupts */
 	pending = mci_readl(host, IDSTS);
 	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
-		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
-				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
 		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
 		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
 		host->dma_ops->complete(host);
@@ -1279,7 +1260,7 @@ static void dw_mci_tasklet_card(unsigned long data)
 			spin_lock(&host->lock);
 
 			dev_dbg(&slot->mmc->class_dev, "card %s\n",
-					present ? "inserted" : "removed");
+				present ? "inserted" : "removed");
 
 			/* Card change detected */
 			slot->last_detect_state = present;
@@ -1288,7 +1269,7 @@ static void dw_mci_tasklet_card(unsigned long data)
 			if (present != 0) {
 				if (host->pdata->setpower)
 					host->pdata->setpower(slot->id,
-						mmc->ocr_avail);
+							      mmc->ocr_avail);
 
 				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
 			}
@@ -1358,10 +1339,9 @@ static void dw_mci_tasklet_card(unsigned long data)
 
 #ifdef CONFIG_MMC_DW_IDMAC
 				ctrl = mci_readl(host, BMOD);
-				ctrl |= 0x01;	/* Software reset of DMA */
+				ctrl |= 0x01; /* Software reset of DMA */
 				mci_writel(host, BMOD, ctrl);
 #endif
-
 			}
 
 			spin_unlock(&host->lock);
@@ -1397,8 +1377,8 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
 	/*
-	 * Start with slot power disabled,
-	 * will be enabled when card is detected
+	 * Start with slot power disabled, it will be enabled when
+	 * a card is detected.
 	 */
 	if (host->pdata->setpower)
 		host->pdata->setpower(id, 0);
@@ -1425,7 +1405,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
 		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
 	} else {
-		/*useful defaults*/
+		/* Useful defaults, if platform data is unset. */
 		mmc->max_segs = 64;
 		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
 		mmc->max_blk_count = 512;
@@ -1453,8 +1433,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
 	return 0;
 }
 
-static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
-				unsigned int id)
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
 {
 	/* Shutdown detect IRQ */
 	if (slot->host->pdata->exit)
@@ -1470,10 +1449,10 @@ static void dw_mci_init_dma(struct dw_mci *host)
 {
 	/* Alloc memory for sg translation */
 	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
-			&host->sg_dma, GFP_KERNEL);
+					  &host->sg_dma, GFP_KERNEL);
 	if (!host->sg_cpu) {
 		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
-				__func__);
+			__func__);
 		goto no_dma;
 	}
 
@@ -1488,12 +1467,12 @@ static void dw_mci_init_dma(struct dw_mci *host)
 
 	if (host->dma_ops->init) {
 		if (host->dma_ops->init(host)) {
-			dev_err(&host->pdev->dev, "%s: Unable to initialise "
-					"DMA Controller.\n", __func__);
+			dev_err(&host->pdev->dev, "%s: Unable to initialize "
+				"DMA Controller.\n", __func__);
 			goto no_dma;
 		}
 	} else {
-		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		dev_err(&host->pdev->dev, "DMA initialization not found.\n");
 		goto no_dma;
 	}
 
@@ -1512,13 +1491,13 @@ static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
 	unsigned int ctrl;
 
 	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
-			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+				SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
 
-	/* wait till resets clear */
+	/* Wait until the reset bits clear */
 	do {
 		ctrl = mci_readl(host, CTRL);
-		if (!(ctrl & (SDMMC_CTRL_RESET |
-			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+		if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | 
+			      SDMMC_CTRL_DMA_RESET)))
 			return true;
 	} while (time_before(jiffies, timeout));
 
@@ -1556,15 +1535,14 @@ static int dw_mci_probe(struct platform_device *pdev)
 	}
 
 	if (((pdata->num_slots > 1) && !(pdata->select_slot))
-			|| !(pdata->init)) {
+	    || !(pdata->init)) {
 		dev_err(&pdev->dev, "Platform data wrong\n");
 		ret = -ENODEV;
 		goto err_freehost;
 	}
 
 	if (!pdata->bus_hz) {
-		dev_err(&pdev->dev,
-			"Bus speed undefined in platform data!\n");
+		dev_err(&pdev->dev, "Bus speed undefined in platform data!\n");
 		ret = -ENODEV;
 		goto err_freehost;
 	}
@@ -1601,21 +1579,21 @@ static int dw_mci_probe(struct platform_device *pdev)
 	} else {
 		/* Check for a reserved value, and warn if it is */
 		WARN((i != 1),
-			"HCON reports a reserved host data width!\n"
-			"Defaulting to 32-bit access.\n");
+		     "HCON reports a reserved host data width!\n"
+		     "Defaulting to 32-bit access.\n");
 		host->push_data = dw_mci_push_data32;
 		host->pull_data = dw_mci_pull_data32;
 		width = 32;
 		host->data_shift = 2;
 	}
 
-	/* reset all blocks */
+	/* Reset all blocks */
 	if (!mci_wait_reset(&pdev->dev, host)) {
 		ret = -ENODEV;
 		goto err_dmaunmap;
 	}
 
-	 /* Clear the interrupts for the host controller */
+	/* Clear the interrupts for the host controller */
 	mci_writel(host, RINTSTS, 0xFFFFFFFF);
 	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
 
@@ -1623,13 +1601,13 @@ static int dw_mci_probe(struct platform_device *pdev)
 	mci_writel(host, TMOUT, 0xFFFFFFFF);
 
 	/*
-	 * FIFO threshold settings  RxMark = fifo_size/2-1,
-	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 * FIFO threshold settings  RxMark  = fifo_size/2 - 1,
+	 *                          Tx Mark = fifo_size/2 DMA Size = 8
 	 */
 	fifo_size = mci_readl(host, FIFOTH);
 	fifo_size = (fifo_size >> 16) & 0x7ff;
 	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
-				((fifo_size/2) << 0)));
+				  ((fifo_size/2) << 0)));
 
 	/* disable clock to CIU */
 	mci_writel(host, CLKENA, 0);
@@ -1637,7 +1615,7 @@ static int dw_mci_probe(struct platform_device *pdev)
 
 	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
 	tasklet_init(&host->card_tasklet,
-		dw_mci_tasklet_card, (unsigned long)host);
+		     dw_mci_tasklet_card, (unsigned long)host);
 
 	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
 	if (ret)
@@ -1660,18 +1638,17 @@ static int dw_mci_probe(struct platform_device *pdev)
 	}
 
 	/*
-	 * enable interrupt for command done, data over, data empty,
-	 * receive ready and error such as transmit, receive timeout, crc error
+	 * Enable interrupts for command done, data over, data empty, card det,
+	 * receive ready, and errors such as transmit, receive timeout, crc error
 	 */
 	mci_writel(host, RINTSTS, 0xFFFFFFFF);
 	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
-				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
-				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
-	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+				  SDMMC_INT_TXDR | SDMMC_INT_RXDR | 
+		   		  DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
 
 	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
-			"%d bit host data width\n",
-			irq, width);
+		 "%d bit host data width\n", irq, width);
 	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
 		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
 
@@ -1690,7 +1667,7 @@ err_dmaunmap:
 	if (host->use_dma && host->dma_ops->exit)
 		host->dma_ops->exit(host);
 	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
-			host->sg_cpu, host->sg_dma);
+			  host->sg_cpu, host->sg_dma);
 	iounmap(host->regs);
 
 err_freehost:


-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-09  6:47       ` Chris Ball
@ 2010-12-09 12:11         ` Will Newton
  2010-12-09 16:01           ` Chris Ball
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-09 12:11 UTC (permalink / raw)
  To: Chris Ball; +Cc: Linux Kernel list, linux-mmc, Matt Fleming

On Thu, Dec 9, 2010 at 6:47 AM, Chris Ball <cjb@laptop.org> wrote:
> Hi Will,
>
> Thanks for the submission (and thanks for the review, Matt!).
>
> Some more comments below, mainly stylistic, and at the bottom I've
> attached an indentation and cleanup patch.  It's okay if there are
> some changes in it that you'd rather not make.
>
> On Wed, Dec 08, 2010 at 02:21:05PM +0000, Will Newton wrote:
>> +config MMC_DW_IDMAC
>> +     depends on MMC_DW
>> +     bool "Internal DMAC interface"
>> +     help
>> +       This selects support for the internal DMAC block within the Synopsys
>> +       Designware Mobile Storage IP block. This disables the external DMA
>> +       interface.
>> +
>
> Is there something we could depend on that would stop this driver being
> presented to everyone, without being far too specific?  At the moment
> we'd be making x86 desktop users say whether they have this IP, which
> isn't good.  Are the architectures that use this IP already upstream?
> Are they all ARM architectures, for instance?

I don't know of any architectures upstream that use this IP block. There is an
SoC from NXP that uses this it but it is not upstream:

http://ics.nxp.com/support/software/lpc313x.bsp.linux/

The architecture we tested and debugged this driver on is not upstream either
unfortunately. :-/

>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -0,0 +1,1806 @@
>> +/*
>> + * Synopsys DesignWare Multimedia Card Interface driver
>> + *  (Based on NXP driver for lpc 31xx)
>> + *
>
> Is the NXP driver publicly available?  If not, we can remove this line.
> The driver looks like it's based on our atmel-mci.c to me.

Yes, that too. ;-)

The driver is available at the link above, I can add the URL to the comment
if that would be helpful.

> It'd be nice if you kerneldoc'd your data structures in the same style as
> atmel-mci does, and many of the locking comments in there apply directly
> to this driver.

Ok, I'll look into that.

>> + * Copyright (C) 2009 NXP Semiconductors
>> + * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
>
> Hm, what's the relationship between NXP and Imagination here?  If
> you're submitting a driver with NXP copyright, I think we should have
> a Signed-off-by line from whomever owns the copyright/wrote that code.
> If that's you, that's fine, but it's unclear to me.

We both have SoCs with this IP block in it, other than that I don't think
there has been any communication between NXP and Imagination about this driver.
Hence the lack of SOB from the original authors.

The driver is available publically under the GPL, so it was my
understanding that
no SOB was required as long as we keep appropriate copyright credit in the code.

>> + *
>> + * 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, or
>> + * (at your option) any later version.
>
> My patch snips from here until the end of this comment, to align with the
> rest of MMC.  One reason not to include the FSF address in every source
> file is that it becomes wrong occasionally, like the address below.  :)

Good idea.

>> +#if defined(CONFIG_DEBUG_FS)
>> +/*
>> + * The debugfs stuff below is mostly optimized away when
>> + * CONFIG_DEBUG_FS is not set.
>> + */
>
> Don't think this comment needs to be here.

Agreed.

>> +static u32 dw_mci_prepare_command(struct mmc_host *mmc,
>> +                               struct mmc_command *cmd)
>> +{
>> +     struct mmc_data *data;
>> +     u32 cmdr;
>> +     cmd->error = -EINPROGRESS;
>> +
>> +     cmdr = cmd->opcode;
>> +
>> +     if (cmdr == MMC_STOP_TRANSMISSION)
>> +             cmdr |= SDMMC_CMD_STOP;
>> +     else
>> +             cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
>> +
>> +     if (cmd->flags & MMC_RSP_PRESENT) {
>> +             cmdr |= SDMMC_CMD_RESP_EXP;
>> +             /* expect the respond, need to set this bit */
>
> I think the comment's referring to the line above, so it should be
> before it.  Also, s/respond/response/, and maybe it could be reworded.

Ok.

>> +             if (cmd->flags & MMC_RSP_136)
>> +                     cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
>> +     }
>> +
>> +     if (cmd->flags & MMC_RSP_CRC)
>> +             cmdr |= SDMMC_CMD_RESP_CRC;
>> +
>> +     data = cmd->data;
>> +     if (data) {
>> +             cmdr |= SDMMC_CMD_DAT_EXP;
>> +             if (data->flags & MMC_DATA_STREAM)
>> +                     cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
>
> I think both of these comments can be removed as obvious.

Ok.

>> +             if (data->flags & MMC_DATA_WRITE)
>> +                     cmdr |= SDMMC_CMD_DAT_WR;
>> +     }
>> +
>> +     return cmdr;
>> +}
>> +
>> +static void dw_mci_start_command(struct dw_mci *host,
>> +                              struct mmc_command *cmd, u32 cmd_flags)
>> +{
>> +     host->cmd = cmd;
>> +     dev_vdbg(&host->pdev->dev,
>> +                     "start command: ARGR=0x%08x CMDR=0x%08x\n",
>> +                     cmd->arg, cmd_flags);
>> +
>> +     /* write to CMDARG register */
>> +     mci_writel(host, CMDARG, cmd->arg);
>> +     wmb();
>> +
>> +     /* write to CMD register */
>> +     mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
>
> Again, these comments aren't very useful.  I do approve of comments in
> general, honest, but they're supposed to say something that isn't
> apparent by just looking at the code.

Yes, these should go.

>> +static void dw_mci_start_request(struct dw_mci *host,
>> +                              struct dw_mci_slot *slot)
>> +{
>> +     struct mmc_request *mrq;
>> +     struct mmc_command *cmd;
>> +     struct mmc_data *data;
>> +     u32 cmdflags;
>> +
>> +     mrq = slot->mrq;
>> +     /* no select the proper slot */
>> +     if (host->pdata->select_slot)
>> +             host->pdata->select_slot(slot->id);
>
> Don't understand this comment.

Maybe a typo, "no" for "now"? I'll remove the comment.

>> +
>> +     /* Slot specific timing and width adjustment */
>> +     dw_mci_setup_bus(slot);
>> +
>> +     host->cur_slot = slot;
>> +     host->mrq = mrq;
>> +
>> +     host->pending_events = 0;
>> +     host->completed_events = 0;
>> +     host->data_status = 0;
>> +
>> +     data = mrq->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, lets send the initialization clock */
>
> I think you mean:
>        /* If this is the first command, send the initialization clock /*

Done.

>> +     if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
>> +             cmdflags |= SDMMC_CMD_INIT;
>> +
>> +     /* we may need to move this code to mci_start_command */
>
> Has this question been resolved yet?

I've removed the comment, it doesn't appear to make logical sense to me.

>> +static int dw_mci_get_ro(struct mmc_host *mmc)
>> +{
>> +     int read_only;
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci_board *brd = slot->host->pdata;
>> +
>> +     if (brd->get_ro != NULL) {
>
> != NULL isn't necessary here.

Ok.

>> +             read_only = brd->get_ro(slot->id);
>> +     } else {
>> +             /* Try on board write protect */
>> +             read_only =
>> +                     mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
>> +     }
>> +
>> +     dev_dbg(&mmc->class_dev, "card is %s\n",
>> +                             read_only ? "read-only" : "read-write");
>> +
>> +     return read_only;
>> +}
>> +
>> +static int dw_mci_get_cd(struct mmc_host *mmc)
>> +{
>> +     int present;
>> +     struct dw_mci_slot *slot = mmc_priv(mmc);
>> +     struct dw_mci_board *brd = slot->host->pdata;
>> +
>> +     if (brd->get_cd != NULL)
>
> (Same.)

Ok.

>> +             present = !brd->get_cd(slot->id);
>> +     else    /* try onboard card detect */
>> +             present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
>> +                             == 0 ? 1 : 0;
>> +
>> +     dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
>
> I expanded this, since being able to grep for "not present" has some value.

Ok.

>> +
>> +     return present;
>> +}
>> +
>> +static const struct mmc_host_ops dw_mci_ops = {
>> +     .request        = dw_mci_request,
>> +     .set_ios        = dw_mci_set_ios,
>> +     .get_ro         = dw_mci_get_ro,
>> +     .get_cd         = dw_mci_get_cd,
>> +};
>> +
>> +static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
>> +     __releases(&host->lock)
>> +     __acquires(&host->lock)
>> +{
>> +     struct dw_mci_slot *slot;
>> +     struct mmc_host *prev_mmc = host->cur_slot->mmc;
>> +
>> +     WARN_ON(host->cmd || host->data);
>> +
>> +     host->cur_slot->mrq = NULL;
>> +     host->mrq = NULL;
>> +     if (!list_empty(&host->queue)) {
>> +             slot = list_entry(host->queue.next,
>> +                             struct dw_mci_slot, queue_node);
>> +             list_del(&slot->queue_node);
>> +             dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
>> +                             mmc_hostname(slot->mmc));
>> +             host->state = STATE_SENDING_CMD;
>> +             dw_mci_start_request(host, slot);
>> +     } else {
>> +             dev_vdbg(&host->pdev->dev, "list empty\n");
>> +             host->state = STATE_IDLE;
>> +     }
>> +
>> +     spin_unlock(&host->lock);
>> +     mmc_request_done(prev_mmc, mrq);
>> +
>> +     spin_lock(&host->lock);
>> +}
>> +
>> +static void dw_mci_command_complete(struct dw_mci *host,
>> +                                 struct mmc_command *cmd)
>> +{
>> +     u32 status = host->cmd_status;
>> +
>> +     host->cmd_status = 0;
>> +
>> +     /* Read the response from the card (up to 16 bytes) */
>> +     if (cmd->flags & MMC_RSP_PRESENT) {
>> +             if (cmd->flags & MMC_RSP_136) {
>> +                     cmd->resp[3] = mci_readl(host, RESP0);
>> +                     cmd->resp[2] = mci_readl(host, RESP1);
>> +                     cmd->resp[1] = mci_readl(host, RESP2);
>> +                     cmd->resp[0] = mci_readl(host, RESP3);
>
> It'd be nice to have a comment explaining why this is in reverse
> order, yet if RSP_136 is set we use RESP0 instead of RESP3.
>
>> +             } else {
>> +                     cmd->resp[0] = mci_readl(host, RESP0);
>> +                     cmd->resp[1] = 0;
>> +                     cmd->resp[2] = 0;
>> +                     cmd->resp[3] = 0;
>> +             }
>> +     }
>> +
>> +     if (status & SDMMC_INT_RTO)
>> +             cmd->error = -ETIMEDOUT;
>> +     else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
>> +             cmd->error = -EILSEQ;
>> +     else if (status & SDMMC_INT_RESP_ERR)
>> +             cmd->error = -EIO;
>> +     else
>> +             cmd->error = 0;
>> +
>> +     if (cmd->error) {
>> +             /* newer ip versions need a delay between retries */
>> +             if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
>> +                     mdelay(20);
>> +
>> +             if (cmd->data) {
>> +                     host->data = NULL;
>> +                     dw_mci_stop_dma(host);
>> +             }
>> +     }
>> +}
>> +
>> +static void dw_mci_tasklet_func(unsigned long priv)
>> +{
>> +     struct dw_mci *host = (struct dw_mci *)priv;
>> +     struct mmc_data *data;
>> +     struct mmc_command *cmd;
>> +     enum dw_mci_state state;
>> +     enum dw_mci_state prev_state;
>> +     u32 status;
>> +
>> +     spin_lock(&host->lock);
>> +
>> +     state = host->state;
>> +     data = host->data;
>> +
>> +     do {
>> +             prev_state = state;
>> +
>> +             switch (state) {
>> +             case STATE_IDLE:
>> +                     break;
>> +
>> +             case STATE_SENDING_CMD:
>> +                     if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
>> +                                             &host->pending_events))
>> +                             break;
>> +
>> +                     cmd = host->cmd;
>> +                     host->cmd = NULL;
>> +                     set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
>> +                     dw_mci_command_complete(host, host->mrq->cmd);
>> +                     if (!host->mrq->data || cmd->error) {
>> +                             dw_mci_request_end(host, host->mrq);
>> +                             goto unlock;
>> +                     }
>> +
>> +                     prev_state = state = STATE_SENDING_DATA;
>> +                     /* fall through */
>> +
>> +             case STATE_SENDING_DATA:
>> +                     if (test_and_clear_bit(EVENT_DATA_ERROR,
>> +                                            &host->pending_events)) {
>> +                             dw_mci_stop_dma(host);
>> +                             if (data->stop)
>> +                                     send_stop_cmd(host, data);
>> +                             state = STATE_DATA_ERROR;
>> +                             break;
>> +                     }
>> +
>> +                     if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
>> +                                             &host->pending_events))
>> +                             break;
>> +
>> +                     set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
>> +                     prev_state = state = STATE_DATA_BUSY;
>> +                     /* fall through */
>> +
>> +             case STATE_DATA_BUSY:
>> +                     if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
>> +                                             &host->pending_events))
>> +                             break;
>> +
>> +                     host->data = NULL;
>> +                     set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
>> +                     status = host->data_status;
>> +
>> +                     if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
>> +                             if (status & SDMMC_INT_DTO) {
>> +                                     dev_err(&host->pdev->dev,
>> +                                                     "data timeout error\n");
>> +                                     data->error = -ETIMEDOUT;
>> +                             } else if (status & SDMMC_INT_DCRC) {
>> +                                     dev_err(&host->pdev->dev,
>> +                                                     "data CRC error\n");
>> +                                     data->error = -EILSEQ;
>> +                             } else {
>> +                                     dev_err(&host->pdev->dev,
>> +                                             "data FIFO error "
>> +                                             "(status=%08x)\n",
>> +                                             status);
>> +                                     data->error = -EIO;
>> +                             }
>> +                     } else {
>> +                             data->bytes_xfered = data->blocks * data->blksz;
>> +                             data->error = 0;
>> +                     }
>> +
>> +                     if (!data->stop) {
>> +                             dw_mci_request_end(host, host->mrq);
>> +                             goto unlock;
>> +                     }
>> +
>> +                     prev_state = state = STATE_SENDING_STOP;
>> +                     if (!data->error)
>> +                             send_stop_cmd(host, data);
>> +                     /* fall through */
>> +
>> +             case STATE_SENDING_STOP:
>> +                     if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
>> +                                             &host->pending_events))
>> +                             break;
>> +
>> +                     host->cmd = NULL;
>> +                     dw_mci_command_complete(host, host->mrq->stop);
>> +                     dw_mci_request_end(host, host->mrq);
>> +                     goto unlock;
>> +
>> +             case STATE_DATA_ERROR:
>> +                     if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
>> +                                             &host->pending_events))
>> +                             break;
>> +
>> +                     state = STATE_DATA_BUSY;
>> +                     break;
>> +             }
>> +     } while (state != prev_state);
>> +
>> +     host->state = state;
>> +unlock:
>> +     spin_unlock(&host->lock);
>> +
>> +}
>> +
>> +static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
>> +{
>> +     u16 *pData = (u16 *)buf;
>
> Changed to pdata.

Ok.

>> +static void dw_mci_read_data_pio(struct dw_mci *host)
>> +{
>> +     struct scatterlist *sg = host->sg;
>> +     void *buf = sg_virt(sg);
>> +     unsigned int offset = host->pio_offset;
>> +     struct mmc_data *data = host->data;
>> +     int shift = host->data_shift;
>> +     u32 status;
>> +     unsigned int nbytes = 0, len, old_len, count = 0;
>> +
>> +     do {
>> +             len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
>> +             if (count == 0)
>> +                     old_len = len;
>> +
>> +             if (likely(offset + len <= sg->length)) {
>
> I agree with Matt, the unlikely/likely aren't necessary.

Removed.

>> +             if (pending & DW_MCI_CMD_ERROR_FLAGS) {
>> +                     mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
>> +                     host->cmd_status = status;
>> +                     smp_wmb();
>> +                     set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
>> +                     tasklet_schedule(&host->tasklet);
>> +             }
>> +
>> +             if (pending & DW_MCI_DATA_ERROR_FLAGS) {
>> +                     /* if there is an error, lets report DATA_ERROR */
>
> The "lets" were ungrammatical and didn't add much, so I removed them.

Ok.

>> +     if (host->pdata->blk_settings) {
>> +             mmc->max_segs = host->pdata->blk_settings->max_segs;
>> +             mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
>> +             mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
>> +             mmc->max_req_size = host->pdata->blk_settings->max_req_size;
>> +             mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
>> +     } else {
>> +             /*useful defaults*/
>> +             mmc->max_segs = 64;
>> +             mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
>> +             mmc->max_blk_count = 512;
>> +             mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
>> +             mmc->max_seg_size = mmc->max_req_size;
>> +     }
>> +#endif /* CONFIG_MMC_DW_IDMAC */
>> +
>> +     /* Assume card is present initially */
>> +     if (dw_mci_get_cd(mmc))
>> +             set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
>> +     else
>> +             clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
>
> Hm, does the code actually make that assumption?  Won't dw_mci_get_cd()
> return an appropriate value, and have the present bit cleared if so?

Yes, comment removed.

>> +     host->pdev = pdev;
>> +     host->pdata = pdata = pdev->dev.platform_data;
>> +     if (!pdata) {
>> +             dev_err(&pdev->dev, "Platform data missing\n");
>> +             ret = -ENODEV;
>> +             goto err_freehost;
>> +     }
>> +
>> +     if (((pdata->num_slots > 1) && !(pdata->select_slot))
>> +                     || !(pdata->init)) {
>> +             dev_err(&pdev->dev, "Platform data wrong\n");
>> +             ret = -ENODEV;
>> +             goto err_freehost;
>> +     }
>
> "Platform data wrong" is pretty unhelpful -- perhaps you could have the
> first test be (!pdata || !pdata->init), and then the second test can
> explain that num_slots is > 1 without a slot selected.

Yes, that would be clearer.

>> +     ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
>> +     if (ret)
>> +             goto err_dmaunmap;
>> +
>> +     platform_set_drvdata(pdev, host);
>> +
>> +     if (host->pdata->num_slots)
>> +             host->num_slots = host->pdata->num_slots;
>> +     else
>> +             host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
>> +
>> +     /* We need at least one slot to succeed ####pd####*/
>
> What does ####pd#### mean?

I expect pd is one of the original authors - I've removed that part of
the comment.

>
> Cleanup patch:

Thanks for the patch, I've applied it and done a couple more minor
changes. I'll send a v3 when it's tested.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-09 12:11         ` Will Newton
@ 2010-12-09 16:01           ` Chris Ball
  2010-12-09 17:24             ` Will Newton
  2010-12-09 17:35             ` [PATCH] " Chris Ball
  0 siblings, 2 replies; 29+ messages in thread
From: Chris Ball @ 2010-12-09 16:01 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Matt Fleming

Hi Will,

On Thu, Dec 09, 2010 at 12:11:06PM +0000, Will Newton wrote:
> > Is there something we could depend on that would stop this driver being
> > presented to everyone, without being far too specific?  At the moment
> > we'd be making x86 desktop users say whether they have this IP, which
> > isn't good.  Are the architectures that use this IP already upstream?
> > Are they all ARM architectures, for instance?
> 
> I don't know of any architectures upstream that use this IP block. There is an
> SoC from NXP that uses this it but it is not upstream:
> 
> http://ics.nxp.com/support/software/lpc313x.bsp.linux/
> 
> The architecture we tested and debugged this driver on is not upstream either
> unfortunately. :-/

Okay.  I think "depends on ARM" is appropriate for now, and we could
expand that later if the block appears in a MIPS SoC or something.
Does that work for you?

> The driver is available at the link above, I can add the URL to the comment
> if that would be helpful.

Sure, sounds good.

> > Hm, what's the relationship between NXP and Imagination here?  If
> > you're submitting a driver with NXP copyright, I think we should have
> > a Signed-off-by line from whomever owns the copyright/wrote that code.
> > If that's you, that's fine, but it's unclear to me.
> 
> We both have SoCs with this IP block in it, other than that I don't think
> there has been any communication between NXP and Imagination about this driver.
> Hence the lack of SOB from the original authors.
> 
> The driver is available publically under the GPL, so it was my
> understanding that
> no SOB was required as long as we keep appropriate copyright credit in the code.

Okay, makes sense.  Thanks,

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-09 16:01           ` Chris Ball
@ 2010-12-09 17:24             ` Will Newton
       [not found]               ` <20101211192320.GA24430@void.printf.net>
  2010-12-09 17:35             ` [PATCH] " Chris Ball
  1 sibling, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-09 17:24 UTC (permalink / raw)
  To: Chris Ball; +Cc: Linux Kernel list, linux-mmc, Matt Fleming

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
Reviewed-by: Matt Fleming <matt@console-pimps.org>
---
 drivers/mmc/host/Kconfig   |   16 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1796 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  153 ++++
 include/linux/mmc/dw_mmc.h |  217 ++++++
 5 files changed, 2183 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

 - patch v2: make changes based on review by Matt Fleming.
 - patch v3: changes based on review by Chris Ball.

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..e3a5ab4 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,22 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.

+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	depends on ARM
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	bool "Internal DMAC interface"
+	depends on MMC_DW
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..2fcc825
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1796 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+/**
+ * struct dw_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @ctype: Card type 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
+ *	&struct dw_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @id: Number of this slot.
+ * @last_detect_state: Most recently observed card detect state.
+ */
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   cmd->opcode, cmd->arg, cmd->flags,
+				   cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				   cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				   data->bytes_xfered, data->blocks,
+				   data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   stop->opcode, stop->arg, stop->flags,
+				   stop->resp[0], stop->resp[1], stop->resp[2],
+				   stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+				   &dw_mci_regs_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+				   &dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				  (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				  (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	/* timeout (maximum) */
+	mci_writel(host, TMOUT, 0xffffffff);
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct
mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		/* We expect a response, so set this bit */
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG;
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE;
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+		 "start command: ARGR=0x%08x CMDR=0x%08x\n",
+		 cmd->arg, cmd_flags);
+
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+			     ((data->flags & MMC_DATA_WRITE)
+			      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
+				    unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+		   SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+			    direction);
+
+	dev_vdbg(&host->pdev->dev,
+		 "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* Disable RX/TX IRQs, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the divide to prevent
+			 * over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			 "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			 " div = %d)\n", slot->id, host->bus_hz, slot->clock,
+			 div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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 */
+	if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
+		cmdflags |= SDMMC_CMD_INIT;
+
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+		 host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	/* Use platform get_ro function, else try on board write protect */
+	if (brd->get_ro)
+		read_only = brd->get_ro(slot->id);
+	else
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+		read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	/* Use platform get_cd function, else try onboard card detect */
+	if (brd->get_cd)
+		present = !brd->get_cd(slot->id);
+	else
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+			== 0 ? 1 : 0;
+
+	if (present)
+		dev_dbg(&mmc->class_dev, "card is present\n");
+	else
+		dev_dbg(&mmc->class_dev, "card is not present\n");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				  struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+			 mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host, struct
mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (status & DW_MCI_DATA_ERROR_FLAGS) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+						"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+						"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pdata = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pdata = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pdata++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pdata = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pdata & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pdata = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pdata & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pdata++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pdata = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pdata = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pdata++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (offset + len <= sg->length) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host, (void *)(buf + offset),
+					remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (offset + len <= sg->length) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host, (void *)(buf + offset),
+					remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status, pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS); /* read-only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+				present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+							      mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01; /* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled, it will be enabled when a card
+	 * is detected.
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/* Useful defaults if platform data is unset. */
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+					  &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+			__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialize "
+				"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialization not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
+				SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
+			      SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata || !pdata->init) {
+		dev_err(&pdev->dev,
+			"Platform data must supply init function\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->select_slot && pdata->num_slots > 1) {
+		dev_err(&pdev->dev,
+			"Platform data must supply select_slot function\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Platform data must supply bus speed\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+		     "HCON reports a reserved host data width!\n"
+		     "Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* Reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	/* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark  = fifo_size / 2 - 1,
+	 *                          Tx Mark = fifo_size / 2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				  ((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		     dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed */
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * Enable interrupts for command done, data over, data empty, card det,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+		   SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+		   DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+		 "%d bit host data width\n", irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			  host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..0ec6856
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,153 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..16b0261
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,217 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+/**
+ * struct dw_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ *	or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ *	transfer is in progress.
+ * @use_dma: Whether DMA channel is initialized or not.
+ * @sg_dma: Bus address of DMA buffer.
+ * @sg_cpu: Virtual address of DMA buffer.
+ * @dma_ops: Pointer to platform-specific DMA callbacks.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ *	command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ *	data transfer. Only valid when EVENT_DATA_COMPLETE or
+ *	EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ *	to be sent.
+ * @dir_status: Direction of current transfer.
+ * @tasklet: Tasklet running the request state machine.
+ * @card_tasklet: Tasklet handling card detect.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ *	to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ *	processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ *	rate and timeout calculations.
+ * @current_speed: Configured rate of the controller.
+ * @num_slots: Number of slots available.
+ * @pdev: Platform device associated with the MMC controller.
+ * @pdata: Platform data associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ * @data_shift: log2 of FIFO item size.
+ * @push_data: Pointer to FIFO push function.
+ * @pull_data: Pointer to FIFO pull function.
+ * @quirks: Set of quirks that apply to specific versions of the IP.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned int bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-09 16:01           ` Chris Ball
  2010-12-09 17:24             ` Will Newton
@ 2010-12-09 17:35             ` Chris Ball
  2010-12-09 17:46               ` Will Newton
  1 sibling, 1 reply; 29+ messages in thread
From: Chris Ball @ 2010-12-09 17:35 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Will Newton, Matt Fleming, linux-kernel, linux-mmc, linux-arm-kernel

Hi Andrew,

On Thu, Dec 09, 2010 at 04:01:57PM +0000, Chris Ball wrote:
> > > Is there something we could depend on that would stop this driver being
> > > presented to everyone, without being far too specific?  At the moment
> > > we'd be making x86 desktop users say whether they have this IP, which
> > > isn't good.  Are the architectures that use this IP already upstream?
> > > Are they all ARM architectures, for instance?
> > 
> > I don't know of any architectures upstream that use this IP block. There is an
> > SoC from NXP that uses this it but it is not upstream:
> > 
> > http://ics.nxp.com/support/software/lpc313x.bsp.linux/
> > 
> > The architecture we tested and debugged this driver on is not upstream either
> > unfortunately. :-/
> 
> Okay.  I think "depends on ARM" is appropriate for now, and we could
> expand that later if the block appears in a MIPS SoC or something.
> Does that work for you?

Oh, hey, rmk says that he seems to recall that akpm says that if the
driver builds on x86 (it does), we should just allow it to be built
there too so that it gets build coverage.  Andrew, does this still
represent your stance?

I've heard the distro maintainers complain about having to investigate
Kconfig entries for drivers that their users would never possibly use,
though, and this certainly falls into that case.  Is there something
better we can do here, to correctly hide this driver from non-ARM users
but also make it clear that it can be built if somebody wants to do a
mass driver build?

Thanks,

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-09 17:35             ` [PATCH] " Chris Ball
@ 2010-12-09 17:46               ` Will Newton
  0 siblings, 0 replies; 29+ messages in thread
From: Will Newton @ 2010-12-09 17:46 UTC (permalink / raw)
  To: Chris Ball
  Cc: Andrew Morton, Matt Fleming, linux-kernel, linux-mmc, linux-arm-kernel

On Thu, Dec 9, 2010 at 5:35 PM, Chris Ball <cjb@laptop.org> wrote:
> Hi Andrew,
>
> On Thu, Dec 09, 2010 at 04:01:57PM +0000, Chris Ball wrote:
>> > > Is there something we could depend on that would stop this driver being
>> > > presented to everyone, without being far too specific?  At the moment
>> > > we'd be making x86 desktop users say whether they have this IP, which
>> > > isn't good.  Are the architectures that use this IP already upstream?
>> > > Are they all ARM architectures, for instance?
>> >
>> > I don't know of any architectures upstream that use this IP block. There is an
>> > SoC from NXP that uses this it but it is not upstream:
>> >
>> > http://ics.nxp.com/support/software/lpc313x.bsp.linux/
>> >
>> > The architecture we tested and debugged this driver on is not upstream either
>> > unfortunately. :-/
>>
>> Okay.  I think "depends on ARM" is appropriate for now, and we could
>> expand that later if the block appears in a MIPS SoC or something.
>> Does that work for you?
>
> Oh, hey, rmk says that he seems to recall that akpm says that if the
> driver builds on x86 (it does), we should just allow it to be built
> there too so that it gets build coverage.  Andrew, does this still
> represent your stance?
>
> I've heard the distro maintainers complain about having to investigate
> Kconfig entries for drivers that their users would never possibly use,
> though, and this certainly falls into that case.  Is there something
> better we can do here, to correctly hide this driver from non-ARM users
> but also make it clear that it can be built if somebody wants to do a
> mass driver build?

The driver does build on x86{_64} and it has been useful for me to do
that in the past to test cosmetic changes. It might also be good to
have it building on x86 to get more compile testing, as I suspect a
lot more people doing randconfig builds are doing it on x86 rather
than arm.

But I'm fine with it either way.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
       [not found]               ` <20101211192320.GA24430@void.printf.net>
@ 2010-12-12  8:41                 ` Russell King - ARM Linux
  2010-12-12 11:15                   ` Russell King - ARM Linux
  2010-12-12 10:57                 ` Will Newton
  2010-12-16 17:04                 ` [PATCH v4] " Will Newton
  2 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-12  8:41 UTC (permalink / raw)
  To: Chris Ball
  Cc: Will Newton, Matt Fleming, linux-mmc, Linux Kernel list,
	linux-arm-kernel

On Sat, Dec 11, 2010 at 07:23:20PM +0000, Chris Ball wrote:
> Hi Will,
> 
> On Thu, Dec 09, 2010 at 05:24:26PM +0000, Will Newton wrote:
> > This adds the mmc host driver for the Synopsys DesignWare mmc
> > host controller, found in a number of embedded SoC designs.
> > 
> > Signed-off-by: Will Newton <will.newton@imgtec.com>
> > Reviewed-by: Matt Fleming <matt@console-pimps.org>
> 
> Running a test build on ARM fails:
> 
> drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_push_data64’:
> drivers/mmc/host/dw_mmc.c:985: error: implicit declaration of function ‘__raw_writeq’
> drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
> drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
> 
> because arch/arm doesn't implement raw versions of these 64-bit accesses.
> I'm surprised that this driver hasn't been compiled on ARM before!  What
> kind of arch are you testing on?  Do you have any ARM hardware (lpc313x?)
> to verify the driver on?

What's the semantics of a 64-bit IO access?  Does the low 32-bit get
written before the high 32-bit, or is it the other way around?  Does
it depend on the endian-ness?  What if some hardware needs the low
32-bit first and other needs the high 32-bit first?

I don't think it's reasonable to expect 32-bit hardware to perform 64-bit
IO accesses.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
       [not found]               ` <20101211192320.GA24430@void.printf.net>
  2010-12-12  8:41                 ` Russell King - ARM Linux
@ 2010-12-12 10:57                 ` Will Newton
  2010-12-12 13:52                   ` Chris Ball
  2010-12-16 17:04                 ` [PATCH v4] " Will Newton
  2 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-12 10:57 UTC (permalink / raw)
  To: Chris Ball; +Cc: Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

On Sat, Dec 11, 2010 at 7:23 PM, Chris Ball <cjb@laptop.org> wrote:
> Hi Will,
>
> On Thu, Dec 09, 2010 at 05:24:26PM +0000, Will Newton wrote:
>> This adds the mmc host driver for the Synopsys DesignWare mmc
>> host controller, found in a number of embedded SoC designs.
>>
>> Signed-off-by: Will Newton <will.newton@imgtec.com>
>> Reviewed-by: Matt Fleming <matt@console-pimps.org>
>
> Running a test build on ARM fails:
>
> drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_push_data64’:
> drivers/mmc/host/dw_mmc.c:985: error: implicit declaration of function ‘__raw_writeq’
> drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
> drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
>
> because arch/arm doesn't implement raw versions of these 64-bit accesses.
> I'm surprised that this driver hasn't been compiled on ARM before!  What

That particular bit of code has been added since it was last built for
arm. Our architecture can do 64bit accesses so we implement readq.
Unfortunately there doesn't seem to be a sane way to conditionalize
code for architectures that have or don't have readq, so I suspect
I'll have to just remove that branch of the if statement for now.

> kind of arch are you testing on?  Do you have any ARM hardware (lpc313x?)
> to verify the driver on?

Our architecture is not merged into mainline yet (although I am
working on that). I don't have an lpc313x board but I would be happy
to work on this code with anyone who does.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12  8:41                 ` Russell King - ARM Linux
@ 2010-12-12 11:15                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-12 11:15 UTC (permalink / raw)
  To: Chris Ball
  Cc: Matt Fleming, linux-mmc, Will Newton, linux-arm-kernel,
	Linux Kernel list

On Sun, Dec 12, 2010 at 08:41:36AM +0000, Russell King - ARM Linux wrote:
> On Sat, Dec 11, 2010 at 07:23:20PM +0000, Chris Ball wrote:
> > Hi Will,
> > 
> > On Thu, Dec 09, 2010 at 05:24:26PM +0000, Will Newton wrote:
> > > This adds the mmc host driver for the Synopsys DesignWare mmc
> > > host controller, found in a number of embedded SoC designs.
> > > 
> > > Signed-off-by: Will Newton <will.newton@imgtec.com>
> > > Reviewed-by: Matt Fleming <matt@console-pimps.org>
> > 
> > Running a test build on ARM fails:
> > 
> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_push_data64’:
> > drivers/mmc/host/dw_mmc.c:985: error: implicit declaration of function ‘__raw_writeq’
> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
> > drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
> > 
> > because arch/arm doesn't implement raw versions of these 64-bit accesses.
> > I'm surprised that this driver hasn't been compiled on ARM before!  What
> > kind of arch are you testing on?  Do you have any ARM hardware (lpc313x?)
> > to verify the driver on?
> 
> What's the semantics of a 64-bit IO access?  Does the low 32-bit get
> written before the high 32-bit, or is it the other way around?  Does
> it depend on the endian-ness?  What if some hardware needs the low
> 32-bit first and other needs the high 32-bit first?
> 
> I don't think it's reasonable to expect 32-bit hardware to perform 64-bit
> IO accesses.

I should cover something else here, in anticipation of someone trying
to be clever...

Using ldrd/strd, or ldm/stm for IO accesses on ARM is a very bad idea
for generic code.  While nothing prevents you from using these for 64-bit
IO accesses, you have to be aware that the normal access guarantees do
not apply.

ldrd/strd is implemented as two individual 32-bit single-access operations,
each of which is atomic, and therefore the instruction can be interrupted
half-way through.  Upon restart, it can repeat the first load/store.

If you're accessing a FIFO, repeated accesses will be a problem, and
will cause data corrpution.  If you're accessing a control register,
the first write could have a side effect (eg, starting DMA) before the
second write has occurred (eg, setting DMA parameters.)

So, even with ldrd/strd, you still have the problem of whether the
64-bit access can be split into two separate 32-bit accesses safely,
but you also have the problem that the individual 32-bit accesses may
be repeated.

You're safer using standard 32-bit load/stores, which won't suffer
from being repeated - but you still have to decide whether high-word
first or low-word first is the correct transfer order.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 10:57                 ` Will Newton
@ 2010-12-12 13:52                   ` Chris Ball
  2010-12-12 14:03                     ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Chris Ball @ 2010-12-12 13:52 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

Hi Will,

On Sun, Dec 12, 2010 at 10:57:44AM +0000, Will Newton wrote:
> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
> > drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
> >
> > because arch/arm doesn't implement raw versions of these 64-bit accesses.
> > I'm surprised that this driver hasn't been compiled on ARM before!  What
> 
> That particular bit of code has been added since it was last built for
> arm. Our architecture can do 64bit accesses so we implement readq.
> Unfortunately there doesn't seem to be a sane way to conditionalize
> code for architectures that have or don't have readq, so I suspect
> I'll have to just remove that branch of the if statement for now.

(Russell, thanks for the excellent explanation.)

Other drivers (MTD, gpio/basic_mmio_gpio.c, fs/fuse, pcm_oss.c)
conditionalize uses of {read,write}q on BITS_PER_LONG >= 64, so
something like this:

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7de6b42..526b5cb 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -988,10 +988,11 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
 		*pData++ = mci_readl(host, DATA);
 		cnt--;
 	}
 }
 
+#if BITS_PER_LONG >= 64
 static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
 {
 	u64 *pData = (u64 *)buf;
 
 	WARN_ON(cnt % 8 != 0);
@@ -1013,10 +1014,11 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
 	while (cnt > 0) {
 		*pData++ = mci_readq(host, DATA);
 		cnt--;
 	}
 }
+#endif
 
 static void dw_mci_read_data_pio(struct dw_mci *host)
 {
 	struct scatterlist *sg = host->sg;
 	void *buf = sg_virt(sg);
@@ -1591,15 +1593,17 @@ static int dw_mci_probe(struct platform_device *pdev)
 	if (!i) {
 		host->push_data = dw_mci_push_data16;
 		host->pull_data = dw_mci_pull_data16;
 		width = 16;
 		host->data_shift = 1;
+#if BITS_PER_LONG >= 64
 	} else if (i == 2) {
 		host->push_data = dw_mci_push_data64;
 		host->pull_data = dw_mci_pull_data64;
 		width = 64;
 		host->data_shift = 3;
+#endif
 	} else {
 		/* Check for a reserved value, and warn if it is */
 		WARN((i != 1),
 			"HCON reports a reserved host data width!\n"
 			"Defaulting to 32-bit access.\n");


This is only useful if you just want the driver to compile (it compiles
on ARM after the above) and don't expect a working device if you find
the HCON programmed with 64-bit width on an ARM board, though.

Thanks,

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 13:52                   ` Chris Ball
@ 2010-12-12 14:03                     ` Will Newton
  2010-12-12 14:11                       ` Russell King - ARM Linux
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-12 14:03 UTC (permalink / raw)
  To: Chris Ball; +Cc: Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

On Sun, Dec 12, 2010 at 1:52 PM, Chris Ball <cjb@laptop.org> wrote:
> Hi Will,
>
> On Sun, Dec 12, 2010 at 10:57:44AM +0000, Will Newton wrote:
>> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
>> > drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
>> >
>> > because arch/arm doesn't implement raw versions of these 64-bit accesses.
>> > I'm surprised that this driver hasn't been compiled on ARM before!  What
>>
>> That particular bit of code has been added since it was last built for
>> arm. Our architecture can do 64bit accesses so we implement readq.
>> Unfortunately there doesn't seem to be a sane way to conditionalize
>> code for architectures that have or don't have readq, so I suspect
>> I'll have to just remove that branch of the if statement for now.
>
> (Russell, thanks for the excellent explanation.)
>
> Other drivers (MTD, gpio/basic_mmio_gpio.c, fs/fuse, pcm_oss.c)
> conditionalize uses of {read,write}q on BITS_PER_LONG >= 64, so
> something like this:

I don't think that's going to work, BITS_PER_LONG isn't equivalent to
"can do 64bit IO accesses", at least it isn't on our architecture. x86
is in the same situation it would appear, although x86 does explicitly
#define readq so it may be possible to #ifdef on that?

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 14:03                     ` Will Newton
@ 2010-12-12 14:11                       ` Russell King - ARM Linux
  2010-12-12 14:31                         ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-12 14:11 UTC (permalink / raw)
  To: Will Newton
  Cc: Chris Ball, Matt Fleming, linux-mmc, Linux Kernel list, linux-arm-kernel

On Sun, Dec 12, 2010 at 02:03:52PM +0000, Will Newton wrote:
> On Sun, Dec 12, 2010 at 1:52 PM, Chris Ball <cjb@laptop.org> wrote:
> > Hi Will,
> >
> > On Sun, Dec 12, 2010 at 10:57:44AM +0000, Will Newton wrote:
> >> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
> >> > drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
> >> >
> >> > because arch/arm doesn't implement raw versions of these 64-bit accesses.
> >> > I'm surprised that this driver hasn't been compiled on ARM before!  What
> >>
> >> That particular bit of code has been added since it was last built for
> >> arm. Our architecture can do 64bit accesses so we implement readq.
> >> Unfortunately there doesn't seem to be a sane way to conditionalize
> >> code for architectures that have or don't have readq, so I suspect
> >> I'll have to just remove that branch of the if statement for now.
> >
> > (Russell, thanks for the excellent explanation.)
> >
> > Other drivers (MTD, gpio/basic_mmio_gpio.c, fs/fuse, pcm_oss.c)
> > conditionalize uses of {read,write}q on BITS_PER_LONG >= 64, so
> > something like this:
> 
> I don't think that's going to work, BITS_PER_LONG isn't equivalent to
> "can do 64bit IO accesses", at least it isn't on our architecture. x86
> is in the same situation it would appear, although x86 does explicitly
> #define readq so it may be possible to #ifdef on that?

Maybe invent CONFIG_HAVE_MMIO_64BIT which architectures can select as
appropriate?

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 14:11                       ` Russell King - ARM Linux
@ 2010-12-12 14:31                         ` Will Newton
  2010-12-12 14:47                           ` Russell King - ARM Linux
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2010-12-12 14:31 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Chris Ball, Matt Fleming, linux-mmc, Linux Kernel list, linux-arm-kernel

On Sun, Dec 12, 2010 at 2:11 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Dec 12, 2010 at 02:03:52PM +0000, Will Newton wrote:
>> On Sun, Dec 12, 2010 at 1:52 PM, Chris Ball <cjb@laptop.org> wrote:
>> > Hi Will,
>> >
>> > On Sun, Dec 12, 2010 at 10:57:44AM +0000, Will Newton wrote:
>> >> > drivers/mmc/host/dw_mmc.c: In function ‘dw_mci_pull_data64’:
>> >> > drivers/mmc/host/dw_mmc.c:998: error: implicit declaration of function ‘__raw_readq’
>> >> >
>> >> > because arch/arm doesn't implement raw versions of these 64-bit accesses.
>> >> > I'm surprised that this driver hasn't been compiled on ARM before!  What
>> >>
>> >> That particular bit of code has been added since it was last built for
>> >> arm. Our architecture can do 64bit accesses so we implement readq.
>> >> Unfortunately there doesn't seem to be a sane way to conditionalize
>> >> code for architectures that have or don't have readq, so I suspect
>> >> I'll have to just remove that branch of the if statement for now.
>> >
>> > (Russell, thanks for the excellent explanation.)
>> >
>> > Other drivers (MTD, gpio/basic_mmio_gpio.c, fs/fuse, pcm_oss.c)
>> > conditionalize uses of {read,write}q on BITS_PER_LONG >= 64, so
>> > something like this:
>>
>> I don't think that's going to work, BITS_PER_LONG isn't equivalent to
>> "can do 64bit IO accesses", at least it isn't on our architecture. x86
>> is in the same situation it would appear, although x86 does explicitly
>> #define readq so it may be possible to #ifdef on that?
>
> Maybe invent CONFIG_HAVE_MMIO_64BIT which architectures can select as
> appropriate?

Wouldn't it be simpler to have a fallback readq/writeq implementation
like the below?

static inline u64 __raw_readq(const volatile void __iomem *addr)
{
        return *(const volatile u64 __force *) addr;
}

It won't break any existing hardware (if your SoC bus does not support
64bit accesses you are unlikely to have peripherals that require it)
and would avoid a number of #ifdefs and/or Kconfig dependencies.

Currently asm-generic/io.h defines the above but it is guarded by
CONFIG_64BIT, which I am not sure is 100% correct in all situations
either.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 14:31                         ` Will Newton
@ 2010-12-12 14:47                           ` Russell King - ARM Linux
  2010-12-12 15:17                             ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Russell King - ARM Linux @ 2010-12-12 14:47 UTC (permalink / raw)
  To: Will Newton
  Cc: Chris Ball, Matt Fleming, linux-mmc, Linux Kernel list, linux-arm-kernel

On Sun, Dec 12, 2010 at 02:31:40PM +0000, Will Newton wrote:
> On Sun, Dec 12, 2010 at 2:11 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > Maybe invent CONFIG_HAVE_MMIO_64BIT which architectures can select as
> > appropriate?
> 
> Wouldn't it be simpler to have a fallback readq/writeq implementation
> like the below?
> 
> static inline u64 __raw_readq(const volatile void __iomem *addr)
> {
>         return *(const volatile u64 __force *) addr;
> }
> 
> It won't break any existing hardware (if your SoC bus does not support
> 64bit accesses you are unlikely to have peripherals that require it)
> and would avoid a number of #ifdefs and/or Kconfig dependencies.

Firstly, why, then, are we discussing fixing dw_mmc.c so it builds on ARM?
Just make its configuration option conditional on !ARM and that specific
problem is solved.

Secondly, providing such a fallback implementation is asking people to
use it, and they'll expect it to work.  Their driver will build and
apparantly work, but because the compiler will use ldrd/strd for it, a
perfectly timed interrupt could cause data corruption.

It's unsafe to provide 64-bit MMIO operations on hardware which is unable
to perform 64-bit MMIO atomically - it will lead to really subtle driver
bugs.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-12 14:47                           ` Russell King - ARM Linux
@ 2010-12-12 15:17                             ` Will Newton
  0 siblings, 0 replies; 29+ messages in thread
From: Will Newton @ 2010-12-12 15:17 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Chris Ball, Matt Fleming, linux-mmc, Linux Kernel list, linux-arm-kernel

On Sun, Dec 12, 2010 at 2:47 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Sun, Dec 12, 2010 at 02:31:40PM +0000, Will Newton wrote:
>> On Sun, Dec 12, 2010 at 2:11 PM, Russell King - ARM Linux
>> <linux@arm.linux.org.uk> wrote:
>> > Maybe invent CONFIG_HAVE_MMIO_64BIT which architectures can select as
>> > appropriate?
>>
>> Wouldn't it be simpler to have a fallback readq/writeq implementation
>> like the below?
>>
>> static inline u64 __raw_readq(const volatile void __iomem *addr)
>> {
>>         return *(const volatile u64 __force *) addr;
>> }
>>
>> It won't break any existing hardware (if your SoC bus does not support
>> 64bit accesses you are unlikely to have peripherals that require it)
>> and would avoid a number of #ifdefs and/or Kconfig dependencies.
>
> Firstly, why, then, are we discussing fixing dw_mmc.c so it builds on ARM?

It's a configurable IP block, so one would assume that if it was
configured with a 64bit FIFO width on ARM then your SoC integration
engineers dropped the ball.

Even on architectures that can do 64bit MMIO, I would expect there to
be systems where that is not safe in practice (e.g. 32bit bus fabric
that will translate a 64bit access into 2 32bit accesses) so I don't
know if it's ideal to make this an architecture level config symbol.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
       [not found]               ` <20101211192320.GA24430@void.printf.net>
  2010-12-12  8:41                 ` Russell King - ARM Linux
  2010-12-12 10:57                 ` Will Newton
@ 2010-12-16 17:04                 ` Will Newton
  2011-01-02  6:20                   ` Chris Ball
  2011-01-18  7:54                   ` Jaehoon Chung
  2 siblings, 2 replies; 29+ messages in thread
From: Will Newton @ 2010-12-16 17:04 UTC (permalink / raw)
  To: Chris Ball; +Cc: Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   16 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1796 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  168 +++++
 include/linux/mmc/dw_mmc.h |  217 ++++++
 5 files changed, 2198 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

 - patch v2: make changes based on review by Matt Fleming.
 - patch v3: changes based on review by Chris Ball.
 - patch v4: make readq use conditional

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..e2373a6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,22 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.

+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	depends on ARM
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	bool "Internal DMAC interface"
+	depends on MMC_DW
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..2fcc825
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1796 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+/**
+ * struct dw_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @ctype: Card type 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
+ *	&struct dw_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @id: Number of this slot.
+ * @last_detect_state: Most recently observed card detect state.
+ */
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   cmd->opcode, cmd->arg, cmd->flags,
+				   cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				   cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				   data->bytes_xfered, data->blocks,
+				   data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				   "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				   stop->opcode, stop->arg, stop->flags,
+				   stop->resp[0], stop->resp[1], stop->resp[2],
+				   stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+				   &dw_mci_regs_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+				   &dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				  (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				  (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	/* timeout (maximum) */
+	mci_writel(host, TMOUT, 0xffffffff);
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct
mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		/* We expect a response, so set this bit */
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG;
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE;
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+		 "start command: ARGR=0x%08x CMDR=0x%08x\n",
+		 cmd->arg, cmd_flags);
+
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+			     ((data->flags & MMC_DATA_WRITE)
+			      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
+				    unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+		   SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+			    direction);
+
+	dev_vdbg(&host->pdev->dev,
+		 "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		 (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* Disable RX/TX IRQs, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the divide to prevent
+			 * over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			 "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			 " div = %d)\n", slot->id, host->bus_hz, slot->clock,
+			 div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			     SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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 */
+	if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))
+		cmdflags |= SDMMC_CMD_INIT;
+
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+		 host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	/* Use platform get_ro function, else try on board write protect */
+	if (brd->get_ro)
+		read_only = brd->get_ro(slot->id);
+	else
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+		read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	/* Use platform get_cd function, else try onboard card detect */
+	if (brd->get_cd)
+		present = !brd->get_cd(slot->id);
+	else
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+			== 0 ? 1 : 0;
+
+	if (present)
+		dev_dbg(&mmc->class_dev, "card is present\n");
+	else
+		dev_dbg(&mmc->class_dev, "card is not present\n");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				  struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+			 mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host, struct
mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (status & DW_MCI_DATA_ERROR_FLAGS) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+						"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+						"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pdata = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pdata = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pdata++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pdata = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pdata & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pdata = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pdata & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pdata++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pdata = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pdata++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pdata = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pdata++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (offset + len <= sg->length) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host, (void *)(buf + offset),
+					remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (offset + len <= sg->length) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host, (void *)(buf + offset),
+					remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status, pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS); /* read-only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+				present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+							      mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01; /* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled, it will be enabled when a card
+	 * is detected.
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/* Useful defaults if platform data is unset. */
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+					  &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+			__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialize "
+				"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialization not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
+				SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
+			      SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata || !pdata->init) {
+		dev_err(&pdev->dev,
+			"Platform data must supply init function\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->select_slot && pdata->num_slots > 1) {
+		dev_err(&pdev->dev,
+			"Platform data must supply select_slot function\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Platform data must supply bus speed\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+		     "HCON reports a reserved host data width!\n"
+		     "Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* Reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	/* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark  = fifo_size / 2 - 1,
+	 *                          Tx Mark = fifo_size / 2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				  ((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		     dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed */
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * Enable interrupts for command done, data over, data empty, card det,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+		   SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+		   DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+		 "%d bit host data width\n", irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			  host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..5dd55a7
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,168 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#ifdef readq
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+#else
+/*
+ * Dummy readq implementation for architectures that don't define it.
+ *
+ * We would assume that none of these architectures would configure
+ * the IP block with a 64bit FIFO width, so this code will never be
+ * executed on those machines. Defining these macros here keeps the
+ * rest of the code free from ifdefs.
+ */
+#define mci_readq(dev, reg)			\
+	(*(volatile u64 __force *)(dev->regs + SDMMC_##reg))
+#define mci_writeq(dev, reg, value)			\
+	(*(volatile u64 __force *)(dev->regs + SDMMC_##reg) = value)
+#endif
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..16b0261
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,217 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+/**
+ * struct dw_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ *	or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ *	transfer is in progress.
+ * @use_dma: Whether DMA channel is initialized or not.
+ * @sg_dma: Bus address of DMA buffer.
+ * @sg_cpu: Virtual address of DMA buffer.
+ * @dma_ops: Pointer to platform-specific DMA callbacks.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ *	command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ *	data transfer. Only valid when EVENT_DATA_COMPLETE or
+ *	EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ *	to be sent.
+ * @dir_status: Direction of current transfer.
+ * @tasklet: Tasklet running the request state machine.
+ * @card_tasklet: Tasklet handling card detect.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ *	to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ *	processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ *	rate and timeout calculations.
+ * @current_speed: Configured rate of the controller.
+ * @num_slots: Number of slots available.
+ * @pdev: Platform device associated with the MMC controller.
+ * @pdata: Platform data associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ * @data_shift: log2 of FIFO item size.
+ * @push_data: Pointer to FIFO push function.
+ * @pull_data: Pointer to FIFO pull function.
+ * @quirks: Set of quirks that apply to specific versions of the IP.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * The @mrq field of struct dw_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned int bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-16 17:04                 ` [PATCH v4] " Will Newton
@ 2011-01-02  6:20                   ` Chris Ball
  2011-01-18  7:54                   ` Jaehoon Chung
  1 sibling, 0 replies; 29+ messages in thread
From: Chris Ball @ 2011-01-02  6:20 UTC (permalink / raw)
  To: Will Newton; +Cc: Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

Hi Will,

On Thu, Dec 16, 2010 at 05:04:36PM +0000, Will Newton wrote:
> This adds the mmc host driver for the Synopsys DesignWare mmc
> host controller, found in a number of embedded SoC designs.
> 
> Signed-off-by: Will Newton <will.newton@imgtec.com>

Thanks, I've pushed this to mmc-next for .38 now after adding:

Reviewed-by: Matt Fleming <matt@console-pimps.org>
Reviewed-by: Chris Ball <cjb@laptop.org>

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2010-12-16 17:04                 ` [PATCH v4] " Will Newton
  2011-01-02  6:20                   ` Chris Ball
@ 2011-01-18  7:54                   ` Jaehoon Chung
  2011-01-18 10:21                     ` Will Newton
  1 sibling, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2011-01-18  7:54 UTC (permalink / raw)
  To: Will Newton
  Cc: Chris Ball, Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

Hi Will.

I have some question...i'm testing dw_mmc in our environment.
but i didn't understand pdata->init(?)...what initialize in there?

plz let me know that..

Thanks
Jaehoon Chung

Will Newton wrote:

> +	host->pdev = pdev;
> +	host->pdata = pdata = pdev->dev.platform_data;
> +	if (!pdata || !pdata->init) {
> +		dev_err(&pdev->dev,
> +			"Platform data must supply init function\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
> +	if (!pdata->select_slot && pdata->num_slots > 1) {
> +		dev_err(&pdev->dev,
> +			"Platform data must supply select_slot function\n");
> +		ret = -ENODEV;
> +		goto err_freehost;
> +	}
> +
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2011-01-18  7:54                   ` Jaehoon Chung
@ 2011-01-18 10:21                     ` Will Newton
  2011-02-08  6:38                       ` Jaehoon Chung
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2011-01-18 10:21 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Chris Ball, Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

On Tue, Jan 18, 2011 at 7:54 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Will.
>
> I have some question...i'm testing dw_mmc in our environment.
> but i didn't understand pdata->init(?)...what initialize in there?

In most of our platforms init is empty so I guess it would be fine to
change that code to ignore a null init pointer. An example of one of
the things we do use the init function for is to switch pins to GPIO
to handle card detect.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2011-01-18 10:21                     ` Will Newton
@ 2011-02-08  6:38                       ` Jaehoon Chung
  2011-02-08 10:29                         ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2011-02-08  6:38 UTC (permalink / raw)
  To: Will Newton
  Cc: Jaehoon Chung, Chris Ball, Linux Kernel list, linux-mmc,
	Matt Fleming, linux-arm-kernel

Hi Will..

I need your help for your code..

In dw_mmc.h, exist some function pointer..

int *get_ro(u32 slot_id);
int *get_cd(u32 slot_id);
...

Just only need slot_id??
If i need the "struct dw_mci", could i append the parameter?

Will Newton wrote:
> On Tue, Jan 18, 2011 at 7:54 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> Hi Will.
>>
>> I have some question...i'm testing dw_mmc in our environment.
>> but i didn't understand pdata->init(?)...what initialize in there?
> 
> In most of our platforms init is empty so I guess it would be fine to
> change that code to ignore a null init pointer. An example of one of
> the things we do use the init function for is to switch pins to GPIO
> to handle card detect.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2011-02-08  6:38                       ` Jaehoon Chung
@ 2011-02-08 10:29                         ` Will Newton
  2011-02-08 10:49                           ` Jaehoon Chung
  0 siblings, 1 reply; 29+ messages in thread
From: Will Newton @ 2011-02-08 10:29 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Chris Ball, Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

On Tue, Feb 8, 2011 at 6:38 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Will..
>
> I need your help for your code..
>
> In dw_mmc.h, exist some function pointer..
>
> int *get_ro(u32 slot_id);
> int *get_cd(u32 slot_id);
> ...
>
> Just only need slot_id??
> If i need the "struct dw_mci", could i append the parameter?

Yes, that should be possible. However I would like to reduce the
number of places the whole struct dw_mci
is passed around if possible, as the whole struct is usually not
needed and it exposes some driver internals.

Which part of struct dw_mci do you need in the callback?

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2011-02-08 10:29                         ` Will Newton
@ 2011-02-08 10:49                           ` Jaehoon Chung
  2011-02-08 12:06                             ` Will Newton
  0 siblings, 1 reply; 29+ messages in thread
From: Jaehoon Chung @ 2011-02-08 10:49 UTC (permalink / raw)
  To: Will Newton
  Cc: Jaehoon Chung, Chris Ball, Linux Kernel list, linux-mmc,
	Matt Fleming, linux-arm-kernel

Hi Will

I'm probing dw_mmc on Samsung SoC...

So i have some board specific data..

But i didn't want to add board specific data in dw_mmc.c
I want to use get_cd()/get_wd()/init()...So i asked to you..:)

If i need not the data information, i will not append them..
you're right, maybe "struct dw_mci" need not..(just example)

And i think that need card_detect irq-handler function..if we use pdata->init..isn't? :)

In future, if i need your help, can i ask to you?

Onemore...Did you test the dw_mmc..?
If you can answer, i want to know the performance..

Will Newton wrote:
> On Tue, Feb 8, 2011 at 6:38 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
>> Hi Will..
>>
>> I need your help for your code..
>>
>> In dw_mmc.h, exist some function pointer..
>>
>> int *get_ro(u32 slot_id);
>> int *get_cd(u32 slot_id);
>> ...
>>
>> Just only need slot_id??
>> If i need the "struct dw_mci", could i append the parameter?
> 
> Yes, that should be possible. However I would like to reduce the
> number of places the whole struct dw_mci
> is passed around if possible, as the whole struct is usually not
> needed and it exposes some driver internals.
> 
> Which part of struct dw_mci do you need in the callback?
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH v4] dw_mmc: Add Synopsys DesignWare mmc host driver.
  2011-02-08 10:49                           ` Jaehoon Chung
@ 2011-02-08 12:06                             ` Will Newton
  0 siblings, 0 replies; 29+ messages in thread
From: Will Newton @ 2011-02-08 12:06 UTC (permalink / raw)
  To: Jaehoon Chung
  Cc: Chris Ball, Linux Kernel list, linux-mmc, Matt Fleming, linux-arm-kernel

On Tue, Feb 8, 2011 at 10:49 AM, Jaehoon Chung <jh80.chung@samsung.com> wrote:
> Hi Will
>
> I'm probing dw_mmc on Samsung SoC...
>
> So i have some board specific data..
>
> But i didn't want to add board specific data in dw_mmc.c
> I want to use get_cd()/get_wd()/init()...So i asked to you..:)

It may be possible to use your board specific data in your callbacks
directly rather than passing the board data to the driver and passing
it back, although it depends on the structure of your code.

> If i need not the data information, i will not append them..
> you're right, maybe "struct dw_mci" need not..(just example)
>
> And i think that need card_detect irq-handler function..if we use pdata->init..isn't? :)

I think the use of pdata->init should be made optional, as it doesn't
seem to be needed on most SoCs.

> In future, if i need your help, can i ask to you?

Yes, certainly. We continue to work on the driver and would be happy
to have our bugs fixed! ;-)

> Onemore...Did you test the dw_mmc..?
> If you can answer, i want to know the performance..

We have tested it, unfortunately I don't have any accurate performance
figures to hand. The perform seems to be in the expected range though.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.
@ 2010-11-29 17:35 Will Newton
  0 siblings, 0 replies; 29+ messages in thread
From: Will Newton @ 2010-11-29 17:35 UTC (permalink / raw)
  To: Linux Kernel list, linux-mmc, Chris Ball

[-- Attachment #1: Type: text/plain, Size: 56245 bytes --]

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   15 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1810 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  155 ++++
 include/linux/mmc/dw_mmc.h |  149 ++++
 5 files changed, 2130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..c99d2c6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.

+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	depends on MMC_DW
+	bool "Internal DMAC interface"
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..fa90b44
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1810 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&dw_mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+			&dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc,
+				  struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		/* expect the respond, need to set this bit */
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	/* write to CMDARG register */
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	/* write to CMD register */
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+		     ((data->flags & MMC_DATA_WRITE)
+		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host,
+				    struct mmc_data *data, unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+			SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+				   direction);
+
+	dev_vdbg(&host->pdev->dev,
+		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* disable irq of RX & TX, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the dvide
+			 * to prevent over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			" div = %d)\n",
+			slot->id, host->bus_hz, slot->clock,
+			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	/* no select the proper slot */
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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, lets send the initialization clock */
+	if (unlikely(
+		test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
+		cmdflags |= SDMMC_CMD_INIT;
+	}
+
+	/* we may need to move this code to mci_start_command */
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_ro != NULL) {
+		read_only = brd->get_ro(slot->id);
+	} else {
+		/* Try on board write protect */
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+	}
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_cd != NULL)
+		present = !brd->get_cd(slot->id);
+	else	/* try onboard card detect */
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+				== 0 ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host,
+				    struct mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pData++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pData++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pData++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (likely(offset + len <= sg->length)) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
+						lets read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (likely(offset + len <= sg->length)) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status,  pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error, lets report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+					present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+						mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01;	/* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled,
+	 * will be enabled when card is detected
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/*useful defaults*/
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	/* Assume card is present initially */
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
+				unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+			&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+				__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialise "
+					"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
+			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET |
+			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (((pdata->num_slots > 1) && !(pdata->select_slot))
+			|| !(pdata->init)) {
+		dev_err(&pdev->dev, "Platform data wrong\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Bus speed undefined in platform data!\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+			"HCON reports a reserved host data width!\n"
+			"Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	 /* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark = fifo_size/2-1,
+	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed ####pd####*/
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * enable interrupt for command done, data over, data empty,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+			"%d bit host data width\n",
+			irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..65bcff7
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,155 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define MCI_SLOT		0
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..d2f92bd
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,149 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned long bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2

[-- Attachment #2: 0001-dw_mmc-Add-Synopsys-DesignWare-mmc-host-driver.patch --]
[-- Type: text/x-patch, Size: 56466 bytes --]

From 199b9628e65de3d796062c9b722865cbf5e9b0b5 Mon Sep 17 00:00:00 2001
From: Will Newton <will.newton@imgtec.com>
Date: Wed, 24 Nov 2010 17:15:04 +0000
Subject: [PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver.

This adds the mmc host driver for the Synopsys DesignWare mmc
host controller, found in a number of embedded SoC designs.

Signed-off-by: Will Newton <will.newton@imgtec.com>
---
 drivers/mmc/host/Kconfig   |   15 +
 drivers/mmc/host/Makefile  |    1 +
 drivers/mmc/host/dw_mmc.c  | 1810 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc.h  |  155 ++++
 include/linux/mmc/dw_mmc.h |  149 ++++
 5 files changed, 2130 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/dw_mmc.c
 create mode 100644 drivers/mmc/host/dw_mmc.h
 create mode 100644 include/linux/mmc/dw_mmc.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f8fa9ef..c99d2c6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -469,6 +469,21 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
 	help
 	  If you say yes here SD-Cards may work on the EZkit.
 
+config MMC_DW
+	tristate "Synopsys DesignWare Memory Card Interface"
+	help
+	  This selects support for the Synopsys DesignWare Mobile Storage IP
+	  block, this provides host support for SD and MMC interfaces, in both
+	  PIO and external DMA modes.
+
+config MMC_DW_IDMAC
+	depends on MMC_DW
+	bool "Internal DMAC interface"
+	help
+	  This selects support for the internal DMAC block within the Synopsys
+	  Designware Mobile Storage IP block. This disables the external DMA
+	  interface.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index d91364d..a5d1cb2 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -31,6 +31,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_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
new file mode 100644
index 0000000..fa90b44
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.c
@@ -0,0 +1,1810 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/bitops.h>
+
+#include "dw_mmc.h"
+
+/* Common flag combinations */
+#define DW_MCI_DATA_ERROR_FLAGS	(SDMMC_INT_DTO | SDMMC_INT_DCRC | \
+				 SDMMC_INT_HTO | SDMMC_INT_SBE  | \
+				 SDMMC_INT_EBE)
+#define DW_MCI_CMD_ERROR_FLAGS	(SDMMC_INT_RTO | SDMMC_INT_RCRC | \
+				 SDMMC_INT_RESP_ERR)
+#define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
+				 DW_MCI_CMD_ERROR_FLAGS  | SDMMC_INT_HLE)
+#define DW_MCI_SEND_STATUS	1
+#define DW_MCI_RECV_STATUS	2
+#define DW_MCI_DMA_THRESHOLD	16
+
+#ifdef CONFIG_MMC_DW_IDMAC
+struct idmac_desc {
+	u32		des0;	/* Control Descriptor */
+#define IDMAC_DES0_DIC	BIT(1)
+#define IDMAC_DES0_LD	BIT(2)
+#define IDMAC_DES0_FD	BIT(3)
+#define IDMAC_DES0_CH	BIT(4)
+#define IDMAC_DES0_ER	BIT(5)
+#define IDMAC_DES0_CES	BIT(30)
+#define IDMAC_DES0_OWN	BIT(31)
+
+	u32		des1;	/* Buffer sizes */
+#define IDMAC_SET_BUFFER1_SIZE(d, s) \
+	((d)->des1 = ((d)->des1 & 0x03ffc000) | ((s) & 0x3fff))
+
+	u32		des2;	/* buffer 1 physical address */
+
+	u32		des3;	/* buffer 2 physical address */
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+struct dw_mci_slot {
+	struct mmc_host		*mmc;
+	struct dw_mci		*host;
+
+	u32			ctype;
+
+	struct mmc_request	*mrq;
+	struct list_head	queue_node;
+
+	unsigned int		clock;
+	unsigned long		flags;
+#define DW_MMC_CARD_PRESENT	0
+#define DW_MMC_CARD_NEED_INIT	1
+	int			id;
+	int			last_detect_state;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int dw_mci_req_show(struct seq_file *s, void *v)
+{
+	struct dw_mci_slot *slot = s->private;
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_command *stop;
+	struct mmc_data	*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_bh(&slot->host->lock);
+	mrq = slot->mrq;
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_bh(&slot->host->lock);
+
+	return 0;
+}
+
+static int dw_mci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_req_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int dw_mci_regs_show(struct seq_file *s, void *v)
+{
+	seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS);
+	seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS);
+	seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD);
+	seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL);
+	seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK);
+	seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA);
+
+	return 0;
+}
+
+static int dw_mci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_mci_regs_show, inode->i_private);
+}
+
+static const struct file_operations dw_mci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= dw_mci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
+{
+	struct mmc_host	*mmc = slot->mmc;
+	struct dw_mci *host = slot->host;
+	struct dentry *root;
+	struct dentry *node;
+
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&dw_mci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	node = debugfs_create_file("req", S_IRUSR, root, slot,
+			&dw_mci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+static void dw_mci_set_timeout(struct dw_mci *host)
+{
+	mci_writel(host, TMOUT, 0xffffffff); /* timeout (maximum) */
+}
+
+static u32 dw_mci_prepare_command(struct mmc_host *mmc,
+				  struct mmc_command *cmd)
+{
+	struct mmc_data	*data;
+	u32 cmdr;
+	cmd->error = -EINPROGRESS;
+
+	cmdr = cmd->opcode;
+
+	if (cmdr == MMC_STOP_TRANSMISSION)
+		cmdr |= SDMMC_CMD_STOP;
+	else
+		cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdr |= SDMMC_CMD_RESP_EXP;
+		/* expect the respond, need to set this bit */
+		if (cmd->flags & MMC_RSP_136)
+			cmdr |= SDMMC_CMD_RESP_LONG; /* expect long respond */
+	}
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdr |= SDMMC_CMD_RESP_CRC;
+
+	data = cmd->data;
+	if (data) {
+		cmdr |= SDMMC_CMD_DAT_EXP;
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= SDMMC_CMD_STRM_MODE; /*  set stream mode */
+		if (data->flags & MMC_DATA_WRITE)
+			cmdr |= SDMMC_CMD_DAT_WR;
+	}
+
+	return cmdr;
+}
+
+static void dw_mci_start_command(struct dw_mci *host,
+				 struct mmc_command *cmd, u32 cmd_flags)
+{
+	host->cmd = cmd;
+	dev_vdbg(&host->pdev->dev,
+			"start command: ARGR=0x%08x CMDR=0x%08x\n",
+			cmd->arg, cmd_flags);
+
+	/* write to CMDARG register */
+	mci_writel(host, CMDARG, cmd->arg);
+	wmb();
+
+	/* write to CMD register */
+	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
+}
+
+static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
+{
+	dw_mci_start_command(host, data->stop, host->stop_cmdr);
+}
+
+/* DMA interface functions */
+static void dw_mci_stop_dma(struct dw_mci *host)
+{
+	if (host->use_dma) {
+		host->dma_ops->stop(host);
+		host->dma_ops->cleanup(host);
+
+	} else {
+		/* Data transfer was stopped by the interrupt handler */
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	}
+}
+
+#ifdef CONFIG_MMC_DW_IDMAC
+static void dw_mci_dma_cleanup(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	if (data)
+		dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+		     ((data->flags & MMC_DATA_WRITE)
+		      ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void dw_mci_idmac_stop_dma(struct dw_mci *host)
+{
+	u32 temp;
+
+	/* Disable and reset the IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp &= ~SDMMC_CTRL_USE_IDMAC;
+	temp |= SDMMC_CTRL_DMA_RESET;
+	mci_writel(host, CTRL, temp);
+
+	/* Stop the IDMAC running */
+	temp = mci_readl(host, BMOD);
+	temp &= ~SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+}
+
+static void dw_mci_idmac_complete_dma(struct dw_mci *host)
+{
+	struct mmc_data *data = host->data;
+
+	dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+	host->dma_ops->cleanup(host);
+
+	/*
+	 * If the card was removed, data will be NULL. No point in trying to
+	 * send the stop command or waiting for NBUSY in this case.
+	 */
+	if (data) {
+		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+}
+
+static void dw_mci_translate_sglist(struct dw_mci *host,
+				    struct mmc_data *data, unsigned int sg_len)
+{
+	int i;
+	struct idmac_desc *desc = host->sg_cpu;
+
+	for (i = 0; i < sg_len; i++, desc++) {
+		unsigned int length = sg_dma_len(&data->sg[i]);
+		u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+		/* Set the OWN bit and disable interrupts for this descriptor */
+		desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+
+		/* Buffer length */
+		IDMAC_SET_BUFFER1_SIZE(desc, length);
+
+		/* Physical address to DMA to/from */
+		desc->des2 = mem_addr;
+	}
+
+	/* Set first descriptor */
+	desc = host->sg_cpu;
+	desc->des0 |= IDMAC_DES0_FD;
+
+	/* Set last descriptor */
+	desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+	desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+	desc->des0 |= IDMAC_DES0_LD;
+
+	wmb();
+}
+
+static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
+{
+	u32 temp;
+
+	dw_mci_translate_sglist(host, host->data, sg_len);
+
+	/* Select IDMAC interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_USE_IDMAC;
+	mci_writel(host, CTRL, temp);
+
+	wmb();
+
+	/* Enable the IDMAC */
+	temp = mci_readl(host, BMOD);
+	temp |= SDMMC_IDMAC_ENABLE;
+	mci_writel(host, BMOD, temp);
+
+	/* Start it running */
+	mci_writel(host, PLDMND, 1);
+}
+
+static int dw_mci_idmac_init(struct dw_mci *host)
+{
+	struct idmac_desc *p;
+	int i;
+
+	/* Number of descriptors in the ring buffer */
+	host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+
+	/* Forward link the descriptor list */
+	for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+		p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+
+	/* Set the last descriptor as the end-of-ring descriptor */
+	p->des3 = host->sg_dma;
+	p->des0 = IDMAC_DES0_ER;
+
+	/* Mask out interrupts - get Tx & Rx complete only */
+	mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
+			SDMMC_IDMAC_INT_TI);
+
+	/* Set the descriptor base address */
+	mci_writel(host, DBADDR, host->sg_dma);
+	return 0;
+}
+
+static struct dw_mci_dma_ops dw_mci_idmac_ops = {
+	.init = dw_mci_idmac_init,
+	.start = dw_mci_idmac_start_dma,
+	.stop = dw_mci_idmac_stop_dma,
+	.complete = dw_mci_idmac_complete_dma,
+	.cleanup = dw_mci_dma_cleanup,
+};
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
+{
+	struct scatterlist *sg;
+	unsigned int i, direction, sg_len;
+	u32 temp;
+
+	/* If we don't have a channel, we can't do DMA */
+	if (!host->use_dma)
+		return -ENODEV;
+
+	/*
+	 * We don't do DMA on "complex" transfers, i.e. with
+	 * non-word-aligned buffers or lengths. Also, we don't bother
+	 * with all the DMA setup overhead for short transfers.
+	 */
+	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+		return -EINVAL;
+	if (data->blksz & 3)
+		return -EINVAL;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3)
+			return -EINVAL;
+	}
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len,
+				   direction);
+
+	dev_vdbg(&host->pdev->dev,
+		"sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n",
+		(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
+		 sg_len);
+
+	/* Enable the DMA interface */
+	temp = mci_readl(host, CTRL);
+	temp |= SDMMC_CTRL_DMA_ENABLE;
+	mci_writel(host, CTRL, temp);
+
+	/* disable irq of RX & TX, let DMA handle it */
+	temp = mci_readl(host, INTMASK);
+	temp  &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
+	mci_writel(host, INTMASK, temp);
+
+	host->dma_ops->start(host, sg_len);
+
+	return 0;
+}
+
+static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
+{
+	u32 temp;
+
+	data->error = -EINPROGRESS;
+
+	WARN_ON(host->data);
+	host->sg = NULL;
+	host->data = data;
+
+	if (dw_mci_submit_data_dma(host, data)) {
+		host->sg = data->sg;
+		host->pio_offset = 0;
+		if (data->flags & MMC_DATA_READ)
+			host->dir_status = DW_MCI_RECV_STATUS;
+		else
+			host->dir_status = DW_MCI_SEND_STATUS;
+
+		temp = mci_readl(host, INTMASK);
+		temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
+		mci_writel(host, INTMASK, temp);
+
+		temp = mci_readl(host, CTRL);
+		temp &= ~SDMMC_CTRL_DMA_ENABLE;
+		mci_writel(host, CTRL, temp);
+	}
+}
+
+static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
+{
+	struct dw_mci *host = slot->host;
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int cmd_status = 0;
+
+	mci_writel(host, CMDARG, arg);
+	wmb();
+	mci_writel(host, CMD, SDMMC_CMD_START | cmd);
+
+	while (time_before(jiffies, timeout)) {
+		cmd_status = mci_readl(host, CMD);
+		if (!(cmd_status & SDMMC_CMD_START))
+			return;
+	}
+	dev_err(&slot->mmc->class_dev,
+		"Timeout sending command (cmd %#x arg %#x status %#x)\n",
+		cmd, arg, cmd_status);
+}
+
+static void dw_mci_setup_bus(struct dw_mci_slot *slot)
+{
+	struct dw_mci *host = slot->host;
+	u32 div;
+
+	if (slot->clock != host->current_speed) {
+		if (host->bus_hz % slot->clock)
+			/*
+			 * move the + 1 after the dvide
+			 * to prevent over-clocking the card.
+			 */
+			div = ((host->bus_hz / slot->clock) >> 1) + 1;
+		else
+			div = (host->bus_hz  / slot->clock) >> 1;
+
+		dev_info(&slot->mmc->class_dev,
+			"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
+			" div = %d)\n",
+			slot->id, host->bus_hz, slot->clock,
+			div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
+
+		/* disable clock */
+		mci_writel(host, CLKENA, 0);
+		mci_writel(host, CLKSRC, 0);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* set clock to desired speed */
+		mci_writel(host, CLKDIV, div);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		/* enable clock */
+		mci_writel(host, CLKENA, SDMMC_CLKEN_ENABLE);
+
+		/* inform CIU */
+		mci_send_cmd(slot,
+			SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+
+		host->current_speed = slot->clock;
+	}
+
+	/* Set the current slot bus width */
+	mci_writel(host, CTYPE, slot->ctype);
+}
+
+static void dw_mci_start_request(struct dw_mci *host,
+				 struct dw_mci_slot *slot)
+{
+	struct mmc_request *mrq;
+	struct mmc_command *cmd;
+	struct mmc_data	*data;
+	u32 cmdflags;
+
+	mrq = slot->mrq;
+	/* no select the proper slot */
+	if (host->pdata->select_slot)
+		host->pdata->select_slot(slot->id);
+
+	/* Slot specific timing and width adjustment */
+	dw_mci_setup_bus(slot);
+
+	host->cur_slot = slot;
+	host->mrq = mrq;
+
+	host->pending_events = 0;
+	host->completed_events = 0;
+	host->data_status = 0;
+
+	data = mrq->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, lets send the initialization clock */
+	if (unlikely(
+		test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags))) {
+		cmdflags |= SDMMC_CMD_INIT;
+	}
+
+	/* we may need to move this code to mci_start_command */
+	if (data) {
+		dw_mci_submit_data(host, data);
+		wmb();
+	}
+
+	dw_mci_start_command(host, cmd, cmdflags);
+
+	if (mrq->stop)
+		host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
+}
+
+static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
+				 struct mmc_request *mrq)
+{
+	dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+			host->state);
+
+	spin_lock_bh(&host->lock);
+	slot->mrq = mrq;
+
+	if (host->state == STATE_IDLE) {
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		list_add_tail(&slot->queue_node, &host->queue);
+	}
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci *host = slot->host;
+
+	WARN_ON(slot->mrq);
+
+	if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	/* We don't support multiple blocks of weird lengths. */
+	dw_mci_queue_request(host, slot, mrq);
+}
+
+static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+
+	/* set default 1 bit mode */
+	slot->ctype = SDMMC_CTYPE_1BIT;
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		slot->ctype = SDMMC_CTYPE_1BIT;
+		break;
+	case MMC_BUS_WIDTH_4:
+		slot->ctype = SDMMC_CTYPE_4BIT;
+		break;
+	}
+
+	if (ios->clock) {
+		/*
+		 * Use mirror of ios->clock to prevent race with mmc
+		 * core ios update when finding the minimum.
+		 */
+		slot->clock = ios->clock;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_UP:
+		set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static int dw_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_ro != NULL) {
+		read_only = brd->get_ro(slot->id);
+	} else {
+		/* Try on board write protect */
+		read_only =
+			mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+	}
+
+	dev_dbg(&mmc->class_dev, "card is %s\n",
+				read_only ? "read-only" : "read-write");
+
+	return read_only;
+}
+
+static int dw_mci_get_cd(struct mmc_host *mmc)
+{
+	int present;
+	struct dw_mci_slot *slot = mmc_priv(mmc);
+	struct dw_mci_board *brd = slot->host->pdata;
+
+	if (brd->get_cd != NULL)
+		present = !brd->get_cd(slot->id);
+	else	/* try onboard card detect */
+		present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
+				== 0 ? 1 : 0;
+
+	dev_dbg(&mmc->class_dev, "card is %spresent\n", present ? "" : "not ");
+
+	return present;
+}
+
+static const struct mmc_host_ops dw_mci_ops = {
+	.request	= dw_mci_request,
+	.set_ios	= dw_mci_set_ios,
+	.get_ro		= dw_mci_get_ro,
+	.get_cd		= dw_mci_get_cd,
+};
+
+static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
+	__releases(&host->lock)
+	__acquires(&host->lock)
+{
+	struct dw_mci_slot *slot;
+	struct mmc_host	*prev_mmc = host->cur_slot->mmc;
+
+	WARN_ON(host->cmd || host->data);
+
+	host->cur_slot->mrq = NULL;
+	host->mrq = NULL;
+	if (!list_empty(&host->queue)) {
+		slot = list_entry(host->queue.next,
+				struct dw_mci_slot, queue_node);
+		list_del(&slot->queue_node);
+		dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+				mmc_hostname(slot->mmc));
+		host->state = STATE_SENDING_CMD;
+		dw_mci_start_request(host, slot);
+	} else {
+		dev_vdbg(&host->pdev->dev, "list empty\n");
+		host->state = STATE_IDLE;
+	}
+
+	spin_unlock(&host->lock);
+	mmc_request_done(prev_mmc, mrq);
+
+	spin_lock(&host->lock);
+}
+
+static void dw_mci_command_complete(struct dw_mci *host,
+				    struct mmc_command *cmd)
+{
+	u32 status = host->cmd_status;
+
+	host->cmd_status = 0;
+
+	/* Read the response from the card (up to 16 bytes) */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			cmd->resp[3] = mci_readl(host, RESP0);
+			cmd->resp[2] = mci_readl(host, RESP1);
+			cmd->resp[1] = mci_readl(host, RESP2);
+			cmd->resp[0] = mci_readl(host, RESP3);
+		} else {
+			cmd->resp[0] = mci_readl(host, RESP0);
+			cmd->resp[1] = 0;
+			cmd->resp[2] = 0;
+			cmd->resp[3] = 0;
+		}
+	}
+
+	if (status & SDMMC_INT_RTO)
+		cmd->error = -ETIMEDOUT;
+	else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
+		cmd->error = -EILSEQ;
+	else if (status & SDMMC_INT_RESP_ERR)
+		cmd->error = -EIO;
+	else
+		cmd->error = 0;
+
+	if (cmd->error) {
+		/* newer ip versions need a delay between retries */
+		if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
+			mdelay(20);
+
+		if (cmd->data) {
+			host->data = NULL;
+			dw_mci_stop_dma(host);
+		}
+	}
+}
+
+static void dw_mci_tasklet_func(unsigned long priv)
+{
+	struct dw_mci *host = (struct dw_mci *)priv;
+	struct mmc_data	*data;
+	struct mmc_command *cmd;
+	enum dw_mci_state state;
+	enum dw_mci_state prev_state;
+	u32 status;
+
+	spin_lock(&host->lock);
+
+	state = host->state;
+	data = host->data;
+
+	do {
+		prev_state = state;
+
+		switch (state) {
+		case STATE_IDLE:
+			break;
+
+		case STATE_SENDING_CMD:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			cmd = host->cmd;
+			host->cmd = NULL;
+			set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
+			dw_mci_command_complete(host, host->mrq->cmd);
+			if (!host->mrq->data || cmd->error) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_DATA;
+			/* fall through */
+
+		case STATE_SENDING_DATA:
+			if (test_and_clear_bit(EVENT_DATA_ERROR,
+					       &host->pending_events)) {
+				dw_mci_stop_dma(host);
+				if (data->stop)
+					send_stop_cmd(host, data);
+				state = STATE_DATA_ERROR;
+				break;
+			}
+
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
+			prev_state = state = STATE_DATA_BUSY;
+			/* fall through */
+
+		case STATE_DATA_BUSY:
+			if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->data = NULL;
+			set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
+			status = host->data_status;
+
+			if (unlikely(status & DW_MCI_DATA_ERROR_FLAGS)) {
+				if (status & SDMMC_INT_DTO) {
+					dev_err(&host->pdev->dev,
+							"data timeout error\n");
+					data->error = -ETIMEDOUT;
+				} else if (status & SDMMC_INT_DCRC) {
+					dev_err(&host->pdev->dev,
+							"data CRC error\n");
+					data->error = -EILSEQ;
+				} else {
+					dev_err(&host->pdev->dev,
+						"data FIFO error "
+						"(status=%08x)\n",
+						status);
+					data->error = -EIO;
+				}
+			} else {
+				data->bytes_xfered = data->blocks * data->blksz;
+				data->error = 0;
+			}
+
+			if (!data->stop) {
+				dw_mci_request_end(host, host->mrq);
+				goto unlock;
+			}
+
+			prev_state = state = STATE_SENDING_STOP;
+			if (!data->error)
+				send_stop_cmd(host, data);
+			/* fall through */
+
+		case STATE_SENDING_STOP:
+			if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
+						&host->pending_events))
+				break;
+
+			host->cmd = NULL;
+			dw_mci_command_complete(host, host->mrq->stop);
+			dw_mci_request_end(host, host->mrq);
+			goto unlock;
+
+		case STATE_DATA_ERROR:
+			if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
+						&host->pending_events))
+				break;
+
+			state = STATE_DATA_BUSY;
+			break;
+		}
+	} while (state != prev_state);
+
+	host->state = state;
+unlock:
+	spin_unlock(&host->lock);
+
+}
+
+static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		mci_writew(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
+{
+	u16 *pData = (u16 *)buf;
+
+	WARN_ON(cnt % 2 != 0);
+
+	cnt = cnt >> 1;
+	while (cnt > 0) {
+		*pData++ = mci_readw(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		mci_writel(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
+{
+	u32 *pData = (u32 *)buf;
+
+	WARN_ON(cnt % 4 != 0);
+	WARN_ON((unsigned long)pData & 0x3);
+
+	cnt = cnt >> 2;
+	while (cnt > 0) {
+		*pData++ = mci_readl(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		mci_writeq(host, DATA, *pData++);
+		cnt--;
+	}
+}
+
+static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
+{
+	u64 *pData = (u64 *)buf;
+
+	WARN_ON(cnt % 8 != 0);
+
+	cnt = cnt >> 3;
+	while (cnt > 0) {
+		*pData++ = mci_readq(host, DATA);
+		cnt--;
+	}
+}
+
+static void dw_mci_read_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len, old_len, count = 0;
+
+	do {
+		len = SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift;
+		if (count == 0)
+			old_len = len;
+
+		if (likely(offset + len <= sg->length)) {
+			host->pull_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+
+			if (offset == sg->length) {
+				flush_dcache_page(sg_page(sg));
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+			host->pull_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			flush_dcache_page(sg_page(sg));
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->pull_data(host, buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+		count++;
+	} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready
+						lets read again*/
+	len = SDMMC_GET_FCNT(mci_readl(host, STATUS));
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_write_data_pio(struct dw_mci *host)
+{
+	struct scatterlist *sg = host->sg;
+	void *buf = sg_virt(sg);
+	unsigned int offset = host->pio_offset;
+	struct mmc_data	*data = host->data;
+	int shift = host->data_shift;
+	u32 status;
+	unsigned int nbytes = 0, len;
+
+	do {
+		len = SDMMC_FIFO_SZ -
+			(SDMMC_GET_FCNT(mci_readl(host, STATUS)) << shift);
+		if (likely(offset + len <= sg->length)) {
+			host->push_data(host, (void *)(buf + offset), len);
+
+			offset += len;
+			nbytes += len;
+			if (offset == sg->length) {
+				host->sg = sg = sg_next(sg);
+				if (!sg)
+					goto done;
+
+				offset = 0;
+				buf = sg_virt(sg);
+			}
+		} else {
+			unsigned int remaining = sg->length - offset;
+
+			host->push_data(host,
+				(void *)(buf + offset), remaining);
+			nbytes += remaining;
+
+			host->sg = sg = sg_next(sg);
+			if (!sg)
+				goto done;
+
+			offset = len - remaining;
+			buf = sg_virt(sg);
+			host->push_data(host, (void *)buf, offset);
+			nbytes += offset;
+		}
+
+		status = mci_readl(host, MINTSTS);
+		mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+		if (status & DW_MCI_DATA_ERROR_FLAGS) {
+			host->data_status = status;
+			data->bytes_xfered += nbytes;
+
+			smp_wmb();
+
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+
+			tasklet_schedule(&host->tasklet);
+			return;
+		}
+	} while (status & SDMMC_INT_TXDR); /* if TXDR, lets write again */
+
+	host->pio_offset = offset;
+	data->bytes_xfered += nbytes;
+
+	return;
+
+done:
+	data->bytes_xfered += nbytes;
+	smp_wmb();
+	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+}
+
+static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
+{
+	if (!host->cmd_status)
+		host->cmd_status = status;
+
+	smp_wmb();
+
+	set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
+{
+	struct dw_mci *host = dev_id;
+	u32 status,  pending;
+	unsigned int pass_count = 0;
+
+	do {
+		status = mci_readl(host, RINTSTS);
+		pending = mci_readl(host, MINTSTS);/* read only mask reg */
+
+		/*
+		 * DTO fix - version 2.10a and below, and only if internal DMA
+		 * is configured.
+		 */
+		if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
+			if (!pending &&
+			    ((mci_readl(host, STATUS) >> 17) & 0x1fff))
+				pending |= SDMMC_INT_DATA_OVER;
+		}
+
+		if (!pending)
+			break;
+
+		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
+			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
+			host->cmd_status = status;
+			smp_wmb();
+			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & DW_MCI_DATA_ERROR_FLAGS) {
+			/* if there is an error, lets report DATA_ERROR */
+			mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
+			host->data_status = status;
+			smp_wmb();
+			set_bit(EVENT_DATA_ERROR, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+
+		if (pending & SDMMC_INT_DATA_OVER) {
+			mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
+			if (!host->data_status)
+				host->data_status = status;
+			smp_wmb();
+			if (host->dir_status == DW_MCI_RECV_STATUS) {
+				if (host->sg != NULL)
+					dw_mci_read_data_pio(host);
+			}
+			set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+			tasklet_schedule(&host->tasklet);
+		}
+
+		if (pending & SDMMC_INT_RXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
+			if (host->sg)
+				dw_mci_read_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_TXDR) {
+			mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
+			if (host->sg)
+				dw_mci_write_data_pio(host);
+		}
+
+		if (pending & SDMMC_INT_CMD_DONE) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
+			dw_mci_cmd_interrupt(host, status);
+		}
+
+		if (pending & SDMMC_INT_CD) {
+			mci_writel(host, RINTSTS, SDMMC_INT_CD);
+			tasklet_schedule(&host->card_tasklet);
+		}
+
+	} while (pass_count++ < 5);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	/* Handle DMA interrupts */
+	pending = mci_readl(host, IDSTS);
+	if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+				SDMMC_IDMAC_INT_RI);
+		mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		host->dma_ops->complete(host);
+	}
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static void dw_mci_tasklet_card(unsigned long data)
+{
+	struct dw_mci *host = (struct dw_mci *)data;
+	int i;
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		struct mmc_host *mmc = slot->mmc;
+		struct mmc_request *mrq;
+		int present;
+		u32 ctrl;
+
+		present = dw_mci_get_cd(mmc);
+		while (present != slot->last_detect_state) {
+			spin_lock(&host->lock);
+
+			dev_dbg(&slot->mmc->class_dev, "card %s\n",
+					present ? "inserted" : "removed");
+
+			/* Card change detected */
+			slot->last_detect_state = present;
+
+			/* Power up slot */
+			if (present != 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id,
+						mmc->ocr_avail);
+
+				set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+			}
+
+			/* Clean up queue if present */
+			mrq = slot->mrq;
+			if (mrq) {
+				if (mrq == host->mrq) {
+					host->data = NULL;
+					host->cmd = NULL;
+
+					switch (host->state) {
+					case STATE_IDLE:
+						break;
+					case STATE_SENDING_CMD:
+						mrq->cmd->error = -ENOMEDIUM;
+						if (!mrq->data)
+							break;
+						/* fall through */
+					case STATE_SENDING_DATA:
+						mrq->data->error = -ENOMEDIUM;
+						dw_mci_stop_dma(host);
+						break;
+					case STATE_DATA_BUSY:
+					case STATE_DATA_ERROR:
+						if (mrq->data->error == -EINPROGRESS)
+							mrq->data->error = -ENOMEDIUM;
+						if (!mrq->stop)
+							break;
+						/* fall through */
+					case STATE_SENDING_STOP:
+						mrq->stop->error = -ENOMEDIUM;
+						break;
+					}
+
+					dw_mci_request_end(host, mrq);
+				} else {
+					list_del(&slot->queue_node);
+					mrq->cmd->error = -ENOMEDIUM;
+					if (mrq->data)
+						mrq->data->error = -ENOMEDIUM;
+					if (mrq->stop)
+						mrq->stop->error = -ENOMEDIUM;
+
+					spin_unlock(&host->lock);
+					mmc_request_done(slot->mmc, mrq);
+					spin_lock(&host->lock);
+				}
+			}
+
+			/* Power down slot */
+			if (present == 0) {
+				if (host->pdata->setpower)
+					host->pdata->setpower(slot->id, 0);
+				clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+				/*
+				 * Clear down the FIFO - doing so generates a
+				 * block interrupt, hence setting the
+				 * scatter-gather pointer to NULL.
+				 */
+				host->sg = NULL;
+
+				ctrl = mci_readl(host, CTRL);
+				ctrl |= SDMMC_CTRL_FIFO_RESET;
+				mci_writel(host, CTRL, ctrl);
+
+#ifdef CONFIG_MMC_DW_IDMAC
+				ctrl = mci_readl(host, BMOD);
+				ctrl |= 0x01;	/* Software reset of DMA */
+				mci_writel(host, BMOD, ctrl);
+#endif
+
+			}
+
+			spin_unlock(&host->lock);
+			present = dw_mci_get_cd(mmc);
+		}
+
+		mmc_detect_change(slot->mmc,
+			msecs_to_jiffies(host->pdata->detect_delay_ms));
+	}
+}
+
+static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
+{
+	struct mmc_host *mmc;
+	struct dw_mci_slot *slot;
+
+	mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), &host->pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	slot = mmc_priv(mmc);
+	slot->id = id;
+	slot->mmc = mmc;
+	slot->host = host;
+
+	mmc->ops = &dw_mci_ops;
+	mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
+	mmc->f_max = host->bus_hz;
+
+	if (host->pdata->get_ocr)
+		mmc->ocr_avail = host->pdata->get_ocr(id);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/*
+	 * Start with slot power disabled,
+	 * will be enabled when card is detected
+	 */
+	if (host->pdata->setpower)
+		host->pdata->setpower(id, 0);
+
+	mmc->caps = 0;
+	if (host->pdata->get_bus_wd)
+		if (host->pdata->get_bus_wd(slot->id) >= 4)
+			mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+#ifdef CONFIG_MMC_DW_IDMAC
+	mmc->max_segs = host->ring_size;
+	mmc->max_blk_size = 65536;
+	mmc->max_blk_count = host->ring_size;
+	mmc->max_seg_size = 0x1000;
+	mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+#else
+	if (host->pdata->blk_settings) {
+		mmc->max_segs = host->pdata->blk_settings->max_segs;
+		mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
+		mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
+		mmc->max_req_size = host->pdata->blk_settings->max_req_size;
+		mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
+	} else {
+		/*useful defaults*/
+		mmc->max_segs = 64;
+		mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
+		mmc->max_blk_count = 512;
+		mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+		mmc->max_seg_size = mmc->max_req_size;
+	}
+#endif /* CONFIG_MMC_DW_IDMAC */
+
+	/* Assume card is present initially */
+	if (dw_mci_get_cd(mmc))
+		set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+	else
+		clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+
+	host->slot[id] = slot;
+	mmc_add_host(mmc);
+
+#if defined(CONFIG_DEBUG_FS)
+	dw_mci_init_debugfs(slot);
+#endif
+
+	/* Card initially undetected */
+	slot->last_detect_state = 0;
+
+	return 0;
+}
+
+static void dw_mci_cleanup_slot(struct dw_mci_slot *slot,
+				unsigned int id)
+{
+	/* Shutdown detect IRQ */
+	if (slot->host->pdata->exit)
+		slot->host->pdata->exit(id);
+
+	/* Debugfs stuff is cleaned up by mmc core */
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[id] = NULL;
+	mmc_free_host(slot->mmc);
+}
+
+static void dw_mci_init_dma(struct dw_mci *host)
+{
+	/* Alloc memory for sg translation */
+	host->sg_cpu = dma_alloc_coherent(&host->pdev->dev, PAGE_SIZE,
+			&host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&host->pdev->dev, "%s: could not alloc DMA memory\n",
+				__func__);
+		goto no_dma;
+	}
+
+	/* Determine which DMA interface to use */
+#ifdef CONFIG_MMC_DW_IDMAC
+	host->dma_ops = &dw_mci_idmac_ops;
+	dev_info(&host->pdev->dev, "Using internal DMA controller.\n");
+#endif
+
+	if (!host->dma_ops)
+		goto no_dma;
+
+	if (host->dma_ops->init) {
+		if (host->dma_ops->init(host)) {
+			dev_err(&host->pdev->dev, "%s: Unable to initialise "
+					"DMA Controller.\n", __func__);
+			goto no_dma;
+		}
+	} else {
+		dev_err(&host->pdev->dev, "DMA initialisation not found.\n");
+		goto no_dma;
+	}
+
+	host->use_dma = 1;
+	return;
+
+no_dma:
+	dev_info(&host->pdev->dev, "Using PIO mode.\n");
+	host->use_dma = 0;
+	return;
+}
+
+static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	unsigned int ctrl;
+
+	mci_writel(host, CTRL, (SDMMC_CTRL_RESET |
+			SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET));
+
+	/* wait till resets clear */
+	do {
+		ctrl = mci_readl(host, CTRL);
+		if (!(ctrl & (SDMMC_CTRL_RESET |
+			      SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)))
+			return true;
+	} while (time_before(jiffies, timeout));
+
+	dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
+
+	return false;
+}
+
+static int dw_mci_probe(struct platform_device *pdev)
+{
+	struct dw_mci *host;
+	struct resource	*regs;
+	struct dw_mci_board *pdata;
+	int irq, ret, i, width;
+	u32 fifo_size;
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs)
+		return -ENXIO;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->pdev = pdev;
+	host->pdata = pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "Platform data missing\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (((pdata->num_slots > 1) && !(pdata->select_slot))
+			|| !(pdata->init)) {
+		dev_err(&pdev->dev, "Platform data wrong\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	if (!pdata->bus_hz) {
+		dev_err(&pdev->dev,
+			"Bus speed undefined in platform data!\n");
+		ret = -ENODEV;
+		goto err_freehost;
+	}
+
+	host->bus_hz = pdata->bus_hz;
+	host->quirks = pdata->quirks;
+
+	spin_lock_init(&host->lock);
+	INIT_LIST_HEAD(&host->queue);
+
+	ret = -ENOMEM;
+	host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+	if (!host->regs)
+		goto err_freehost;
+
+	host->dma_ops = pdata->dma_ops;
+	dw_mci_init_dma(host);
+
+	/*
+	 * Get the host data width - this assumes that HCON has been set with
+	 * the correct values.
+	 */
+	i = (mci_readl(host, HCON) >> 7) & 0x7;
+	if (!i) {
+		host->push_data = dw_mci_push_data16;
+		host->pull_data = dw_mci_pull_data16;
+		width = 16;
+		host->data_shift = 1;
+	} else if (i == 2) {
+		host->push_data = dw_mci_push_data64;
+		host->pull_data = dw_mci_pull_data64;
+		width = 64;
+		host->data_shift = 3;
+	} else {
+		/* Check for a reserved value, and warn if it is */
+		WARN((i != 1),
+			"HCON reports a reserved host data width!\n"
+			"Defaulting to 32-bit access.\n");
+		host->push_data = dw_mci_push_data32;
+		host->pull_data = dw_mci_pull_data32;
+		width = 32;
+		host->data_shift = 2;
+	}
+
+	/* reset all blocks */
+	if (!mci_wait_reset(&pdev->dev, host)) {
+		ret = -ENODEV;
+		goto err_dmaunmap;
+	}
+
+	 /* Clear the interrupts for the host controller */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	/* Put in max timeout */
+	mci_writel(host, TMOUT, 0xFFFFFFFF);
+
+	/*
+	 * FIFO threshold settings  RxMark = fifo_size/2-1,
+	 *                          Tx Mark =fifo_size/2 DMA Size = 8
+	 */
+	fifo_size = mci_readl(host, FIFOTH);
+	fifo_size = (fifo_size >> 16) & 0x7ff;
+	mci_writel(host, FIFOTH, ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
+				((fifo_size/2) << 0)));
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
+	tasklet_init(&host->card_tasklet,
+		dw_mci_tasklet_card, (unsigned long)host);
+
+	ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
+	if (ret)
+		goto err_dmaunmap;
+
+	platform_set_drvdata(pdev, host);
+
+	if (host->pdata->num_slots)
+		host->num_slots = host->pdata->num_slots;
+	else
+		host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
+
+	/* We need at least one slot to succeed ####pd####*/
+	for (i = 0; i < host->num_slots; i++) {
+		ret = dw_mci_init_slot(host, i);
+		if (ret) {
+			ret = -ENODEV;
+			goto err_init_slot;
+		}
+	}
+
+	/*
+	 * enable interrupt for command done, data over, data empty,
+	 * receive ready and error such as transmit, receive timeout, crc error
+	 */
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+				SDMMC_INT_TXDR | SDMMC_INT_RXDR |
+				DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+	mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /*enable mci interrupt*/
+
+	dev_info(&pdev->dev, "DW MMC controller at irq %d, "
+			"%d bit host data width\n",
+			irq, width);
+	if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
+		dev_info(&pdev->dev, "Internal DMAC interrupt fix enabled.\n");
+
+	return 0;
+
+err_init_slot:
+	/* De-init any initialized slots */
+	while (i > 0) {
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+		i--;
+	}
+	free_irq(irq, host);
+
+err_dmaunmap:
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+	dma_free_coherent(&host->pdev->dev, PAGE_SIZE,
+			host->sg_cpu, host->sg_dma);
+	iounmap(host->regs);
+
+err_freehost:
+	kfree(host);
+	return ret;
+}
+
+static int __exit dw_mci_remove(struct platform_device *pdev)
+{
+	struct dw_mci *host = platform_get_drvdata(pdev);
+	int i;
+
+	mci_writel(host, RINTSTS, 0xFFFFFFFF);
+	mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < host->num_slots; i++) {
+		dev_dbg(&pdev->dev, "remove slot %d\n", i);
+		if (host->slot[i])
+			dw_mci_cleanup_slot(host->slot[i], i);
+	}
+
+	/* disable clock to CIU */
+	mci_writel(host, CLKENA, 0);
+	mci_writel(host, CLKSRC, 0);
+
+	free_irq(platform_get_irq(pdev, 0), host);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+	if (host->use_dma && host->dma_ops->exit)
+		host->dma_ops->exit(host);
+
+	iounmap(host->regs);
+
+	kfree(host);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * 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)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_suspend_host(slot->mmc);
+		if (ret < 0) {
+			while (--i >= 0) {
+				slot = host->slot[i];
+				if (slot)
+					mmc_resume_host(host->slot[i]->mmc);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int dw_mci_resume(struct platform_device *pdev)
+{
+	int i, ret;
+	struct dw_mci *host = platform_get_drvdata(pdev);
+
+	for (i = 0; i < host->num_slots; i++) {
+		struct dw_mci_slot *slot = host->slot[i];
+		if (!slot)
+			continue;
+		ret = mmc_resume_host(host->slot[i]->mmc);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+#define dw_mci_suspend	NULL
+#define dw_mci_resume	NULL
+#endif /* CONFIG_PM */
+
+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",
+	},
+};
+
+static int __init dw_mci_init(void)
+{
+	return platform_driver_probe(&dw_mci_driver, dw_mci_probe);
+}
+
+static void __exit dw_mci_exit(void)
+{
+	platform_driver_unregister(&dw_mci_driver);
+}
+
+module_init(dw_mci_init);
+module_exit(dw_mci_exit);
+
+MODULE_DESCRIPTION("DW Multimedia Card Interface driver");
+MODULE_AUTHOR("NXP Semiconductor VietNam");
+MODULE_AUTHOR("Imagination Technologies Ltd");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
new file mode 100644
index 0000000..65bcff7
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc.h
@@ -0,0 +1,155 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _DW_MMC_H_
+#define _DW_MMC_H_
+
+#define MCI_SLOT		0
+
+#define SDMMC_CTRL		0x000
+#define SDMMC_PWREN		0x004
+#define SDMMC_CLKDIV		0x008
+#define SDMMC_CLKSRC		0x00c
+#define SDMMC_CLKENA		0x010
+#define SDMMC_TMOUT		0x014
+#define SDMMC_CTYPE		0x018
+#define SDMMC_BLKSIZ		0x01c
+#define SDMMC_BYTCNT		0x020
+#define SDMMC_INTMASK		0x024
+#define SDMMC_CMDARG		0x028
+#define SDMMC_CMD		0x02c
+#define SDMMC_RESP0		0x030
+#define SDMMC_RESP1		0x034
+#define SDMMC_RESP2		0x038
+#define SDMMC_RESP3		0x03c
+#define SDMMC_MINTSTS		0x040
+#define SDMMC_RINTSTS		0x044
+#define SDMMC_STATUS		0x048
+#define SDMMC_FIFOTH		0x04c
+#define SDMMC_CDETECT		0x050
+#define SDMMC_WRTPRT		0x054
+#define SDMMC_GPIO		0x058
+#define SDMMC_TCBCNT		0x05c
+#define SDMMC_TBBCNT		0x060
+#define SDMMC_DEBNCE		0x064
+#define SDMMC_USRID		0x068
+#define SDMMC_VERID		0x06c
+#define SDMMC_HCON		0x070
+#define SDMMC_BMOD		0x080
+#define SDMMC_PLDMND		0x084
+#define SDMMC_DBADDR		0x088
+#define SDMMC_IDSTS		0x08c
+#define SDMMC_IDINTEN		0x090
+#define SDMMC_DSCADDR		0x094
+#define SDMMC_BUFADDR		0x098
+#define SDMMC_DATA		0x100
+#define SDMMC_DATA_ADR		0x100
+
+/* shift bit field */
+#define _SBF(f, v)		((v) << (f))
+
+/* Control register defines */
+#define SDMMC_CTRL_USE_IDMAC		BIT(25)
+#define SDMMC_CTRL_CEATA_INT_EN		BIT(11)
+#define SDMMC_CTRL_SEND_AS_CCSD		BIT(10)
+#define SDMMC_CTRL_SEND_CCSD		BIT(9)
+#define SDMMC_CTRL_ABRT_READ_DATA	BIT(8)
+#define SDMMC_CTRL_SEND_IRQ_RESP	BIT(7)
+#define SDMMC_CTRL_READ_WAIT		BIT(6)
+#define SDMMC_CTRL_DMA_ENABLE		BIT(5)
+#define SDMMC_CTRL_INT_ENABLE		BIT(4)
+#define SDMMC_CTRL_DMA_RESET		BIT(2)
+#define SDMMC_CTRL_FIFO_RESET		BIT(1)
+#define SDMMC_CTRL_RESET		BIT(0)
+/* Clock Enable register defines */
+#define SDMMC_CLKEN_LOW_PWR		BIT(16)
+#define SDMMC_CLKEN_ENABLE		BIT(0)
+/* time-out register defines */
+#define SDMMC_TMOUT_DATA(n)		_SBF(8, (n))
+#define SDMMC_TMOUT_DATA_MSK		0xFFFFFF00
+#define SDMMC_TMOUT_RESP(n)		((n) & 0xFF)
+#define SDMMC_TMOUT_RESP_MSK		0xFF
+/* card-type register defines */
+#define SDMMC_CTYPE_8BIT		BIT(16)
+#define SDMMC_CTYPE_4BIT		BIT(0)
+#define SDMMC_CTYPE_1BIT		0
+/* Interrupt status & mask register defines */
+#define SDMMC_INT_SDIO			BIT(16)
+#define SDMMC_INT_EBE			BIT(15)
+#define SDMMC_INT_ACD			BIT(14)
+#define SDMMC_INT_SBE			BIT(13)
+#define SDMMC_INT_HLE			BIT(12)
+#define SDMMC_INT_FRUN			BIT(11)
+#define SDMMC_INT_HTO			BIT(10)
+#define SDMMC_INT_DTO			BIT(9)
+#define SDMMC_INT_RTO			BIT(8)
+#define SDMMC_INT_DCRC			BIT(7)
+#define SDMMC_INT_RCRC			BIT(6)
+#define SDMMC_INT_RXDR			BIT(5)
+#define SDMMC_INT_TXDR			BIT(4)
+#define SDMMC_INT_DATA_OVER		BIT(3)
+#define SDMMC_INT_CMD_DONE		BIT(2)
+#define SDMMC_INT_RESP_ERR		BIT(1)
+#define SDMMC_INT_CD			BIT(0)
+#define SDMMC_INT_ERROR			0xbfc2
+/* Command register defines */
+#define SDMMC_CMD_START			BIT(31)
+#define SDMMC_CMD_CCS_EXP		BIT(23)
+#define SDMMC_CMD_CEATA_RD		BIT(22)
+#define SDMMC_CMD_UPD_CLK		BIT(21)
+#define SDMMC_CMD_INIT			BIT(15)
+#define SDMMC_CMD_STOP			BIT(14)
+#define SDMMC_CMD_PRV_DAT_WAIT		BIT(13)
+#define SDMMC_CMD_SEND_STOP		BIT(12)
+#define SDMMC_CMD_STRM_MODE		BIT(11)
+#define SDMMC_CMD_DAT_WR		BIT(10)
+#define SDMMC_CMD_DAT_EXP		BIT(9)
+#define SDMMC_CMD_RESP_CRC		BIT(8)
+#define SDMMC_CMD_RESP_LONG		BIT(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_FIFO_SZ			32
+/* Internal DMAC interrupt defines */
+#define SDMMC_IDMAC_INT_AI		BIT(9)
+#define SDMMC_IDMAC_INT_NI		BIT(8)
+#define SDMMC_IDMAC_INT_CES		BIT(5)
+#define SDMMC_IDMAC_INT_DU		BIT(4)
+#define SDMMC_IDMAC_INT_FBE		BIT(2)
+#define SDMMC_IDMAC_INT_RI		BIT(1)
+#define SDMMC_IDMAC_INT_TI		BIT(0)
+/* Internal DMAC bus mode bits */
+#define SDMMC_IDMAC_ENABLE		BIT(7)
+#define SDMMC_IDMAC_FB			BIT(1)
+#define SDMMC_IDMAC_SWRESET		BIT(0)
+
+/* Register access macros */
+#define mci_readl(dev, reg)			\
+	__raw_readl(dev->regs + SDMMC_##reg)
+#define mci_writel(dev, reg, value)			\
+	__raw_writel((value), dev->regs + SDMMC_##reg)
+
+/* 16-bit FIFO access macros */
+#define mci_readw(dev, reg)			\
+	__raw_readw(dev->regs + SDMMC_##reg)
+#define mci_writew(dev, reg, value)			\
+	__raw_writew((value), dev->regs + SDMMC_##reg)
+
+/* 64-bit FIFO access macros */
+#define mci_readq(dev, reg)			\
+	__raw_readq(dev->regs + SDMMC_##reg)
+#define mci_writeq(dev, reg, value)			\
+	__raw_writeq((value), dev->regs + SDMMC_##reg)
+
+#endif /* _DW_MMC_H_ */
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
new file mode 100644
index 0000000..d2f92bd
--- /dev/null
+++ b/include/linux/mmc/dw_mmc.h
@@ -0,0 +1,149 @@
+/*
+ * Synopsys DesignWare Multimedia Card Interface driver
+ *  (Based on NXP driver for lpc 31xx)
+ *
+ * Copyright (C) 2009 NXP Semiconductors
+ * Copyright (C) 2009, 2010 Imagination Technologies Ltd.
+ *
+ * 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, or
+ * (at your option) any later version.
+ */
+
+#ifndef _LINUX_MMC_DW_MMC_H_
+#define _LINUX_MMC_DW_MMC_H_
+
+#define MAX_MCI_SLOTS	2
+
+enum dw_mci_state {
+	STATE_IDLE = 0,
+	STATE_SENDING_CMD,
+	STATE_SENDING_DATA,
+	STATE_DATA_BUSY,
+	STATE_SENDING_STOP,
+	STATE_DATA_ERROR,
+};
+
+enum {
+	EVENT_CMD_COMPLETE = 0,
+	EVENT_XFER_COMPLETE,
+	EVENT_DATA_COMPLETE,
+	EVENT_DATA_ERROR,
+	EVENT_XFER_ERROR
+};
+
+struct mmc_data;
+
+struct dw_mci {
+	spinlock_t		lock;
+	void __iomem		*regs;
+
+	struct scatterlist	*sg;
+	unsigned int		pio_offset;
+
+	struct dw_mci_slot	*cur_slot;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	/* DMA interface members*/
+	int			use_dma;
+
+	dma_addr_t		sg_dma;
+	void			*sg_cpu;
+	struct dw_mci_dma_ops	*dma_ops;
+#ifdef CONFIG_MMC_DW_IDMAC
+	unsigned int		ring_size;
+#else
+	struct dw_mci_dma_data	*dma_data;
+#endif
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_cmdr;
+	u32			dir_status;
+	struct tasklet_struct	tasklet;
+	struct tasklet_struct	card_tasklet;
+	unsigned long		pending_events;
+	unsigned long		completed_events;
+	enum dw_mci_state	state;
+	struct list_head	queue;
+
+	u32			bus_hz;
+	u32			current_speed;
+	u32			num_slots;
+	struct platform_device	*pdev;
+	struct dw_mci_board	*pdata;
+	struct dw_mci_slot	*slot[MAX_MCI_SLOTS];
+
+	/* FIFO push and pull */
+	int			data_shift;
+	void (*push_data)(struct dw_mci *host, void *buf, int cnt);
+	void (*pull_data)(struct dw_mci *host, void *buf, int cnt);
+
+	/* Workaround flags */
+	u32			quirks;
+};
+
+/* DMA ops for Internal/External DMAC interface */
+struct dw_mci_dma_ops {
+	/* DMA Ops */
+	int (*init)(struct dw_mci *host);
+	void (*start)(struct dw_mci *host, unsigned int sg_len);
+	void (*complete)(struct dw_mci *host);
+	void (*stop)(struct dw_mci *host);
+	void (*cleanup)(struct dw_mci *host);
+	void (*exit)(struct dw_mci *host);
+};
+
+/* IP Quirks/flags. */
+/* No special quirks or flags to cater for */
+#define DW_MCI_QUIRK_NONE		0
+/* DTO fix for command transmission with IDMAC configured */
+#define DW_MCI_QUIRK_IDMAC_DTO		1
+/* delay needed between retries on some 2.11a implementations */
+#define DW_MCI_QUIRK_RETRY_DELAY	2
+/* High Speed Capable - Supports HS cards (upto 50MHz) */
+#define DW_MCI_QUIRK_HIGHSPEED		4
+
+
+struct dma_pdata;
+
+struct block_settings {
+	unsigned short	max_segs;	/* see blk_queue_max_segments */
+	unsigned int	max_blk_size;	/* maximum size of one mmc block */
+	unsigned int	max_blk_count;	/* maximum number of blocks in one req*/
+	unsigned int	max_req_size;	/* maximum number of bytes in one req*/
+	unsigned int	max_seg_size;	/* see blk_queue_max_segment_size */
+};
+
+/* Board platform data */
+struct dw_mci_board {
+	u32 num_slots;
+
+	u32 quirks; /* Workaround / Quirk flags */
+	unsigned long bus_hz; /* Bus speed */
+
+	/* delay in mS before detecting cards after interrupt */
+	u32 detect_delay_ms;
+
+	int (*init)(u32 slot_id, irq_handler_t , void *);
+	int (*get_ro)(u32 slot_id);
+	int (*get_cd)(u32 slot_id);
+	int (*get_ocr)(u32 slot_id);
+	int (*get_bus_wd)(u32 slot_id);
+	/*
+	 * Enable power to selected slot and set voltage to desired level.
+	 * Voltage levels are specified using MMC_VDD_xxx defines defined
+	 * in linux/mmc/host.h file.
+	 */
+	void (*setpower)(u32 slot_id, u32 volt);
+	void (*exit)(u32 slot_id);
+	void (*select_slot)(u32 slot_id);
+
+	struct dw_mci_dma_ops *dma_ops;
+	struct dma_pdata *data;
+	struct block_settings *blk_settings;
+};
+
+#endif /* _LINUX_MMC_DW_MMC_H_ */
-- 
1.7.2.2


^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2011-02-08 12:07 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-06 15:53 [RESEND PATCH] dw_mmc: Add Synopsys DesignWare mmc host driver Will Newton
2010-12-08 11:55 ` Matt Fleming
2010-12-08 13:14   ` Will Newton
2010-12-08 14:21     ` [PATCH] " Will Newton
2010-12-08 16:07       ` Matt Fleming
2010-12-09  6:47       ` Chris Ball
2010-12-09 12:11         ` Will Newton
2010-12-09 16:01           ` Chris Ball
2010-12-09 17:24             ` Will Newton
     [not found]               ` <20101211192320.GA24430@void.printf.net>
2010-12-12  8:41                 ` Russell King - ARM Linux
2010-12-12 11:15                   ` Russell King - ARM Linux
2010-12-12 10:57                 ` Will Newton
2010-12-12 13:52                   ` Chris Ball
2010-12-12 14:03                     ` Will Newton
2010-12-12 14:11                       ` Russell King - ARM Linux
2010-12-12 14:31                         ` Will Newton
2010-12-12 14:47                           ` Russell King - ARM Linux
2010-12-12 15:17                             ` Will Newton
2010-12-16 17:04                 ` [PATCH v4] " Will Newton
2011-01-02  6:20                   ` Chris Ball
2011-01-18  7:54                   ` Jaehoon Chung
2011-01-18 10:21                     ` Will Newton
2011-02-08  6:38                       ` Jaehoon Chung
2011-02-08 10:29                         ` Will Newton
2011-02-08 10:49                           ` Jaehoon Chung
2011-02-08 12:06                             ` Will Newton
2010-12-09 17:35             ` [PATCH] " Chris Ball
2010-12-09 17:46               ` Will Newton
  -- strict thread matches above, loose matches on Subject: below --
2010-11-29 17:35 Will Newton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).