Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (42 commits)
  atmel-mci: fix sdc_reg typo
  tmio_mmc: add maintainer
  mmc: Add OpenFirmware bindings for SDHCI driver
  sdhci: Add quirk for forcing maximum block size to 2048 bytes
  sdhci: Add quirk for controllers that need IRQ re-init after reset
  sdhci: Add quirk for controllers that need small delays for PIO
  sdhci: Add set_clock callback and a quirk for nonstandard clocks
  sdhci: Add get_{max,timeout}_clock callbacks
  sdhci: Add support for hosts reporting inverted write-protect state
  sdhci: Add support for card-detection polling
  sdhci: Enable only relevant (DMA/PIO) interrupts during transfers
  sdhci: Split card-detection IRQs management from sdhci_init()
  sdhci: Add support for bus-specific IO memory accessors
  mmc_spi: adjust for delayed data token response
  omap_hsmmc: Wait for SDBP
  omap_hsmmc: Fix MMC3 dma
  omap_hsmmc: Disable SDBP at suspend
  omap_hsmmc: Do not prefix slot name
  omap_hsmmc: Allow cover switch to cause rescan
  omap_hsmmc: Add 8-bit bus width mode support
  ...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6360b9b..9e2eb31 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2924,6 +2924,12 @@
 L:	netdev@vger.kernel.org
 S:	Supported
 
+MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
+P:	Nicolas Pitre
+M:	nico@cam.org
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
 MARVELL YUKON / SYSKONNECT DRIVER
 P:	Mirko Lindner
 M:	mlindner@syskonnect.de
@@ -3916,7 +3922,14 @@
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
 P:	Pierre Ossman
 M:	drzeus-sdhci@drzeus.cx
-L:	sdhci-devel@list.drzeus.cx
+L:	sdhci-devel@lists.ossman.eu
+S:	Maintained
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
+P:	Anton Vorontsov
+M:	avorontsov@ru.mvista.com
+L:	linuxppc-dev@ozlabs.org
+L:	sdhci-devel@lists.ossman.eu
 S:	Maintained
 
 SECURITY SUBSYSTEM
@@ -4394,6 +4407,11 @@
 W:	http://www.buzzard.org.uk/toshiba/
 S:	Maintained
 
+TMIO MMC DRIVER
+P:	Ian Molton
+M:	ian@mnementh.co.uk
+S:	Maintained
+
 TPM DEVICE DRIVER
 P:	Debora Velarde
 M:	debora@linux.vnet.ibm.com
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 513eb09..fe8041e 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -41,6 +41,8 @@
 
 #include "queue.h"
 
+MODULE_ALIAS("mmc:block");
+
 /*
  * max 8 partitions per card
  */
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index f210a8e..bdb165f 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -84,6 +84,14 @@
 	}
 
 	retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
+	if (retval)
+		return retval;
+
+	/*
+	 * Request the mmc_block device.  Note: that this is a direct request
+	 * for the module it carries no information as to what is inserted.
+	 */
+	retval = add_uevent_var(env, "MODALIAS=mmc:block");
 
 	return retval;
 }
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 1445ea8..fa073ab 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -298,6 +298,21 @@
 			data->timeout_clks = 0;
 		}
 	}
+	/*
+	 * Some cards need very high timeouts if driven in SPI mode.
+	 * The worst observed timeout was 900ms after writing a
+	 * continuous stream of data until the internal logic
+	 * overflowed.
+	 */
+	if (mmc_host_is_spi(card->host)) {
+		if (data->flags & MMC_DATA_WRITE) {
+			if (data->timeout_ns < 1000000000)
+				data->timeout_ns = 1000000000;	/* 1s */
+		} else {
+			if (data->timeout_ns < 100000000)
+				data->timeout_ns =  100000000;	/* 100ms */
+		}
+	}
 }
 EXPORT_SYMBOL(mmc_set_data_timeout);
 
@@ -915,6 +930,7 @@
 	spin_unlock_irqrestore(&host->lock, flags);
 #endif
 
+	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
 	mmc_bus_get(host);
@@ -942,6 +958,7 @@
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+	cancel_delayed_work(&host->detect);
 	mmc_flush_scheduled_work();
 
 	mmc_bus_get(host);
@@ -975,6 +992,7 @@
 	mmc_bus_get(host);
 	if (host->bus_ops && !host->bus_dead) {
 		mmc_power_up(host);
+		mmc_select_voltage(host, host->ocr);
 		BUG_ON(!host->bus_ops->resume);
 		host->bus_ops->resume(host);
 	}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 1237bb4..610dbd1 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -184,6 +184,68 @@
 DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
 		NULL, "%08llx\n");
 
+#define EXT_CSD_STR_LEN 1025
+
+static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
+{
+	struct mmc_card *card = inode->i_private;
+	char *buf;
+	ssize_t n = 0;
+	u8 *ext_csd;
+	int err, i;
+
+	buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ext_csd = kmalloc(512, GFP_KERNEL);
+	if (!ext_csd) {
+		err = -ENOMEM;
+		goto out_free;
+	}
+
+	mmc_claim_host(card->host);
+	err = mmc_send_ext_csd(card, ext_csd);
+	mmc_release_host(card->host);
+	if (err)
+		goto out_free;
+
+	for (i = 511; i >= 0; i--)
+		n += sprintf(buf + n, "%02x", ext_csd[i]);
+	n += sprintf(buf + n, "\n");
+	BUG_ON(n != EXT_CSD_STR_LEN);
+
+	filp->private_data = buf;
+	kfree(ext_csd);
+	return 0;
+
+out_free:
+	kfree(buf);
+	kfree(ext_csd);
+	return err;
+}
+
+static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
+				size_t cnt, loff_t *ppos)
+{
+	char *buf = filp->private_data;
+
+	return simple_read_from_buffer(ubuf, cnt, ppos,
+				       buf, EXT_CSD_STR_LEN);
+}
+
+static int mmc_ext_csd_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
+static struct file_operations mmc_dbg_ext_csd_fops = {
+	.open		= mmc_ext_csd_open,
+	.read		= mmc_ext_csd_read,
+	.release	= mmc_ext_csd_release,
+};
+
 void mmc_add_card_debugfs(struct mmc_card *card)
 {
 	struct mmc_host	*host = card->host;
@@ -211,6 +273,11 @@
 					&mmc_dbg_card_status_fops))
 			goto err;
 
+	if (mmc_card_mmc(card))
+		if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
+					&mmc_dbg_ext_csd_fops))
+			goto err;
+
 	return;
 
 err:
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index 956bd76..963f293 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -223,10 +223,18 @@
 		if (tpl_code == 0xff)
 			break;
 
+		/* null entries have no link field or data */
+		if (tpl_code == 0x00)
+			continue;
+
 		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
 		if (ret)
 			break;
 
+		/* a size of 0xff also means we're done */
+		if (tpl_link == 0xff)
+			break;
+
 		this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
 		if (!this)
 			return -ENOMEM;
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
index c8fa095..4eb7825 100644
--- a/drivers/mmc/core/sdio_ops.c
+++ b/drivers/mmc/core/sdio_ops.c
@@ -76,6 +76,10 @@
 	BUG_ON(!card);
 	BUG_ON(fn > 7);
 
+	/* sanity check */
+	if (addr & ~0x1FFFF)
+		return -EINVAL;
+
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = SD_IO_RW_DIRECT;
@@ -125,6 +129,10 @@
 	WARN_ON(blocks == 0);
 	WARN_ON(blksz == 0);
 
+	/* sanity check */
+	if (addr & ~0x1FFFF)
+		return -EINVAL;
+
 	memset(&mrq, 0, sizeof(struct mmc_request));
 	memset(&cmd, 0, sizeof(struct mmc_command));
 	memset(&data, 0, sizeof(struct mmc_data));
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 6fbb246..b4cf691 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -37,6 +37,13 @@
 
 	  If unsure, say N.
 
+config MMC_SDHCI_IO_ACCESSORS
+	bool
+	depends on MMC_SDHCI
+	help
+	  This is silent Kconfig symbol that is selected by the drivers that
+	  need to overwrite SDHCI IO memory accessors.
+
 config MMC_SDHCI_PCI
 	tristate "SDHCI support on PCI bus"
 	depends on MMC_SDHCI && PCI
@@ -65,6 +72,17 @@
 
 	  If unsure, say Y.
 
+config MMC_SDHCI_OF
+	tristate "SDHCI support on OpenFirmware platforms"
+	depends on MMC_SDHCI && PPC_OF
+	select MMC_SDHCI_IO_ACCESSORS
+	help
+	  This selects the OF support for Secure Digital Host Controller
+	  Interfaces. So far, only the Freescale eSDHC controller is known
+	  to exist on OF platforms.
+
+	  If unsure, say N.
+
 config MMC_OMAP
 	tristate "TI OMAP Multimedia Card Interface support"
 	depends on ARCH_OMAP
@@ -171,6 +189,17 @@
           To compile this driver as a module, choose M here: the
 	  module will be called tifm_sd.
 
+config MMC_MVSDIO
+	tristate "Marvell MMC/SD/SDIO host driver"
+	depends on PLAT_ORION
+	---help---
+	  This selects the Marvell SDIO host driver.
+	  SDIO may currently be found on the Kirkwood 88F6281 and 88F6192
+	  SoC controllers.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mvsdio.
+
 config MMC_SPI
 	tristate "MMC/SD/SDIO over SPI"
 	depends on SPI_MASTER && !HIGHMEM && HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index dedec55..970a997 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
 obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o
+obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
@@ -20,6 +21,7 @@
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+obj-$(CONFIG_MMC_MVSDIO)	+= mvsdio.o
 obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
 ifeq ($(CONFIG_OF),y)
 obj-$(CONFIG_MMC_SPI)		+= of_mmc_spi.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index e94e920..cf6a100 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -812,7 +812,7 @@
 		slot->sdc_reg |= MCI_SDCBUS_1BIT;
 		break;
 	case MMC_BUS_WIDTH_4:
-		slot->sdc_reg = MCI_SDCBUS_4BIT;
+		slot->sdc_reg |= MCI_SDCBUS_4BIT;
 		break;
 	}
 
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 87e211d..72f8bde 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -279,8 +279,11 @@
 		 * so it can always DMA directly into the target buffer.
 		 * It'd probably be better to memcpy() the first chunk and
 		 * avoid extra i/o calls...
+		 *
+		 * Note we check for more than 8 bytes, because in practice,
+		 * some SD cards are slow...
 		 */
-		for (i = 2; i < 9; i++) {
+		for (i = 2; i < 16; i++) {
 			value = mmc_spi_readbytes(host, 1);
 			if (value < 0)
 				goto done;
@@ -609,6 +612,7 @@
 	struct spi_device	*spi = host->spi;
 	int			status, i;
 	struct scratch		*scratch = host->data;
+	u32			pattern;
 
 	if (host->mmc->use_spi_crc)
 		scratch->crc_val = cpu_to_be16(
@@ -636,8 +640,27 @@
 	 * doesn't necessarily tell whether the write operation succeeded;
 	 * it just says if the transmission was ok and whether *earlier*
 	 * writes succeeded; see the standard.
+	 *
+	 * In practice, there are (even modern SDHC-)cards which are late
+	 * in sending the response, and miss the time frame by a few bits,
+	 * so we have to cope with this situation and check the response
+	 * bit-by-bit. Arggh!!!
 	 */
-	switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+	pattern  = scratch->status[0] << 24;
+	pattern |= scratch->status[1] << 16;
+	pattern |= scratch->status[2] << 8;
+	pattern |= scratch->status[3];
+
+	/* First 3 bit of pattern are undefined */
+	pattern |= 0xE0000000;
+
+	/* left-adjust to leading 0 bit */
+	while (pattern & 0x80000000)
+		pattern <<= 1;
+	/* right-adjust for pattern matching. Code is in bit 4..0 now. */
+	pattern >>= 27;
+
+	switch (pattern) {
 	case SPI_RESPONSE_ACCEPTED:
 		status = 0;
 		break;
@@ -668,8 +691,9 @@
 	/* Return when not busy.  If we didn't collect that status yet,
 	 * we'll need some more I/O.
 	 */
-	for (i = 1; i < sizeof(scratch->status); i++) {
-		if (scratch->status[i] != 0)
+	for (i = 4; i < sizeof(scratch->status); i++) {
+		/* card is non-busy if the most recent bit is 1 */
+		if (scratch->status[i] & 0x01)
 			return 0;
 	}
 	return mmc_spi_wait_unbusy(host, timeout);
@@ -1204,10 +1228,12 @@
 
 	/* MMC and SD specs only seem to care that sampling is on the
 	 * rising edge ... meaning SPI modes 0 or 3.  So either SPI mode
-	 * should be legit.  We'll use mode 0 since it seems to be a
-	 * bit less troublesome on some hardware ... unclear why.
+	 * should be legit.  We'll use mode 0 since the steady state is 0,
+	 * which is appropriate for hotplugging, unless the platform data
+	 * specify mode 3 (if hardware is not compatible to mode 0).
 	 */
-	spi->mode = SPI_MODE_0;
+	if (spi->mode != SPI_MODE_3)
+		spi->mode = SPI_MODE_0;
 	spi->bits_per_word = 8;
 
 	status = spi_setup(spi);
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
new file mode 100644
index 0000000..b5c375d
--- /dev/null
+++ b/drivers/mmc/host/mvsdio.c
@@ -0,0 +1,885 @@
+/*
+ * Marvell MMC/SD/SDIO driver
+ *
+ * Authors: Maen Suleiman, Nicolas Pitre
+ * Copyright (C) 2008-2009 Marvell Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/mbus.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mmc/host.h>
+
+#include <asm/sizes.h>
+#include <asm/unaligned.h>
+#include <plat/mvsdio.h>
+
+#include "mvsdio.h"
+
+#define DRIVER_NAME	"mvsdio"
+
+static int maxfreq = MVSD_CLOCKRATE_MAX;
+static int nodma;
+
+struct mvsd_host {
+	void __iomem *base;
+	struct mmc_request *mrq;
+	spinlock_t lock;
+	unsigned int xfer_mode;
+	unsigned int intr_en;
+	unsigned int ctrl;
+	unsigned int pio_size;
+	void *pio_ptr;
+	unsigned int sg_frags;
+	unsigned int ns_per_clk;
+	unsigned int clock;
+	unsigned int base_clock;
+	struct timer_list timer;
+	struct mmc_host *mmc;
+	struct device *dev;
+	struct resource *res;
+	int irq;
+	int gpio_card_detect;
+	int gpio_write_protect;
+};
+
+#define mvsd_write(offs, val)	writel(val, iobase + (offs))
+#define mvsd_read(offs)		readl(iobase + (offs))
+
+static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
+{
+	void __iomem *iobase = host->base;
+	unsigned int tmout;
+	int tmout_index;
+
+	/* If timeout=0 then maximum timeout index is used. */
+	tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
+	tmout += data->timeout_clks;
+	tmout_index = fls(tmout - 1) - 12;
+	if (tmout_index < 0)
+		tmout_index = 0;
+	if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX)
+		tmout_index = MVSD_HOST_CTRL_TMOUT_MAX;
+
+	dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n",
+		(data->flags & MMC_DATA_READ) ? "read" : "write",
+		(u32)sg_virt(data->sg), data->blocks, data->blksz,
+		tmout, tmout_index);
+
+	host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK;
+	host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index);
+	mvsd_write(MVSD_HOST_CTRL, host->ctrl);
+	mvsd_write(MVSD_BLK_COUNT, data->blocks);
+	mvsd_write(MVSD_BLK_SIZE, data->blksz);
+
+	if (nodma || (data->blksz | data->sg->offset) & 3) {
+		/*
+		 * We cannot do DMA on a buffer which offset or size
+		 * is not aligned on a 4-byte boundary.
+		 */
+		host->pio_size = data->blocks * data->blksz;
+		host->pio_ptr = sg_virt(data->sg);
+		if (!nodma)
+			printk(KERN_DEBUG "%s: fallback to PIO for data "
+					  "at 0x%p size %d\n",
+					  mmc_hostname(host->mmc),
+					  host->pio_ptr, host->pio_size);
+		return 1;
+	} else {
+		dma_addr_t phys_addr;
+		int dma_dir = (data->flags & MMC_DATA_READ) ?
+			DMA_FROM_DEVICE : DMA_TO_DEVICE;
+		host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
+					    data->sg_len, dma_dir);
+		phys_addr = sg_dma_address(data->sg);
+		mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
+		mvsd_write(MVSD_SYS_ADDR_HI,  (u32)phys_addr >> 16);
+		return 0;
+	}
+}
+
+static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct mvsd_host *host = mmc_priv(mmc);
+	void __iomem *iobase = host->base;
+	struct mmc_command *cmd = mrq->cmd;
+	u32 cmdreg = 0, xfer = 0, intr = 0;
+	unsigned long flags;
+
+	BUG_ON(host->mrq != NULL);
+	host->mrq = mrq;
+
+	dev_dbg(host->dev, "cmd %d (hw state 0x%04x)\n",
+		cmd->opcode, mvsd_read(MVSD_HW_STATE));
+
+	cmdreg = MVSD_CMD_INDEX(cmd->opcode);
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdreg |= MVSD_CMD_RSP_48BUSY;
+	else if (cmd->flags & MMC_RSP_136)
+		cmdreg |= MVSD_CMD_RSP_136;
+	else if (cmd->flags & MMC_RSP_PRESENT)
+		cmdreg |= MVSD_CMD_RSP_48;
+	else
+		cmdreg |= MVSD_CMD_RSP_NONE;
+
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdreg |= MVSD_CMD_CHECK_CMDCRC;
+
+	if (cmd->flags & MMC_RSP_OPCODE)
+		cmdreg |= MVSD_CMD_INDX_CHECK;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdreg |= MVSD_UNEXPECTED_RESP;
+		intr |= MVSD_NOR_UNEXP_RSP;
+	}
+
+	if (mrq->data) {
+		struct mmc_data *data = mrq->data;
+		int pio;
+
+		cmdreg |= MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16;
+		xfer |= MVSD_XFER_MODE_HW_WR_DATA_EN;
+		if (data->flags & MMC_DATA_READ)
+			xfer |= MVSD_XFER_MODE_TO_HOST;
+
+		pio = mvsd_setup_data(host, data);
+		if (pio) {
+			xfer |= MVSD_XFER_MODE_PIO;
+			/* PIO section of mvsd_irq has comments on those bits */
+			if (data->flags & MMC_DATA_WRITE)
+				intr |= MVSD_NOR_TX_AVAIL;
+			else if (host->pio_size > 32)
+				intr |= MVSD_NOR_RX_FIFO_8W;
+			else
+				intr |= MVSD_NOR_RX_READY;
+		}
+
+		if (data->stop) {
+			struct mmc_command *stop = data->stop;
+			u32 cmd12reg = 0;
+
+			mvsd_write(MVSD_AUTOCMD12_ARG_LOW, stop->arg & 0xffff);
+			mvsd_write(MVSD_AUTOCMD12_ARG_HI,  stop->arg >> 16);
+
+			if (stop->flags & MMC_RSP_BUSY)
+				cmd12reg |= MVSD_AUTOCMD12_BUSY;
+			if (stop->flags & MMC_RSP_OPCODE)
+				cmd12reg |= MVSD_AUTOCMD12_INDX_CHECK;
+			cmd12reg |= MVSD_AUTOCMD12_INDEX(stop->opcode);
+			mvsd_write(MVSD_AUTOCMD12_CMD, cmd12reg);
+
+			xfer |= MVSD_XFER_MODE_AUTO_CMD12;
+			intr |= MVSD_NOR_AUTOCMD12_DONE;
+		} else {
+			intr |= MVSD_NOR_XFER_DONE;
+		}
+	} else {
+		intr |= MVSD_NOR_CMD_DONE;
+	}
+
+	mvsd_write(MVSD_ARG_LOW, cmd->arg & 0xffff);
+	mvsd_write(MVSD_ARG_HI,  cmd->arg >> 16);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
+	host->xfer_mode |= xfer;
+	mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+
+	mvsd_write(MVSD_NOR_INTR_STATUS, ~MVSD_NOR_CARD_INT);
+	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+	mvsd_write(MVSD_CMD, cmdreg);
+
+	host->intr_en &= MVSD_NOR_CARD_INT;
+	host->intr_en |= intr | MVSD_NOR_ERROR;
+	mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+	mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
+
+	mod_timer(&host->timer, jiffies + 5 * HZ);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static u32 mvsd_finish_cmd(struct mvsd_host *host, struct mmc_command *cmd,
+			   u32 err_status)
+{
+	void __iomem *iobase = host->base;
+
+	if (cmd->flags & MMC_RSP_136) {
+		unsigned int response[8], i;
+		for (i = 0; i < 8; i++)
+			response[i] = mvsd_read(MVSD_RSP(i));
+		cmd->resp[0] =		((response[0] & 0x03ff) << 22) |
+					((response[1] & 0xffff) << 6) |
+					((response[2] & 0xfc00) >> 10);
+		cmd->resp[1] =		((response[2] & 0x03ff) << 22) |
+					((response[3] & 0xffff) << 6) |
+					((response[4] & 0xfc00) >> 10);
+		cmd->resp[2] =		((response[4] & 0x03ff) << 22) |
+					((response[5] & 0xffff) << 6) |
+					((response[6] & 0xfc00) >> 10);
+		cmd->resp[3] =		((response[6] & 0x03ff) << 22) |
+					((response[7] & 0x3fff) << 8);
+	} else if (cmd->flags & MMC_RSP_PRESENT) {
+		unsigned int response[3], i;
+		for (i = 0; i < 3; i++)
+			response[i] = mvsd_read(MVSD_RSP(i));
+		cmd->resp[0] =		((response[2] & 0x003f) << (8 - 8)) |
+					((response[1] & 0xffff) << (14 - 8)) |
+					((response[0] & 0x03ff) << (30 - 8));
+		cmd->resp[1] =		((response[0] & 0xfc00) >> 10);
+		cmd->resp[2] = 0;
+		cmd->resp[3] = 0;
+	}
+
+	if (err_status & MVSD_ERR_CMD_TIMEOUT) {
+		cmd->error = -ETIMEDOUT;
+	} else if (err_status & (MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT |
+				 MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT)) {
+		cmd->error = -EILSEQ;
+	}
+	err_status &= ~(MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC |
+			MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX |
+			MVSD_ERR_CMD_STARTBIT);
+
+	return err_status;
+}
+
+static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
+			    u32 err_status)
+{
+	void __iomem *iobase = host->base;
+
+	if (host->pio_ptr) {
+		host->pio_ptr = NULL;
+		host->pio_size = 0;
+	} else {
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
+			     (data->flags & MMC_DATA_READ) ?
+				DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	}
+
+	if (err_status & MVSD_ERR_DATA_TIMEOUT)
+		data->error = -ETIMEDOUT;
+	else if (err_status & (MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT))
+		data->error = -EILSEQ;
+	else if (err_status & MVSD_ERR_XFER_SIZE)
+		data->error = -EBADE;
+	err_status &= ~(MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC |
+			MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE);
+
+	dev_dbg(host->dev, "data done: blocks_left=%d, bytes_left=%d\n",
+		mvsd_read(MVSD_CURR_BLK_LEFT), mvsd_read(MVSD_CURR_BYTE_LEFT));
+	data->bytes_xfered =
+		(data->blocks - mvsd_read(MVSD_CURR_BLK_LEFT)) * data->blksz;
+	/* We can't be sure about the last block when errors are detected */
+	if (data->bytes_xfered && data->error)
+		data->bytes_xfered -= data->blksz;
+
+	/* Handle Auto cmd 12 response */
+	if (data->stop) {
+		unsigned int response[3], i;
+		for (i = 0; i < 3; i++)
+			response[i] = mvsd_read(MVSD_AUTO_RSP(i));
+		data->stop->resp[0] =	((response[2] & 0x003f) << (8 - 8)) |
+					((response[1] & 0xffff) << (14 - 8)) |
+					((response[0] & 0x03ff) << (30 - 8));
+		data->stop->resp[1] =	((response[0] & 0xfc00) >> 10);
+		data->stop->resp[2] = 0;
+		data->stop->resp[3] = 0;
+
+		if (err_status & MVSD_ERR_AUTOCMD12) {
+			u32 err_cmd12 = mvsd_read(MVSD_AUTOCMD12_ERR_STATUS);
+			dev_dbg(host->dev, "c12err 0x%04x\n", err_cmd12);
+			if (err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE)
+				data->stop->error = -ENOEXEC;
+			else if (err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT)
+				data->stop->error = -ETIMEDOUT;
+			else if (err_cmd12)
+				data->stop->error = -EILSEQ;
+			err_status &= ~MVSD_ERR_AUTOCMD12;
+		}
+	}
+
+	return err_status;
+}
+
+static irqreturn_t mvsd_irq(int irq, void *dev)
+{
+	struct mvsd_host *host = dev;
+	void __iomem *iobase = host->base;
+	u32 intr_status, intr_done_mask;
+	int irq_handled = 0;
+
+	intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+	dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
+		intr_status, mvsd_read(MVSD_NOR_INTR_EN),
+		mvsd_read(MVSD_HW_STATE));
+
+	spin_lock(&host->lock);
+
+	/* PIO handling, if needed. Messy business... */
+	if (host->pio_size &&
+	    (intr_status & host->intr_en &
+	     (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
+		u16 *p = host->pio_ptr;
+		int s = host->pio_size;
+		while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
+			readsw(iobase + MVSD_FIFO, p, 16);
+			p += 16;
+			s -= 32;
+			intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+		}
+		/*
+		 * Normally we'd use < 32 here, but the RX_FIFO_8W bit
+		 * doesn't appear to assert when there is exactly 32 bytes
+		 * (8 words) left to fetch in a transfer.
+		 */
+		if (s <= 32) {
+			while (s >= 4 && (intr_status & MVSD_NOR_RX_READY)) {
+				put_unaligned(mvsd_read(MVSD_FIFO), p++);
+				put_unaligned(mvsd_read(MVSD_FIFO), p++);
+				s -= 4;
+				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+			}
+			if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
+				u16 val[2] = {0, 0};
+				val[0] = mvsd_read(MVSD_FIFO);
+				val[1] = mvsd_read(MVSD_FIFO);
+				memcpy(p, &val, s);
+				s = 0;
+				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+			}
+			if (s == 0) {
+				host->intr_en &=
+				     ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
+				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+			} else if (host->intr_en & MVSD_NOR_RX_FIFO_8W) {
+				host->intr_en &= ~MVSD_NOR_RX_FIFO_8W;
+				host->intr_en |= MVSD_NOR_RX_READY;
+				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+			}
+		}
+		dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
+			s, intr_status, mvsd_read(MVSD_HW_STATE));
+		host->pio_ptr = p;
+		host->pio_size = s;
+		irq_handled = 1;
+	} else if (host->pio_size &&
+		   (intr_status & host->intr_en &
+		    (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
+		u16 *p = host->pio_ptr;
+		int s = host->pio_size;
+		/*
+		 * The TX_FIFO_8W bit is unreliable. When set, bursting
+		 * 16 halfwords all at once in the FIFO drops data. Actually
+		 * TX_AVAIL does go off after only one word is pushed even if
+		 * TX_FIFO_8W remains set.
+		 */
+		while (s >= 4 && (intr_status & MVSD_NOR_TX_AVAIL)) {
+			mvsd_write(MVSD_FIFO, get_unaligned(p++));
+			mvsd_write(MVSD_FIFO, get_unaligned(p++));
+			s -= 4;
+			intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+		}
+		if (s < 4) {
+			if (s && (intr_status & MVSD_NOR_TX_AVAIL)) {
+				u16 val[2] = {0, 0};
+				memcpy(&val, p, s);
+				mvsd_write(MVSD_FIFO, val[0]);
+				mvsd_write(MVSD_FIFO, val[1]);
+				s = 0;
+				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+			}
+			if (s == 0) {
+				host->intr_en &=
+				     ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
+				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+			}
+		}
+		dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
+			s, intr_status, mvsd_read(MVSD_HW_STATE));
+		host->pio_ptr = p;
+		host->pio_size = s;
+		irq_handled = 1;
+	}
+
+	mvsd_write(MVSD_NOR_INTR_STATUS, intr_status);
+
+	intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY |
+			 MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W;
+	if (intr_status & host->intr_en & ~intr_done_mask) {
+		struct mmc_request *mrq = host->mrq;
+		struct mmc_command *cmd = mrq->cmd;
+		u32 err_status = 0;
+
+		del_timer(&host->timer);
+		host->mrq = NULL;
+
+		host->intr_en &= MVSD_NOR_CARD_INT;
+		mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+		mvsd_write(MVSD_ERR_INTR_EN, 0);
+
+		spin_unlock(&host->lock);
+
+		if (intr_status & MVSD_NOR_UNEXP_RSP) {
+			cmd->error = -EPROTO;
+		} else if (intr_status & MVSD_NOR_ERROR) {
+			err_status = mvsd_read(MVSD_ERR_INTR_STATUS);
+			dev_dbg(host->dev, "err 0x%04x\n", err_status);
+		}
+
+		err_status = mvsd_finish_cmd(host, cmd, err_status);
+		if (mrq->data)
+			err_status = mvsd_finish_data(host, mrq->data, err_status);
+		if (err_status) {
+			printk(KERN_ERR "%s: unhandled error status %#04x\n",
+					mmc_hostname(host->mmc), err_status);
+			cmd->error = -ENOMSG;
+		}
+
+		mmc_request_done(host->mmc, mrq);
+		irq_handled = 1;
+	} else
+		spin_unlock(&host->lock);
+
+	if (intr_status & MVSD_NOR_CARD_INT) {
+		mmc_signal_sdio_irq(host->mmc);
+		irq_handled = 1;
+	}
+
+	if (irq_handled)
+		return IRQ_HANDLED;
+
+	printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
+			"pio=%d\n", mmc_hostname(host->mmc), intr_status,
+			host->intr_en, host->pio_size);
+	return IRQ_NONE;
+}
+
+static void mvsd_timeout_timer(unsigned long data)
+{
+	struct mvsd_host *host = (struct mvsd_host *)data;
+	void __iomem *iobase = host->base;
+	struct mmc_request *mrq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	mrq = host->mrq;
+	if (mrq) {
+		printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
+				mmc_hostname(host->mmc));
+		printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
+				"intr_en=0x%04x\n", mmc_hostname(host->mmc),
+				mvsd_read(MVSD_HW_STATE),
+				mvsd_read(MVSD_NOR_INTR_STATUS),
+				mvsd_read(MVSD_NOR_INTR_EN));
+
+		host->mrq = NULL;
+
+		mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+
+		host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
+		mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+
+		host->intr_en &= MVSD_NOR_CARD_INT;
+		mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+		mvsd_write(MVSD_ERR_INTR_EN, 0);
+		mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+
+		mrq->cmd->error = -ETIMEDOUT;
+		mvsd_finish_cmd(host, mrq->cmd, 0);
+		if (mrq->data) {
+			mrq->data->error = -ETIMEDOUT;
+			mvsd_finish_data(host, mrq->data, 0);
+		}
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (mrq)
+		mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
+{
+	struct mvsd_host *host = dev;
+	mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+	return IRQ_HANDLED;
+}
+
+static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct mvsd_host *host = mmc_priv(mmc);
+	void __iomem *iobase = host->base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (enable) {
+		host->xfer_mode |= MVSD_XFER_MODE_INT_CHK_EN;
+		host->intr_en |= MVSD_NOR_CARD_INT;
+	} else {
+		host->xfer_mode &= ~MVSD_XFER_MODE_INT_CHK_EN;
+		host->intr_en &= ~MVSD_NOR_CARD_INT;
+	}
+	mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+	mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mvsd_get_ro(struct mmc_host *mmc)
+{
+	struct mvsd_host *host = mmc_priv(mmc);
+
+	if (host->gpio_write_protect)
+		return gpio_get_value(host->gpio_write_protect);
+
+	/*
+	 * Board doesn't support read only detection; let the mmc core
+	 * decide what to do.
+	 */
+	return -ENOSYS;
+}
+
+static void mvsd_power_up(struct mvsd_host *host)
+{
+	void __iomem *iobase = host->base;
+	dev_dbg(host->dev, "power up\n");
+	mvsd_write(MVSD_NOR_INTR_EN, 0);
+	mvsd_write(MVSD_ERR_INTR_EN, 0);
+	mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+	mvsd_write(MVSD_XFER_MODE, 0);
+	mvsd_write(MVSD_NOR_STATUS_EN, 0xffff);
+	mvsd_write(MVSD_ERR_STATUS_EN, 0xffff);
+	mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
+	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+}
+
+static void mvsd_power_down(struct mvsd_host *host)
+{
+	void __iomem *iobase = host->base;
+	dev_dbg(host->dev, "power down\n");
+	mvsd_write(MVSD_NOR_INTR_EN, 0);
+	mvsd_write(MVSD_ERR_INTR_EN, 0);
+	mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+	mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
+	mvsd_write(MVSD_NOR_STATUS_EN, 0);
+	mvsd_write(MVSD_ERR_STATUS_EN, 0);
+	mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
+	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+}
+
+static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mvsd_host *host = mmc_priv(mmc);
+	void __iomem *iobase = host->base;
+	u32 ctrl_reg = 0;
+
+	if (ios->power_mode == MMC_POWER_UP)
+		mvsd_power_up(host);
+
+	if (ios->clock == 0) {
+		mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
+		mvsd_write(MVSD_CLK_DIV, MVSD_BASE_DIV_MAX);
+		host->clock = 0;
+		dev_dbg(host->dev, "clock off\n");
+	} else if (ios->clock != host->clock) {
+		u32 m = DIV_ROUND_UP(host->base_clock, ios->clock) - 1;
+		if (m > MVSD_BASE_DIV_MAX)
+			m = MVSD_BASE_DIV_MAX;
+		mvsd_write(MVSD_CLK_DIV, m);
+		host->clock = ios->clock;
+		host->ns_per_clk = 1000000000 / (host->base_clock / (m+1));
+		dev_dbg(host->dev, "clock=%d (%d), div=0x%04x\n",
+			ios->clock, host->base_clock / (m+1), m);
+	}
+
+	/* default transfer mode */
+	ctrl_reg |= MVSD_HOST_CTRL_BIG_ENDIAN;
+	ctrl_reg &= ~MVSD_HOST_CTRL_LSB_FIRST;
+
+	/* default to maximum timeout */
+	ctrl_reg |= MVSD_HOST_CTRL_TMOUT_MASK;
+	ctrl_reg |= MVSD_HOST_CTRL_TMOUT_EN;
+
+	if (ios->bus_mode == MMC_BUSMODE_PUSHPULL)
+		ctrl_reg |= MVSD_HOST_CTRL_PUSH_PULL_EN;
+
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS;
+
+	if (ios->timing == MMC_TIMING_MMC_HS ||
+	    ios->timing == MMC_TIMING_SD_HS)
+		ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN;
+
+	host->ctrl = ctrl_reg;
+	mvsd_write(MVSD_HOST_CTRL, ctrl_reg);
+	dev_dbg(host->dev, "ctrl 0x%04x: %s %s %s\n", ctrl_reg,
+		(ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN) ?
+			"push-pull" : "open-drain",
+		(ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS) ?
+			"4bit-width" : "1bit-width",
+		(ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN) ?
+			"high-speed" : "");
+
+	if (ios->power_mode == MMC_POWER_OFF)
+		mvsd_power_down(host);
+}
+
+static const struct mmc_host_ops mvsd_ops = {
+	.request		= mvsd_request,
+	.get_ro			= mvsd_get_ro,
+	.set_ios		= mvsd_set_ios,
+	.enable_sdio_irq	= mvsd_enable_sdio_irq,
+};
+
+static void __init mv_conf_mbus_windows(struct mvsd_host *host,
+					struct mbus_dram_target_info *dram)
+{
+	void __iomem *iobase = host->base;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		writel(0, iobase + MVSD_WINDOW_CTRL(i));
+		writel(0, iobase + MVSD_WINDOW_BASE(i));
+	}
+
+	for (i = 0; i < dram->num_cs; i++) {
+		struct mbus_dram_window *cs = dram->cs + i;
+		writel(((cs->size - 1) & 0xffff0000) |
+		       (cs->mbus_attr << 8) |
+		       (dram->mbus_dram_target_id << 4) | 1,
+		       iobase + MVSD_WINDOW_CTRL(i));
+		writel(cs->base, iobase + MVSD_WINDOW_BASE(i));
+	}
+}
+
+static int __init mvsd_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = NULL;
+	struct mvsd_host *host = NULL;
+	const struct mvsdio_platform_data *mvsd_data;
+	struct resource *r;
+	int ret, irq;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	mvsd_data = pdev->dev.platform_data;
+	if (!r || irq < 0 || !mvsd_data)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
+	if (!r)
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->dev = &pdev->dev;
+	host->res = r;
+	host->base_clock = mvsd_data->clock / 2;
+
+	mmc->ops = &mvsd_ops;
+
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
+		    MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+	mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
+	mmc->f_max = maxfreq;
+
+	mmc->max_blk_size = 2048;
+	mmc->max_blk_count = 65535;
+
+	mmc->max_hw_segs = 1;
+	mmc->max_phys_segs = 1;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+
+	spin_lock_init(&host->lock);
+
+	host->base = ioremap(r->start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* (Re-)program MBUS remapping windows if we are asked to. */
+	if (mvsd_data->dram != NULL)
+		mv_conf_mbus_windows(host, mvsd_data->dram);
+
+	mvsd_power_down(host);
+
+	ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
+	if (ret) {
+		printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
+		goto out;
+	} else
+		host->irq = irq;
+
+	if (mvsd_data->gpio_card_detect) {
+		ret = gpio_request(mvsd_data->gpio_card_detect,
+				   DRIVER_NAME " cd");
+		if (ret == 0) {
+			gpio_direction_input(mvsd_data->gpio_card_detect);
+			irq = gpio_to_irq(mvsd_data->gpio_card_detect);
+			ret = request_irq(irq, mvsd_card_detect_irq,
+					  IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
+					  DRIVER_NAME " cd", host);
+			if (ret == 0)
+				host->gpio_card_detect =
+					mvsd_data->gpio_card_detect;
+			else
+				gpio_free(mvsd_data->gpio_card_detect);
+		}
+	}
+	if (!host->gpio_card_detect)
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+	if (mvsd_data->gpio_write_protect) {
+		ret = gpio_request(mvsd_data->gpio_write_protect,
+				   DRIVER_NAME " wp");
+		if (ret == 0) {
+			gpio_direction_input(mvsd_data->gpio_write_protect);
+			host->gpio_write_protect =
+				mvsd_data->gpio_write_protect;
+		}
+	}
+
+	setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
+	platform_set_drvdata(pdev, mmc);
+	ret = mmc_add_host(mmc);
+	if (ret)
+		goto out;
+
+	printk(KERN_NOTICE "%s: %s driver initialized, ",
+			   mmc_hostname(mmc), DRIVER_NAME);
+	if (host->gpio_card_detect)
+		printk("using GPIO %d for card detection\n",
+		       host->gpio_card_detect);
+	else
+		printk("lacking card detect (fall back to polling)\n");
+	return 0;
+
+out:
+	if (host) {
+		if (host->irq)
+			free_irq(host->irq, host);
+		if (host->gpio_card_detect) {
+			free_irq(gpio_to_irq(host->gpio_card_detect), host);
+			gpio_free(host->gpio_card_detect);
+		}
+		if (host->gpio_write_protect)
+			gpio_free(host->gpio_write_protect);
+		if (host->base)
+			iounmap(host->base);
+	}
+	if (r)
+		release_resource(r);
+	if (mmc)
+		mmc_free_host(mmc);
+
+	return ret;
+}
+
+static int __exit mvsd_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+	if (mmc) {
+		struct mvsd_host *host = mmc_priv(mmc);
+
+		if (host->gpio_card_detect) {
+			free_irq(gpio_to_irq(host->gpio_card_detect), host);
+			gpio_free(host->gpio_card_detect);
+		}
+		mmc_remove_host(mmc);
+		free_irq(host->irq, host);
+		if (host->gpio_write_protect)
+			gpio_free(host->gpio_write_protect);
+		del_timer_sync(&host->timer);
+		mvsd_power_down(host);
+		iounmap(host->base);
+		release_resource(host->res);
+		mmc_free_host(mmc);
+	}
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mvsd_suspend(struct platform_device *dev, pm_message_t state,
+			   u32 level)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc && level == SUSPEND_DISABLE)
+		ret = mmc_suspend_host(mmc, state);
+
+	return ret;
+}
+
+static int mvsd_resume(struct platform_device *dev, u32 level)
+{
+	struct mmc_host *mmc = platform_dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc && level == RESUME_ENABLE)
+		ret = mmc_resume_host(mmc);
+
+	return ret;
+}
+#else
+#define mvsd_suspend	NULL
+#define mvsd_resume	NULL
+#endif
+
+static struct platform_driver mvsd_driver = {
+	.remove		= __exit_p(mvsd_remove),
+	.suspend	= mvsd_suspend,
+	.resume		= mvsd_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init mvsd_init(void)
+{
+	return platform_driver_probe(&mvsd_driver, mvsd_probe);
+}
+
+static void __exit mvsd_exit(void)
+{
+	platform_driver_unregister(&mvsd_driver);
+}
+
+module_init(mvsd_init);
+module_exit(mvsd_exit);
+
+/* maximum card clock frequency (default 50MHz) */
+module_param(maxfreq, int, 0);
+
+/* force PIO transfers all the time */
+module_param(nodma, int, 0);
+
+MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre");
+MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mvsdio.h b/drivers/mmc/host/mvsdio.h
new file mode 100644
index 0000000..7d9727b
--- /dev/null
+++ b/drivers/mmc/host/mvsdio.h
@@ -0,0 +1,190 @@
+/*
+ *  Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MVSDIO_H
+#define __MVSDIO_H
+
+/*
+ * Clock rates
+ */
+
+#define MVSD_CLOCKRATE_MAX			50000000
+#define MVSD_BASE_DIV_MAX			0x7ff
+
+
+/*
+ * Register offsets
+ */
+
+#define MVSD_SYS_ADDR_LOW			0x000
+#define MVSD_SYS_ADDR_HI			0x004
+#define MVSD_BLK_SIZE				0x008
+#define MVSD_BLK_COUNT				0x00c
+#define MVSD_ARG_LOW				0x010
+#define MVSD_ARG_HI				0x014
+#define MVSD_XFER_MODE				0x018
+#define MVSD_CMD				0x01c
+#define MVSD_RSP(i)				(0x020 + ((i)<<2))
+#define MVSD_RSP0				0x020
+#define MVSD_RSP1				0x024
+#define MVSD_RSP2				0x028
+#define MVSD_RSP3				0x02c
+#define MVSD_RSP4				0x030
+#define MVSD_RSP5				0x034
+#define MVSD_RSP6				0x038
+#define MVSD_RSP7				0x03c
+#define MVSD_FIFO				0x040
+#define MVSD_RSP_CRC7				0x044
+#define MVSD_HW_STATE				0x048
+#define MVSD_HOST_CTRL				0x050
+#define MVSD_BLK_GAP_CTRL			0x054
+#define MVSD_CLK_CTRL				0x058
+#define MVSD_SW_RESET				0x05c
+#define MVSD_NOR_INTR_STATUS			0x060
+#define MVSD_ERR_INTR_STATUS			0x064
+#define MVSD_NOR_STATUS_EN			0x068
+#define MVSD_ERR_STATUS_EN			0x06c
+#define MVSD_NOR_INTR_EN			0x070
+#define MVSD_ERR_INTR_EN			0x074
+#define MVSD_AUTOCMD12_ERR_STATUS		0x078
+#define MVSD_CURR_BYTE_LEFT			0x07c
+#define MVSD_CURR_BLK_LEFT			0x080
+#define MVSD_AUTOCMD12_ARG_LOW			0x084
+#define MVSD_AUTOCMD12_ARG_HI			0x088
+#define MVSD_AUTOCMD12_CMD			0x08c
+#define MVSD_AUTO_RSP(i)			(0x090 + ((i)<<2))
+#define MVSD_AUTO_RSP0				0x090
+#define MVSD_AUTO_RSP1				0x094
+#define MVSD_AUTO_RSP2				0x098
+#define MVSD_CLK_DIV				0x128
+
+#define MVSD_WINDOW_CTRL(i)			(0x108 + ((i) << 3))
+#define MVSD_WINDOW_BASE(i)			(0x10c + ((i) << 3))
+
+
+/*
+ * MVSD_CMD
+ */
+
+#define MVSD_CMD_RSP_NONE			(0 << 0)
+#define MVSD_CMD_RSP_136			(1 << 0)
+#define MVSD_CMD_RSP_48				(2 << 0)
+#define MVSD_CMD_RSP_48BUSY			(3 << 0)
+
+#define MVSD_CMD_CHECK_DATACRC16		(1 << 2)
+#define MVSD_CMD_CHECK_CMDCRC			(1 << 3)
+#define MVSD_CMD_INDX_CHECK			(1 << 4)
+#define MVSD_CMD_DATA_PRESENT			(1 << 5)
+#define MVSD_UNEXPECTED_RESP			(1 << 7)
+#define MVSD_CMD_INDEX(x)			((x) << 8)
+
+
+/*
+ * MVSD_AUTOCMD12_CMD
+ */
+
+#define MVSD_AUTOCMD12_BUSY			(1 << 0)
+#define MVSD_AUTOCMD12_INDX_CHECK		(1 << 1)
+#define MVSD_AUTOCMD12_INDEX(x)			((x) << 8)
+
+/*
+ * MVSD_XFER_MODE
+ */
+
+#define MVSD_XFER_MODE_WR_DATA_START		(1 << 0)
+#define MVSD_XFER_MODE_HW_WR_DATA_EN		(1 << 1)
+#define MVSD_XFER_MODE_AUTO_CMD12		(1 << 2)
+#define MVSD_XFER_MODE_INT_CHK_EN		(1 << 3)
+#define MVSD_XFER_MODE_TO_HOST			(1 << 4)
+#define MVSD_XFER_MODE_STOP_CLK			(1 << 5)
+#define MVSD_XFER_MODE_PIO			(1 << 6)
+
+
+/*
+ * MVSD_HOST_CTRL
+ */
+
+#define MVSD_HOST_CTRL_PUSH_PULL_EN 		(1 << 0)
+
+#define MVSD_HOST_CTRL_CARD_TYPE_MEM_ONLY 	(0 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_ONLY 	(1 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO 	(2 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_MMC 	(3 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_MASK	 	(3 << 1)
+
+#define MVSD_HOST_CTRL_BIG_ENDIAN 		(1 << 3)
+#define MVSD_HOST_CTRL_LSB_FIRST 		(1 << 4)
+#define MVSD_HOST_CTRL_DATA_WIDTH_4_BITS 	(1 << 9)
+#define MVSD_HOST_CTRL_HI_SPEED_EN 		(1 << 10)
+
+#define MVSD_HOST_CTRL_TMOUT_MAX 		0xf
+#define MVSD_HOST_CTRL_TMOUT_MASK 		(0xf << 11)
+#define MVSD_HOST_CTRL_TMOUT(x) 		((x) << 11)
+#define MVSD_HOST_CTRL_TMOUT_EN 		(1 << 15)
+
+
+/*
+ * MVSD_SW_RESET
+ */
+
+#define MVSD_SW_RESET_NOW			(1 << 8)
+
+
+/*
+ * Normal interrupt status bits
+ */
+
+#define MVSD_NOR_CMD_DONE			(1 << 0)
+#define MVSD_NOR_XFER_DONE			(1 << 1)
+#define MVSD_NOR_BLK_GAP_EVT			(1 << 2)
+#define MVSD_NOR_DMA_DONE			(1 << 3)
+#define MVSD_NOR_TX_AVAIL			(1 << 4)
+#define MVSD_NOR_RX_READY			(1 << 5)
+#define MVSD_NOR_CARD_INT			(1 << 8)
+#define MVSD_NOR_READ_WAIT_ON			(1 << 9)
+#define MVSD_NOR_RX_FIFO_8W			(1 << 10)
+#define MVSD_NOR_TX_FIFO_8W			(1 << 11)
+#define MVSD_NOR_SUSPEND_ON			(1 << 12)
+#define MVSD_NOR_AUTOCMD12_DONE			(1 << 13)
+#define MVSD_NOR_UNEXP_RSP			(1 << 14)
+#define MVSD_NOR_ERROR				(1 << 15)
+
+
+/*
+ * Error status bits
+ */
+
+#define MVSD_ERR_CMD_TIMEOUT			(1 << 0)
+#define MVSD_ERR_CMD_CRC			(1 << 1)
+#define MVSD_ERR_CMD_ENDBIT			(1 << 2)
+#define MVSD_ERR_CMD_INDEX			(1 << 3)
+#define MVSD_ERR_DATA_TIMEOUT			(1 << 4)
+#define MVSD_ERR_DATA_CRC			(1 << 5)
+#define MVSD_ERR_DATA_ENDBIT			(1 << 6)
+#define MVSD_ERR_AUTOCMD12			(1 << 8)
+#define MVSD_ERR_CMD_STARTBIT			(1 << 9)
+#define MVSD_ERR_XFER_SIZE			(1 << 10)
+#define MVSD_ERR_RESP_T_BIT			(1 << 11)
+#define MVSD_ERR_CRC_ENDBIT			(1 << 12)
+#define MVSD_ERR_CRC_STARTBIT			(1 << 13)
+#define MVSD_ERR_CRC_STATUS			(1 << 14)
+
+
+/*
+ * CMD12 error status bits
+ */
+
+#define MVSD_AUTOCMD12_ERR_NOTEXE		(1 << 0)
+#define MVSD_AUTOCMD12_ERR_TIMEOUT		(1 << 1)
+#define MVSD_AUTOCMD12_ERR_CRC			(1 << 2)
+#define MVSD_AUTOCMD12_ERR_ENDBIT		(1 << 3)
+#define MVSD_AUTOCMD12_ERR_INDEX		(1 << 4)
+#define MVSD_AUTOCMD12_ERR_RESP_T_BIT		(1 << 5)
+#define MVSD_AUTOCMD12_ERR_RESP_STARTBIT	(1 << 6)
+
+#endif
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 3916a56..d183be6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -56,6 +56,7 @@
 #define SDVS18			(0x5 << 9)
 #define SDVS30			(0x6 << 9)
 #define SDVS33			(0x7 << 9)
+#define SDVS_MASK		0x00000E00
 #define SDVSCLR			0xFFFFF1FF
 #define SDVSDET			0x00000400
 #define AUTOIDLE		0x1
@@ -76,6 +77,7 @@
 #define MSBS			(1 << 5)
 #define BCE			(1 << 1)
 #define FOUR_BIT		(1 << 1)
+#define DW8			(1 << 5)
 #define CC			0x1
 #define TC			0x02
 #define OD			0x1
@@ -98,10 +100,8 @@
  */
 #define OMAP_MMC1_DEVID		0
 #define OMAP_MMC2_DEVID		1
+#define OMAP_MMC3_DEVID		2
 
-#define OMAP_MMC_DATADIR_NONE	0
-#define OMAP_MMC_DATADIR_READ	1
-#define OMAP_MMC_DATADIR_WRITE	2
 #define MMC_TIMEOUT_MS		20
 #define OMAP_MMC_MASTER_CLOCK	96000000
 #define DRIVER_NAME		"mmci-omap-hs"
@@ -137,18 +137,18 @@
 	resource_size_t		mapbase;
 	unsigned int		id;
 	unsigned int		dma_len;
-	unsigned int		dma_dir;
+	unsigned int		dma_sg_idx;
 	unsigned char		bus_mode;
-	unsigned char		datadir;
 	u32			*buffer;
 	u32			bytesleft;
 	int			suspended;
 	int			irq;
 	int			carddetect;
 	int			use_dma, dma_ch;
-	int			initstr;
+	int			dma_line_tx, dma_line_rx;
 	int			slot_id;
 	int			dbclk_enabled;
+	int			response_busy;
 	struct	omap_mmc_platform_data	*pdata;
 };
 
@@ -218,7 +218,7 @@
 	struct mmc_omap_host *host = mmc_priv(mmc);
 	struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id];
 
-	return sprintf(buf, "slot:%s\n", slot.name);
+	return sprintf(buf, "%s\n", slot.name);
 }
 
 static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
@@ -243,10 +243,14 @@
 	OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
 	OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
 
+	host->response_busy = 0;
 	if (cmd->flags & MMC_RSP_PRESENT) {
 		if (cmd->flags & MMC_RSP_136)
 			resptype = 1;
-		else
+		else if (cmd->flags & MMC_RSP_BUSY) {
+			resptype = 3;
+			host->response_busy = 1;
+		} else
 			resptype = 2;
 	}
 
@@ -275,19 +279,35 @@
 	OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
 }
 
+static int
+mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	if (data->flags & MMC_DATA_WRITE)
+		return DMA_TO_DEVICE;
+	else
+		return DMA_FROM_DEVICE;
+}
+
 /*
  * Notify the transfer complete to MMC core
  */
 static void
 mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 {
+	if (!data) {
+		struct mmc_request *mrq = host->mrq;
+
+		host->mrq = NULL;
+		mmc_omap_fclk_lazy_disable(host);
+		mmc_request_done(host->mmc, mrq);
+		return;
+	}
+
 	host->data = NULL;
 
 	if (host->use_dma && host->dma_ch != -1)
 		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
-			host->dma_dir);
-
-	host->datadir = OMAP_MMC_DATADIR_NONE;
+			mmc_omap_get_dma_dir(host, data));
 
 	if (!data->error)
 		data->bytes_xfered += data->blocks * (data->blksz);
@@ -322,7 +342,7 @@
 			cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
 		}
 	}
-	if (host->data == NULL || cmd->error) {
+	if ((host->data == NULL && !host->response_busy) || cmd->error) {
 		host->mrq = NULL;
 		mmc_request_done(host->mmc, cmd->mrq);
 	}
@@ -331,19 +351,18 @@
 /*
  * DMA clean up for command errors
  */
-static void mmc_dma_cleanup(struct mmc_omap_host *host)
+static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
 {
-	host->data->error = -ETIMEDOUT;
+	host->data->error = errno;
 
 	if (host->use_dma && host->dma_ch != -1) {
 		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
-			host->dma_dir);
+			mmc_omap_get_dma_dir(host, host->data));
 		omap_free_dma(host->dma_ch);
 		host->dma_ch = -1;
 		up(&host->sem);
 	}
 	host->data = NULL;
-	host->datadir = OMAP_MMC_DATADIR_NONE;
 }
 
 /*
@@ -412,7 +431,7 @@
 	struct mmc_data *data;
 	int end_cmd = 0, end_trans = 0, status;
 
-	if (host->cmd == NULL && host->data == NULL) {
+	if (host->mrq == NULL) {
 		OMAP_HSMMC_WRITE(host->base, STAT,
 			OMAP_HSMMC_READ(host->base, STAT));
 		return IRQ_HANDLED;
@@ -437,18 +456,24 @@
 				}
 				end_cmd = 1;
 			}
-			if (host->data) {
-				mmc_dma_cleanup(host);
+			if (host->data || host->response_busy) {
+				if (host->data)
+					mmc_dma_cleanup(host, -ETIMEDOUT);
+				host->response_busy = 0;
 				mmc_omap_reset_controller_fsm(host, SRD);
 			}
 		}
 		if ((status & DATA_TIMEOUT) ||
 			(status & DATA_CRC)) {
-			if (host->data) {
-				if (status & DATA_TIMEOUT)
-					mmc_dma_cleanup(host);
+			if (host->data || host->response_busy) {
+				int err = (status & DATA_TIMEOUT) ?
+						-ETIMEDOUT : -EILSEQ;
+
+				if (host->data)
+					mmc_dma_cleanup(host, err);
 				else
-					host->data->error = -EILSEQ;
+					host->mrq->cmd->error = err;
+				host->response_busy = 0;
 				mmc_omap_reset_controller_fsm(host, SRD);
 				end_trans = 1;
 			}
@@ -473,6 +498,19 @@
 	return IRQ_HANDLED;
 }
 
+static void set_sd_bus_power(struct mmc_omap_host *host)
+{
+	unsigned long i;
+
+	OMAP_HSMMC_WRITE(host->base, HCTL,
+			 OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+	for (i = 0; i < loops_per_jiffy; i++) {
+		if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+			break;
+		cpu_relax();
+	}
+}
+
 /*
  * Switch MMC interface voltage ... only relevant for MMC1.
  *
@@ -485,9 +523,6 @@
 	u32 reg_val = 0;
 	int ret;
 
-	if (host->id != OMAP_MMC1_DEVID)
-		return 0;
-
 	/* Disable the clocks */
 	clk_disable(host->fclk);
 	clk_disable(host->iclk);
@@ -532,9 +567,7 @@
 		reg_val |= SDVS30;
 
 	OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
-
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-		OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+	set_sd_bus_power(host);
 
 	return 0;
 err:
@@ -551,7 +584,10 @@
 						mmc_carddetect_work);
 	struct omap_mmc_slot_data *slot = &mmc_slot(host);
 
-	host->carddetect = slot->card_detect(slot->card_detect_irq);
+	if (mmc_slot(host).card_detect)
+		host->carddetect = slot->card_detect(slot->card_detect_irq);
+	else
+		host->carddetect = -ENOSYS;
 
 	sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
 	if (host->carddetect) {
@@ -574,6 +610,48 @@
 	return IRQ_HANDLED;
 }
 
+static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+				     struct mmc_data *data)
+{
+	int sync_dev;
+
+	if (data->flags & MMC_DATA_WRITE)
+		sync_dev = host->dma_line_tx;
+	else
+		sync_dev = host->dma_line_rx;
+	return sync_dev;
+}
+
+static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+				       struct mmc_data *data,
+				       struct scatterlist *sgl)
+{
+	int blksz, nblk, dma_ch;
+
+	dma_ch = host->dma_ch;
+	if (data->flags & MMC_DATA_WRITE) {
+		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+			sg_dma_address(sgl), 0, 0);
+	} else {
+		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+					(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+			sg_dma_address(sgl), 0, 0);
+	}
+
+	blksz = host->data->blksz;
+	nblk = sg_dma_len(sgl) / blksz;
+
+	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+			blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
+			mmc_omap_get_dma_sync_dev(host, data),
+			!(data->flags & MMC_DATA_WRITE));
+
+	omap_start_dma(dma_ch);
+}
+
 /*
  * DMA call back function
  */
@@ -587,6 +665,14 @@
 	if (host->dma_ch < 0)
 		return;
 
+	host->dma_sg_idx++;
+	if (host->dma_sg_idx < host->dma_len) {
+		/* Fire up the next transfer. */
+		mmc_omap_config_dma_params(host, host->data,
+					   host->data->sg + host->dma_sg_idx);
+		return;
+	}
+
 	omap_free_dma(host->dma_ch);
 	host->dma_ch = -1;
 	/*
@@ -597,38 +683,28 @@
 }
 
 /*
- * Configure dma src and destination parameters
- */
-static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
-				struct mmc_data *data)
-{
-	if (sync_dir == 0) {
-		omap_set_dma_dest_params(host->dma_ch, 0,
-			OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_src_params(host->dma_ch, 0,
-			OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(&data->sg[0]), 0, 0);
-	} else {
-		omap_set_dma_src_params(host->dma_ch, 0,
-			OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_dest_params(host->dma_ch, 0,
-			OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(&data->sg[0]), 0, 0);
-	}
-	return 0;
-}
-/*
  * Routine to configure and start DMA for the MMC card
  */
 static int
 mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
 {
-	int sync_dev, sync_dir = 0;
-	int dma_ch = 0, ret = 0, err = 1;
+	int dma_ch = 0, ret = 0, err = 1, i;
 	struct mmc_data *data = req->data;
 
+	/* Sanity check: all the SG entries must be aligned by block size. */
+	for (i = 0; i < host->dma_len; i++) {
+		struct scatterlist *sgl;
+
+		sgl = data->sg + i;
+		if (sgl->length % data->blksz)
+			return -EINVAL;
+	}
+	if ((data->blksz % 4) != 0)
+		/* REVISIT: The MMC buffer increments only when MSB is written.
+		 * Return error for blksz which is non multiple of four.
+		 */
+		return -EINVAL;
+
 	/*
 	 * If for some reason the DMA transfer is still active,
 	 * we wait for timeout period and free the dma
@@ -647,49 +723,22 @@
 			return err;
 	}
 
-	if (!(data->flags & MMC_DATA_WRITE)) {
-		host->dma_dir = DMA_FROM_DEVICE;
-		if (host->id == OMAP_MMC1_DEVID)
-			sync_dev = OMAP24XX_DMA_MMC1_RX;
-		else
-			sync_dev = OMAP24XX_DMA_MMC2_RX;
-	} else {
-		host->dma_dir = DMA_TO_DEVICE;
-		if (host->id == OMAP_MMC1_DEVID)
-			sync_dev = OMAP24XX_DMA_MMC1_TX;
-		else
-			sync_dev = OMAP24XX_DMA_MMC2_TX;
-	}
-
-	ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
-			host, &dma_ch);
+	ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
+			       mmc_omap_dma_cb,host, &dma_ch);
 	if (ret != 0) {
-		dev_dbg(mmc_dev(host->mmc),
+		dev_err(mmc_dev(host->mmc),
 			"%s: omap_request_dma() failed with %d\n",
 			mmc_hostname(host->mmc), ret);
 		return ret;
 	}
 
 	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-			data->sg_len, host->dma_dir);
+			data->sg_len, mmc_omap_get_dma_dir(host, data));
 	host->dma_ch = dma_ch;
+	host->dma_sg_idx = 0;
 
-	if (!(data->flags & MMC_DATA_WRITE))
-		mmc_omap_config_dma_param(1, host, data);
-	else
-		mmc_omap_config_dma_param(0, host, data);
+	mmc_omap_config_dma_params(host, data, data->sg);
 
-	if ((data->blksz % 4) == 0)
-		omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-			(data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
-			sync_dev, sync_dir);
-	else
-		/* REVISIT: The MMC buffer increments only when MSB is written.
-		 * Return error for blksz which is non multiple of four.
-		 */
-		return -EINVAL;
-
-	omap_start_dma(dma_ch);
 	return 0;
 }
 
@@ -739,7 +788,6 @@
 	host->data = req->data;
 
 	if (req->data == NULL) {
-		host->datadir = OMAP_MMC_DATADIR_NONE;
 		OMAP_HSMMC_WRITE(host->base, BLK, 0);
 		return 0;
 	}
@@ -748,9 +796,6 @@
 					| (req->data->blocks << 16));
 	set_data_timeout(host, req);
 
-	host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
-			OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
-
 	if (host->use_dma) {
 		ret = mmc_omap_start_dma_transfer(host, req);
 		if (ret != 0) {
@@ -782,36 +827,29 @@
 	u16 dsor = 0;
 	unsigned long regval;
 	unsigned long timeout;
+	u32 con;
 
 	switch (ios->power_mode) {
 	case MMC_POWER_OFF:
 		mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
-		/*
-		 * Reset interface voltage to 3V if it's 1.8V now;
-		 * only relevant on MMC-1, the others always use 1.8V.
-		 *
-		 * REVISIT: If we are able to detect cards after unplugging
-		 * a 1.8V card, this code should not be needed.
-		 */
-		if (host->id != OMAP_MMC1_DEVID)
-			break;
-		if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-			int vdd = fls(host->mmc->ocr_avail) - 1;
-			if (omap_mmc_switch_opcond(host, vdd) != 0)
-				host->mmc->ios.vdd = vdd;
-		}
 		break;
 	case MMC_POWER_UP:
 		mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
 		break;
 	}
 
+	con = OMAP_HSMMC_READ(host->base, CON);
 	switch (mmc->ios.bus_width) {
+	case MMC_BUS_WIDTH_8:
+		OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+		break;
 	case MMC_BUS_WIDTH_4:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
 		OMAP_HSMMC_WRITE(host->base, HCTL,
 			OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
 		break;
 	case MMC_BUS_WIDTH_1:
+		OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
 		OMAP_HSMMC_WRITE(host->base, HCTL,
 			OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
 		break;
@@ -891,6 +929,33 @@
 	return pdata->slots[0].get_ro(host->dev, 0);
 }
 
+static void omap_hsmmc_init(struct mmc_omap_host *host)
+{
+	u32 hctl, capa, value;
+
+	/* Only MMC1 supports 3.0V */
+	if (host->id == OMAP_MMC1_DEVID) {
+		hctl = SDVS30;
+		capa = VS30 | VS18;
+	} else {
+		hctl = SDVS18;
+		capa = VS18;
+	}
+
+	value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
+	OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);
+
+	value = OMAP_HSMMC_READ(host->base, CAPA);
+	OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
+
+	/* Set the controller to AUTO IDLE mode */
+	value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
+	OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
+
+	/* Set SD bus power bit */
+	set_sd_bus_power(host);
+}
+
 static struct mmc_host_ops mmc_omap_ops = {
 	.request = omap_mmc_request,
 	.set_ios = omap_mmc_set_ios,
@@ -906,7 +971,6 @@
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
 	int ret = 0, irq;
-	u32 hctl, capa;
 
 	if (pdata == NULL) {
 		dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -996,10 +1060,11 @@
 		else
 			host->dbclk_enabled = 1;
 
-#ifdef CONFIG_MMC_BLOCK_BOUNCE
-	mmc->max_phys_segs = 1;
-	mmc->max_hw_segs = 1;
-#endif
+	/* Since we do only SG emulation, we can have as many segs
+	 * as we want. */
+	mmc->max_phys_segs = 1024;
+	mmc->max_hw_segs = 1024;
+
 	mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
 	mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
@@ -1008,32 +1073,32 @@
 	mmc->ocr_avail = mmc_slot(host).ocr_mask;
 	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
-	if (pdata->slots[host->slot_id].wires >= 4)
+	if (pdata->slots[host->slot_id].wires >= 8)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+	else if (pdata->slots[host->slot_id].wires >= 4)
 		mmc->caps |= MMC_CAP_4_BIT_DATA;
 
-	/* Only MMC1 supports 3.0V */
-	if (host->id == OMAP_MMC1_DEVID) {
-		hctl = SDVS30;
-		capa = VS30 | VS18;
-	} else {
-		hctl = SDVS18;
-		capa = VS18;
+	omap_hsmmc_init(host);
+
+	/* Select DMA lines */
+	switch (host->id) {
+	case OMAP_MMC1_DEVID:
+		host->dma_line_tx = OMAP24XX_DMA_MMC1_TX;
+		host->dma_line_rx = OMAP24XX_DMA_MMC1_RX;
+		break;
+	case OMAP_MMC2_DEVID:
+		host->dma_line_tx = OMAP24XX_DMA_MMC2_TX;
+		host->dma_line_rx = OMAP24XX_DMA_MMC2_RX;
+		break;
+	case OMAP_MMC3_DEVID:
+		host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
+		host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
+		break;
+	default:
+		dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
+		goto err_irq;
 	}
 
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) | hctl);
-
-	OMAP_HSMMC_WRITE(host->base, CAPA,
-			OMAP_HSMMC_READ(host->base, CAPA) | capa);
-
-	/* Set the controller to AUTO IDLE mode */
-	OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
-			OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
-
-	/* Set SD bus power bit */
-	OMAP_HSMMC_WRITE(host->base, HCTL,
-			OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
-
 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
 			mmc_hostname(mmc), host);
@@ -1051,7 +1116,7 @@
 	}
 
 	/* Request IRQ for card detect */
-	if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
+	if ((mmc_slot(host).card_detect_irq)) {
 		ret = request_irq(mmc_slot(host).card_detect_irq,
 				  omap_mmc_cd_handler,
 				  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -1074,8 +1139,8 @@
 		if (ret < 0)
 			goto err_slot_name;
 	}
-	if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect &&
-			host->pdata->slots[host->slot_id].get_cover_state) {
+	if (mmc_slot(host).card_detect_irq &&
+	    host->pdata->slots[host->slot_id].get_cover_state) {
 		ret = device_create_file(&mmc->class_dev,
 					&dev_attr_cover_switch);
 		if (ret < 0)
@@ -1173,20 +1238,8 @@
 						" level suspend\n");
 			}
 
-			if (host->id == OMAP_MMC1_DEVID
-					&& !(OMAP_HSMMC_READ(host->base, HCTL)
-							& SDVSDET)) {
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					& SDVSCLR);
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					| SDVS30);
-				OMAP_HSMMC_WRITE(host->base, HCTL,
-					OMAP_HSMMC_READ(host->base, HCTL)
-					| SDBP);
-			}
-
+			OMAP_HSMMC_WRITE(host->base, HCTL,
+					 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
 			clk_disable(host->fclk);
 			clk_disable(host->iclk);
 			clk_disable(host->dbclk);
@@ -1222,6 +1275,8 @@
 			dev_dbg(mmc_dev(host->mmc),
 					"Enabling debounce clk failed\n");
 
+		omap_hsmmc_init(host);
+
 		if (host->pdata->resume) {
 			ret = host->pdata->resume(&pdev->dev, host->slot_id);
 			if (ret)
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
new file mode 100644
index 0000000..3ff4ac3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of.c
@@ -0,0 +1,309 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *	    Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include "sdhci.h"
+
+struct sdhci_of_data {
+	unsigned int quirks;
+	struct sdhci_ops ops;
+};
+
+struct sdhci_of_host {
+	unsigned int clock;
+	u16 xfer_mode_shadow;
+};
+
+/*
+ * Ops and quirks for the Freescale eSDHC controller.
+ */
+
+#define ESDHC_DMA_SYSCTL	0x40c
+#define ESDHC_DMA_SNOOP		0x00000040
+
+#define ESDHC_SYSTEM_CONTROL	0x2c
+#define ESDHC_CLOCK_MASK	0x0000fff0
+#define ESDHC_PREDIV_SHIFT	8
+#define ESDHC_DIVIDER_SHIFT	4
+#define ESDHC_CLOCK_PEREN	0x00000004
+#define ESDHC_CLOCK_HCKEN	0x00000002
+#define ESDHC_CLOCK_IPGEN	0x00000001
+
+static u32 esdhc_readl(struct sdhci_host *host, int reg)
+{
+	return in_be32(host->ioaddr + reg);
+}
+
+static u16 esdhc_readw(struct sdhci_host *host, int reg)
+{
+	return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+static u8 esdhc_readb(struct sdhci_host *host, int reg)
+{
+	return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	out_be32(host->ioaddr + reg, val);
+}
+
+static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+	int base = reg & ~0x3;
+	int shift = (reg & 0x2) * 8;
+
+	switch (reg) {
+	case SDHCI_TRANSFER_MODE:
+		/*
+		 * Postpone this write, we must do it together with a
+		 * command write that is down below.
+		 */
+		of_host->xfer_mode_shadow = val;
+		return;
+	case SDHCI_COMMAND:
+		esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
+			     SDHCI_TRANSFER_MODE);
+		return;
+	case SDHCI_BLOCK_SIZE:
+		/*
+		 * Two last DMA bits are reserved, and first one is used for
+		 * non-standard blksz of 4096 bytes that we don't support
+		 * yet. So clear the DMA boundary bits.
+		 */
+		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+		/* fall through */
+	}
+	clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	int base = reg & ~0x3;
+	int shift = (reg & 0x3) * 8;
+
+	clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+
+static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	int div;
+	int pre_div = 2;
+
+	clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
+
+	if (clock == 0)
+		goto out;
+
+	if (host->max_clk / 16 > clock) {
+		for (; pre_div < 256; pre_div *= 2) {
+			if (host->max_clk / pre_div < clock * 16)
+				break;
+		}
+	}
+
+	for (div = 1; div <= 16; div++) {
+		if (host->max_clk / (div * pre_div) <= clock)
+			break;
+	}
+
+	pre_div >>= 1;
+
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
+		  div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
+	mdelay(100);
+out:
+	host->clock = clock;
+}
+
+static int esdhc_enable_dma(struct sdhci_host *host)
+{
+	setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+	return 0;
+}
+
+static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+
+	return of_host->clock;
+}
+
+static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+
+	return of_host->clock / 1000;
+}
+
+static struct sdhci_of_data sdhci_esdhc = {
+	.quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+		  SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+		  SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+		  SDHCI_QUIRK_NO_BUSY_IRQ |
+		  SDHCI_QUIRK_NONSTANDARD_CLOCK |
+		  SDHCI_QUIRK_PIO_NEEDS_DELAY |
+		  SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
+		  SDHCI_QUIRK_NO_CARD_NO_RESET,
+	.ops = {
+		.readl = esdhc_readl,
+		.readw = esdhc_readw,
+		.readb = esdhc_readb,
+		.writel = esdhc_writel,
+		.writew = esdhc_writew,
+		.writeb = esdhc_writeb,
+		.set_clock = esdhc_set_clock,
+		.enable_dma = esdhc_enable_dma,
+		.get_max_clock = esdhc_get_max_clock,
+		.get_timeout_clock = esdhc_get_timeout_clock,
+	},
+};
+
+#ifdef CONFIG_PM
+
+static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	return mmc_suspend_host(host->mmc, state);
+}
+
+static int sdhci_of_resume(struct of_device *ofdev)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	return mmc_resume_host(host->mmc);
+}
+
+#else
+
+#define sdhci_of_suspend NULL
+#define sdhci_of_resume NULL
+
+#endif
+
+static int __devinit sdhci_of_probe(struct of_device *ofdev,
+				 const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct sdhci_of_data *sdhci_of_data = match->data;
+	struct sdhci_host *host;
+	struct sdhci_of_host *of_host;
+	const u32 *clk;
+	int size;
+	int ret;
+
+	if (!of_device_is_available(np))
+		return -ENODEV;
+
+	host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
+	if (!host)
+		return -ENOMEM;
+
+	of_host = sdhci_priv(host);
+	dev_set_drvdata(&ofdev->dev, host);
+
+	host->ioaddr = of_iomap(np, 0);
+	if (!host->ioaddr) {
+		ret = -ENOMEM;
+		goto err_addr_map;
+	}
+
+	host->irq = irq_of_parse_and_map(np, 0);
+	if (!host->irq) {
+		ret = -EINVAL;
+		goto err_no_irq;
+	}
+
+	host->hw_name = dev_name(&ofdev->dev);
+	if (sdhci_of_data) {
+		host->quirks = sdhci_of_data->quirks;
+		host->ops = &sdhci_of_data->ops;
+	}
+
+	clk = of_get_property(np, "clock-frequency", &size);
+	if (clk && size == sizeof(*clk) && *clk)
+		of_host->clock = *clk;
+
+	ret = sdhci_add_host(host);
+	if (ret)
+		goto err_add_host;
+
+	return 0;
+
+err_add_host:
+	irq_dispose_mapping(host->irq);
+err_no_irq:
+	iounmap(host->ioaddr);
+err_addr_map:
+	sdhci_free_host(host);
+	return ret;
+}
+
+static int __devexit sdhci_of_remove(struct of_device *ofdev)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	sdhci_remove_host(host, 0);
+	sdhci_free_host(host);
+	irq_dispose_mapping(host->irq);
+	iounmap(host->ioaddr);
+	return 0;
+}
+
+static const struct of_device_id sdhci_of_match[] = {
+	{ .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
+	{ .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+	{ .compatible = "generic-sdhci", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sdhci_of_match);
+
+static struct of_platform_driver sdhci_of_driver = {
+	.driver.name = "sdhci-of",
+	.match_table = sdhci_of_match,
+	.probe = sdhci_of_probe,
+	.remove = __devexit_p(sdhci_of_remove),
+	.suspend = sdhci_of_suspend,
+	.resume	= sdhci_of_resume,
+};
+
+static int __init sdhci_of_init(void)
+{
+	return of_register_platform_driver(&sdhci_of_driver);
+}
+module_init(sdhci_of_init);
+
+static void __exit sdhci_of_exit(void)
+{
+	of_unregister_platform_driver(&sdhci_of_driver);
+}
+module_exit(sdhci_of_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+	      "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index accb592..30d8e3d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,35 +48,35 @@
 	printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
 
 	printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version:  0x%08x\n",
-		readl(host->ioaddr + SDHCI_DMA_ADDRESS),
-		readw(host->ioaddr + SDHCI_HOST_VERSION));
+		sdhci_readl(host, SDHCI_DMA_ADDRESS),
+		sdhci_readw(host, SDHCI_HOST_VERSION));
 	printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt:  0x%08x\n",
-		readw(host->ioaddr + SDHCI_BLOCK_SIZE),
-		readw(host->ioaddr + SDHCI_BLOCK_COUNT));
+		sdhci_readw(host, SDHCI_BLOCK_SIZE),
+		sdhci_readw(host, SDHCI_BLOCK_COUNT));
 	printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
-		readl(host->ioaddr + SDHCI_ARGUMENT),
-		readw(host->ioaddr + SDHCI_TRANSFER_MODE));
+		sdhci_readl(host, SDHCI_ARGUMENT),
+		sdhci_readw(host, SDHCI_TRANSFER_MODE));
 	printk(KERN_DEBUG DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
-		readl(host->ioaddr + SDHCI_PRESENT_STATE),
-		readb(host->ioaddr + SDHCI_HOST_CONTROL));
+		sdhci_readl(host, SDHCI_PRESENT_STATE),
+		sdhci_readb(host, SDHCI_HOST_CONTROL));
 	printk(KERN_DEBUG DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
-		readb(host->ioaddr + SDHCI_POWER_CONTROL),
-		readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
+		sdhci_readb(host, SDHCI_POWER_CONTROL),
+		sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
 	printk(KERN_DEBUG DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
-		readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
-		readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
+		sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
+		sdhci_readw(host, SDHCI_CLOCK_CONTROL));
 	printk(KERN_DEBUG DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
-		readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
-		readl(host->ioaddr + SDHCI_INT_STATUS));
+		sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
+		sdhci_readl(host, SDHCI_INT_STATUS));
 	printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
-		readl(host->ioaddr + SDHCI_INT_ENABLE),
-		readl(host->ioaddr + SDHCI_SIGNAL_ENABLE));
+		sdhci_readl(host, SDHCI_INT_ENABLE),
+		sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
 	printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
-		readw(host->ioaddr + SDHCI_ACMD12_ERR),
-		readw(host->ioaddr + SDHCI_SLOT_INT_STATUS));
+		sdhci_readw(host, SDHCI_ACMD12_ERR),
+		sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
 	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Max curr: 0x%08x\n",
-		readl(host->ioaddr + SDHCI_CAPABILITIES),
-		readl(host->ioaddr + SDHCI_MAX_CURRENT));
+		sdhci_readl(host, SDHCI_CAPABILITIES),
+		sdhci_readl(host, SDHCI_MAX_CURRENT));
 
 	printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
 }
@@ -87,17 +87,65 @@
  *                                                                           *
 \*****************************************************************************/
 
+static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+	u32 ier;
+
+	ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+	ier &= ~clear;
+	ier |= set;
+	sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
+{
+	sdhci_clear_set_irqs(host, 0, irqs);
+}
+
+static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
+{
+	sdhci_clear_set_irqs(host, irqs, 0);
+}
+
+static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
+{
+	u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+
+	if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+		return;
+
+	if (enable)
+		sdhci_unmask_irqs(host, irqs);
+	else
+		sdhci_mask_irqs(host, irqs);
+}
+
+static void sdhci_enable_card_detection(struct sdhci_host *host)
+{
+	sdhci_set_card_detection(host, true);
+}
+
+static void sdhci_disable_card_detection(struct sdhci_host *host)
+{
+	sdhci_set_card_detection(host, false);
+}
+
 static void sdhci_reset(struct sdhci_host *host, u8 mask)
 {
 	unsigned long timeout;
+	u32 uninitialized_var(ier);
 
 	if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
-		if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+		if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
 			SDHCI_CARD_PRESENT))
 			return;
 	}
 
-	writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+		ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+
+	sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
 
 	if (mask & SDHCI_RESET_ALL)
 		host->clock = 0;
@@ -106,7 +154,7 @@
 	timeout = 100;
 
 	/* hw clears the bit when it's done */
-	while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+	while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
 		if (timeout == 0) {
 			printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
 				mmc_hostname(host->mmc), (int)mask);
@@ -116,42 +164,44 @@
 		timeout--;
 		mdelay(1);
 	}
+
+	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+		sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
 }
 
 static void sdhci_init(struct sdhci_host *host)
 {
-	u32 intmask;
-
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
-	intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+	sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+		SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
 		SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
 		SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
-		SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
-		SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
-		SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
-		SDHCI_INT_ADMA_ERROR;
+		SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
+}
 
-	writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
-	writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+static void sdhci_reinit(struct sdhci_host *host)
+{
+	sdhci_init(host);
+	sdhci_enable_card_detection(host);
 }
 
 static void sdhci_activate_led(struct sdhci_host *host)
 {
 	u8 ctrl;
 
-	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 	ctrl |= SDHCI_CTRL_LED;
-	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 }
 
 static void sdhci_deactivate_led(struct sdhci_host *host)
 {
 	u8 ctrl;
 
-	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 	ctrl &= ~SDHCI_CTRL_LED;
-	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 }
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -205,7 +255,7 @@
 
 		while (len) {
 			if (chunk == 0) {
-				scratch = readl(host->ioaddr + SDHCI_BUFFER);
+				scratch = sdhci_readl(host, SDHCI_BUFFER);
 				chunk = 4;
 			}
 
@@ -257,7 +307,7 @@
 			len--;
 
 			if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
-				writel(scratch, host->ioaddr + SDHCI_BUFFER);
+				sdhci_writel(host, scratch, SDHCI_BUFFER);
 				chunk = 0;
 				scratch = 0;
 			}
@@ -292,7 +342,10 @@
 		(host->data->blocks == 1))
 		mask = ~0;
 
-	while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+	while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
+		if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY)
+			udelay(100);
+
 		if (host->data->flags & MMC_DATA_READ)
 			sdhci_read_block_pio(host);
 		else
@@ -561,6 +614,17 @@
 	return count;
 }
 
+static void sdhci_set_transfer_irqs(struct sdhci_host *host)
+{
+	u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
+	u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
+
+	if (host->flags & SDHCI_REQ_USE_DMA)
+		sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
+	else
+		sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
+}
+
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 {
 	u8 count;
@@ -581,7 +645,7 @@
 	host->data_early = 0;
 
 	count = sdhci_calc_timeout(host, data);
-	writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+	sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
 
 	if (host->flags & SDHCI_USE_DMA)
 		host->flags |= SDHCI_REQ_USE_DMA;
@@ -661,8 +725,8 @@
 				WARN_ON(1);
 				host->flags &= ~SDHCI_REQ_USE_DMA;
 			} else {
-				writel(host->adma_addr,
-					host->ioaddr + SDHCI_ADMA_ADDRESS);
+				sdhci_writel(host, host->adma_addr,
+					SDHCI_ADMA_ADDRESS);
 			}
 		} else {
 			int sg_cnt;
@@ -681,8 +745,8 @@
 				host->flags &= ~SDHCI_REQ_USE_DMA;
 			} else {
 				WARN_ON(sg_cnt != 1);
-				writel(sg_dma_address(data->sg),
-					host->ioaddr + SDHCI_DMA_ADDRESS);
+				sdhci_writel(host, sg_dma_address(data->sg),
+					SDHCI_DMA_ADDRESS);
 			}
 		}
 	}
@@ -693,14 +757,14 @@
 	 * is ADMA.
 	 */
 	if (host->version >= SDHCI_SPEC_200) {
-		ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+		ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 		ctrl &= ~SDHCI_CTRL_DMA_MASK;
 		if ((host->flags & SDHCI_REQ_USE_DMA) &&
 			(host->flags & SDHCI_USE_ADMA))
 			ctrl |= SDHCI_CTRL_ADMA32;
 		else
 			ctrl |= SDHCI_CTRL_SDMA;
-		writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 	}
 
 	if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@@ -709,10 +773,11 @@
 		host->blocks = data->blocks;
 	}
 
+	sdhci_set_transfer_irqs(host);
+
 	/* We do not handle DMA boundaries, so set it to max (512 KiB) */
-	writew(SDHCI_MAKE_BLKSZ(7, data->blksz),
-		host->ioaddr + SDHCI_BLOCK_SIZE);
-	writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
+	sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
+	sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
 }
 
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
@@ -733,7 +798,7 @@
 	if (host->flags & SDHCI_REQ_USE_DMA)
 		mode |= SDHCI_TRNS_DMA;
 
-	writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
 }
 
 static void sdhci_finish_data(struct sdhci_host *host)
@@ -802,7 +867,7 @@
 	if (host->mrq->data && (cmd == host->mrq->data->stop))
 		mask &= ~SDHCI_DATA_INHIBIT;
 
-	while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+	while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
 		if (timeout == 0) {
 			printk(KERN_ERR "%s: Controller never released "
 				"inhibit bit(s).\n", mmc_hostname(host->mmc));
@@ -821,7 +886,7 @@
 
 	sdhci_prepare_data(host, cmd->data);
 
-	writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+	sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
 
 	sdhci_set_transfer_mode(host, cmd->data);
 
@@ -849,8 +914,7 @@
 	if (cmd->data)
 		flags |= SDHCI_CMD_DATA;
 
-	writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
-		host->ioaddr + SDHCI_COMMAND);
+	sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }
 
 static void sdhci_finish_command(struct sdhci_host *host)
@@ -863,15 +927,15 @@
 		if (host->cmd->flags & MMC_RSP_136) {
 			/* CRC is stripped so we need to do some shifting. */
 			for (i = 0;i < 4;i++) {
-				host->cmd->resp[i] = readl(host->ioaddr +
+				host->cmd->resp[i] = sdhci_readl(host,
 					SDHCI_RESPONSE + (3-i)*4) << 8;
 				if (i != 3)
 					host->cmd->resp[i] |=
-						readb(host->ioaddr +
+						sdhci_readb(host,
 						SDHCI_RESPONSE + (3-i)*4-1);
 			}
 		} else {
-			host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+			host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
 		}
 	}
 
@@ -895,7 +959,13 @@
 	if (clock == host->clock)
 		return;
 
-	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+	if (host->ops->set_clock) {
+		host->ops->set_clock(host, clock);
+		if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
+			return;
+	}
+
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
 
 	if (clock == 0)
 		goto out;
@@ -908,11 +978,11 @@
 
 	clk = div << SDHCI_DIVIDER_SHIFT;
 	clk |= SDHCI_CLOCK_INT_EN;
-	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
 	/* Wait max 10 ms */
 	timeout = 10;
-	while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
 		& SDHCI_CLOCK_INT_STABLE)) {
 		if (timeout == 0) {
 			printk(KERN_ERR "%s: Internal clock never "
@@ -925,7 +995,7 @@
 	}
 
 	clk |= SDHCI_CLOCK_CARD_EN;
-	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
 out:
 	host->clock = clock;
@@ -939,7 +1009,7 @@
 		return;
 
 	if (power == (unsigned short)-1) {
-		writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
 		goto out;
 	}
 
@@ -948,7 +1018,7 @@
 	 * a new value. Some controllers don't seem to like this though.
 	 */
 	if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
-		writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+		sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
 
 	pwr = SDHCI_POWER_ON;
 
@@ -973,10 +1043,9 @@
 	 * and set turn on power at the same time, so set the voltage first.
 	 */
 	if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
-		writeb(pwr & ~SDHCI_POWER_ON,
-				host->ioaddr + SDHCI_POWER_CONTROL);
+		sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
 
-	writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+	sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
 out:
 	host->power = power;
@@ -991,6 +1060,7 @@
 static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct sdhci_host *host;
+	bool present;
 	unsigned long flags;
 
 	host = mmc_priv(mmc);
@@ -1005,8 +1075,14 @@
 
 	host->mrq = mrq;
 
-	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
-		|| (host->flags & SDHCI_DEVICE_DEAD)) {
+	/* If polling, assume that the card is always present. */
+	if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+		present = true;
+	else
+		present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+				SDHCI_CARD_PRESENT;
+
+	if (!present || host->flags & SDHCI_DEVICE_DEAD) {
 		host->mrq->cmd->error = -ENOMEDIUM;
 		tasklet_schedule(&host->finish_tasklet);
 	} else
@@ -1034,8 +1110,8 @@
 	 * Should clear out any weird states.
 	 */
 	if (ios->power_mode == MMC_POWER_OFF) {
-		writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-		sdhci_init(host);
+		sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+		sdhci_reinit(host);
 	}
 
 	sdhci_set_clock(host, ios->clock);
@@ -1045,7 +1121,7 @@
 	else
 		sdhci_set_power(host, ios->vdd);
 
-	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
 	if (ios->bus_width == MMC_BUS_WIDTH_4)
 		ctrl |= SDHCI_CTRL_4BITBUS;
@@ -1057,7 +1133,7 @@
 	else
 		ctrl &= ~SDHCI_CTRL_HISPD;
 
-	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
 	/*
 	 * Some (ENE) controllers go apeshit on some ios operation,
@@ -1085,10 +1161,12 @@
 	if (host->flags & SDHCI_DEVICE_DEAD)
 		present = 0;
 	else
-		present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+		present = sdhci_readl(host, SDHCI_PRESENT_STATE);
 
 	spin_unlock_irqrestore(&host->lock, flags);
 
+	if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
+		return !!(present & SDHCI_WRITE_PROTECT);
 	return !(present & SDHCI_WRITE_PROTECT);
 }
 
@@ -1096,7 +1174,6 @@
 {
 	struct sdhci_host *host;
 	unsigned long flags;
-	u32 ier;
 
 	host = mmc_priv(mmc);
 
@@ -1105,15 +1182,10 @@
 	if (host->flags & SDHCI_DEVICE_DEAD)
 		goto out;
 
-	ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
-
-	ier &= ~SDHCI_INT_CARD_INT;
 	if (enable)
-		ier |= SDHCI_INT_CARD_INT;
-
-	writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
-	writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-
+		sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
+	else
+		sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
 out:
 	mmiowb();
 
@@ -1142,7 +1214,7 @@
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+	if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
 		if (host->mrq) {
 			printk(KERN_ERR "%s: Card removed during transfer!\n",
 				mmc_hostname(host->mmc));
@@ -1346,8 +1418,8 @@
 		 * we need to at least restart the transfer.
 		 */
 		if (intmask & SDHCI_INT_DMA_END)
-			writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
-				host->ioaddr + SDHCI_DMA_ADDRESS);
+			sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS),
+				SDHCI_DMA_ADDRESS);
 
 		if (intmask & SDHCI_INT_DATA_END) {
 			if (host->cmd) {
@@ -1373,7 +1445,7 @@
 
 	spin_lock(&host->lock);
 
-	intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+	intmask = sdhci_readl(host, SDHCI_INT_STATUS);
 
 	if (!intmask || intmask == 0xffffffff) {
 		result = IRQ_NONE;
@@ -1384,22 +1456,22 @@
 		mmc_hostname(host->mmc), intmask);
 
 	if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
-		writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
-			host->ioaddr + SDHCI_INT_STATUS);
+		sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
+			SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
 		tasklet_schedule(&host->card_tasklet);
 	}
 
 	intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
 
 	if (intmask & SDHCI_INT_CMD_MASK) {
-		writel(intmask & SDHCI_INT_CMD_MASK,
-			host->ioaddr + SDHCI_INT_STATUS);
+		sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
+			SDHCI_INT_STATUS);
 		sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
 	}
 
 	if (intmask & SDHCI_INT_DATA_MASK) {
-		writel(intmask & SDHCI_INT_DATA_MASK,
-			host->ioaddr + SDHCI_INT_STATUS);
+		sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
+			SDHCI_INT_STATUS);
 		sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
 	}
 
@@ -1410,7 +1482,7 @@
 	if (intmask & SDHCI_INT_BUS_POWER) {
 		printk(KERN_ERR "%s: Card is consuming too much power!\n",
 			mmc_hostname(host->mmc));
-		writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+		sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
 	}
 
 	intmask &= ~SDHCI_INT_BUS_POWER;
@@ -1425,7 +1497,7 @@
 			mmc_hostname(host->mmc), intmask);
 		sdhci_dumpregs(host);
 
-		writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+		sdhci_writel(host, intmask, SDHCI_INT_STATUS);
 	}
 
 	result = IRQ_HANDLED;
@@ -1455,6 +1527,8 @@
 {
 	int ret;
 
+	sdhci_disable_card_detection(host);
+
 	ret = mmc_suspend_host(host->mmc, state);
 	if (ret)
 		return ret;
@@ -1487,6 +1561,8 @@
 	if (ret)
 		return ret;
 
+	sdhci_enable_card_detection(host);
+
 	return 0;
 }
 
@@ -1537,7 +1613,7 @@
 
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
-	host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
 	host->version = (host->version & SDHCI_SPEC_VER_MASK)
 				>> SDHCI_SPEC_VER_SHIFT;
 	if (host->version > SDHCI_SPEC_200) {
@@ -1546,7 +1622,7 @@
 			host->version);
 	}
 
-	caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
 
 	if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
 		host->flags |= SDHCI_USE_DMA;
@@ -1614,19 +1690,27 @@
 
 	host->max_clk =
 		(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
-	if (host->max_clk == 0) {
-		printk(KERN_ERR "%s: Hardware doesn't specify base clock "
-			"frequency.\n", mmc_hostname(mmc));
-		return -ENODEV;
-	}
 	host->max_clk *= 1000000;
+	if (host->max_clk == 0) {
+		if (!host->ops->get_max_clock) {
+			printk(KERN_ERR
+			       "%s: Hardware doesn't specify base clock "
+			       "frequency.\n", mmc_hostname(mmc));
+			return -ENODEV;
+		}
+		host->max_clk = host->ops->get_max_clock(host);
+	}
 
 	host->timeout_clk =
 		(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
 	if (host->timeout_clk == 0) {
-		printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
-			"frequency.\n", mmc_hostname(mmc));
-		return -ENODEV;
+		if (!host->ops->get_timeout_clock) {
+			printk(KERN_ERR
+			       "%s: Hardware doesn't specify timeout clock "
+			       "frequency.\n", mmc_hostname(mmc));
+			return -ENODEV;
+		}
+		host->timeout_clk = host->ops->get_timeout_clock(host);
 	}
 	if (caps & SDHCI_TIMEOUT_CLK_UNIT)
 		host->timeout_clk *= 1000;
@@ -1642,6 +1726,9 @@
 	if (caps & SDHCI_CAN_DO_HISPD)
 		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
 
+	if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+
 	mmc->ocr_avail = 0;
 	if (caps & SDHCI_CAN_VDD_330)
 		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
@@ -1690,13 +1777,19 @@
 	 * Maximum block size. This varies from controller to controller and
 	 * is specified in the capabilities register.
 	 */
-	mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
-	if (mmc->max_blk_size >= 3) {
-		printk(KERN_WARNING "%s: Invalid maximum block size, "
-			"assuming 512 bytes\n", mmc_hostname(mmc));
-		mmc->max_blk_size = 512;
-	} else
-		mmc->max_blk_size = 512 << mmc->max_blk_size;
+	if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
+		mmc->max_blk_size = 2;
+	} else {
+		mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
+				SDHCI_MAX_BLOCK_SHIFT;
+		if (mmc->max_blk_size >= 3) {
+			printk(KERN_WARNING "%s: Invalid maximum block size, "
+				"assuming 512 bytes\n", mmc_hostname(mmc));
+			mmc->max_blk_size = 0;
+		}
+	}
+
+	mmc->max_blk_size = 512 << mmc->max_blk_size;
 
 	/*
 	 * Maximum block count.
@@ -1746,6 +1839,8 @@
 		(host->flags & SDHCI_USE_ADMA)?"A":"",
 		(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
 
+	sdhci_enable_card_detection(host);
+
 	return 0;
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -1782,6 +1877,8 @@
 		spin_unlock_irqrestore(&host->lock, flags);
 	}
 
+	sdhci_disable_card_detection(host);
+
 	mmc_remove_host(host->mmc);
 
 #ifdef SDHCI_USE_LEDS_CLASS
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 43c37c6..f20a834 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -10,6 +10,9 @@
  */
 
 #include <linux/scatterlist.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/io.h>
 
 /*
  * Controller registers
@@ -123,6 +126,7 @@
 		SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
 		SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
 		SDHCI_INT_DATA_END_BIT)
+#define SDHCI_INT_ALL_MASK	((unsigned int)-1)
 
 #define SDHCI_ACMD12_ERR	0x3C
 
@@ -210,6 +214,18 @@
 #define SDHCI_QUIRK_BROKEN_SMALL_PIO			(1<<13)
 /* Controller does not provide transfer-complete interrupt when not busy */
 #define SDHCI_QUIRK_NO_BUSY_IRQ				(1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION		(1<<15)
+/* Controller reports inverted write-protect state */
+#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT		(1<<16)
+/* Controller has nonstandard clock management */
+#define SDHCI_QUIRK_NONSTANDARD_CLOCK			(1<<17)
+/* Controller does not like fast PIO transfers */
+#define SDHCI_QUIRK_PIO_NEEDS_DELAY			(1<<18)
+/* Controller losing signal/interrupt enable states after reset */
+#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET		(1<<19)
+/* Controller has to be forced to use block size of 2048 bytes */
+#define SDHCI_QUIRK_FORCE_BLK_SZ_2048			(1<<20)
 
 	int			irq;		/* Device IRQ */
 	void __iomem *		ioaddr;		/* Mapped address */
@@ -267,9 +283,105 @@
 
 
 struct sdhci_ops {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+	u32		(*readl)(struct sdhci_host *host, int reg);
+	u16		(*readw)(struct sdhci_host *host, int reg);
+	u8		(*readb)(struct sdhci_host *host, int reg);
+	void		(*writel)(struct sdhci_host *host, u32 val, int reg);
+	void		(*writew)(struct sdhci_host *host, u16 val, int reg);
+	void		(*writeb)(struct sdhci_host *host, u8 val, int reg);
+#endif
+
+	void	(*set_clock)(struct sdhci_host *host, unsigned int clock);
+
 	int		(*enable_dma)(struct sdhci_host *host);
+	unsigned int	(*get_max_clock)(struct sdhci_host *host);
+	unsigned int	(*get_timeout_clock)(struct sdhci_host *host);
 };
 
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	if (unlikely(host->ops->writel))
+		host->ops->writel(host, val, reg);
+	else
+		writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	if (unlikely(host->ops->writew))
+		host->ops->writew(host, val, reg);
+	else
+		writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	if (unlikely(host->ops->writeb))
+		host->ops->writeb(host, val, reg);
+	else
+		writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+	if (unlikely(host->ops->readl))
+		return host->ops->readl(host, reg);
+	else
+		return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+	if (unlikely(host->ops->readw))
+		return host->ops->readw(host, reg);
+	else
+		return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+	if (unlikely(host->ops->readb))
+		return host->ops->readb(host, reg);
+	else
+		return readb(host->ioaddr + reg);
+}
+
+#else
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+	return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+	return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+	return readb(host->ioaddr + reg);
+}
+
+#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
 
 extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
 	size_t priv_size);
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 6a7a619..63fbd5b 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -568,11 +568,11 @@
 	host->mmc = mmc;
 	platform_set_drvdata(dev, mmc);
 
-	host->ctl = ioremap(res_ctl->start, res_ctl->end - res_ctl->start);
+	host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
 	if (!host->ctl)
 		goto host_free;
 
-	host->cnf = ioremap(res_cnf->start, res_cnf->end - res_cnf->start);
+	host->cnf = ioremap(res_cnf->start, resource_size(res_cnf));
 	if (!host->cnf)
 		goto unmap_ctl;
 
@@ -650,10 +650,10 @@
 	if (mmc) {
 		struct tmio_mmc_host *host = mmc_priv(mmc);
 		mmc_remove_host(mmc);
-		mmc_free_host(mmc);
 		free_irq(host->irq, host);
 		iounmap(host->ctl);
 		iounmap(host->cnf);
+		mmc_free_host(mmc);
 	}
 
 	return 0;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index ba2b424..9c831ab 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -8,6 +8,9 @@
  * published by the Free Software Foundation.
  *
  */
+
+#include <linux/highmem.h>
+
 #define CNF_CMD     0x04
 #define CNF_CTL_BASE   0x10
 #define CNF_INT_PIN  0x3d