mmc: Move host and card drivers to subdirs

Clean up the drivers/mmc directory by moving card and host drivers
into subdirectories.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
new file mode 100644
index 0000000..ed4deab
--- /dev/null
+++ b/drivers/mmc/host/Kconfig
@@ -0,0 +1,103 @@
+#
+# MMC/SD host controller drivers
+#
+
+comment "MMC/SD Host Controller Drivers"
+	depends on MMC
+
+config MMC_ARMMMCI
+	tristate "ARM AMBA Multimedia Card Interface support"
+	depends on ARM_AMBA && MMC
+	help
+	  This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
+	  Interface (PL180 and PL181) support.  If you have an ARM(R)
+	  platform with a Multimedia Card slot, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_PXA
+	tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
+	depends on ARCH_PXA && MMC
+	help
+	  This selects the Intel(R) PXA(R) Multimedia card Interface.
+	  If you have a PXA(R) platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_SDHCI
+	tristate "Secure Digital Host Controller Interface support  (EXPERIMENTAL)"
+	depends on PCI && MMC && EXPERIMENTAL
+	help
+	  This select the generic Secure Digital Host Controller Interface.
+	  It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
+	  and Toshiba(R). Most controllers found in laptops are of this type.
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_OMAP
+	tristate "TI OMAP Multimedia Card Interface support"
+	depends on ARCH_OMAP && MMC
+	select TPS65010 if MACH_OMAP_H2
+	help
+	  This selects the TI OMAP Multimedia card Interface.
+	  If you have an OMAP board with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_WBSD
+	tristate "Winbond W83L51xD SD/MMC Card Interface support"
+	depends on MMC && ISA_DMA_API
+	help
+	  This selects the Winbond(R) W83L51xD Secure digital and
+          Multimedia card Interface.
+	  If you have a machine with a integrated W83L518D or W83L519D
+	  SD/MMC card reader, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_AU1X
+	tristate "Alchemy AU1XX0 MMC Card Interface support"
+	depends on MMC && SOC_AU1200
+	help
+	  This selects the AMD Alchemy(R) Multimedia card interface.
+	  If you have a Alchemy platform with a MMC slot, say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_AT91
+	tristate "AT91 SD/MMC Card Interface support"
+	depends on ARCH_AT91 && MMC
+	help
+	  This selects the AT91 MCI controller.
+
+	  If unsure, say N.
+
+config MMC_IMX
+	tristate "Motorola i.MX Multimedia Card Interface support"
+	depends on ARCH_IMX && MMC
+	help
+	  This selects the Motorola i.MX Multimedia card Interface.
+	  If you have a i.MX platform with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
+config MMC_TIFM_SD
+	tristate "TI Flash Media MMC/SD Interface support  (EXPERIMENTAL)"
+	depends on MMC && EXPERIMENTAL && PCI
+	select TIFM_CORE
+	help
+	  Say Y here if you want to be able to access MMC/SD cards with
+	  the Texas Instruments(R) Flash Media card reader, found in many
+	  laptops.
+	  This option 'selects' (turns on, enables) 'TIFM_CORE', but you
+	  probably also need appropriate card reader host adapter, such as
+	  'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
+	  (TIFM_7XX1)'.
+
+          To compile this driver as a module, choose M here: the
+	  module will be called tifm_sd.
+
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
new file mode 100644
index 0000000..6685f64
--- /dev/null
+++ b/drivers/mmc/host/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for MMC/SD host controller drivers
+#
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+	EXTRA_CFLAGS		+= -DDEBUG
+endif
+
+obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
+obj-$(CONFIG_MMC_PXA)		+= pxamci.o
+obj-$(CONFIG_MMC_IMX)		+= imxmmc.o
+obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
+obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
+obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
+obj-$(CONFIG_MMC_OMAP)		+= omap.o
+obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
+obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
new file mode 100644
index 0000000..e37943c
--- /dev/null
+++ b/drivers/mmc/host/at91_mci.c
@@ -0,0 +1,1001 @@
+/*
+ *  linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver
+ *
+ *  Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
+ *
+ *  Copyright (C) 2006 Malcolm Noyes
+ *
+ * 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.
+ */
+
+/*
+   This is the AT91 MCI driver that has been tested with both MMC cards
+   and SD-cards.  Boards that support write protect are now supported.
+   The CCAT91SBC001 board does not support SD cards.
+
+   The three entry points are at91_mci_request, at91_mci_set_ios
+   and at91_mci_get_ro.
+
+   SET IOS
+     This configures the device to put it into the correct mode and clock speed
+     required.
+
+   MCI REQUEST
+     MCI request processes the commands sent in the mmc_request structure. This
+     can consist of a processing command and a stop command in the case of
+     multiple block transfers.
+
+     There are three main types of request, commands, reads and writes.
+
+     Commands are straight forward. The command is submitted to the controller and
+     the request function returns. When the controller generates an interrupt to indicate
+     the command is finished, the response to the command are read and the mmc_request_done
+     function called to end the request.
+
+     Reads and writes work in a similar manner to normal commands but involve the PDC (DMA)
+     controller to manage the transfers.
+
+     A read is done from the controller directly to the scatterlist passed in from the request.
+     Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte
+     swapped in the scatterlist buffers.  AT91SAM926x are not affected by this bug.
+
+     The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY
+
+     A write is slightly different in that the bytes to write are read from the scatterlist
+     into a dma memory buffer (this is in case the source buffer should be read only). The
+     entire write buffer is then done from this single dma memory buffer.
+
+     The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY
+
+   GET RO
+     Gets the status of the write protect pin, if available.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/mmc.h>
+#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/at91_mci.h>
+
+#define DRIVER_NAME "at91_mci"
+
+#undef	SUPPORT_4WIRE
+
+#define FL_SENT_COMMAND	(1 << 0)
+#define FL_SENT_STOP	(1 << 1)
+
+#define AT91_MCI_ERRORS	(AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE	\
+		| AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE		\
+		| AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)			
+
+#define at91_mci_read(host, reg)	__raw_readl((host)->baseaddr + (reg))
+#define at91_mci_write(host, reg, val)	__raw_writel((val), (host)->baseaddr + (reg))
+
+
+/*
+ * Low level type for this driver
+ */
+struct at91mci_host
+{
+	struct mmc_host *mmc;
+	struct mmc_command *cmd;
+	struct mmc_request *request;
+
+	void __iomem *baseaddr;
+	int irq;
+
+	struct at91_mmc_data *board;
+	int present;
+
+	struct clk *mci_clk;
+
+	/*
+	 * Flag indicating when the command has been sent. This is used to
+	 * work out whether or not to send the stop
+	 */
+	unsigned int flags;
+	/* flag for current bus settings */
+	u32 bus_mode;
+
+	/* DMA buffer used for transmitting */
+	unsigned int* buffer;
+	dma_addr_t physical_address;
+	unsigned int total_length;
+
+	/* Latest in the scatterlist that has been enabled for transfer, but not freed */
+	int in_use_index;
+
+	/* Latest in the scatterlist that has been enabled for transfer */
+	int transfer_index;
+};
+
+/*
+ * Copy from sg to a dma block - used for transfers
+ */
+static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
+{
+	unsigned int len, i, size;
+	unsigned *dmabuf = host->buffer;
+
+	size = host->total_length;
+	len = data->sg_len;
+
+	/*
+	 * Just loop through all entries. Size might not
+	 * be the entire list though so make sure that
+	 * we do not transfer too much.
+	 */
+	for (i = 0; i < len; i++) {
+		struct scatterlist *sg;
+		int amount;
+		unsigned int *sgbuffer;
+
+		sg = &data->sg[i];
+
+		sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+		amount = min(size, sg->length);
+		size -= amount;
+
+		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */
+			int index;
+
+			for (index = 0; index < (amount / 4); index++)
+				*dmabuf++ = swab32(sgbuffer[index]);
+		}
+		else
+			memcpy(dmabuf, sgbuffer, amount);
+
+		kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
+
+		if (size == 0)
+			break;
+	}
+
+	/*
+	 * Check that we didn't get a request to transfer
+	 * more data than can fit into the SG list.
+	 */
+	BUG_ON(size != 0);
+}
+
+/*
+ * Prepare a dma read
+ */
+static void at91mci_pre_dma_read(struct at91mci_host *host)
+{
+	int i;
+	struct scatterlist *sg;
+	struct mmc_command *cmd;
+	struct mmc_data *data;
+
+	pr_debug("pre dma read\n");
+
+	cmd = host->cmd;
+	if (!cmd) {
+		pr_debug("no command\n");
+		return;
+	}
+
+	data = cmd->data;
+	if (!data) {
+		pr_debug("no data\n");
+		return;
+	}
+
+	for (i = 0; i < 2; i++) {
+		/* nothing left to transfer */
+		if (host->transfer_index >= data->sg_len) {
+			pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index);
+			break;
+		}
+
+		/* Check to see if this needs filling */
+		if (i == 0) {
+			if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) {
+				pr_debug("Transfer active in current\n");
+				continue;
+			}
+		}
+		else {
+			if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) {
+				pr_debug("Transfer active in next\n");
+				continue;
+			}
+		}
+
+		/* Setup the next transfer */
+		pr_debug("Using transfer index %d\n", host->transfer_index);
+
+		sg = &data->sg[host->transfer_index++];
+		pr_debug("sg = %p\n", sg);
+
+		sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE);
+
+		pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length);
+
+		if (i == 0) {
+			at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
+			at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4);
+		}
+		else {
+			at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
+			at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4);
+		}
+	}
+
+	pr_debug("pre dma read done\n");
+}
+
+/*
+ * Handle after a dma read
+ */
+static void at91mci_post_dma_read(struct at91mci_host *host)
+{
+	struct mmc_command *cmd;
+	struct mmc_data *data;
+
+	pr_debug("post dma read\n");
+
+	cmd = host->cmd;
+	if (!cmd) {
+		pr_debug("no command\n");
+		return;
+	}
+
+	data = cmd->data;
+	if (!data) {
+		pr_debug("no data\n");
+		return;
+	}
+
+	while (host->in_use_index < host->transfer_index) {
+		unsigned int *buffer;
+
+		struct scatterlist *sg;
+
+		pr_debug("finishing index %d\n", host->in_use_index);
+
+		sg = &data->sg[host->in_use_index++];
+
+		pr_debug("Unmapping page %08X\n", sg->dma_address);
+
+		dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
+
+		/* Swap the contents of the buffer */
+		buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+		pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+
+		data->bytes_xfered += sg->length;
+
+		if (cpu_is_at91rm9200()) {	/* AT91RM9200 errata */
+			int index;
+
+			for (index = 0; index < (sg->length / 4); index++)
+				buffer[index] = swab32(buffer[index]);
+		}
+
+		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+		flush_dcache_page(sg->page);
+	}
+
+	/* Is there another transfer to trigger? */
+	if (host->transfer_index < data->sg_len)
+		at91mci_pre_dma_read(host);
+	else {
+		at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+	}
+
+	pr_debug("post dma read done\n");
+}
+
+/*
+ * Handle transmitted data
+ */
+static void at91_mci_handle_transmitted(struct at91mci_host *host)
+{
+	struct mmc_command *cmd;
+	struct mmc_data *data;
+
+	pr_debug("Handling the transmit\n");
+
+	/* Disable the transfer */
+	at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+	/* Now wait for cmd ready */
+	at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+
+	cmd = host->cmd;
+	if (!cmd) return;
+
+	data = cmd->data;
+	if (!data) return;
+
+	data->bytes_xfered = host->total_length;
+}
+
+/*
+ * Enable the controller
+ */
+static void at91_mci_enable(struct at91mci_host *host)
+{
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+	at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+	at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
+
+	/* use Slot A or B (only one at same time) */
+	at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
+}
+
+/*
+ * Disable the controller
+ */
+static void at91_mci_disable(struct at91mci_host *host)
+{
+	at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
+}
+
+/*
+ * Send a command
+ * return the interrupts to enable
+ */
+static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+{
+	unsigned int cmdr, mr;
+	unsigned int block_length;
+	struct mmc_data *data = cmd->data;
+
+	unsigned int blocks;
+	unsigned int ier = 0;
+
+	host->cmd = cmd;
+
+	/* Not sure if this is needed */
+#if 0
+	if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
+		pr_debug("Clearing timeout\n");
+		at91_mci_write(host, AT91_MCI_ARGR, 0);
+		at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD);
+		while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
+			/* spin */
+			pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
+		}
+	}
+#endif
+	cmdr = cmd->opcode;
+
+	if (mmc_resp_type(cmd) == MMC_RSP_NONE)
+		cmdr |= AT91_MCI_RSPTYP_NONE;
+	else {
+		/* if a response is expected then allow maximum response latancy */
+		cmdr |= AT91_MCI_MAXLAT;
+		/* set 136 bit response for R2, 48 bit response otherwise */
+		if (mmc_resp_type(cmd) == MMC_RSP_R2)
+			cmdr |= AT91_MCI_RSPTYP_136;
+		else
+			cmdr |= AT91_MCI_RSPTYP_48;
+	}
+
+	if (data) {
+		block_length = data->blksz;
+		blocks = data->blocks;
+
+		/* always set data start - also set direction flag for read */
+		if (data->flags & MMC_DATA_READ)
+			cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START);
+		else if (data->flags & MMC_DATA_WRITE)
+			cmdr |= AT91_MCI_TRCMD_START;
+
+		if (data->flags & MMC_DATA_STREAM)
+			cmdr |= AT91_MCI_TRTYP_STREAM;
+		if (data->flags & MMC_DATA_MULTI)
+			cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+	}
+	else {
+		block_length = 0;
+		blocks = 0;
+	}
+
+	if (cmd->opcode == MMC_STOP_TRANSMISSION)
+		cmdr |= AT91_MCI_TRCMD_STOP;
+
+	if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		cmdr |= AT91_MCI_OPDCMD;
+
+	/*
+	 * Set the arguments and send the command
+	 */
+	pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n",
+		cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR));
+
+	if (!data) {
+		at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS);
+		at91_mci_write(host, ATMEL_PDC_RPR, 0);
+		at91_mci_write(host, ATMEL_PDC_RCR, 0);
+		at91_mci_write(host, ATMEL_PDC_RNPR, 0);
+		at91_mci_write(host, ATMEL_PDC_RNCR, 0);
+		at91_mci_write(host, ATMEL_PDC_TPR, 0);
+		at91_mci_write(host, ATMEL_PDC_TCR, 0);
+		at91_mci_write(host, ATMEL_PDC_TNPR, 0);
+		at91_mci_write(host, ATMEL_PDC_TNCR, 0);
+
+		at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+		at91_mci_write(host, AT91_MCI_CMDR, cmdr);
+		return AT91_MCI_CMDRDY;
+	}
+
+	mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;	/* zero block length and PDC mode */
+	at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+
+	/*
+	 * Disable the PDC controller
+	 */
+	at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+	if (cmdr & AT91_MCI_TRCMD_START) {
+		data->bytes_xfered = 0;
+		host->transfer_index = 0;
+		host->in_use_index = 0;
+		if (cmdr & AT91_MCI_TRDIR) {
+			/*
+			 * Handle a read
+			 */
+			host->buffer = NULL;
+			host->total_length = 0;
+
+			at91mci_pre_dma_read(host);
+			ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+		}
+		else {
+			/*
+			 * Handle a write
+			 */
+			host->total_length = block_length * blocks;
+			host->buffer = dma_alloc_coherent(NULL,
+						  host->total_length,
+						  &host->physical_address, GFP_KERNEL);
+
+			at91mci_sg_to_dma(host, data);
+
+			pr_debug("Transmitting %d bytes\n", host->total_length);
+
+			at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+			at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
+			ier = AT91_MCI_TXBUFE;
+		}
+	}
+
+	/*
+	 * Send the command and then enable the PDC - not the other way round as
+	 * the data sheet says
+	 */
+
+	at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+	at91_mci_write(host, AT91_MCI_CMDR, cmdr);
+
+	if (cmdr & AT91_MCI_TRCMD_START) {
+		if (cmdr & AT91_MCI_TRDIR)
+			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
+		else
+			at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+	}
+	return ier;
+}
+
+/*
+ * Wait for a command to complete
+ */
+static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
+{
+	unsigned int ier;
+
+	ier = at91_mci_send_command(host, cmd);
+
+	pr_debug("setting ier to %08X\n", ier);
+
+	/* Stop on errors or the required value */
+	at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
+}
+
+/*
+ * Process the next step in the request
+ */
+static void at91mci_process_next(struct at91mci_host *host)
+{
+	if (!(host->flags & FL_SENT_COMMAND)) {
+		host->flags |= FL_SENT_COMMAND;
+		at91mci_process_command(host, host->request->cmd);
+	}
+	else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
+		host->flags |= FL_SENT_STOP;
+		at91mci_process_command(host, host->request->stop);
+	}
+	else
+		mmc_request_done(host->mmc, host->request);
+}
+
+/*
+ * Handle a command that has been completed
+ */
+static void at91mci_completed_command(struct at91mci_host *host)
+{
+	struct mmc_command *cmd = host->cmd;
+	unsigned int status;
+
+	at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+
+	cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
+	cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
+	cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
+	cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
+
+	if (host->buffer) {
+		dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address);
+		host->buffer = NULL;
+	}
+
+	status = at91_mci_read(host, AT91_MCI_SR);
+
+	pr_debug("Status = %08X [%08X %08X %08X %08X]\n",
+		 status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+
+	if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE |
+			AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE |
+			AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) {
+		if ((status & AT91_MCI_RCRCE) &&
+			((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) {
+			cmd->error = MMC_ERR_NONE;
+		}
+		else {
+			if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
+				cmd->error = MMC_ERR_TIMEOUT;
+			else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
+				cmd->error = MMC_ERR_BADCRC;
+			else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE))
+				cmd->error = MMC_ERR_FIFO;
+			else
+				cmd->error = MMC_ERR_FAILED;
+
+			pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n",
+				 cmd->error, cmd->opcode, cmd->retries);
+		}
+	}
+	else
+		cmd->error = MMC_ERR_NONE;
+
+	at91mci_process_next(host);
+}
+
+/*
+ * Handle an MMC request
+ */
+static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct at91mci_host *host = mmc_priv(mmc);
+	host->request = mrq;
+	host->flags = 0;
+
+	at91mci_process_next(host);
+}
+
+/*
+ * Set the IOS
+ */
+static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	int clkdiv;
+	struct at91mci_host *host = mmc_priv(mmc);
+	unsigned long at91_master_clock = clk_get_rate(host->mci_clk);
+
+	host->bus_mode = ios->bus_mode;
+
+	if (ios->clock == 0) {
+		/* Disable the MCI controller */
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+		clkdiv = 0;
+	}
+	else {
+		/* Enable the MCI controller */
+		at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+
+		if ((at91_master_clock % (ios->clock * 2)) == 0)
+			clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
+		else
+			clkdiv = (at91_master_clock / ios->clock) / 2;
+
+		pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv,
+			at91_master_clock / (2 * (clkdiv + 1)));
+	}
+	if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) {
+		pr_debug("MMC: Setting controller bus width to 4\n");
+		at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
+	}
+	else {
+		pr_debug("MMC: Setting controller bus width to 1\n");
+		at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
+	}
+
+	/* Set the clock divider */
+	at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
+
+	/* maybe switch power to the card */
+	if (host->board->vcc_pin) {
+		switch (ios->power_mode) {
+			case MMC_POWER_OFF:
+				at91_set_gpio_value(host->board->vcc_pin, 0);
+				break;
+			case MMC_POWER_UP:
+			case MMC_POWER_ON:
+				at91_set_gpio_value(host->board->vcc_pin, 1);
+				break;
+		}
+	}
+}
+
+/*
+ * Handle an interrupt
+ */
+static irqreturn_t at91_mci_irq(int irq, void *devid)
+{
+	struct at91mci_host *host = devid;
+	int completed = 0;
+	unsigned int int_status, int_mask;
+
+	int_status = at91_mci_read(host, AT91_MCI_SR);
+	int_mask = at91_mci_read(host, AT91_MCI_IMR);
+	
+	pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask,
+		int_status & int_mask);
+	
+	int_status = int_status & int_mask;
+
+	if (int_status & AT91_MCI_ERRORS) {
+		completed = 1;
+		
+		if (int_status & AT91_MCI_UNRE)
+			pr_debug("MMC: Underrun error\n");
+		if (int_status & AT91_MCI_OVRE)
+			pr_debug("MMC: Overrun error\n");
+		if (int_status & AT91_MCI_DTOE)
+			pr_debug("MMC: Data timeout\n");
+		if (int_status & AT91_MCI_DCRCE)
+			pr_debug("MMC: CRC error in data\n");
+		if (int_status & AT91_MCI_RTOE)
+			pr_debug("MMC: Response timeout\n");
+		if (int_status & AT91_MCI_RENDE)
+			pr_debug("MMC: Response end bit error\n");
+		if (int_status & AT91_MCI_RCRCE)
+			pr_debug("MMC: Response CRC error\n");
+		if (int_status & AT91_MCI_RDIRE)
+			pr_debug("MMC: Response direction error\n");
+		if (int_status & AT91_MCI_RINDE)
+			pr_debug("MMC: Response index error\n");
+	} else {
+		/* Only continue processing if no errors */
+
+		if (int_status & AT91_MCI_TXBUFE) {
+			pr_debug("TX buffer empty\n");
+			at91_mci_handle_transmitted(host);
+		}
+
+		if (int_status & AT91_MCI_RXBUFF) {
+			pr_debug("RX buffer full\n");
+			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+		}
+
+		if (int_status & AT91_MCI_ENDTX)
+			pr_debug("Transmit has ended\n");
+
+		if (int_status & AT91_MCI_ENDRX) {
+			pr_debug("Receive has ended\n");
+			at91mci_post_dma_read(host);
+		}
+
+		if (int_status & AT91_MCI_NOTBUSY) {
+			pr_debug("Card is ready\n");
+			at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
+		}
+
+		if (int_status & AT91_MCI_DTIP)
+			pr_debug("Data transfer in progress\n");
+
+		if (int_status & AT91_MCI_BLKE)
+			pr_debug("Block transfer has ended\n");
+
+		if (int_status & AT91_MCI_TXRDY)
+			pr_debug("Ready to transmit\n");
+
+		if (int_status & AT91_MCI_RXRDY)
+			pr_debug("Ready to receive\n");
+
+		if (int_status & AT91_MCI_CMDRDY) {
+			pr_debug("Command ready\n");
+			completed = 1;
+		}
+	}
+
+	if (completed) {
+		pr_debug("Completed command\n");
+		at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+		at91mci_completed_command(host);
+	} else
+		at91_mci_write(host, AT91_MCI_IDR, int_status);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
+{
+	struct at91mci_host *host = _host;
+	int present = !at91_get_gpio_value(irq);
+
+	/*
+	 * we expect this irq on both insert and remove,
+	 * and use a short delay to debounce.
+	 */
+	if (present != host->present) {
+		host->present = present;
+		pr_debug("%s: card %s\n", mmc_hostname(host->mmc),
+			present ? "insert" : "remove");
+		if (!present) {
+			pr_debug("****** Resetting SD-card bus width ******\n");
+			at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
+		}
+		mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+	}
+	return IRQ_HANDLED;
+}
+
+static int at91_mci_get_ro(struct mmc_host *mmc)
+{
+	int read_only = 0;
+	struct at91mci_host *host = mmc_priv(mmc);
+
+	if (host->board->wp_pin) {
+		read_only = at91_get_gpio_value(host->board->wp_pin);
+		printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc),
+				(read_only ? "read-only" : "read-write") );
+	}
+	else {
+		printk(KERN_WARNING "%s: host does not support reading read-only "
+				"switch.  Assuming write-enable.\n", mmc_hostname(mmc));
+	}
+	return read_only;
+}
+
+static const struct mmc_host_ops at91_mci_ops = {
+	.request	= at91_mci_request,
+	.set_ios	= at91_mci_set_ios,
+	.get_ro		= at91_mci_get_ro,
+};
+
+/*
+ * Probe for the device
+ */
+static int __init at91_mci_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc;
+	struct at91mci_host *host;
+	struct resource *res;
+	int ret;
+
+	pr_debug("Probe MCI devices\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME))
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
+	if (!mmc) {
+		pr_debug("Failed to allocate mmc host\n");
+		release_mem_region(res->start, res->end - res->start + 1);
+		return -ENOMEM;
+	}
+
+	mmc->ops = &at91_mci_ops;
+	mmc->f_min = 375000;
+	mmc->f_max = 25000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->caps = MMC_CAP_BYTEBLOCK;
+
+	mmc->max_blk_size = 4095;
+	mmc->max_blk_count = mmc->max_req_size;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->buffer = NULL;
+	host->bus_mode = 0;
+	host->board = pdev->dev.platform_data;
+	if (host->board->wire4) {
+#ifdef SUPPORT_4WIRE
+		mmc->caps |= MMC_CAP_4_BIT_DATA;
+#else
+		printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
+#endif
+	}
+
+	/*
+	 * Get Clock
+	 */
+	host->mci_clk = clk_get(&pdev->dev, "mci_clk");
+	if (IS_ERR(host->mci_clk)) {
+		printk(KERN_ERR "AT91 MMC: no clock defined.\n");
+		mmc_free_host(mmc);
+		release_mem_region(res->start, res->end - res->start + 1);
+		return -ENODEV;
+	}
+
+	/*
+	 * Map I/O region
+	 */
+	host->baseaddr = ioremap(res->start, res->end - res->start + 1);
+	if (!host->baseaddr) {
+		clk_put(host->mci_clk);
+		mmc_free_host(mmc);
+		release_mem_region(res->start, res->end - res->start + 1);
+		return -ENOMEM;
+	}
+
+	/*
+	 * Reset hardware
+	 */
+	clk_enable(host->mci_clk);		/* Enable the peripheral clock */
+	at91_mci_disable(host);
+	at91_mci_enable(host);
+
+	/*
+	 * Allocate the MCI interrupt
+	 */
+	host->irq = platform_get_irq(pdev, 0);
+	ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+	if (ret) {
+		printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n");
+		clk_disable(host->mci_clk);
+		clk_put(host->mci_clk);
+		mmc_free_host(mmc);
+		iounmap(host->baseaddr);
+		release_mem_region(res->start, res->end - res->start + 1);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+
+	/*
+	 * Add host to MMC layer
+	 */
+	if (host->board->det_pin)
+		host->present = !at91_get_gpio_value(host->board->det_pin);
+	else
+		host->present = -1;
+
+	mmc_add_host(mmc);
+
+	/*
+	 * monitor card insertion/removal if we can
+	 */
+	if (host->board->det_pin) {
+		ret = request_irq(host->board->det_pin, at91_mmc_det_irq,
+				0, DRIVER_NAME, host);
+		if (ret)
+			printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n");
+	}
+
+	pr_debug("Added MCI driver\n");
+
+	return 0;
+}
+
+/*
+ * Remove a device
+ */
+static int __exit at91_mci_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+	struct at91mci_host *host;
+	struct resource *res;
+
+	if (!mmc)
+		return -1;
+
+	host = mmc_priv(mmc);
+
+	if (host->present != -1) {
+		free_irq(host->board->det_pin, host);
+		cancel_delayed_work(&host->mmc->detect);
+	}
+
+	at91_mci_disable(host);
+	mmc_remove_host(mmc);
+	free_irq(host->irq, host);
+
+	clk_disable(host->mci_clk);			/* Disable the peripheral clock */
+	clk_put(host->mci_clk);
+
+	iounmap(host->baseaddr);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+
+	mmc_free_host(mmc);
+	platform_set_drvdata(pdev, NULL);
+	pr_debug("MCI Removed\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	if (mmc)
+		ret = mmc_suspend_host(mmc, state);
+
+	return ret;
+}
+
+static int at91_mci_resume(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	if (mmc)
+		ret = mmc_resume_host(mmc);
+
+	return ret;
+}
+#else
+#define at91_mci_suspend	NULL
+#define at91_mci_resume		NULL
+#endif
+
+static struct platform_driver at91_mci_driver = {
+	.remove		= __exit_p(at91_mci_remove),
+	.suspend	= at91_mci_suspend,
+	.resume		= at91_mci_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init at91_mci_init(void)
+{
+	return platform_driver_probe(&at91_mci_driver, at91_mci_probe);
+}
+
+static void __exit at91_mci_exit(void)
+{
+	platform_driver_unregister(&at91_mci_driver);
+}
+
+module_init(at91_mci_init);
+module_exit(at91_mci_exit);
+
+MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");
+MODULE_AUTHOR("Nick Randell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
new file mode 100644
index 0000000..b7156a4
--- /dev/null
+++ b/drivers/mmc/host/au1xmmc.c
@@ -0,0 +1,1031 @@
+/*
+ * linux/drivers/mmc/au1xmmc.c - AU1XX0 MMC driver
+ *
+ *  Copyright (c) 2005, Advanced Micro Devices, Inc.
+ *
+ *  Developed with help from the 2.4.30 MMC AU1XXX controller including
+ *  the following copyright notices:
+ *     Copyright (c) 2003-2004 Embedded Edge, LLC.
+ *     Portions Copyright (C) 2002 Embedix, Inc
+ *     Copyright 2002 Hewlett-Packard Company
+
+ *  2.6 version of this driver inspired by:
+ *     (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman,
+ *     All Rights Reserved.
+ *     (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King,
+ *     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.
+ */
+
+/* Why is a timer used to detect insert events?
+ *
+ * From the AU1100 MMC application guide:
+ * If the Au1100-based design is intended to support both MultiMediaCards
+ * and 1- or 4-data bit SecureDigital cards, then the solution is to
+ * connect a weak (560KOhm) pull-up resistor to connector pin 1.
+ * In doing so, a MMC card never enters SPI-mode communications,
+ * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective
+ * (the low to high transition will not occur).
+ *
+ * So we use the timer to check the status manually.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+#include <asm/io.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1100_mmc.h>
+#include <asm/scatterlist.h>
+
+#include <au1xxx.h>
+#include "au1xmmc.h"
+
+#define DRIVER_NAME "au1xxx-mmc"
+
+/* Set this to enable special debugging macros */
+
+#ifdef DEBUG
+#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args)
+#else
+#define DBG(fmt, idx, args...)
+#endif
+
+const struct {
+	u32 iobase;
+	u32 tx_devid, rx_devid;
+	u16 bcsrpwr;
+	u16 bcsrstatus;
+	u16 wpstatus;
+} au1xmmc_card_table[] = {
+	{ SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0,
+	  BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP },
+#ifndef CONFIG_MIPS_DB1200
+	{ SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1,
+	  BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP }
+#endif
+};
+
+#define AU1XMMC_CONTROLLER_COUNT \
+	(sizeof(au1xmmc_card_table) / sizeof(au1xmmc_card_table[0]))
+
+/* This array stores pointers for the hosts (used by the IRQ handler) */
+struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT];
+static int dma = 1;
+
+#ifdef MODULE
+module_param(dma, bool, 0);
+MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)");
+#endif
+
+static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
+{
+	u32 val = au_readl(HOST_CONFIG(host));
+	val |= mask;
+	au_writel(val, HOST_CONFIG(host));
+	au_sync();
+}
+
+static inline void FLUSH_FIFO(struct au1xmmc_host *host)
+{
+	u32 val = au_readl(HOST_CONFIG2(host));
+
+	au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host));
+	au_sync_delay(1);
+
+	/* SEND_STOP will turn off clock control - this re-enables it */
+	val &= ~SD_CONFIG2_DF;
+
+	au_writel(val, HOST_CONFIG2(host));
+	au_sync();
+}
+
+static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask)
+{
+	u32 val = au_readl(HOST_CONFIG(host));
+	val &= ~mask;
+	au_writel(val, HOST_CONFIG(host));
+	au_sync();
+}
+
+static inline void SEND_STOP(struct au1xmmc_host *host)
+{
+
+	/* We know the value of CONFIG2, so avoid a read we don't need */
+	u32 mask = SD_CONFIG2_EN;
+
+	WARN_ON(host->status != HOST_S_DATA);
+	host->status = HOST_S_STOP;
+
+	au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host));
+	au_sync();
+
+	/* Send the stop commmand */
+	au_writel(STOP_CMD, HOST_CMD(host));
+}
+
+static void au1xmmc_set_power(struct au1xmmc_host *host, int state)
+{
+
+	u32 val = au1xmmc_card_table[host->id].bcsrpwr;
+
+	bcsr->board &= ~val;
+	if (state) bcsr->board |= val;
+
+	au_sync_delay(1);
+}
+
+static inline int au1xmmc_card_inserted(struct au1xmmc_host *host)
+{
+	return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus)
+		? 1 : 0;
+}
+
+static int au1xmmc_card_readonly(struct mmc_host *mmc)
+{
+	struct au1xmmc_host *host = mmc_priv(mmc);
+	return (bcsr->status & au1xmmc_card_table[host->id].wpstatus)
+		? 1 : 0;
+}
+
+static void au1xmmc_finish_request(struct au1xmmc_host *host)
+{
+
+	struct mmc_request *mrq = host->mrq;
+
+	host->mrq = NULL;
+	host->flags &= HOST_F_ACTIVE;
+
+	host->dma.len = 0;
+	host->dma.dir = 0;
+
+	host->pio.index  = 0;
+	host->pio.offset = 0;
+	host->pio.len = 0;
+
+	host->status = HOST_S_IDLE;
+
+	bcsr->disk_leds |= (1 << 8);
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+static void au1xmmc_tasklet_finish(unsigned long param)
+{
+	struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+	au1xmmc_finish_request(host);
+}
+
+static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
+				struct mmc_command *cmd)
+{
+
+	u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
+
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_NONE:
+		break;
+	case MMC_RSP_R1:
+		mmccmd |= SD_CMD_RT_1;
+		break;
+	case MMC_RSP_R1B:
+		mmccmd |= SD_CMD_RT_1B;
+		break;
+	case MMC_RSP_R2:
+		mmccmd |= SD_CMD_RT_2;
+		break;
+	case MMC_RSP_R3:
+		mmccmd |= SD_CMD_RT_3;
+		break;
+	default:
+		printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
+			mmc_resp_type(cmd));
+		return MMC_ERR_INVALID;
+	}
+
+	switch(cmd->opcode) {
+	case MMC_READ_SINGLE_BLOCK:
+	case SD_APP_SEND_SCR:
+		mmccmd |= SD_CMD_CT_2;
+		break;
+	case MMC_READ_MULTIPLE_BLOCK:
+		mmccmd |= SD_CMD_CT_4;
+		break;
+	case MMC_WRITE_BLOCK:
+		mmccmd |= SD_CMD_CT_1;
+		break;
+
+	case MMC_WRITE_MULTIPLE_BLOCK:
+		mmccmd |= SD_CMD_CT_3;
+		break;
+	case MMC_STOP_TRANSMISSION:
+		mmccmd |= SD_CMD_CT_7;
+		break;
+	}
+
+	au_writel(cmd->arg, HOST_CMDARG(host));
+	au_sync();
+
+	if (wait)
+		IRQ_OFF(host, SD_CONFIG_CR);
+
+	au_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
+	au_sync();
+
+	/* Wait for the command to go on the line */
+
+	while(1) {
+		if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO))
+			break;
+	}
+
+	/* Wait for the command to come back */
+
+	if (wait) {
+		u32 status = au_readl(HOST_STATUS(host));
+
+		while(!(status & SD_STATUS_CR))
+			status = au_readl(HOST_STATUS(host));
+
+		/* Clear the CR status */
+		au_writel(SD_STATUS_CR, HOST_STATUS(host));
+
+		IRQ_ON(host, SD_CONFIG_CR);
+	}
+
+	return MMC_ERR_NONE;
+}
+
+static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
+{
+
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_data *data;
+	u32 crc;
+
+	WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP);
+
+	if (host->mrq == NULL)
+		return;
+
+	data = mrq->cmd->data;
+
+	if (status == 0)
+		status = au_readl(HOST_STATUS(host));
+
+	/* The transaction is really over when the SD_STATUS_DB bit is clear */
+
+	while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
+		status = au_readl(HOST_STATUS(host));
+
+	data->error = MMC_ERR_NONE;
+	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
+
+        /* Process any errors */
+
+	crc = (status & (SD_STATUS_WC | SD_STATUS_RC));
+	if (host->flags & HOST_F_XMIT)
+		crc |= ((status & 0x07) == 0x02) ? 0 : 1;
+
+	if (crc)
+		data->error = MMC_ERR_BADCRC;
+
+	/* Clear the CRC bits */
+	au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
+
+	data->bytes_xfered = 0;
+
+	if (data->error == MMC_ERR_NONE) {
+		if (host->flags & HOST_F_DMA) {
+			u32 chan = DMA_CHANNEL(host);
+
+			chan_tab_t *c = *((chan_tab_t **) chan);
+			au1x_dma_chan_t *cp = c->chan_ptr;
+			data->bytes_xfered = cp->ddma_bytecnt;
+		}
+		else
+			data->bytes_xfered =
+				(data->blocks * data->blksz) -
+				host->pio.len;
+	}
+
+	au1xmmc_finish_request(host);
+}
+
+static void au1xmmc_tasklet_data(unsigned long param)
+{
+	struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+
+	u32 status = au_readl(HOST_STATUS(host));
+	au1xmmc_data_complete(host, status);
+}
+
+#define AU1XMMC_MAX_TRANSFER 8
+
+static void au1xmmc_send_pio(struct au1xmmc_host *host)
+{
+
+	struct mmc_data *data = 0;
+	int sg_len, max, count = 0;
+	unsigned char *sg_ptr;
+	u32 status = 0;
+	struct scatterlist *sg;
+
+	data = host->mrq->data;
+
+	if (!(host->flags & HOST_F_XMIT))
+		return;
+
+	/* This is the pointer to the data buffer */
+	sg = &data->sg[host->pio.index];
+	sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset;
+
+	/* This is the space left inside the buffer */
+	sg_len = data->sg[host->pio.index].length - host->pio.offset;
+
+	/* Check to if we need less then the size of the sg_buffer */
+
+	max = (sg_len > host->pio.len) ? host->pio.len : sg_len;
+	if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER;
+
+	for(count = 0; count < max; count++ ) {
+		unsigned char val;
+
+		status = au_readl(HOST_STATUS(host));
+
+		if (!(status & SD_STATUS_TH))
+			break;
+
+		val = *sg_ptr++;
+
+		au_writel((unsigned long) val, HOST_TXPORT(host));
+		au_sync();
+	}
+
+	host->pio.len -= count;
+	host->pio.offset += count;
+
+	if (count == sg_len) {
+		host->pio.index++;
+		host->pio.offset = 0;
+	}
+
+	if (host->pio.len == 0) {
+		IRQ_OFF(host, SD_CONFIG_TH);
+
+		if (host->flags & HOST_F_STOP)
+			SEND_STOP(host);
+
+		tasklet_schedule(&host->data_task);
+	}
+}
+
+static void au1xmmc_receive_pio(struct au1xmmc_host *host)
+{
+
+	struct mmc_data *data = 0;
+	int sg_len = 0, max = 0, count = 0;
+	unsigned char *sg_ptr = 0;
+	u32 status = 0;
+	struct scatterlist *sg;
+
+	data = host->mrq->data;
+
+	if (!(host->flags & HOST_F_RECV))
+		return;
+
+	max = host->pio.len;
+
+	if (host->pio.index < host->dma.len) {
+		sg = &data->sg[host->pio.index];
+		sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset;
+
+		/* This is the space left inside the buffer */
+		sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
+
+		/* Check to if we need less then the size of the sg_buffer */
+		if (sg_len < max) max = sg_len;
+	}
+
+	if (max > AU1XMMC_MAX_TRANSFER)
+		max = AU1XMMC_MAX_TRANSFER;
+
+	for(count = 0; count < max; count++ ) {
+		u32 val;
+		status = au_readl(HOST_STATUS(host));
+
+		if (!(status & SD_STATUS_NE))
+			break;
+
+		if (status & SD_STATUS_RC) {
+			DBG("RX CRC Error [%d + %d].\n", host->id,
+					host->pio.len, count);
+			break;
+		}
+
+		if (status & SD_STATUS_RO) {
+			DBG("RX Overrun [%d + %d]\n", host->id,
+					host->pio.len, count);
+			break;
+		}
+		else if (status & SD_STATUS_RU) {
+			DBG("RX Underrun [%d + %d]\n", host->id,
+					host->pio.len,	count);
+			break;
+		}
+
+		val = au_readl(HOST_RXPORT(host));
+
+		if (sg_ptr)
+			*sg_ptr++ = (unsigned char) (val & 0xFF);
+	}
+
+	host->pio.len -= count;
+	host->pio.offset += count;
+
+	if (sg_len && count == sg_len) {
+		host->pio.index++;
+		host->pio.offset = 0;
+	}
+
+	if (host->pio.len == 0) {
+		//IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF);
+		IRQ_OFF(host, SD_CONFIG_NE);
+
+		if (host->flags & HOST_F_STOP)
+			SEND_STOP(host);
+
+		tasklet_schedule(&host->data_task);
+	}
+}
+
+/* static void au1xmmc_cmd_complete
+   This is called when a command has been completed - grab the response
+   and check for errors.  Then start the data transfer if it is indicated.
+*/
+
+static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
+{
+
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_command *cmd;
+	int trans;
+
+	if (!host->mrq)
+		return;
+
+	cmd = mrq->cmd;
+	cmd->error = MMC_ERR_NONE;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			u32 r[4];
+			int i;
+
+			r[0] = au_readl(host->iobase + SD_RESP3);
+			r[1] = au_readl(host->iobase + SD_RESP2);
+			r[2] = au_readl(host->iobase + SD_RESP1);
+			r[3] = au_readl(host->iobase + SD_RESP0);
+
+			/* The CRC is omitted from the response, so really
+			 * we only got 120 bytes, but the engine expects
+			 * 128 bits, so we have to shift things up
+			 */
+
+			for(i = 0; i < 4; i++) {
+				cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;
+				if (i != 3)
+					cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24;
+			}
+		} else {
+			/* Techincally, we should be getting all 48 bits of
+			 * the response (SD_RESP1 + SD_RESP2), but because
+			 * our response omits the CRC, our data ends up
+			 * being shifted 8 bits to the right.  In this case,
+			 * that means that the OSR data starts at bit 31,
+			 * so we can just read RESP0 and return that
+			 */
+			cmd->resp[0] = au_readl(host->iobase + SD_RESP0);
+		}
+	}
+
+        /* Figure out errors */
+
+	if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
+		cmd->error = MMC_ERR_BADCRC;
+
+	trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
+
+	if (!trans || cmd->error != MMC_ERR_NONE) {
+
+		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);
+		tasklet_schedule(&host->finish_task);
+		return;
+	}
+
+	host->status = HOST_S_DATA;
+
+	if (host->flags & HOST_F_DMA) {
+		u32 channel = DMA_CHANNEL(host);
+
+		/* Start the DMA as soon as the buffer gets something in it */
+
+		if (host->flags & HOST_F_RECV) {
+			u32 mask = SD_STATUS_DB | SD_STATUS_NE;
+
+			while((status & mask) != mask)
+				status = au_readl(HOST_STATUS(host));
+		}
+
+		au1xxx_dbdma_start(channel);
+	}
+}
+
+static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)
+{
+
+	unsigned int pbus = get_au1x00_speed();
+	unsigned int divisor;
+	u32 config;
+
+	/* From databook:
+	   divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1
+	*/
+
+	pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);
+	pbus /= 2;
+
+	divisor = ((pbus / rate) / 2) - 1;
+
+	config = au_readl(HOST_CONFIG(host));
+
+	config &= ~(SD_CONFIG_DIV);
+	config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE;
+
+	au_writel(config, HOST_CONFIG(host));
+	au_sync();
+}
+
+static int
+au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
+{
+
+	int datalen = data->blocks * data->blksz;
+
+	if (dma != 0)
+		host->flags |= HOST_F_DMA;
+
+	if (data->flags & MMC_DATA_READ)
+		host->flags |= HOST_F_RECV;
+	else
+		host->flags |= HOST_F_XMIT;
+
+	if (host->mrq->stop)
+		host->flags |= HOST_F_STOP;
+
+	host->dma.dir = DMA_BIDIRECTIONAL;
+
+	host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+				   data->sg_len, host->dma.dir);
+
+	if (host->dma.len == 0)
+		return MMC_ERR_TIMEOUT;
+
+	au_writel(data->blksz - 1, HOST_BLKSIZE(host));
+
+	if (host->flags & HOST_F_DMA) {
+		int i;
+		u32 channel = DMA_CHANNEL(host);
+
+		au1xxx_dbdma_stop(channel);
+
+		for(i = 0; i < host->dma.len; i++) {
+			u32 ret = 0, flags = DDMA_FLAGS_NOIE;
+			struct scatterlist *sg = &data->sg[i];
+			int sg_len = sg->length;
+
+			int len = (datalen > sg_len) ? sg_len : datalen;
+
+			if (i == host->dma.len - 1)
+				flags = DDMA_FLAGS_IE;
+
+    			if (host->flags & HOST_F_XMIT){
+      				ret = au1xxx_dbdma_put_source_flags(channel,
+					(void *) (page_address(sg->page) +
+						  sg->offset),
+					len, flags);
+			}
+    			else {
+      				ret = au1xxx_dbdma_put_dest_flags(channel,
+					(void *) (page_address(sg->page) +
+						  sg->offset),
+					len, flags);
+			}
+
+    			if (!ret)
+				goto dataerr;
+
+			datalen -= len;
+		}
+	}
+	else {
+		host->pio.index = 0;
+		host->pio.offset = 0;
+		host->pio.len = datalen;
+
+		if (host->flags & HOST_F_XMIT)
+			IRQ_ON(host, SD_CONFIG_TH);
+		else
+			IRQ_ON(host, SD_CONFIG_NE);
+			//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
+	}
+
+	return MMC_ERR_NONE;
+
+ dataerr:
+	dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir);
+	return MMC_ERR_TIMEOUT;
+}
+
+/* static void au1xmmc_request
+   This actually starts a command or data transaction
+*/
+
+static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
+{
+
+	struct au1xmmc_host *host = mmc_priv(mmc);
+	int ret = MMC_ERR_NONE;
+
+	WARN_ON(irqs_disabled());
+	WARN_ON(host->status != HOST_S_IDLE);
+
+	host->mrq = mrq;
+	host->status = HOST_S_CMD;
+
+	bcsr->disk_leds &= ~(1 << 8);
+
+	if (mrq->data) {
+		FLUSH_FIFO(host);
+		ret = au1xmmc_prepare_data(host, mrq->data);
+	}
+
+	if (ret == MMC_ERR_NONE)
+		ret = au1xmmc_send_command(host, 0, mrq->cmd);
+
+	if (ret != MMC_ERR_NONE) {
+		mrq->cmd->error = ret;
+		au1xmmc_finish_request(host);
+	}
+}
+
+static void au1xmmc_reset_controller(struct au1xmmc_host *host)
+{
+
+	/* Apply the clock */
+	au_writel(SD_ENABLE_CE, HOST_ENABLE(host));
+        au_sync_delay(1);
+
+	au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host));
+	au_sync_delay(5);
+
+	au_writel(~0, HOST_STATUS(host));
+	au_sync();
+
+	au_writel(0, HOST_BLKSIZE(host));
+	au_writel(0x001fffff, HOST_TIMEOUT(host));
+	au_sync();
+
+	au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
+        au_sync();
+
+	au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host));
+	au_sync_delay(1);
+
+	au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
+	au_sync();
+
+	/* Configure interrupts */
+	au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host));
+	au_sync();
+}
+
+
+static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
+{
+	struct au1xmmc_host *host = mmc_priv(mmc);
+
+	if (ios->power_mode == MMC_POWER_OFF)
+		au1xmmc_set_power(host, 0);
+	else if (ios->power_mode == MMC_POWER_ON) {
+		au1xmmc_set_power(host, 1);
+	}
+
+	if (ios->clock && ios->clock != host->clock) {
+		au1xmmc_set_clock(host, ios->clock);
+		host->clock = ios->clock;
+	}
+}
+
+static void au1xmmc_dma_callback(int irq, void *dev_id)
+{
+	struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id;
+
+	/* Avoid spurious interrupts */
+
+	if (!host->mrq)
+		return;
+
+	if (host->flags & HOST_F_STOP)
+		SEND_STOP(host);
+
+	tasklet_schedule(&host->data_task);
+}
+
+#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT)
+#define STATUS_DATA_IN  (SD_STATUS_NE)
+#define STATUS_DATA_OUT (SD_STATUS_TH)
+
+static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
+{
+
+	u32 status;
+	int i, ret = 0;
+
+	disable_irq(AU1100_SD_IRQ);
+
+	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) {
+		struct au1xmmc_host * host = au1xmmc_hosts[i];
+		u32 handled = 1;
+
+		status = au_readl(HOST_STATUS(host));
+
+		if (host->mrq && (status & STATUS_TIMEOUT)) {
+			if (status & SD_STATUS_RAT)
+				host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+
+			else if (status & SD_STATUS_DT)
+				host->mrq->data->error = MMC_ERR_TIMEOUT;
+
+			/* In PIO mode, interrupts might still be enabled */
+			IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
+
+			//IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF);
+			tasklet_schedule(&host->finish_task);
+		}
+#if 0
+		else if (status & SD_STATUS_DD) {
+
+			/* Sometimes we get a DD before a NE in PIO mode */
+
+			if (!(host->flags & HOST_F_DMA) &&
+					(status & SD_STATUS_NE))
+				au1xmmc_receive_pio(host);
+			else {
+				au1xmmc_data_complete(host, status);
+				//tasklet_schedule(&host->data_task);
+			}
+		}
+#endif
+		else if (status & (SD_STATUS_CR)) {
+			if (host->status == HOST_S_CMD)
+				au1xmmc_cmd_complete(host,status);
+		}
+		else if (!(host->flags & HOST_F_DMA)) {
+			if ((host->flags & HOST_F_XMIT) &&
+			    (status & STATUS_DATA_OUT))
+				au1xmmc_send_pio(host);
+			else if ((host->flags & HOST_F_RECV) &&
+			    (status & STATUS_DATA_IN))
+				au1xmmc_receive_pio(host);
+		}
+		else if (status & 0x203FBC70) {
+			DBG("Unhandled status %8.8x\n", host->id, status);
+			handled = 0;
+		}
+
+		au_writel(status, HOST_STATUS(host));
+		au_sync();
+
+		ret |= handled;
+	}
+
+	enable_irq(AU1100_SD_IRQ);
+	return ret;
+}
+
+static void au1xmmc_poll_event(unsigned long arg)
+{
+	struct au1xmmc_host *host = (struct au1xmmc_host *) arg;
+
+	int card = au1xmmc_card_inserted(host);
+        int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0;
+
+	if (card != controller) {
+		host->flags &= ~HOST_F_ACTIVE;
+		if (card) host->flags |= HOST_F_ACTIVE;
+		mmc_detect_change(host->mmc, 0);
+	}
+
+	if (host->mrq != NULL) {
+		u32 status = au_readl(HOST_STATUS(host));
+		DBG("PENDING - %8.8x\n", host->id, status);
+	}
+
+	mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT);
+}
+
+static dbdev_tab_t au1xmmc_mem_dbdev =
+{
+	DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0
+};
+
+static void au1xmmc_init_dma(struct au1xmmc_host *host)
+{
+
+	u32 rxchan, txchan;
+
+	int txid = au1xmmc_card_table[host->id].tx_devid;
+	int rxid = au1xmmc_card_table[host->id].rx_devid;
+
+	/* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
+	   of 8 bits.  And since devices are shared, we need to create
+	   our own to avoid freaking out other devices
+	*/
+
+	int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
+
+	txchan = au1xxx_dbdma_chan_alloc(memid, txid,
+					 au1xmmc_dma_callback, (void *) host);
+
+	rxchan = au1xxx_dbdma_chan_alloc(rxid, memid,
+					 au1xmmc_dma_callback, (void *) host);
+
+	au1xxx_dbdma_set_devwidth(txchan, 8);
+	au1xxx_dbdma_set_devwidth(rxchan, 8);
+
+	au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT);
+	au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT);
+
+	host->tx_chan = txchan;
+	host->rx_chan = rxchan;
+}
+
+static const struct mmc_host_ops au1xmmc_ops = {
+	.request	= au1xmmc_request,
+	.set_ios	= au1xmmc_set_ios,
+	.get_ro		= au1xmmc_card_readonly,
+};
+
+static int __devinit au1xmmc_probe(struct platform_device *pdev)
+{
+
+	int i, ret = 0;
+
+	/* THe interrupt is shared among all controllers */
+	ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0);
+
+	if (ret) {
+		printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n",
+				AU1100_SD_IRQ, ret);
+		return -ENXIO;
+	}
+
+	disable_irq(AU1100_SD_IRQ);
+
+	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) {
+		struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev);
+		struct au1xmmc_host *host = 0;
+
+		if (!mmc) {
+			printk(DRIVER_NAME "ERROR: no mem for host %d\n", i);
+			au1xmmc_hosts[i] = 0;
+			continue;
+		}
+
+		mmc->ops = &au1xmmc_ops;
+
+		mmc->f_min =   450000;
+		mmc->f_max = 24000000;
+
+		mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
+		mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT;
+
+		mmc->max_blk_size = 2048;
+		mmc->max_blk_count = 512;
+
+		mmc->ocr_avail = AU1XMMC_OCR;
+
+		host = mmc_priv(mmc);
+		host->mmc = mmc;
+
+		host->id = i;
+		host->iobase = au1xmmc_card_table[host->id].iobase;
+		host->clock = 0;
+		host->power_mode = MMC_POWER_OFF;
+
+		host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0;
+		host->status = HOST_S_IDLE;
+
+		init_timer(&host->timer);
+
+		host->timer.function = au1xmmc_poll_event;
+		host->timer.data = (unsigned long) host;
+		host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT;
+
+		tasklet_init(&host->data_task, au1xmmc_tasklet_data,
+				(unsigned long) host);
+
+		tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
+				(unsigned long) host);
+
+		spin_lock_init(&host->lock);
+
+		if (dma != 0)
+			au1xmmc_init_dma(host);
+
+		au1xmmc_reset_controller(host);
+
+		mmc_add_host(mmc);
+		au1xmmc_hosts[i] = host;
+
+		add_timer(&host->timer);
+
+		printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n",
+		       host->id, host->iobase, dma ? "dma" : "pio");
+	}
+
+	enable_irq(AU1100_SD_IRQ);
+
+	return 0;
+}
+
+static int __devexit au1xmmc_remove(struct platform_device *pdev)
+{
+
+	int i;
+
+	disable_irq(AU1100_SD_IRQ);
+
+	for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) {
+		struct au1xmmc_host *host = au1xmmc_hosts[i];
+		if (!host) continue;
+
+		tasklet_kill(&host->data_task);
+		tasklet_kill(&host->finish_task);
+
+		del_timer_sync(&host->timer);
+		au1xmmc_set_power(host, 0);
+
+		mmc_remove_host(host->mmc);
+
+		au1xxx_dbdma_chan_free(host->tx_chan);
+		au1xxx_dbdma_chan_free(host->rx_chan);
+
+		au_writel(0x0, HOST_ENABLE(host));
+		au_sync();
+	}
+
+	free_irq(AU1100_SD_IRQ, 0);
+	return 0;
+}
+
+static struct platform_driver au1xmmc_driver = {
+	.probe         = au1xmmc_probe,
+	.remove        = au1xmmc_remove,
+	.suspend       = NULL,
+	.resume        = NULL,
+	.driver        = {
+		.name  = DRIVER_NAME,
+	},
+};
+
+static int __init au1xmmc_init(void)
+{
+	return platform_driver_register(&au1xmmc_driver);
+}
+
+static void __exit au1xmmc_exit(void)
+{
+	platform_driver_unregister(&au1xmmc_driver);
+}
+
+module_init(au1xmmc_init);
+module_exit(au1xmmc_exit);
+
+#ifdef MODULE
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX");
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/drivers/mmc/host/au1xmmc.h b/drivers/mmc/host/au1xmmc.h
new file mode 100644
index 0000000..341cbdf
--- /dev/null
+++ b/drivers/mmc/host/au1xmmc.h
@@ -0,0 +1,96 @@
+#ifndef _AU1XMMC_H_
+#define _AU1XMMC_H_
+
+/* Hardware definitions */
+
+#define AU1XMMC_DESCRIPTOR_COUNT 1
+#define AU1XMMC_DESCRIPTOR_SIZE  2048
+
+#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30  | \
+		      MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33  | \
+		      MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
+
+/* Easy access macros */
+
+#define HOST_STATUS(h)	((h)->iobase + SD_STATUS)
+#define HOST_CONFIG(h)	((h)->iobase + SD_CONFIG)
+#define HOST_ENABLE(h)	((h)->iobase + SD_ENABLE)
+#define HOST_TXPORT(h)	((h)->iobase + SD_TXPORT)
+#define HOST_RXPORT(h)	((h)->iobase + SD_RXPORT)
+#define HOST_CMDARG(h)	((h)->iobase + SD_CMDARG)
+#define HOST_BLKSIZE(h)	((h)->iobase + SD_BLKSIZE)
+#define HOST_CMD(h)	((h)->iobase + SD_CMD)
+#define HOST_CONFIG2(h)	((h)->iobase + SD_CONFIG2)
+#define HOST_TIMEOUT(h)	((h)->iobase + SD_TIMEOUT)
+#define HOST_DEBUG(h)	((h)->iobase + SD_DEBUG)
+
+#define DMA_CHANNEL(h) \
+	( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
+
+/* This gives us a hard value for the stop command that we can write directly
+ * to the command register
+ */
+
+#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO)
+
+/* This is the set of interrupts that we configure by default */
+
+#if 0
+#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \
+		SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
+#endif
+
+#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \
+		SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I)
+/* The poll event (looking for insert/remove events runs twice a second */
+#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
+
+struct au1xmmc_host {
+  struct mmc_host *mmc;
+  struct mmc_request *mrq;
+
+  u32 id;
+
+  u32 flags;
+  u32 iobase;
+  u32 clock;
+  u32 bus_width;
+  u32 power_mode;
+
+  int status;
+
+   struct {
+	   int len;
+	   int dir;
+  } dma;
+
+   struct {
+	   int index;
+	   int offset;
+	   int len;
+  } pio;
+
+  u32 tx_chan;
+  u32 rx_chan;
+
+  struct timer_list timer;
+  struct tasklet_struct finish_task;
+  struct tasklet_struct data_task;
+
+  spinlock_t lock;
+};
+
+/* Status flags used by the host structure */
+
+#define HOST_F_XMIT   0x0001
+#define HOST_F_RECV   0x0002
+#define HOST_F_DMA    0x0010
+#define HOST_F_ACTIVE 0x0100
+#define HOST_F_STOP   0x1000
+
+#define HOST_S_IDLE   0x0001
+#define HOST_S_CMD    0x0002
+#define HOST_S_DATA   0x0003
+#define HOST_S_STOP   0x0004
+
+#endif
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
new file mode 100644
index 0000000..7ee2045
--- /dev/null
+++ b/drivers/mmc/host/imxmmc.c
@@ -0,0 +1,1137 @@
+/*
+ *  linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver
+ *
+ *  Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
+ *  Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ *  derived from pxamci.c by Russell King
+ *
+ * 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.
+ *
+ *  2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *             Changed to conform redesigned i.MX scatter gather DMA interface
+ *
+ *  2005-11-04 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *             Updated for 2.6.14 kernel
+ *
+ *  2005-12-13 Jay Monkman <jtm@smoothsmoothie.com>
+ *             Found and corrected problems in the write path
+ *
+ *  2005-12-30 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *             The event handling rewritten right way in softirq.
+ *             Added many ugly hacks and delays to overcome SDHC
+ *             deficiencies
+ *
+ */
+
+#ifdef CONFIG_MMC_DEBUG
+#define DEBUG
+#else
+#undef  DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/delay.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+#include <asm/arch/mmc.h>
+#include <asm/arch/imx-dma.h>
+
+#include "imxmmc.h"
+
+#define DRIVER_NAME "imx-mmc"
+
+#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
+	              INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
+		      INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
+
+struct imxmci_host {
+	struct mmc_host		*mmc;
+	spinlock_t		lock;
+	struct resource		*res;
+	int			irq;
+	imx_dmach_t		dma;
+	unsigned int		clkrt;
+	unsigned int		cmdat;
+	volatile unsigned int	imask;
+	unsigned int		power_mode;
+	unsigned int		present;
+	struct imxmmc_platform_data *pdata;
+
+	struct mmc_request	*req;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	struct timer_list	timer;
+	struct tasklet_struct	tasklet;
+	unsigned int		status_reg;
+	unsigned long		pending_events;
+	/* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */
+	u16			*data_ptr;
+	unsigned int		data_cnt;
+	atomic_t		stuck_timeout;
+
+	unsigned int		dma_nents;
+	unsigned int		dma_size;
+	unsigned int		dma_dir;
+	int			dma_allocated;
+
+	unsigned char		actual_bus_width;
+
+	int			prev_cmd_code;
+};
+
+#define IMXMCI_PEND_IRQ_b	0
+#define IMXMCI_PEND_DMA_END_b	1
+#define IMXMCI_PEND_DMA_ERR_b	2
+#define IMXMCI_PEND_WAIT_RESP_b	3
+#define IMXMCI_PEND_DMA_DATA_b	4
+#define IMXMCI_PEND_CPU_DATA_b	5
+#define IMXMCI_PEND_CARD_XCHG_b	6
+#define IMXMCI_PEND_SET_INIT_b	7
+#define IMXMCI_PEND_STARTED_b	8
+
+#define IMXMCI_PEND_IRQ_m	(1 << IMXMCI_PEND_IRQ_b)
+#define IMXMCI_PEND_DMA_END_m	(1 << IMXMCI_PEND_DMA_END_b)
+#define IMXMCI_PEND_DMA_ERR_m	(1 << IMXMCI_PEND_DMA_ERR_b)
+#define IMXMCI_PEND_WAIT_RESP_m	(1 << IMXMCI_PEND_WAIT_RESP_b)
+#define IMXMCI_PEND_DMA_DATA_m	(1 << IMXMCI_PEND_DMA_DATA_b)
+#define IMXMCI_PEND_CPU_DATA_m	(1 << IMXMCI_PEND_CPU_DATA_b)
+#define IMXMCI_PEND_CARD_XCHG_m	(1 << IMXMCI_PEND_CARD_XCHG_b)
+#define IMXMCI_PEND_SET_INIT_m	(1 << IMXMCI_PEND_SET_INIT_b)
+#define IMXMCI_PEND_STARTED_m	(1 << IMXMCI_PEND_STARTED_b)
+
+static void imxmci_stop_clock(struct imxmci_host *host)
+{
+	int i = 0;
+	MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK;
+	while(i < 0x1000) {
+	        if(!(i & 0x7f))
+			MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK;
+
+		if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) {
+			/* Check twice before cut */
+			if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN))
+				return;
+		}
+
+		i++;
+	}
+	dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
+}
+
+static int imxmci_start_clock(struct imxmci_host *host)
+{
+	unsigned int trials = 0;
+	unsigned int delay_limit = 128;
+	unsigned long flags;
+
+	MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
+
+	clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
+
+	/*
+	 * Command start of the clock, this usually succeeds in less
+	 * then 6 delay loops, but during card detection (low clockrate)
+	 * it takes up to 5000 delay loops and sometimes fails for the first time
+	 */
+	MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+
+	do {
+		unsigned int delay = delay_limit;
+
+		while(delay--){
+			if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+				/* Check twice before cut */
+				if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+					return 0;
+
+			if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+				return 0;
+		}
+
+		local_irq_save(flags);
+		/*
+		 * Ensure, that request is not doubled under all possible circumstances.
+		 * It is possible, that cock running state is missed, because some other
+		 * IRQ or schedule delays this function execution and the clocks has
+		 * been already stopped by other means (response processing, SDHC HW)
+		 */
+		if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+			MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+		local_irq_restore(flags);
+
+	} while(++trials<256);
+
+	dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
+
+	return -1;
+}
+
+static void imxmci_softreset(void)
+{
+	/* reset sequence */
+	MMC_STR_STP_CLK = 0x8;
+	MMC_STR_STP_CLK = 0xD;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+	MMC_STR_STP_CLK = 0x5;
+
+	MMC_RES_TO = 0xff;
+	MMC_BLK_LEN = 512;
+	MMC_NOB = 1;
+}
+
+static int imxmci_busy_wait_for_status(struct imxmci_host *host,
+			unsigned int *pstat, unsigned int stat_mask,
+			int timeout, const char *where)
+{
+	int loops=0;
+	while(!(*pstat & stat_mask)) {
+		loops+=2;
+		if(loops >= timeout) {
+			dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
+				where, *pstat, stat_mask);
+			return -1;
+		}
+		udelay(2);
+		*pstat |= MMC_STATUS;
+	}
+	if(!loops)
+		return 0;
+
+	/* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
+	if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000))
+		dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
+			loops, where, *pstat, stat_mask);
+	return loops;
+}
+
+static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned int blksz = data->blksz;
+	unsigned int datasz = nob * blksz;
+	int i;
+
+	if (data->flags & MMC_DATA_STREAM)
+		nob = 0xffff;
+
+	host->data = data;
+	data->bytes_xfered = 0;
+
+	MMC_NOB = nob;
+	MMC_BLK_LEN = blksz;
+
+	/*
+	 * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
+	 * We are in big troubles for non-512 byte transfers according to note in the paragraph
+	 * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least.
+	 * The situation is even more complex in reality. The SDHC in not able to handle wll
+	 * partial FIFO fills and reads. The length has to be rounded up to burst size multiple.
+	 * This is required for SCR read at least.
+	 */
+	if (datasz < 512) {
+		host->dma_size = datasz;
+		if (data->flags & MMC_DATA_READ) {
+			host->dma_dir = DMA_FROM_DEVICE;
+
+			/* Hack to enable read SCR */
+			MMC_NOB = 1;
+			MMC_BLK_LEN = 512;
+		} else {
+			host->dma_dir = DMA_TO_DEVICE;
+		}
+
+		/* Convert back to virtual address */
+		host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset);
+		host->data_cnt = 0;
+
+		clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
+		set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
+
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		host->dma_dir = DMA_FROM_DEVICE;
+		host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
+						data->sg_len,  host->dma_dir);
+
+		imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
+			host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ);
+
+		/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
+		CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
+	} else {
+		host->dma_dir = DMA_TO_DEVICE;
+
+		host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
+						data->sg_len,  host->dma_dir);
+
+		imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
+			host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE);
+
+		/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
+		CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
+	}
+
+#if 1	/* This code is there only for consistency checking and can be disabled in future */
+	host->dma_size = 0;
+	for(i=0; i<host->dma_nents; i++)
+		host->dma_size+=data->sg[i].length;
+
+	if (datasz > host->dma_size) {
+		dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
+		       datasz, host->dma_size);
+	}
+#endif
+
+	host->dma_size = datasz;
+
+	wmb();
+
+	if(host->actual_bus_width == MMC_BUS_WIDTH_4)
+		BLR(host->dma) = 0;	/* burst 64 byte read / 64 bytes write */
+	else
+		BLR(host->dma) = 16;	/* burst 16 byte read / 16 bytes write */
+
+	RSSR(host->dma) = DMA_REQ_SDHC;
+
+	set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
+	clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
+
+	/* start DMA engine for read, write is delayed after initial response */
+	if (host->dma_dir == DMA_FROM_DEVICE) {
+		imx_dma_enable(host->dma);
+	}
+}
+
+static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+	unsigned long flags;
+	u32 imask;
+
+	WARN_ON(host->cmd != NULL);
+	host->cmd = cmd;
+
+	/* Ensure, that clock are stopped else command programming and start fails */
+	imxmci_stop_clock(host);
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdat |= CMD_DAT_CONT_BUSY;
+
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_R1: /* short CRC, OPCODE */
+	case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
+		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
+		break;
+	case MMC_RSP_R2: /* long 136 bit + CRC */
+		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
+		break;
+	case MMC_RSP_R3: /* short */
+		cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
+		break;
+	default:
+		break;
+	}
+
+	if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) )
+		cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
+
+	if ( host->actual_bus_width == MMC_BUS_WIDTH_4 )
+		cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
+
+	MMC_CMD = cmd->opcode;
+	MMC_ARGH = cmd->arg >> 16;
+	MMC_ARGL = cmd->arg & 0xffff;
+	MMC_CMD_DAT_CONT = cmdat;
+
+	atomic_set(&host->stuck_timeout, 0);
+	set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
+
+
+	imask = IMXMCI_INT_MASK_DEFAULT;
+	imask &= ~INT_MASK_END_CMD_RES;
+	if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) {
+		/*imask &= ~INT_MASK_BUF_READY;*/
+		imask &= ~INT_MASK_DATA_TRAN;
+		if ( cmdat & CMD_DAT_CONT_WRITE )
+			imask &= ~INT_MASK_WRITE_OP_DONE;
+		if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+			imask &= ~INT_MASK_BUF_READY;
+	}
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask = imask;
+	MMC_INT_MASK = host->imask;
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
+		cmd->opcode, cmd->opcode, imask);
+
+	imxmci_start_clock(host);
+}
+
+static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
+			IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
+
+	host->imask = IMXMCI_INT_MASK_DEFAULT;
+	MMC_INT_MASK = host->imask;
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if(req && req->cmd)
+		host->prev_cmd_code = req->cmd->opcode;
+
+	host->req = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+	mmc_request_done(host->mmc, req);
+}
+
+static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
+{
+	struct mmc_data *data = host->data;
+	int data_error;
+
+	if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){
+		imx_dma_disable(host->dma);
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
+			     host->dma_dir);
+	}
+
+	if ( stat & STATUS_ERR_MASK ) {
+		dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
+		if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
+			data->error = MMC_ERR_BADCRC;
+		else if(stat & STATUS_TIME_OUT_READ)
+			data->error = MMC_ERR_TIMEOUT;
+		else
+			data->error = MMC_ERR_FAILED;
+	} else {
+		data->bytes_xfered = host->dma_size;
+	}
+
+	data_error = data->error;
+
+	host->data = NULL;
+
+	return data_error;
+}
+
+static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
+{
+	struct mmc_command *cmd = host->cmd;
+	int i;
+	u32 a,b,c;
+	struct mmc_data *data = host->data;
+
+	if (!cmd)
+		return 0;
+
+	host->cmd = NULL;
+
+	if (stat & STATUS_TIME_OUT_RESP) {
+		dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+		dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	if(cmd->flags & MMC_RSP_PRESENT) {
+		if(cmd->flags & MMC_RSP_136) {
+			for (i = 0; i < 4; i++) {
+				u32 a = MMC_RES_FIFO & 0xffff;
+				u32 b = MMC_RES_FIFO & 0xffff;
+				cmd->resp[i] = a<<16 | b;
+			}
+		} else {
+			a = MMC_RES_FIFO & 0xffff;
+			b = MMC_RES_FIFO & 0xffff;
+			c = MMC_RES_FIFO & 0xffff;
+			cmd->resp[0] = a<<24 | b<<8 | c>>8;
+		}
+	}
+
+	dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
+		cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
+
+	if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) {
+		if (host->req->data->flags & MMC_DATA_WRITE) {
+
+			/* Wait for FIFO to be empty before starting DMA write */
+
+			stat = MMC_STATUS;
+			if(imxmci_busy_wait_for_status(host, &stat,
+				STATUS_APPL_BUFF_FE,
+				40, "imxmci_cmd_done DMA WR") < 0) {
+				cmd->error = MMC_ERR_FIFO;
+				imxmci_finish_data(host, stat);
+				if(host->req)
+					imxmci_finish_request(host, host->req);
+				dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
+				       stat);
+				return 0;
+			}
+
+			if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+				imx_dma_enable(host->dma);
+			}
+		}
+	} else {
+		struct mmc_request *req;
+		imxmci_stop_clock(host);
+		req = host->req;
+
+		if(data)
+			imxmci_finish_data(host, stat);
+
+		if( req ) {
+			imxmci_finish_request(host, req);
+		} else {
+			dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
+		}
+	}
+
+	return 1;
+}
+
+static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
+{
+	struct mmc_data *data = host->data;
+	int data_error;
+
+	if (!data)
+		return 0;
+
+	data_error = imxmci_finish_data(host, stat);
+
+	if (host->req->stop) {
+		imxmci_stop_clock(host);
+		imxmci_start_cmd(host, host->req->stop, 0);
+	} else {
+		struct mmc_request *req;
+		req = host->req;
+		if( req ) {
+			imxmci_finish_request(host, req);
+		} else {
+			dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
+		}
+	}
+
+	return 1;
+}
+
+static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
+{
+	int i;
+	int burst_len;
+	int trans_done = 0;
+	unsigned int stat = *pstat;
+
+	if(host->actual_bus_width != MMC_BUS_WIDTH_4)
+		burst_len = 16;
+	else
+		burst_len = 64;
+
+	/* This is unfortunately required */
+	dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",
+		stat);
+
+	udelay(20);	/* required for clocks < 8MHz*/
+
+	if(host->dma_dir == DMA_FROM_DEVICE) {
+		imxmci_busy_wait_for_status(host, &stat,
+				STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
+				STATUS_TIME_OUT_READ,
+				50, "imxmci_cpu_driven_data read");
+
+		while((stat & (STATUS_APPL_BUFF_FF |  STATUS_DATA_TRANS_DONE)) &&
+		      !(stat & STATUS_TIME_OUT_READ) &&
+		      (host->data_cnt < 512)) {
+
+			udelay(20);	/* required for clocks < 8MHz*/
+
+			for(i = burst_len; i>=2 ; i-=2) {
+				u16 data;
+				data = MMC_BUFFER_ACCESS;
+				udelay(10);	/* required for clocks < 8MHz*/
+				if(host->data_cnt+2 <= host->dma_size) {
+					*(host->data_ptr++) = data;
+				} else {
+					if(host->data_cnt < host->dma_size)
+						*(u8*)(host->data_ptr) = data;
+				}
+				host->data_cnt += 2;
+			}
+
+			stat = MMC_STATUS;
+
+			dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
+				host->data_cnt, burst_len, stat);
+		}
+
+		if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
+			trans_done = 1;
+
+		if(host->dma_size & 0x1ff)
+			stat &= ~STATUS_CRC_READ_ERR;
+
+		if(stat & STATUS_TIME_OUT_READ) {
+			dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
+				stat);
+			trans_done = -1;
+		}
+
+	} else {
+		imxmci_busy_wait_for_status(host, &stat,
+				STATUS_APPL_BUFF_FE,
+				20, "imxmci_cpu_driven_data write");
+
+		while((stat & STATUS_APPL_BUFF_FE) &&
+		      (host->data_cnt < host->dma_size)) {
+			if(burst_len >= host->dma_size - host->data_cnt) {
+				burst_len = host->dma_size - host->data_cnt;
+				host->data_cnt = host->dma_size;
+				trans_done = 1;
+			} else {
+				host->data_cnt += burst_len;
+			}
+
+			for(i = burst_len; i>0 ; i-=2)
+				MMC_BUFFER_ACCESS = *(host->data_ptr++);
+
+			stat = MMC_STATUS;
+
+			dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
+				burst_len, stat);
+		}
+	}
+
+	*pstat = stat;
+
+	return trans_done;
+}
+
+static void imxmci_dma_irq(int dma, void *devid)
+{
+	struct imxmci_host *host = devid;
+	uint32_t stat = MMC_STATUS;
+
+	atomic_set(&host->stuck_timeout, 0);
+	host->status_reg = stat;
+	set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t imxmci_irq(int irq, void *devid)
+{
+	struct imxmci_host *host = devid;
+	uint32_t stat = MMC_STATUS;
+	int handled = 1;
+
+	MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;
+
+	atomic_set(&host->stuck_timeout, 0);
+	host->status_reg = stat;
+	set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
+	set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
+	tasklet_schedule(&host->tasklet);
+
+	return IRQ_RETVAL(handled);;
+}
+
+static void imxmci_tasklet_fnc(unsigned long data)
+{
+	struct imxmci_host *host = (struct imxmci_host *)data;
+	u32 stat;
+	unsigned int data_dir_mask = 0;	/* STATUS_WR_CRC_ERROR_CODE_MASK */
+	int timeout = 0;
+
+	if(atomic_read(&host->stuck_timeout) > 4) {
+		char *what;
+		timeout = 1;
+		stat = MMC_STATUS;
+		host->status_reg = stat;
+		if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+			if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
+				what = "RESP+DMA";
+			else
+				what = "RESP";
+		else
+			if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
+				if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
+					what = "DATA";
+				else
+					what = "DMA";
+			else
+				what = "???";
+
+		dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
+		       what, stat, MMC_INT_MASK);
+		dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
+		       MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));
+		dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
+		       host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<<host->actual_bus_width, host->dma_size);
+	}
+
+	if(!host->present || timeout)
+		host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
+				    STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
+
+	if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
+		clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
+
+		stat = MMC_STATUS;
+		/*
+		 * This is not required in theory, but there is chance to miss some flag
+		 * which clears automatically by mask write, FreeScale original code keeps
+		 * stat from IRQ time so do I
+		 */
+		stat |= host->status_reg;
+
+		if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+			stat &= ~STATUS_CRC_READ_ERR;
+
+		if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+			imxmci_busy_wait_for_status(host, &stat,
+					STATUS_END_CMD_RESP | STATUS_ERR_MASK,
+					20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
+		}
+
+		if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
+			if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+				imxmci_cmd_done(host, stat);
+			if(host->data && (stat & STATUS_ERR_MASK))
+				imxmci_data_done(host, stat);
+		}
+
+		if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
+			stat |= MMC_STATUS;
+			if(imxmci_cpu_driven_data(host, &stat)){
+				if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+					imxmci_cmd_done(host, stat);
+				atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
+							&host->pending_events);
+				imxmci_data_done(host, stat);
+			}
+		}
+	}
+
+	if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
+	   !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+
+		stat = MMC_STATUS;
+		/* Same as above */
+		stat |= host->status_reg;
+
+		if(host->dma_dir == DMA_TO_DEVICE) {
+			data_dir_mask = STATUS_WRITE_OP_DONE;
+		} else {
+			data_dir_mask = STATUS_DATA_TRANS_DONE;
+		}
+
+		if(stat & data_dir_mask) {
+			clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
+			imxmci_data_done(host, stat);
+		}
+	}
+
+	if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
+
+		if(host->cmd)
+			imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
+
+		if(host->data)
+			imxmci_data_done(host, STATUS_TIME_OUT_READ |
+					 STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
+
+		if(host->req)
+			imxmci_finish_request(host, host->req);
+
+		mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+
+	}
+}
+
+static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+	struct imxmci_host *host = mmc_priv(mmc);
+	unsigned int cmdat;
+
+	WARN_ON(host->req != NULL);
+
+	host->req = req;
+
+	cmdat = 0;
+
+	if (req->data) {
+		imxmci_setup_data(host, req->data);
+
+		cmdat |= CMD_DAT_CONT_DATA_ENABLE;
+
+		if (req->data->flags & MMC_DATA_WRITE)
+			cmdat |= CMD_DAT_CONT_WRITE;
+
+		if (req->data->flags & MMC_DATA_STREAM) {
+			cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
+		}
+	}
+
+	imxmci_start_cmd(host, req->cmd, cmdat);
+}
+
+#define CLK_RATE 19200000
+
+static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct imxmci_host *host = mmc_priv(mmc);
+	int prescaler;
+
+	if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
+		host->actual_bus_width = MMC_BUS_WIDTH_4;
+		imx_gpio_mode(PB11_PF_SD_DAT3);
+	}else{
+		host->actual_bus_width = MMC_BUS_WIDTH_1;
+		imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
+	}
+
+	if ( host->power_mode != ios->power_mode ) {
+		switch (ios->power_mode) {
+		case MMC_POWER_OFF:
+        		break;
+		case MMC_POWER_UP:
+			set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
+        		break;
+		case MMC_POWER_ON:
+        		break;
+		}
+		host->power_mode = ios->power_mode;
+	}
+
+	if ( ios->clock ) {
+		unsigned int clk;
+
+		/* The prescaler is 5 for PERCLK2 equal to 96MHz
+		 * then 96MHz / 5 = 19.2 MHz
+		 */
+		clk=imx_get_perclk2();
+		prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
+		switch(prescaler) {
+		case 0:
+		case 1:	prescaler = 0;
+			break;
+		case 2:	prescaler = 1;
+			break;
+		case 3:	prescaler = 2;
+			break;
+		case 4:	prescaler = 4;
+			break;
+		default:
+		case 5:	prescaler = 5;
+			break;
+		}
+
+		dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
+			clk, prescaler);
+
+		for(clk=0; clk<8; clk++) {
+			int x;
+			x = CLK_RATE / (1<<clk);
+			if( x <= ios->clock)
+				break;
+		}
+
+		MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */
+
+		imxmci_stop_clock(host);
+		MMC_CLK_RATE = (prescaler<<3) | clk;
+		/*
+		 * Under my understanding, clock should not be started there, because it would
+		 * initiate SDHC sequencer and send last or random command into card
+		 */
+		/*imxmci_start_clock(host);*/
+
+		dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
+	} else {
+		imxmci_stop_clock(host);
+	}
+}
+
+static const struct mmc_host_ops imxmci_ops = {
+	.request	= imxmci_request,
+	.set_ios	= imxmci_set_ios,
+};
+
+static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
+{
+	int i;
+
+	for (i = 0; i < dev->num_resources; i++)
+		if (dev->resource[i].flags == mask && nr-- == 0)
+			return &dev->resource[i];
+	return NULL;
+}
+
+static int platform_device_irq(struct platform_device *dev, int nr)
+{
+	int i;
+
+	for (i = 0; i < dev->num_resources; i++)
+		if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
+			return dev->resource[i].start;
+	return NO_IRQ;
+}
+
+static void imxmci_check_status(unsigned long data)
+{
+	struct imxmci_host *host = (struct imxmci_host *)data;
+
+	if( host->pdata->card_present() != host->present ) {
+		host->present ^= 1;
+		dev_info(mmc_dev(host->mmc), "card %s\n",
+		      host->present ? "inserted" : "removed");
+
+		set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+	}
+
+	if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
+	   test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+		atomic_inc(&host->stuck_timeout);
+		if(atomic_read(&host->stuck_timeout) > 4)
+			tasklet_schedule(&host->tasklet);
+	} else {
+		atomic_set(&host->stuck_timeout, 0);
+
+	}
+
+	mod_timer(&host->timer, jiffies + (HZ>>1));
+}
+
+static int imxmci_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc;
+	struct imxmci_host *host = NULL;
+	struct resource *r;
+	int ret = 0, irq;
+
+	printk(KERN_INFO "i.MX mmc driver\n");
+
+	r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_device_irq(pdev, 0);
+	if (!r || irq == NO_IRQ)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, 0x100, "IMXMCI");
+	if (!r)
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mmc->ops = &imxmci_ops;
+	mmc->f_min = 150000;
+	mmc->f_max = CLK_RATE/2;
+	mmc->ocr_avail = MMC_VDD_32_33;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK;
+
+	/* MMC core transfer sizes tunable parameters */
+	mmc->max_hw_segs = 64;
+	mmc->max_phys_segs = 64;
+	mmc->max_seg_size = 64*512;	/* default PAGE_CACHE_SIZE */
+	mmc->max_req_size = 64*512;	/* default PAGE_CACHE_SIZE */
+	mmc->max_blk_size = 2048;
+	mmc->max_blk_count = 65535;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->dma_allocated = 0;
+	host->pdata = pdev->dev.platform_data;
+
+	spin_lock_init(&host->lock);
+	host->res = r;
+	host->irq = irq;
+
+	imx_gpio_mode(PB8_PF_SD_DAT0);
+	imx_gpio_mode(PB9_PF_SD_DAT1);
+	imx_gpio_mode(PB10_PF_SD_DAT2);
+	/* Configured as GPIO with pull-up to ensure right MCC card mode */
+	/* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */
+	imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
+	/* imx_gpio_mode(PB11_PF_SD_DAT3); */
+	imx_gpio_mode(PB12_PF_SD_CLK);
+	imx_gpio_mode(PB13_PF_SD_CMD);
+
+	imxmci_softreset();
+
+	if ( MMC_REV_NO != 0x390 ) {
+		dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
+		        MMC_REV_NO);
+		goto out;
+	}
+
+	MMC_READ_TO = 0x2db4; /* recommended in data sheet */
+
+	host->imask = IMXMCI_INT_MASK_DEFAULT;
+	MMC_INT_MASK = host->imask;
+
+
+	if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){
+		dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
+		ret = -EBUSY;
+		goto out;
+	}
+	host->dma_allocated=1;
+	imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
+
+	tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
+	host->status_reg=0;
+	host->pending_events=0;
+
+	ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);
+	if (ret)
+		goto out;
+
+	host->present = host->pdata->card_present();
+	init_timer(&host->timer);
+	host->timer.data = (unsigned long)host;
+	host->timer.function = imxmci_check_status;
+	add_timer(&host->timer);
+	mod_timer(&host->timer, jiffies + (HZ>>1));
+
+	platform_set_drvdata(pdev, mmc);
+
+	mmc_add_host(mmc);
+
+	return 0;
+
+out:
+	if (host) {
+		if(host->dma_allocated){
+			imx_dma_free(host->dma);
+			host->dma_allocated=0;
+		}
+	}
+	if (mmc)
+		mmc_free_host(mmc);
+	release_resource(r);
+	return ret;
+}
+
+static int imxmci_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (mmc) {
+		struct imxmci_host *host = mmc_priv(mmc);
+
+		tasklet_disable(&host->tasklet);
+
+		del_timer_sync(&host->timer);
+		mmc_remove_host(mmc);
+
+		free_irq(host->irq, host);
+		if(host->dma_allocated){
+			imx_dma_free(host->dma);
+			host->dma_allocated=0;
+		}
+
+		tasklet_kill(&host->tasklet);
+
+		release_resource(host->res);
+
+		mmc_free_host(mmc);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc)
+		ret = mmc_suspend_host(mmc, state);
+
+	return ret;
+}
+
+static int imxmci_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	struct imxmci_host *host;
+	int ret = 0;
+
+	if (mmc) {
+		host = mmc_priv(mmc);
+		if(host)
+			set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
+		ret = mmc_resume_host(mmc);
+	}
+
+	return ret;
+}
+#else
+#define imxmci_suspend  NULL
+#define imxmci_resume   NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver imxmci_driver = {
+	.probe		= imxmci_probe,
+	.remove		= imxmci_remove,
+	.suspend	= imxmci_suspend,
+	.resume		= imxmci_resume,
+	.driver		= {
+		.name		= DRIVER_NAME,
+	}
+};
+
+static int __init imxmci_init(void)
+{
+	return platform_driver_register(&imxmci_driver);
+}
+
+static void __exit imxmci_exit(void)
+{
+	platform_driver_unregister(&imxmci_driver);
+}
+
+module_init(imxmci_init);
+module_exit(imxmci_exit);
+
+MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h
new file mode 100644
index 0000000..e5339e3
--- /dev/null
+++ b/drivers/mmc/host/imxmmc.h
@@ -0,0 +1,67 @@
+
+# define __REG16(x)	(*((volatile u16 *)IO_ADDRESS(x)))
+
+#define MMC_STR_STP_CLK  __REG16(IMX_MMC_BASE + 0x00)
+#define MMC_STATUS       __REG16(IMX_MMC_BASE + 0x04)
+#define MMC_CLK_RATE     __REG16(IMX_MMC_BASE + 0x08)
+#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
+#define MMC_RES_TO       __REG16(IMX_MMC_BASE + 0x10)
+#define MMC_READ_TO      __REG16(IMX_MMC_BASE + 0x14)
+#define MMC_BLK_LEN      __REG16(IMX_MMC_BASE + 0x18)
+#define MMC_NOB          __REG16(IMX_MMC_BASE + 0x1C)
+#define MMC_REV_NO       __REG16(IMX_MMC_BASE + 0x20)
+#define MMC_INT_MASK     __REG16(IMX_MMC_BASE + 0x24)
+#define MMC_CMD          __REG16(IMX_MMC_BASE + 0x28)
+#define MMC_ARGH         __REG16(IMX_MMC_BASE + 0x2C)
+#define MMC_ARGL         __REG16(IMX_MMC_BASE + 0x30)
+#define MMC_RES_FIFO     __REG16(IMX_MMC_BASE + 0x34)
+#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
+#define MMC_BUFFER_ACCESS_OFS 0x38
+
+
+#define STR_STP_CLK_ENDIAN              (1<<5)
+#define STR_STP_CLK_RESET               (1<<3)
+#define STR_STP_CLK_ENABLE              (1<<2)
+#define STR_STP_CLK_START_CLK           (1<<1)
+#define STR_STP_CLK_STOP_CLK            (1<<0)
+#define STATUS_CARD_PRESENCE            (1<<15)
+#define STATUS_SDIO_INT_ACTIVE          (1<<14)
+#define STATUS_END_CMD_RESP             (1<<13)
+#define STATUS_WRITE_OP_DONE            (1<<12)
+#define STATUS_DATA_TRANS_DONE          (1<<11)
+#define STATUS_WR_CRC_ERROR_CODE_MASK   (3<<10)
+#define STATUS_CARD_BUS_CLK_RUN         (1<<8)
+#define STATUS_APPL_BUFF_FF             (1<<7)
+#define STATUS_APPL_BUFF_FE             (1<<6)
+#define STATUS_RESP_CRC_ERR             (1<<5)
+#define STATUS_CRC_READ_ERR             (1<<3)
+#define STATUS_CRC_WRITE_ERR            (1<<2)
+#define STATUS_TIME_OUT_RESP            (1<<1)
+#define STATUS_TIME_OUT_READ            (1<<0)
+#define STATUS_ERR_MASK                 0x2f
+#define CLK_RATE_PRESCALER(x)           ((x) & 0x7)
+#define CLK_RATE_CLK_RATE(x)            (((x) & 0x7) << 3)
+#define CMD_DAT_CONT_CMD_RESP_LONG_OFF  (1<<12)
+#define CMD_DAT_CONT_STOP_READWAIT      (1<<11)
+#define CMD_DAT_CONT_START_READWAIT     (1<<10)
+#define CMD_DAT_CONT_BUS_WIDTH_1        (0<<8)
+#define CMD_DAT_CONT_BUS_WIDTH_4        (2<<8)
+#define CMD_DAT_CONT_INIT               (1<<7)
+#define CMD_DAT_CONT_BUSY               (1<<6)
+#define CMD_DAT_CONT_STREAM_BLOCK       (1<<5)
+#define CMD_DAT_CONT_WRITE              (1<<4)
+#define CMD_DAT_CONT_DATA_ENABLE        (1<<3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
+#define INT_MASK_AUTO_CARD_DETECT       (1<<6)
+#define INT_MASK_DAT0_EN                (1<<5)
+#define INT_MASK_SDIO                   (1<<4)
+#define INT_MASK_BUF_READY              (1<<3)
+#define INT_MASK_END_CMD_RES            (1<<2)
+#define INT_MASK_WRITE_OP_DONE          (1<<1)
+#define INT_MASK_DATA_TRAN              (1<<0)
+#define INT_ALL                         (0x7f)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
new file mode 100644
index 0000000..d11c2d2
--- /dev/null
+++ b/drivers/mmc/host/mmci.c
@@ -0,0 +1,702 @@
+/*
+ *  linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/mmc/host.h>
+#include <linux/amba/bus.h>
+#include <linux/clk.h>
+
+#include <asm/cacheflush.h>
+#include <asm/div64.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <asm/sizes.h>
+#include <asm/mach/mmc.h>
+
+#include "mmci.h"
+
+#define DRIVER_NAME "mmci-pl18x"
+
+#define DBG(host,fmt,args...)	\
+	pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
+
+static unsigned int fmax = 515633;
+
+static void
+mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
+{
+	writel(0, host->base + MMCICOMMAND);
+
+	BUG_ON(host->data);
+
+	host->mrq = NULL;
+	host->cmd = NULL;
+
+	if (mrq->data)
+		mrq->data->bytes_xfered = host->data_xfered;
+
+	/*
+	 * Need to drop the host lock here; mmc_request_done may call
+	 * back into the driver...
+	 */
+	spin_unlock(&host->lock);
+	mmc_request_done(host->mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+static void mmci_stop_data(struct mmci_host *host)
+{
+	writel(0, host->base + MMCIDATACTRL);
+	writel(0, host->base + MMCIMASK1);
+	host->data = NULL;
+}
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+	unsigned int datactrl, timeout, irqmask;
+	unsigned long long clks;
+	void __iomem *base;
+	int blksz_bits;
+
+	DBG(host, "blksz %04x blks %04x flags %08x\n",
+	    data->blksz, data->blocks, data->flags);
+
+	host->data = data;
+	host->size = data->blksz;
+	host->data_xfered = 0;
+
+	mmci_init_sg(host, data);
+
+	clks = (unsigned long long)data->timeout_ns * host->cclk;
+	do_div(clks, 1000000000UL);
+
+	timeout = data->timeout_clks + (unsigned int)clks;
+
+	base = host->base;
+	writel(timeout, base + MMCIDATATIMER);
+	writel(host->size, base + MMCIDATALENGTH);
+
+	blksz_bits = ffs(data->blksz) - 1;
+	BUG_ON(1 << blksz_bits != data->blksz);
+
+	datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
+	if (data->flags & MMC_DATA_READ) {
+		datactrl |= MCI_DPSM_DIRECTION;
+		irqmask = MCI_RXFIFOHALFFULLMASK;
+
+		/*
+		 * If we have less than a FIFOSIZE of bytes to transfer,
+		 * trigger a PIO interrupt as soon as any data is available.
+		 */
+		if (host->size < MCI_FIFOSIZE)
+			irqmask |= MCI_RXDATAAVLBLMASK;
+	} else {
+		/*
+		 * We don't actually need to include "FIFO empty" here
+		 * since its implicit in "FIFO half empty".
+		 */
+		irqmask = MCI_TXFIFOHALFEMPTYMASK;
+	}
+
+	writel(datactrl, base + MMCIDATACTRL);
+	writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
+	writel(irqmask, base + MMCIMASK1);
+}
+
+static void
+mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
+{
+	void __iomem *base = host->base;
+
+	DBG(host, "op %02x arg %08x flags %08x\n",
+	    cmd->opcode, cmd->arg, cmd->flags);
+
+	if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+		writel(0, base + MMCICOMMAND);
+		udelay(1);
+	}
+
+	c |= cmd->opcode | MCI_CPSM_ENABLE;
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136)
+			c |= MCI_CPSM_LONGRSP;
+		c |= MCI_CPSM_RESPONSE;
+	}
+	if (/*interrupt*/0)
+		c |= MCI_CPSM_INTERRUPT;
+
+	host->cmd = cmd;
+
+	writel(cmd->arg, base + MMCIARGUMENT);
+	writel(c, base + MMCICOMMAND);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+	      unsigned int status)
+{
+	if (status & MCI_DATABLOCKEND) {
+		host->data_xfered += data->blksz;
+	}
+	if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+		if (status & MCI_DATACRCFAIL)
+			data->error = MMC_ERR_BADCRC;
+		else if (status & MCI_DATATIMEOUT)
+			data->error = MMC_ERR_TIMEOUT;
+		else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+			data->error = MMC_ERR_FIFO;
+		status |= MCI_DATAEND;
+
+		/*
+		 * We hit an error condition.  Ensure that any data
+		 * partially written to a page is properly coherent.
+		 */
+		if (host->sg_len && data->flags & MMC_DATA_READ)
+			flush_dcache_page(host->sg_ptr->page);
+	}
+	if (status & MCI_DATAEND) {
+		mmci_stop_data(host);
+
+		if (!data->stop) {
+			mmci_request_end(host, data->mrq);
+		} else {
+			mmci_start_command(host, data->stop, 0);
+		}
+	}
+}
+
+static void
+mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
+	     unsigned int status)
+{
+	void __iomem *base = host->base;
+
+	host->cmd = NULL;
+
+	cmd->resp[0] = readl(base + MMCIRESPONSE0);
+	cmd->resp[1] = readl(base + MMCIRESPONSE1);
+	cmd->resp[2] = readl(base + MMCIRESPONSE2);
+	cmd->resp[3] = readl(base + MMCIRESPONSE3);
+
+	if (status & MCI_CMDTIMEOUT) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+	}
+
+	if (!cmd->data || cmd->error != MMC_ERR_NONE) {
+		if (host->data)
+			mmci_stop_data(host);
+		mmci_request_end(host, cmd->mrq);
+	} else if (!(cmd->data->flags & MMC_DATA_READ)) {
+		mmci_start_data(host, cmd->data);
+	}
+}
+
+static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
+{
+	void __iomem *base = host->base;
+	char *ptr = buffer;
+	u32 status;
+
+	do {
+		int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
+
+		if (count > remain)
+			count = remain;
+
+		if (count <= 0)
+			break;
+
+		readsl(base + MMCIFIFO, ptr, count >> 2);
+
+		ptr += count;
+		remain -= count;
+
+		if (remain == 0)
+			break;
+
+		status = readl(base + MMCISTATUS);
+	} while (status & MCI_RXDATAAVLBL);
+
+	return ptr - buffer;
+}
+
+static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
+{
+	void __iomem *base = host->base;
+	char *ptr = buffer;
+
+	do {
+		unsigned int count, maxcnt;
+
+		maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
+		count = min(remain, maxcnt);
+
+		writesl(base + MMCIFIFO, ptr, count >> 2);
+
+		ptr += count;
+		remain -= count;
+
+		if (remain == 0)
+			break;
+
+		status = readl(base + MMCISTATUS);
+	} while (status & MCI_TXFIFOHALFEMPTY);
+
+	return ptr - buffer;
+}
+
+/*
+ * PIO data transfer IRQ handler.
+ */
+static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
+{
+	struct mmci_host *host = dev_id;
+	void __iomem *base = host->base;
+	u32 status;
+
+	status = readl(base + MMCISTATUS);
+
+	DBG(host, "irq1 %08x\n", status);
+
+	do {
+		unsigned long flags;
+		unsigned int remain, len;
+		char *buffer;
+
+		/*
+		 * For write, we only need to test the half-empty flag
+		 * here - if the FIFO is completely empty, then by
+		 * definition it is more than half empty.
+		 *
+		 * For read, check for data available.
+		 */
+		if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
+			break;
+
+		/*
+		 * Map the current scatter buffer.
+		 */
+		buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
+		remain = host->sg_ptr->length - host->sg_off;
+
+		len = 0;
+		if (status & MCI_RXACTIVE)
+			len = mmci_pio_read(host, buffer, remain);
+		if (status & MCI_TXACTIVE)
+			len = mmci_pio_write(host, buffer, remain, status);
+
+		/*
+		 * Unmap the buffer.
+		 */
+		mmci_kunmap_atomic(host, buffer, &flags);
+
+		host->sg_off += len;
+		host->size -= len;
+		remain -= len;
+
+		if (remain)
+			break;
+
+		/*
+		 * If we were reading, and we have completed this
+		 * page, ensure that the data cache is coherent.
+		 */
+		if (status & MCI_RXACTIVE)
+			flush_dcache_page(host->sg_ptr->page);
+
+		if (!mmci_next_sg(host))
+			break;
+
+		status = readl(base + MMCISTATUS);
+	} while (1);
+
+	/*
+	 * If we're nearing the end of the read, switch to
+	 * "any data available" mode.
+	 */
+	if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
+		writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
+
+	/*
+	 * If we run out of data, disable the data IRQs; this
+	 * prevents a race where the FIFO becomes empty before
+	 * the chip itself has disabled the data path, and
+	 * stops us racing with our data end IRQ.
+	 */
+	if (host->size == 0) {
+		writel(0, base + MMCIMASK1);
+		writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Handle completion of command and data transfers.
+ */
+static irqreturn_t mmci_irq(int irq, void *dev_id)
+{
+	struct mmci_host *host = dev_id;
+	u32 status;
+	int ret = 0;
+
+	spin_lock(&host->lock);
+
+	do {
+		struct mmc_command *cmd;
+		struct mmc_data *data;
+
+		status = readl(host->base + MMCISTATUS);
+		status &= readl(host->base + MMCIMASK0);
+		writel(status, host->base + MMCICLEAR);
+
+		DBG(host, "irq0 %08x\n", status);
+
+		data = host->data;
+		if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
+			      MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
+			mmci_data_irq(host, data, status);
+
+		cmd = host->cmd;
+		if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+			mmci_cmd_irq(host, cmd, status);
+
+		ret = 1;
+	} while (status);
+
+	spin_unlock(&host->lock);
+
+	return IRQ_RETVAL(ret);
+}
+
+static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct mmci_host *host = mmc_priv(mmc);
+
+	WARN_ON(host->mrq != NULL);
+
+	spin_lock_irq(&host->lock);
+
+	host->mrq = mrq;
+
+	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
+		mmci_start_data(host, mrq->data);
+
+	mmci_start_command(host, mrq->cmd, 0);
+
+	spin_unlock_irq(&host->lock);
+}
+
+static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmci_host *host = mmc_priv(mmc);
+	u32 clk = 0, pwr = 0;
+
+	if (ios->clock) {
+		if (ios->clock >= host->mclk) {
+			clk = MCI_CLK_BYPASS;
+			host->cclk = host->mclk;
+		} else {
+			clk = host->mclk / (2 * ios->clock) - 1;
+			if (clk > 256)
+				clk = 255;
+			host->cclk = host->mclk / (2 * (clk + 1));
+		}
+		clk |= MCI_CLK_ENABLE;
+	}
+
+	if (host->plat->translate_vdd)
+		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		break;
+	case MMC_POWER_UP:
+		pwr |= MCI_PWR_UP;
+		break;
+	case MMC_POWER_ON:
+		pwr |= MCI_PWR_ON;
+		break;
+	}
+
+	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		pwr |= MCI_ROD;
+
+	writel(clk, host->base + MMCICLOCK);
+
+	if (host->pwr != pwr) {
+		host->pwr = pwr;
+		writel(pwr, host->base + MMCIPOWER);
+	}
+}
+
+static const struct mmc_host_ops mmci_ops = {
+	.request	= mmci_request,
+	.set_ios	= mmci_set_ios,
+};
+
+static void mmci_check_status(unsigned long data)
+{
+	struct mmci_host *host = (struct mmci_host *)data;
+	unsigned int status;
+
+	status = host->plat->status(mmc_dev(host->mmc));
+	if (status ^ host->oldstat)
+		mmc_detect_change(host->mmc, 0);
+
+	host->oldstat = status;
+	mod_timer(&host->timer, jiffies + HZ);
+}
+
+static int mmci_probe(struct amba_device *dev, void *id)
+{
+	struct mmc_platform_data *plat = dev->dev.platform_data;
+	struct mmci_host *host;
+	struct mmc_host *mmc;
+	int ret;
+
+	/* must have platform data */
+	if (!plat) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = amba_request_regions(dev, DRIVER_NAME);
+	if (ret)
+		goto out;
+
+	mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto rel_regions;
+	}
+
+	host = mmc_priv(mmc);
+	host->clk = clk_get(&dev->dev, "MCLK");
+	if (IS_ERR(host->clk)) {
+		ret = PTR_ERR(host->clk);
+		host->clk = NULL;
+		goto host_free;
+	}
+
+	ret = clk_enable(host->clk);
+	if (ret)
+		goto clk_free;
+
+	host->plat = plat;
+	host->mclk = clk_get_rate(host->clk);
+	host->mmc = mmc;
+	host->base = ioremap(dev->res.start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto clk_disable;
+	}
+
+	mmc->ops = &mmci_ops;
+	mmc->f_min = (host->mclk + 511) / 512;
+	mmc->f_max = min(host->mclk, fmax);
+	mmc->ocr_avail = plat->ocr_mask;
+	mmc->caps = MMC_CAP_MULTIWRITE;
+
+	/*
+	 * We can do SGIO
+	 */
+	mmc->max_hw_segs = 16;
+	mmc->max_phys_segs = NR_SG;
+
+	/*
+	 * Since we only have a 16-bit data length register, we must
+	 * ensure that we don't exceed 2^16-1 bytes in a single request.
+	 */
+	mmc->max_req_size = 65535;
+
+	/*
+	 * Set the maximum segment size.  Since we aren't doing DMA
+	 * (yet) we are only limited by the data length register.
+	 */
+	mmc->max_seg_size = mmc->max_req_size;
+
+	/*
+	 * Block size can be up to 2048 bytes, but must be a power of two.
+	 */
+	mmc->max_blk_size = 2048;
+
+	/*
+	 * No limit on the number of blocks transferred.
+	 */
+	mmc->max_blk_count = mmc->max_req_size;
+
+	spin_lock_init(&host->lock);
+
+	writel(0, host->base + MMCIMASK0);
+	writel(0, host->base + MMCIMASK1);
+	writel(0xfff, host->base + MMCICLEAR);
+
+	ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
+	if (ret)
+		goto unmap;
+
+	ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
+	if (ret)
+		goto irq0_free;
+
+	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+	amba_set_drvdata(dev, mmc);
+
+	mmc_add_host(mmc);
+
+	printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
+		mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
+		(unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
+
+	init_timer(&host->timer);
+	host->timer.data = (unsigned long)host;
+	host->timer.function = mmci_check_status;
+	host->timer.expires = jiffies + HZ;
+	add_timer(&host->timer);
+
+	return 0;
+
+ irq0_free:
+	free_irq(dev->irq[0], host);
+ unmap:
+	iounmap(host->base);
+ clk_disable:
+	clk_disable(host->clk);
+ clk_free:
+	clk_put(host->clk);
+ host_free:
+	mmc_free_host(mmc);
+ rel_regions:
+	amba_release_regions(dev);
+ out:
+	return ret;
+}
+
+static int mmci_remove(struct amba_device *dev)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+
+	amba_set_drvdata(dev, NULL);
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		del_timer_sync(&host->timer);
+
+		mmc_remove_host(mmc);
+
+		writel(0, host->base + MMCIMASK0);
+		writel(0, host->base + MMCIMASK1);
+
+		writel(0, host->base + MMCICOMMAND);
+		writel(0, host->base + MMCIDATACTRL);
+
+		free_irq(dev->irq[0], host);
+		free_irq(dev->irq[1], host);
+
+		iounmap(host->base);
+		clk_disable(host->clk);
+		clk_put(host->clk);
+
+		mmc_free_host(mmc);
+
+		amba_release_regions(dev);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmci_suspend(struct amba_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		ret = mmc_suspend_host(mmc, state);
+		if (ret == 0)
+			writel(0, host->base + MMCIMASK0);
+	}
+
+	return ret;
+}
+
+static int mmci_resume(struct amba_device *dev)
+{
+	struct mmc_host *mmc = amba_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc) {
+		struct mmci_host *host = mmc_priv(mmc);
+
+		writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+		ret = mmc_resume_host(mmc);
+	}
+
+	return ret;
+}
+#else
+#define mmci_suspend	NULL
+#define mmci_resume	NULL
+#endif
+
+static struct amba_id mmci_ids[] = {
+	{
+		.id	= 0x00041180,
+		.mask	= 0x000fffff,
+	},
+	{
+		.id	= 0x00041181,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+static struct amba_driver mmci_driver = {
+	.drv		= {
+		.name	= DRIVER_NAME,
+	},
+	.probe		= mmci_probe,
+	.remove		= mmci_remove,
+	.suspend	= mmci_suspend,
+	.resume		= mmci_resume,
+	.id_table	= mmci_ids,
+};
+
+static int __init mmci_init(void)
+{
+	return amba_driver_register(&mmci_driver);
+}
+
+static void __exit mmci_exit(void)
+{
+	amba_driver_unregister(&mmci_driver);
+}
+
+module_init(mmci_init);
+module_exit(mmci_exit);
+module_param(fmax, uint, 0444);
+
+MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
new file mode 100644
index 0000000..6d7eadc
--- /dev/null
+++ b/drivers/mmc/host/mmci.h
@@ -0,0 +1,179 @@
+/*
+ *  linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ */
+#define MMCIPOWER		0x000
+#define MCI_PWR_OFF		0x00
+#define MCI_PWR_UP		0x02
+#define MCI_PWR_ON		0x03
+#define MCI_OD			(1 << 6)
+#define MCI_ROD			(1 << 7)
+
+#define MMCICLOCK		0x004
+#define MCI_CLK_ENABLE		(1 << 8)
+#define MCI_CLK_PWRSAVE		(1 << 9)
+#define MCI_CLK_BYPASS		(1 << 10)
+
+#define MMCIARGUMENT		0x008
+#define MMCICOMMAND		0x00c
+#define MCI_CPSM_RESPONSE	(1 << 6)
+#define MCI_CPSM_LONGRSP	(1 << 7)
+#define MCI_CPSM_INTERRUPT	(1 << 8)
+#define MCI_CPSM_PENDING	(1 << 9)
+#define MCI_CPSM_ENABLE		(1 << 10)
+
+#define MMCIRESPCMD		0x010
+#define MMCIRESPONSE0		0x014
+#define MMCIRESPONSE1		0x018
+#define MMCIRESPONSE2		0x01c
+#define MMCIRESPONSE3		0x020
+#define MMCIDATATIMER		0x024
+#define MMCIDATALENGTH		0x028
+#define MMCIDATACTRL		0x02c
+#define MCI_DPSM_ENABLE		(1 << 0)
+#define MCI_DPSM_DIRECTION	(1 << 1)
+#define MCI_DPSM_MODE		(1 << 2)
+#define MCI_DPSM_DMAENABLE	(1 << 3)
+
+#define MMCIDATACNT		0x030
+#define MMCISTATUS		0x034
+#define MCI_CMDCRCFAIL		(1 << 0)
+#define MCI_DATACRCFAIL		(1 << 1)
+#define MCI_CMDTIMEOUT		(1 << 2)
+#define MCI_DATATIMEOUT		(1 << 3)
+#define MCI_TXUNDERRUN		(1 << 4)
+#define MCI_RXOVERRUN		(1 << 5)
+#define MCI_CMDRESPEND		(1 << 6)
+#define MCI_CMDSENT		(1 << 7)
+#define MCI_DATAEND		(1 << 8)
+#define MCI_DATABLOCKEND	(1 << 10)
+#define MCI_CMDACTIVE		(1 << 11)
+#define MCI_TXACTIVE		(1 << 12)
+#define MCI_RXACTIVE		(1 << 13)
+#define MCI_TXFIFOHALFEMPTY	(1 << 14)
+#define MCI_RXFIFOHALFFULL	(1 << 15)
+#define MCI_TXFIFOFULL		(1 << 16)
+#define MCI_RXFIFOFULL		(1 << 17)
+#define MCI_TXFIFOEMPTY		(1 << 18)
+#define MCI_RXFIFOEMPTY		(1 << 19)
+#define MCI_TXDATAAVLBL		(1 << 20)
+#define MCI_RXDATAAVLBL		(1 << 21)
+
+#define MMCICLEAR		0x038
+#define MCI_CMDCRCFAILCLR	(1 << 0)
+#define MCI_DATACRCFAILCLR	(1 << 1)
+#define MCI_CMDTIMEOUTCLR	(1 << 2)
+#define MCI_DATATIMEOUTCLR	(1 << 3)
+#define MCI_TXUNDERRUNCLR	(1 << 4)
+#define MCI_RXOVERRUNCLR	(1 << 5)
+#define MCI_CMDRESPENDCLR	(1 << 6)
+#define MCI_CMDSENTCLR		(1 << 7)
+#define MCI_DATAENDCLR		(1 << 8)
+#define MCI_DATABLOCKENDCLR	(1 << 10)
+
+#define MMCIMASK0		0x03c
+#define MCI_CMDCRCFAILMASK	(1 << 0)
+#define MCI_DATACRCFAILMASK	(1 << 1)
+#define MCI_CMDTIMEOUTMASK	(1 << 2)
+#define MCI_DATATIMEOUTMASK	(1 << 3)
+#define MCI_TXUNDERRUNMASK	(1 << 4)
+#define MCI_RXOVERRUNMASK	(1 << 5)
+#define MCI_CMDRESPENDMASK	(1 << 6)
+#define MCI_CMDSENTMASK		(1 << 7)
+#define MCI_DATAENDMASK		(1 << 8)
+#define MCI_DATABLOCKENDMASK	(1 << 10)
+#define MCI_CMDACTIVEMASK	(1 << 11)
+#define MCI_TXACTIVEMASK	(1 << 12)
+#define MCI_RXACTIVEMASK	(1 << 13)
+#define MCI_TXFIFOHALFEMPTYMASK	(1 << 14)
+#define MCI_RXFIFOHALFFULLMASK	(1 << 15)
+#define MCI_TXFIFOFULLMASK	(1 << 16)
+#define MCI_RXFIFOFULLMASK	(1 << 17)
+#define MCI_TXFIFOEMPTYMASK	(1 << 18)
+#define MCI_RXFIFOEMPTYMASK	(1 << 19)
+#define MCI_TXDATAAVLBLMASK	(1 << 20)
+#define MCI_RXDATAAVLBLMASK	(1 << 21)
+
+#define MMCIMASK1		0x040
+#define MMCIFIFOCNT		0x048
+#define MMCIFIFO		0x080 /* to 0x0bc */
+
+#define MCI_IRQENABLE	\
+	(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK|	\
+	MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK|	\
+	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK)
+
+/*
+ * The size of the FIFO in bytes.
+ */
+#define MCI_FIFOSIZE	(16*4)
+	
+#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+
+#define NR_SG		16
+
+struct clk;
+
+struct mmci_host {
+	void __iomem		*base;
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+	struct mmc_host		*mmc;
+	struct clk		*clk;
+
+	unsigned int		data_xfered;
+
+	spinlock_t		lock;
+
+	unsigned int		mclk;
+	unsigned int		cclk;
+	u32			pwr;
+	struct mmc_platform_data *plat;
+
+	struct timer_list	timer;
+	unsigned int		oldstat;
+
+	unsigned int		sg_len;
+
+	/* pio stuff */
+	struct scatterlist	*sg_ptr;
+	unsigned int		sg_off;
+	unsigned int		size;
+};
+
+static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
+{
+	/*
+	 * Ideally, we want the higher levels to pass us a scatter list.
+	 */
+	host->sg_len = data->sg_len;
+	host->sg_ptr = data->sg;
+	host->sg_off = 0;
+}
+
+static inline int mmci_next_sg(struct mmci_host *host)
+{
+	host->sg_ptr++;
+	host->sg_off = 0;
+	return --host->sg_len;
+}
+
+static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
+{
+	struct scatterlist *sg = host->sg_ptr;
+
+	local_irq_save(*flags);
+	return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
+{
+	kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+	local_irq_restore(*flags);
+}
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
new file mode 100644
index 0000000..e851384
--- /dev/null
+++ b/drivers/mmc/host/omap.c
@@ -0,0 +1,1288 @@
+/*
+ *  linux/drivers/media/mmc/omap.c
+ *
+ *  Copyright (C) 2004 Nokia Corporation
+ *  Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
+ *  Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ *  Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/fpga.h>
+#include <asm/arch/tps65010.h>
+
+#define	OMAP_MMC_REG_CMD	0x00
+#define	OMAP_MMC_REG_ARGL	0x04
+#define	OMAP_MMC_REG_ARGH	0x08
+#define	OMAP_MMC_REG_CON	0x0c
+#define	OMAP_MMC_REG_STAT	0x10
+#define	OMAP_MMC_REG_IE		0x14
+#define	OMAP_MMC_REG_CTO	0x18
+#define	OMAP_MMC_REG_DTO	0x1c
+#define	OMAP_MMC_REG_DATA	0x20
+#define	OMAP_MMC_REG_BLEN	0x24
+#define	OMAP_MMC_REG_NBLK	0x28
+#define	OMAP_MMC_REG_BUF	0x2c
+#define OMAP_MMC_REG_SDIO	0x34
+#define	OMAP_MMC_REG_REV	0x3c
+#define	OMAP_MMC_REG_RSP0	0x40
+#define	OMAP_MMC_REG_RSP1	0x44
+#define	OMAP_MMC_REG_RSP2	0x48
+#define	OMAP_MMC_REG_RSP3	0x4c
+#define	OMAP_MMC_REG_RSP4	0x50
+#define	OMAP_MMC_REG_RSP5	0x54
+#define	OMAP_MMC_REG_RSP6	0x58
+#define	OMAP_MMC_REG_RSP7	0x5c
+#define	OMAP_MMC_REG_IOSR	0x60
+#define	OMAP_MMC_REG_SYSC	0x64
+#define	OMAP_MMC_REG_SYSS	0x68
+
+#define	OMAP_MMC_STAT_CARD_ERR		(1 << 14)
+#define	OMAP_MMC_STAT_CARD_IRQ		(1 << 13)
+#define	OMAP_MMC_STAT_OCR_BUSY		(1 << 12)
+#define	OMAP_MMC_STAT_A_EMPTY		(1 << 11)
+#define	OMAP_MMC_STAT_A_FULL		(1 << 10)
+#define	OMAP_MMC_STAT_CMD_CRC		(1 <<  8)
+#define	OMAP_MMC_STAT_CMD_TOUT		(1 <<  7)
+#define	OMAP_MMC_STAT_DATA_CRC		(1 <<  6)
+#define	OMAP_MMC_STAT_DATA_TOUT		(1 <<  5)
+#define	OMAP_MMC_STAT_END_BUSY		(1 <<  4)
+#define	OMAP_MMC_STAT_END_OF_DATA	(1 <<  3)
+#define	OMAP_MMC_STAT_CARD_BUSY		(1 <<  2)
+#define	OMAP_MMC_STAT_END_OF_CMD	(1 <<  0)
+
+#define OMAP_MMC_READ(host, reg)	__raw_readw((host)->virt_base + OMAP_MMC_REG_##reg)
+#define OMAP_MMC_WRITE(host, reg, val)	__raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg)
+
+/*
+ * Command types
+ */
+#define OMAP_MMC_CMDTYPE_BC	0
+#define OMAP_MMC_CMDTYPE_BCR	1
+#define OMAP_MMC_CMDTYPE_AC	2
+#define OMAP_MMC_CMDTYPE_ADTC	3
+
+
+#define DRIVER_NAME "mmci-omap"
+
+/* Specifies how often in millisecs to poll for card status changes
+ * when the cover switch is open */
+#define OMAP_MMC_SWITCH_POLL_DELAY	500
+
+static int mmc_omap_enable_poll = 1;
+
+struct mmc_omap_host {
+	int			initialized;
+	int			suspended;
+	struct mmc_request *	mrq;
+	struct mmc_command *	cmd;
+	struct mmc_data *	data;
+	struct mmc_host *	mmc;
+	struct device *		dev;
+	unsigned char		id; /* 16xx chips have 2 MMC blocks */
+	struct clk *		iclk;
+	struct clk *		fclk;
+	struct resource		*mem_res;
+	void __iomem		*virt_base;
+	unsigned int		phys_base;
+	int			irq;
+	unsigned char		bus_mode;
+	unsigned char		hw_bus_mode;
+
+	unsigned int		sg_len;
+	int			sg_idx;
+	u16 *			buffer;
+	u32			buffer_bytes_left;
+	u32			total_bytes_left;
+
+	unsigned		use_dma:1;
+	unsigned		brs_received:1, dma_done:1;
+	unsigned		dma_is_read:1;
+	unsigned		dma_in_use:1;
+	int			dma_ch;
+	spinlock_t		dma_lock;
+	struct timer_list	dma_timer;
+	unsigned		dma_len;
+
+	short			power_pin;
+	short			wp_pin;
+
+	int			switch_pin;
+	struct work_struct	switch_work;
+	struct timer_list	switch_timer;
+	int			switch_last_state;
+};
+
+static inline int
+mmc_omap_cover_is_open(struct mmc_omap_host *host)
+{
+	if (host->switch_pin < 0)
+		return 0;
+	return omap_get_gpio_datain(host->switch_pin);
+}
+
+static ssize_t
+mmc_omap_show_cover_switch(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct mmc_omap_host *host = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" :
+			"closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+
+static ssize_t
+mmc_omap_show_enable_poll(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll);
+}
+
+static ssize_t
+mmc_omap_store_enable_poll(struct device *dev,
+	struct device_attribute *attr, const char *buf,
+	size_t size)
+{
+	int enable_poll;
+
+	if (sscanf(buf, "%10d", &enable_poll) != 1)
+		return -EINVAL;
+
+	if (enable_poll != mmc_omap_enable_poll) {
+		struct mmc_omap_host *host = dev_get_drvdata(dev);
+
+		mmc_omap_enable_poll = enable_poll;
+		if (enable_poll && host->switch_pin >= 0)
+			schedule_work(&host->switch_work);
+	}
+	return size;
+}
+
+static DEVICE_ATTR(enable_poll, 0664,
+		   mmc_omap_show_enable_poll, mmc_omap_store_enable_poll);
+
+static void
+mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+	u32 cmdreg;
+	u32 resptype;
+	u32 cmdtype;
+
+	host->cmd = cmd;
+
+	resptype = 0;
+	cmdtype = 0;
+
+	/* Our hardware needs to know exact type */
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_NONE:
+		break;
+	case MMC_RSP_R1:
+	case MMC_RSP_R1B:
+		/* resp 1, 1b, 6, 7 */
+		resptype = 1;
+		break;
+	case MMC_RSP_R2:
+		resptype = 2;
+		break;
+	case MMC_RSP_R3:
+		resptype = 3;
+		break;
+	default:
+		dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd));
+		break;
+	}
+
+	if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {
+		cmdtype = OMAP_MMC_CMDTYPE_ADTC;
+	} else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {
+		cmdtype = OMAP_MMC_CMDTYPE_BC;
+	} else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {
+		cmdtype = OMAP_MMC_CMDTYPE_BCR;
+	} else {
+		cmdtype = OMAP_MMC_CMDTYPE_AC;
+	}
+
+	cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
+
+	if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+		cmdreg |= 1 << 6;
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdreg |= 1 << 11;
+
+	if (host->data && !(host->data->flags & MMC_DATA_WRITE))
+		cmdreg |= 1 << 15;
+
+	clk_enable(host->fclk);
+
+	OMAP_MMC_WRITE(host, CTO, 200);
+	OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
+	OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
+	OMAP_MMC_WRITE(host, IE,
+		       OMAP_MMC_STAT_A_EMPTY    | OMAP_MMC_STAT_A_FULL    |
+		       OMAP_MMC_STAT_CMD_CRC    | OMAP_MMC_STAT_CMD_TOUT  |
+		       OMAP_MMC_STAT_DATA_CRC   | OMAP_MMC_STAT_DATA_TOUT |
+		       OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR  |
+		       OMAP_MMC_STAT_END_OF_DATA);
+	OMAP_MMC_WRITE(host, CMD, cmdreg);
+}
+
+static void
+mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	if (host->dma_in_use) {
+		enum dma_data_direction dma_data_dir;
+
+		BUG_ON(host->dma_ch < 0);
+		if (data->error != MMC_ERR_NONE)
+			omap_stop_dma(host->dma_ch);
+		/* Release DMA channel lazily */
+		mod_timer(&host->dma_timer, jiffies + HZ);
+		if (data->flags & MMC_DATA_WRITE)
+			dma_data_dir = DMA_TO_DEVICE;
+		else
+			dma_data_dir = DMA_FROM_DEVICE;
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+			     dma_data_dir);
+	}
+	host->data = NULL;
+	host->sg_len = 0;
+	clk_disable(host->fclk);
+
+	/* NOTE:  MMC layer will sometimes poll-wait CMD13 next, issuing
+	 * dozens of requests until the card finishes writing data.
+	 * It'd be cheaper to just wait till an EOFB interrupt arrives...
+	 */
+
+	if (!data->stop) {
+		host->mrq = NULL;
+		mmc_request_done(host->mmc, data->mrq);
+		return;
+	}
+
+	mmc_omap_start_command(host, data->stop);
+}
+
+static void
+mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	unsigned long flags;
+	int done;
+
+	if (!host->dma_in_use) {
+		mmc_omap_xfer_done(host, data);
+		return;
+	}
+	done = 0;
+	spin_lock_irqsave(&host->dma_lock, flags);
+	if (host->dma_done)
+		done = 1;
+	else
+		host->brs_received = 1;
+	spin_unlock_irqrestore(&host->dma_lock, flags);
+	if (done)
+		mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_dma_timer(unsigned long data)
+{
+	struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+	BUG_ON(host->dma_ch < 0);
+	omap_free_dma(host->dma_ch);
+	host->dma_ch = -1;
+}
+
+static void
+mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	unsigned long flags;
+	int done;
+
+	done = 0;
+	spin_lock_irqsave(&host->dma_lock, flags);
+	if (host->brs_received)
+		done = 1;
+	else
+		host->dma_done = 1;
+	spin_unlock_irqrestore(&host->dma_lock, flags);
+	if (done)
+		mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+	host->cmd = NULL;
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if (cmd->flags & MMC_RSP_136) {
+			/* response type 2 */
+			cmd->resp[3] =
+				OMAP_MMC_READ(host, RSP0) |
+				(OMAP_MMC_READ(host, RSP1) << 16);
+			cmd->resp[2] =
+				OMAP_MMC_READ(host, RSP2) |
+				(OMAP_MMC_READ(host, RSP3) << 16);
+			cmd->resp[1] =
+				OMAP_MMC_READ(host, RSP4) |
+				(OMAP_MMC_READ(host, RSP5) << 16);
+			cmd->resp[0] =
+				OMAP_MMC_READ(host, RSP6) |
+				(OMAP_MMC_READ(host, RSP7) << 16);
+		} else {
+			/* response types 1, 1b, 3, 4, 5, 6 */
+			cmd->resp[0] =
+				OMAP_MMC_READ(host, RSP6) |
+				(OMAP_MMC_READ(host, RSP7) << 16);
+		}
+	}
+
+	if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
+		host->mrq = NULL;
+		clk_disable(host->fclk);
+		mmc_request_done(host->mmc, cmd->mrq);
+	}
+}
+
+/* PIO only */
+static void
+mmc_omap_sg_to_buf(struct mmc_omap_host *host)
+{
+	struct scatterlist *sg;
+
+	sg = host->data->sg + host->sg_idx;
+	host->buffer_bytes_left = sg->length;
+	host->buffer = page_address(sg->page) + sg->offset;
+	if (host->buffer_bytes_left > host->total_bytes_left)
+		host->buffer_bytes_left = host->total_bytes_left;
+}
+
+/* PIO only */
+static void
+mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
+{
+	int n;
+
+	if (host->buffer_bytes_left == 0) {
+		host->sg_idx++;
+		BUG_ON(host->sg_idx == host->sg_len);
+		mmc_omap_sg_to_buf(host);
+	}
+	n = 64;
+	if (n > host->buffer_bytes_left)
+		n = host->buffer_bytes_left;
+	host->buffer_bytes_left -= n;
+	host->total_bytes_left -= n;
+	host->data->bytes_xfered += n;
+
+	if (write) {
+		__raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+	} else {
+		__raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+	}
+}
+
+static inline void mmc_omap_report_irq(u16 status)
+{
+	static const char *mmc_omap_status_bits[] = {
+		"EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
+		"CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR"
+	};
+	int i, c = 0;
+
+	for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+		if (status & (1 << i)) {
+			if (c)
+				printk(" ");
+			printk("%s", mmc_omap_status_bits[i]);
+			c++;
+		}
+}
+
+static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+{
+	struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id;
+	u16 status;
+	int end_command;
+	int end_transfer;
+	int transfer_error;
+
+	if (host->cmd == NULL && host->data == NULL) {
+		status = OMAP_MMC_READ(host, STAT);
+		dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+		if (status != 0) {
+			OMAP_MMC_WRITE(host, STAT, status);
+			OMAP_MMC_WRITE(host, IE, 0);
+		}
+		return IRQ_HANDLED;
+	}
+
+	end_command = 0;
+	end_transfer = 0;
+	transfer_error = 0;
+
+	while ((status = OMAP_MMC_READ(host, STAT)) != 0) {
+		OMAP_MMC_WRITE(host, STAT, status);
+#ifdef CONFIG_MMC_DEBUG
+		dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
+			status, host->cmd != NULL ? host->cmd->opcode : -1);
+		mmc_omap_report_irq(status);
+		printk("\n");
+#endif
+		if (host->total_bytes_left) {
+			if ((status & OMAP_MMC_STAT_A_FULL) ||
+			    (status & OMAP_MMC_STAT_END_OF_DATA))
+				mmc_omap_xfer_data(host, 0);
+			if (status & OMAP_MMC_STAT_A_EMPTY)
+				mmc_omap_xfer_data(host, 1);
+		}
+
+		if (status & OMAP_MMC_STAT_END_OF_DATA) {
+			end_transfer = 1;
+		}
+
+		if (status & OMAP_MMC_STAT_DATA_TOUT) {
+			dev_dbg(mmc_dev(host->mmc), "data timeout\n");
+			if (host->data) {
+				host->data->error |= MMC_ERR_TIMEOUT;
+				transfer_error = 1;
+			}
+		}
+
+		if (status & OMAP_MMC_STAT_DATA_CRC) {
+			if (host->data) {
+				host->data->error |= MMC_ERR_BADCRC;
+				dev_dbg(mmc_dev(host->mmc),
+					 "data CRC error, bytes left %d\n",
+					host->total_bytes_left);
+				transfer_error = 1;
+			} else {
+				dev_dbg(mmc_dev(host->mmc), "data CRC error\n");
+			}
+		}
+
+		if (status & OMAP_MMC_STAT_CMD_TOUT) {
+			/* Timeouts are routine with some commands */
+			if (host->cmd) {
+				if (host->cmd->opcode != MMC_ALL_SEND_CID &&
+						host->cmd->opcode !=
+						MMC_SEND_OP_COND &&
+						host->cmd->opcode !=
+						MMC_APP_CMD &&
+						!mmc_omap_cover_is_open(host))
+					dev_err(mmc_dev(host->mmc),
+						"command timeout, CMD %d\n",
+						host->cmd->opcode);
+				host->cmd->error = MMC_ERR_TIMEOUT;
+				end_command = 1;
+			}
+		}
+
+		if (status & OMAP_MMC_STAT_CMD_CRC) {
+			if (host->cmd) {
+				dev_err(mmc_dev(host->mmc),
+					"command CRC error (CMD%d, arg 0x%08x)\n",
+					host->cmd->opcode, host->cmd->arg);
+				host->cmd->error = MMC_ERR_BADCRC;
+				end_command = 1;
+			} else
+				dev_err(mmc_dev(host->mmc),
+					"command CRC error without cmd?\n");
+		}
+
+		if (status & OMAP_MMC_STAT_CARD_ERR) {
+			if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) {
+				u32 response = OMAP_MMC_READ(host, RSP6)
+					| (OMAP_MMC_READ(host, RSP7) << 16);
+				/* STOP sometimes sets must-ignore bits */
+				if (!(response & (R1_CC_ERROR
+								| R1_ILLEGAL_COMMAND
+								| R1_COM_CRC_ERROR))) {
+					end_command = 1;
+					continue;
+				}
+			}
+
+			dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n",
+				host->cmd->opcode);
+			if (host->cmd) {
+				host->cmd->error = MMC_ERR_FAILED;
+				end_command = 1;
+			}
+			if (host->data) {
+				host->data->error = MMC_ERR_FAILED;
+				transfer_error = 1;
+			}
+		}
+
+		/*
+		 * NOTE: On 1610 the END_OF_CMD may come too early when
+		 * starting a write 
+		 */
+		if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
+		    (!(status & OMAP_MMC_STAT_A_EMPTY))) {
+			end_command = 1;
+		}
+	}
+
+	if (end_command) {
+		mmc_omap_cmd_done(host, host->cmd);
+	}
+	if (transfer_error)
+		mmc_omap_xfer_done(host, host->data);
+	else if (end_transfer)
+		mmc_omap_end_of_data(host, host->data);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id)
+{
+	struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id;
+
+	schedule_work(&host->switch_work);
+
+	return IRQ_HANDLED;
+}
+
+static void mmc_omap_switch_timer(unsigned long arg)
+{
+	struct mmc_omap_host *host = (struct mmc_omap_host *) arg;
+
+	schedule_work(&host->switch_work);
+}
+
+static void mmc_omap_switch_handler(struct work_struct *work)
+{
+	struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work);
+	struct mmc_card *card;
+	static int complained = 0;
+	int cards = 0, cover_open;
+
+	if (host->switch_pin == -1)
+		return;
+	cover_open = mmc_omap_cover_is_open(host);
+	if (cover_open != host->switch_last_state) {
+		kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
+		host->switch_last_state = cover_open;
+	}
+	mmc_detect_change(host->mmc, 0);
+	list_for_each_entry(card, &host->mmc->cards, node) {
+		if (mmc_card_present(card))
+			cards++;
+	}
+	if (mmc_omap_cover_is_open(host)) {
+		if (!complained) {
+			dev_info(mmc_dev(host->mmc), "cover is open");
+			complained = 1;
+		}
+		if (mmc_omap_enable_poll)
+			mod_timer(&host->switch_timer, jiffies +
+				msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY));
+	} else {
+		complained = 0;
+	}
+}
+
+/* Prepare to transfer the next segment of a scatterlist */
+static void
+mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	int dma_ch = host->dma_ch;
+	unsigned long data_addr;
+	u16 buf, frame;
+	u32 count;
+	struct scatterlist *sg = &data->sg[host->sg_idx];
+	int src_port = 0;
+	int dst_port = 0;
+	int sync_dev = 0;
+
+	data_addr = host->phys_base + OMAP_MMC_REG_DATA;
+	frame = data->blksz;
+	count = sg_dma_len(sg);
+
+	if ((data->blocks == 1) && (count > data->blksz))
+		count = frame;
+
+	host->dma_len = count;
+
+	/* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
+	 * Use 16 or 32 word frames when the blocksize is at least that large.
+	 * Blocksize is usually 512 bytes; but not for some SD reads.
+	 */
+	if (cpu_is_omap15xx() && frame > 32)
+		frame = 32;
+	else if (frame > 64)
+		frame = 64;
+	count /= frame;
+	frame >>= 1;
+
+	if (!(data->flags & MMC_DATA_WRITE)) {
+		buf = 0x800f | ((frame - 1) << 8);
+
+		if (cpu_class_is_omap1()) {
+			src_port = OMAP_DMA_PORT_TIPB;
+			dst_port = OMAP_DMA_PORT_EMIFF;
+		}
+		if (cpu_is_omap24xx())
+			sync_dev = OMAP24XX_DMA_MMC1_RX;
+
+		omap_set_dma_src_params(dma_ch, src_port,
+					OMAP_DMA_AMODE_CONSTANT,
+					data_addr, 0, 0);
+		omap_set_dma_dest_params(dma_ch, dst_port,
+					 OMAP_DMA_AMODE_POST_INC,
+					 sg_dma_address(sg), 0, 0);
+		omap_set_dma_dest_data_pack(dma_ch, 1);
+		omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+	} else {
+		buf = 0x0f80 | ((frame - 1) << 0);
+
+		if (cpu_class_is_omap1()) {
+			src_port = OMAP_DMA_PORT_EMIFF;
+			dst_port = OMAP_DMA_PORT_TIPB;
+		}
+		if (cpu_is_omap24xx())
+			sync_dev = OMAP24XX_DMA_MMC1_TX;
+
+		omap_set_dma_dest_params(dma_ch, dst_port,
+					 OMAP_DMA_AMODE_CONSTANT,
+					 data_addr, 0, 0);
+		omap_set_dma_src_params(dma_ch, src_port,
+					OMAP_DMA_AMODE_POST_INC,
+					sg_dma_address(sg), 0, 0);
+		omap_set_dma_src_data_pack(dma_ch, 1);
+		omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+	}
+
+	/* Max limit for DMA frame count is 0xffff */
+	BUG_ON(count > 0xffff);
+
+	OMAP_MMC_WRITE(host, BUF, buf);
+	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
+				     frame, count, OMAP_DMA_SYNC_FRAME,
+				     sync_dev, 0);
+}
+
+/* A scatterlist segment completed */
+static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+{
+	struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+	struct mmc_data *mmcdat = host->data;
+
+	if (unlikely(host->dma_ch < 0)) {
+		dev_err(mmc_dev(host->mmc),
+			"DMA callback while DMA not enabled\n");
+		return;
+	}
+	/* FIXME: We really should do something to _handle_ the errors */
+	if (ch_status & OMAP1_DMA_TOUT_IRQ) {
+		dev_err(mmc_dev(host->mmc),"DMA timeout\n");
+		return;
+	}
+	if (ch_status & OMAP_DMA_DROP_IRQ) {
+		dev_err(mmc_dev(host->mmc), "DMA sync error\n");
+		return;
+	}
+	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+		return;
+	}
+	mmcdat->bytes_xfered += host->dma_len;
+	host->sg_idx++;
+	if (host->sg_idx < host->sg_len) {
+		mmc_omap_prepare_dma(host, host->data);
+		omap_start_dma(host->dma_ch);
+	} else
+		mmc_omap_dma_done(host, host->data);
+}
+
+static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
+{
+	const char *dev_name;
+	int sync_dev, dma_ch, is_read, r;
+
+	is_read = !(data->flags & MMC_DATA_WRITE);
+	del_timer_sync(&host->dma_timer);
+	if (host->dma_ch >= 0) {
+		if (is_read == host->dma_is_read)
+			return 0;
+		omap_free_dma(host->dma_ch);
+		host->dma_ch = -1;
+	}
+
+	if (is_read) {
+		if (host->id == 1) {
+			sync_dev = OMAP_DMA_MMC_RX;
+			dev_name = "MMC1 read";
+		} else {
+			sync_dev = OMAP_DMA_MMC2_RX;
+			dev_name = "MMC2 read";
+		}
+	} else {
+		if (host->id == 1) {
+			sync_dev = OMAP_DMA_MMC_TX;
+			dev_name = "MMC1 write";
+		} else {
+			sync_dev = OMAP_DMA_MMC2_TX;
+			dev_name = "MMC2 write";
+		}
+	}
+	r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb,
+			     host, &dma_ch);
+	if (r != 0) {
+		dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
+		return r;
+	}
+	host->dma_ch = dma_ch;
+	host->dma_is_read = is_read;
+
+	return 0;
+}
+
+static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+	u16 reg;
+
+	reg = OMAP_MMC_READ(host, SDIO);
+	reg &= ~(1 << 5);
+	OMAP_MMC_WRITE(host, SDIO, reg);
+	/* Set maximum timeout */
+	OMAP_MMC_WRITE(host, CTO, 0xff);
+}
+
+static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+	int timeout;
+	u16 reg;
+
+	/* Convert ns to clock cycles by assuming 20MHz frequency
+	 * 1 cycle at 20MHz = 500 ns
+	 */
+	timeout = req->data->timeout_clks + req->data->timeout_ns / 500;
+
+	/* Check if we need to use timeout multiplier register */
+	reg = OMAP_MMC_READ(host, SDIO);
+	if (timeout > 0xffff) {
+		reg |= (1 << 5);
+		timeout /= 1024;
+	} else
+		reg &= ~(1 << 5);
+	OMAP_MMC_WRITE(host, SDIO, reg);
+	OMAP_MMC_WRITE(host, DTO, timeout);
+}
+
+static void
+mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+{
+	struct mmc_data *data = req->data;
+	int i, use_dma, block_size;
+	unsigned sg_len;
+
+	host->data = data;
+	if (data == NULL) {
+		OMAP_MMC_WRITE(host, BLEN, 0);
+		OMAP_MMC_WRITE(host, NBLK, 0);
+		OMAP_MMC_WRITE(host, BUF, 0);
+		host->dma_in_use = 0;
+		set_cmd_timeout(host, req);
+		return;
+	}
+
+	block_size = data->blksz;
+
+	OMAP_MMC_WRITE(host, NBLK, data->blocks - 1);
+	OMAP_MMC_WRITE(host, BLEN, block_size - 1);
+	set_data_timeout(host, req);
+
+	/* cope with calling layer confusion; it issues "single
+	 * block" writes using multi-block scatterlists.
+	 */
+	sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+	/* Only do DMA for entire blocks */
+	use_dma = host->use_dma;
+	if (use_dma) {
+		for (i = 0; i < sg_len; i++) {
+			if ((data->sg[i].length % block_size) != 0) {
+				use_dma = 0;
+				break;
+			}
+		}
+	}
+
+	host->sg_idx = 0;
+	if (use_dma) {
+		if (mmc_omap_get_dma_channel(host, data) == 0) {
+			enum dma_data_direction dma_data_dir;
+
+			if (data->flags & MMC_DATA_WRITE)
+				dma_data_dir = DMA_TO_DEVICE;
+			else
+				dma_data_dir = DMA_FROM_DEVICE;
+
+			host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+						sg_len, dma_data_dir);
+			host->total_bytes_left = 0;
+			mmc_omap_prepare_dma(host, req->data);
+			host->brs_received = 0;
+			host->dma_done = 0;
+			host->dma_in_use = 1;
+		} else
+			use_dma = 0;
+	}
+
+	/* Revert to PIO? */
+	if (!use_dma) {
+		OMAP_MMC_WRITE(host, BUF, 0x1f1f);
+		host->total_bytes_left = data->blocks * block_size;
+		host->sg_len = sg_len;
+		mmc_omap_sg_to_buf(host);
+		host->dma_in_use = 0;
+	}
+}
+
+static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = req;
+
+	/* only touch fifo AFTER the controller readies it */
+	mmc_omap_prepare_data(host, req);
+	mmc_omap_start_command(host, req->cmd);
+	if (host->dma_in_use)
+		omap_start_dma(host->dma_ch);
+}
+
+static void innovator_fpga_socket_power(int on)
+{
+#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX)
+	if (on) {
+		fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3),
+		     OMAP1510_FPGA_POWER);
+	} else {
+		fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3),
+		     OMAP1510_FPGA_POWER);
+	}
+#endif
+}
+
+/*
+ * Turn the socket power on/off. Innovator uses FPGA, most boards
+ * probably use GPIO.
+ */
+static void mmc_omap_power(struct mmc_omap_host *host, int on)
+{
+	if (on) {
+		if (machine_is_omap_innovator())
+			innovator_fpga_socket_power(1);
+		else if (machine_is_omap_h2())
+			tps65010_set_gpio_out_value(GPIO3, HIGH);
+		else if (machine_is_omap_h3())
+			/* GPIO 4 of TPS65010 sends SD_EN signal */
+			tps65010_set_gpio_out_value(GPIO4, HIGH);
+		else if (cpu_is_omap24xx()) {
+			u16 reg = OMAP_MMC_READ(host, CON);
+			OMAP_MMC_WRITE(host, CON, reg | (1 << 11));
+		} else
+			if (host->power_pin >= 0)
+				omap_set_gpio_dataout(host->power_pin, 1);
+	} else {
+		if (machine_is_omap_innovator())
+			innovator_fpga_socket_power(0);
+		else if (machine_is_omap_h2())
+			tps65010_set_gpio_out_value(GPIO3, LOW);
+		else if (machine_is_omap_h3())
+			tps65010_set_gpio_out_value(GPIO4, LOW);
+		else if (cpu_is_omap24xx()) {
+			u16 reg = OMAP_MMC_READ(host, CON);
+			OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11));
+		} else
+			if (host->power_pin >= 0)
+				omap_set_gpio_dataout(host->power_pin, 0);
+	}
+}
+
+static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+	int dsor;
+	int realclock, i;
+
+	realclock = ios->clock;
+
+	if (ios->clock == 0)
+		dsor = 0;
+	else {
+		int func_clk_rate = clk_get_rate(host->fclk);
+
+		dsor = func_clk_rate / realclock;
+		if (dsor < 1)
+			dsor = 1;
+
+		if (func_clk_rate / dsor > realclock)
+			dsor++;
+
+		if (dsor > 250)
+			dsor = 250;
+		dsor++;
+
+		if (ios->bus_width == MMC_BUS_WIDTH_4)
+			dsor |= 1 << 15;
+	}
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		mmc_omap_power(host, 0);
+		break;
+	case MMC_POWER_UP:
+	case MMC_POWER_ON:
+		mmc_omap_power(host, 1);
+		dsor |= 1 << 11;
+		break;
+	}
+
+	host->bus_mode = ios->bus_mode;
+	host->hw_bus_mode = host->bus_mode;
+
+	clk_enable(host->fclk);
+
+	/* On insanely high arm_per frequencies something sometimes
+	 * goes somehow out of sync, and the POW bit is not being set,
+	 * which results in the while loop below getting stuck.
+	 * Writing to the CON register twice seems to do the trick. */
+	for (i = 0; i < 2; i++)
+		OMAP_MMC_WRITE(host, CON, dsor);
+	if (ios->power_mode == MMC_POWER_UP) {
+		/* Send clock cycles, poll completion */
+		OMAP_MMC_WRITE(host, IE, 0);
+		OMAP_MMC_WRITE(host, STAT, 0xffff);
+		OMAP_MMC_WRITE(host, CMD, 1 << 7);
+		while ((OMAP_MMC_READ(host, STAT) & 1) == 0);
+		OMAP_MMC_WRITE(host, STAT, 1);
+	}
+	clk_disable(host->fclk);
+}
+
+static int mmc_omap_get_ro(struct mmc_host *mmc)
+{
+	struct mmc_omap_host *host = mmc_priv(mmc);
+
+	return host->wp_pin && omap_get_gpio_datain(host->wp_pin);
+}
+
+static const struct mmc_host_ops mmc_omap_ops = {
+	.request	= mmc_omap_request,
+	.set_ios	= mmc_omap_set_ios,
+	.get_ro		= mmc_omap_get_ro,
+};
+
+static int __init mmc_omap_probe(struct platform_device *pdev)
+{
+	struct omap_mmc_conf *minfo = pdev->dev.platform_data;
+	struct mmc_host *mmc;
+	struct mmc_omap_host *host = NULL;
+	struct resource *res;
+	int ret = 0;
+	int irq;
+
+	if (minfo == NULL) {
+		dev_err(&pdev->dev, "platform data missing\n");
+		return -ENXIO;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (res == NULL || irq < 0)
+		return -ENXIO;
+
+	res = request_mem_region(res->start, res->end - res->start + 1,
+			         pdev->name);
+	if (res == NULL)
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
+	if (mmc == NULL) {
+		ret = -ENOMEM;
+		goto err_free_mem_region;
+	}
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+
+	spin_lock_init(&host->dma_lock);
+	init_timer(&host->dma_timer);
+	host->dma_timer.function = mmc_omap_dma_timer;
+	host->dma_timer.data = (unsigned long) host;
+
+	host->id = pdev->id;
+	host->mem_res = res;
+	host->irq = irq;
+
+	if (cpu_is_omap24xx()) {
+		host->iclk = clk_get(&pdev->dev, "mmc_ick");
+		if (IS_ERR(host->iclk))
+			goto err_free_mmc_host;
+		clk_enable(host->iclk);
+	}
+
+	if (!cpu_is_omap24xx())
+		host->fclk = clk_get(&pdev->dev, "mmc_ck");
+	else
+		host->fclk = clk_get(&pdev->dev, "mmc_fck");
+
+	if (IS_ERR(host->fclk)) {
+		ret = PTR_ERR(host->fclk);
+		goto err_free_iclk;
+	}
+
+	/* REVISIT:
+	 * Also, use minfo->cover to decide how to manage
+	 * the card detect sensing.
+	 */
+	host->power_pin = minfo->power_pin;
+	host->switch_pin = minfo->switch_pin;
+	host->wp_pin = minfo->wp_pin;
+	host->use_dma = 1;
+	host->dma_ch = -1;
+
+	host->irq = irq;
+	host->phys_base = host->mem_res->start;
+	host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base);
+
+	mmc->ops = &mmc_omap_ops;
+	mmc->f_min = 400000;
+	mmc->f_max = 24000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
+
+	if (minfo->wire4)
+		 mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+	/* Use scatterlist DMA to reduce per-transfer costs.
+	 * NOTE max_seg_size assumption that small blocks aren't
+	 * normally used (except e.g. for reading SD registers).
+	 */
+	mmc->max_phys_segs = 32;
+	mmc->max_hw_segs = 32;
+	mmc->max_blk_size = 2048;	/* BLEN is 11 bits (+1) */
+	mmc->max_blk_count = 2048;	/* NBLK is 11 bits (+1) */
+	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_seg_size = mmc->max_req_size;
+
+	if (host->power_pin >= 0) {
+		if ((ret = omap_request_gpio(host->power_pin)) != 0) {
+			dev_err(mmc_dev(host->mmc),
+				"Unable to get GPIO pin for MMC power\n");
+			goto err_free_fclk;
+		}
+		omap_set_gpio_direction(host->power_pin, 0);
+	}
+
+	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
+	if (ret)
+		goto err_free_power_gpio;
+
+	host->dev = &pdev->dev;
+	platform_set_drvdata(pdev, host);
+
+	if (host->switch_pin >= 0) {
+		INIT_WORK(&host->switch_work, mmc_omap_switch_handler);
+		init_timer(&host->switch_timer);
+		host->switch_timer.function = mmc_omap_switch_timer;
+		host->switch_timer.data = (unsigned long) host;
+		if (omap_request_gpio(host->switch_pin) != 0) {
+			dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n");
+			host->switch_pin = -1;
+			goto no_switch;
+		}
+
+		omap_set_gpio_direction(host->switch_pin, 1);
+		ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin),
+				  mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host);
+		if (ret) {
+			dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n");
+			omap_free_gpio(host->switch_pin);
+			host->switch_pin = -1;
+			goto no_switch;
+		}
+		ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
+		if (ret == 0) {
+			ret = device_create_file(&pdev->dev, &dev_attr_enable_poll);
+			if (ret != 0)
+				device_remove_file(&pdev->dev, &dev_attr_cover_switch);
+		}
+		if (ret) {
+			dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n");
+			free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+			omap_free_gpio(host->switch_pin);
+			host->switch_pin = -1;
+			goto no_switch;
+		}
+		if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host))
+			schedule_work(&host->switch_work);
+	}
+
+	mmc_add_host(mmc);
+
+	return 0;
+
+no_switch:
+	/* FIXME: Free other resources too. */
+	if (host) {
+		if (host->iclk && !IS_ERR(host->iclk))
+			clk_put(host->iclk);
+		if (host->fclk && !IS_ERR(host->fclk))
+			clk_put(host->fclk);
+		mmc_free_host(host->mmc);
+	}
+err_free_power_gpio:
+	if (host->power_pin >= 0)
+		omap_free_gpio(host->power_pin);
+err_free_fclk:
+	clk_put(host->fclk);
+err_free_iclk:
+	if (host->iclk != NULL) {
+		clk_disable(host->iclk);
+		clk_put(host->iclk);
+	}
+err_free_mmc_host:
+	mmc_free_host(host->mmc);
+err_free_mem_region:
+	release_mem_region(res->start, res->end - res->start + 1);
+	return ret;
+}
+
+static int mmc_omap_remove(struct platform_device *pdev)
+{
+	struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	BUG_ON(host == NULL);
+
+	mmc_remove_host(host->mmc);
+	free_irq(host->irq, host);
+
+	if (host->power_pin >= 0)
+		omap_free_gpio(host->power_pin);
+	if (host->switch_pin >= 0) {
+		device_remove_file(&pdev->dev, &dev_attr_enable_poll);
+		device_remove_file(&pdev->dev, &dev_attr_cover_switch);
+		free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+		omap_free_gpio(host->switch_pin);
+		host->switch_pin = -1;
+		del_timer_sync(&host->switch_timer);
+		flush_scheduled_work();
+	}
+	if (host->iclk && !IS_ERR(host->iclk))
+		clk_put(host->iclk);
+	if (host->fclk && !IS_ERR(host->fclk))
+		clk_put(host->fclk);
+
+	release_mem_region(pdev->resource[0].start,
+			   pdev->resource[0].end - pdev->resource[0].start + 1);
+
+	mmc_free_host(host->mmc);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+	int ret = 0;
+	struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+	if (host && host->suspended)
+		return 0;
+
+	if (host) {
+		ret = mmc_suspend_host(host->mmc, mesg);
+		if (ret == 0)
+			host->suspended = 1;
+	}
+	return ret;
+}
+
+static int mmc_omap_resume(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+	if (host && !host->suspended)
+		return 0;
+
+	if (host) {
+		ret = mmc_resume_host(host->mmc);
+		if (ret == 0)
+			host->suspended = 0;
+	}
+
+	return ret;
+}
+#else
+#define mmc_omap_suspend	NULL
+#define mmc_omap_resume		NULL
+#endif
+
+static struct platform_driver mmc_omap_driver = {
+	.probe		= mmc_omap_probe,
+	.remove		= mmc_omap_remove,
+	.suspend	= mmc_omap_suspend,
+	.resume		= mmc_omap_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init mmc_omap_init(void)
+{
+	return platform_driver_register(&mmc_omap_driver);
+}
+
+static void __exit mmc_omap_exit(void)
+{
+	platform_driver_unregister(&mmc_omap_driver);
+}
+
+module_init(mmc_omap_init);
+module_exit(mmc_omap_exit);
+
+MODULE_DESCRIPTION("OMAP Multimedia Card driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_AUTHOR("Juha Yrjölä");
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
new file mode 100644
index 0000000..a98ff98
--- /dev/null
+++ b/drivers/mmc/host/pxamci.c
@@ -0,0 +1,616 @@
+/*
+ *  linux/drivers/mmc/pxa.c - PXA MMCI driver
+ *
+ *  Copyright (C) 2003 Russell King, 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.
+ *
+ *  This hardware is really sick:
+ *   - No way to clear interrupts.
+ *   - Have to turn off the clock whenever we touch the device.
+ *   - Doesn't tell you how many data blocks were transferred.
+ *  Yuck!
+ *
+ *	1 and 3 byte data transfers not supported
+ *	max block length up to 1023
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <asm/sizes.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/mmc.h>
+
+#include "pxamci.h"
+
+#define DRIVER_NAME	"pxa2xx-mci"
+
+#define NR_SG	1
+
+struct pxamci_host {
+	struct mmc_host		*mmc;
+	spinlock_t		lock;
+	struct resource		*res;
+	void __iomem		*base;
+	int			irq;
+	int			dma;
+	unsigned int		clkrt;
+	unsigned int		cmdat;
+	unsigned int		imask;
+	unsigned int		power_mode;
+	struct pxamci_platform_data *pdata;
+
+	struct mmc_request	*mrq;
+	struct mmc_command	*cmd;
+	struct mmc_data		*data;
+
+	dma_addr_t		sg_dma;
+	struct pxa_dma_desc	*sg_cpu;
+	unsigned int		dma_len;
+
+	unsigned int		dma_dir;
+};
+
+static void pxamci_stop_clock(struct pxamci_host *host)
+{
+	if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+		unsigned long timeout = 10000;
+		unsigned int v;
+
+		writel(STOP_CLOCK, host->base + MMC_STRPCL);
+
+		do {
+			v = readl(host->base + MMC_STAT);
+			if (!(v & STAT_CLK_EN))
+				break;
+			udelay(1);
+		} while (timeout--);
+
+		if (v & STAT_CLK_EN)
+			dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
+	}
+}
+
+static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask &= ~mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	host->imask |= mask;
+	writel(host->imask, host->base + MMC_I_MASK);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
+{
+	unsigned int nob = data->blocks;
+	unsigned long long clks;
+	unsigned int timeout;
+	u32 dcmd;
+	int i;
+
+	host->data = data;
+
+	if (data->flags & MMC_DATA_STREAM)
+		nob = 0xffff;
+
+	writel(nob, host->base + MMC_NOB);
+	writel(data->blksz, host->base + MMC_BLKLEN);
+
+	clks = (unsigned long long)data->timeout_ns * CLOCKRATE;
+	do_div(clks, 1000000000UL);
+	timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
+	writel((timeout + 255) / 256, host->base + MMC_RDTO);
+
+	if (data->flags & MMC_DATA_READ) {
+		host->dma_dir = DMA_FROM_DEVICE;
+		dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
+		DRCMRTXMMC = 0;
+		DRCMRRXMMC = host->dma | DRCMR_MAPVLD;
+	} else {
+		host->dma_dir = DMA_TO_DEVICE;
+		dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
+		DRCMRRXMMC = 0;
+		DRCMRTXMMC = host->dma | DRCMR_MAPVLD;
+	}
+
+	dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
+
+	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+				   host->dma_dir);
+
+	for (i = 0; i < host->dma_len; i++) {
+		if (data->flags & MMC_DATA_READ) {
+			host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
+			host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
+		} else {
+			host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
+			host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
+		}
+		host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
+		host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+					sizeof(struct pxa_dma_desc);
+	}
+	host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
+	wmb();
+
+	DDADR(host->dma) = host->sg_dma;
+	DCSR(host->dma) = DCSR_RUN;
+}
+
+static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+	WARN_ON(host->cmd != NULL);
+	host->cmd = cmd;
+
+	if (cmd->flags & MMC_RSP_BUSY)
+		cmdat |= CMDAT_BUSY;
+
+#define RSP_TYPE(x)	((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+	switch (RSP_TYPE(mmc_resp_type(cmd))) {
+	case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
+		cmdat |= CMDAT_RESP_SHORT;
+		break;
+	case RSP_TYPE(MMC_RSP_R3):
+		cmdat |= CMDAT_RESP_R3;
+		break;
+	case RSP_TYPE(MMC_RSP_R2):
+		cmdat |= CMDAT_RESP_R2;
+		break;
+	default:
+		break;
+	}
+
+	writel(cmd->opcode, host->base + MMC_CMD);
+	writel(cmd->arg >> 16, host->base + MMC_ARGH);
+	writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
+	writel(cmdat, host->base + MMC_CMDAT);
+	writel(host->clkrt, host->base + MMC_CLKRT);
+
+	writel(START_CLOCK, host->base + MMC_STRPCL);
+
+	pxamci_enable_irq(host, END_CMD_RES);
+}
+
+static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
+{
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+	mmc_request_done(host->mmc, mrq);
+}
+
+static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_command *cmd = host->cmd;
+	int i;
+	u32 v;
+
+	if (!cmd)
+		return 0;
+
+	host->cmd = NULL;
+
+	/*
+	 * Did I mention this is Sick.  We always need to
+	 * discard the upper 8 bits of the first 16-bit word.
+	 */
+	v = readl(host->base + MMC_RES) & 0xffff;
+	for (i = 0; i < 4; i++) {
+		u32 w1 = readl(host->base + MMC_RES) & 0xffff;
+		u32 w2 = readl(host->base + MMC_RES) & 0xffff;
+		cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
+		v = w2;
+	}
+
+	if (stat & STAT_TIME_OUT_RESPONSE) {
+		cmd->error = MMC_ERR_TIMEOUT;
+	} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+#ifdef CONFIG_PXA27x
+		/*
+		 * workaround for erratum #42:
+		 * Intel PXA27x Family Processor Specification Update Rev 001
+		 */
+		if (cmd->opcode == MMC_ALL_SEND_CID ||
+		    cmd->opcode == MMC_SEND_CSD ||
+		    cmd->opcode == MMC_SEND_CID) {
+			/* a bogus CRC error can appear if the msb of
+			   the 15 byte response is a one */
+			if ((cmd->resp[0] & 0x80000000) == 0)
+				cmd->error = MMC_ERR_BADCRC;
+		} else {
+			pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode);
+		}
+#else
+		cmd->error = MMC_ERR_BADCRC;
+#endif
+	}
+
+	pxamci_disable_irq(host, END_CMD_RES);
+	if (host->data && cmd->error == MMC_ERR_NONE) {
+		pxamci_enable_irq(host, DATA_TRAN_DONE);
+	} else {
+		pxamci_finish_request(host, host->mrq);
+	}
+
+	return 1;
+}
+
+static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
+{
+	struct mmc_data *data = host->data;
+
+	if (!data)
+		return 0;
+
+	DCSR(host->dma) = 0;
+	dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+		     host->dma_dir);
+
+	if (stat & STAT_READ_TIME_OUT)
+		data->error = MMC_ERR_TIMEOUT;
+	else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
+		data->error = MMC_ERR_BADCRC;
+
+	/*
+	 * There appears to be a hardware design bug here.  There seems to
+	 * be no way to find out how much data was transferred to the card.
+	 * This means that if there was an error on any block, we mark all
+	 * data blocks as being in error.
+	 */
+	if (data->error == MMC_ERR_NONE)
+		data->bytes_xfered = data->blocks * data->blksz;
+	else
+		data->bytes_xfered = 0;
+
+	pxamci_disable_irq(host, DATA_TRAN_DONE);
+
+	host->data = NULL;
+	if (host->mrq->stop) {
+		pxamci_stop_clock(host);
+		pxamci_start_cmd(host, host->mrq->stop, 0);
+	} else {
+		pxamci_finish_request(host, host->mrq);
+	}
+
+	return 1;
+}
+
+static irqreturn_t pxamci_irq(int irq, void *devid)
+{
+	struct pxamci_host *host = devid;
+	unsigned int ireg;
+	int handled = 0;
+
+	ireg = readl(host->base + MMC_I_REG);
+
+	if (ireg) {
+		unsigned stat = readl(host->base + MMC_STAT);
+
+		pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat);
+
+		if (ireg & END_CMD_RES)
+			handled |= pxamci_cmd_done(host, stat);
+		if (ireg & DATA_TRAN_DONE)
+			handled |= pxamci_data_done(host, stat);
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct pxamci_host *host = mmc_priv(mmc);
+	unsigned int cmdat;
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = mrq;
+
+	pxamci_stop_clock(host);
+
+	cmdat = host->cmdat;
+	host->cmdat &= ~CMDAT_INIT;
+
+	if (mrq->data) {
+		pxamci_setup_data(host, mrq->data);
+
+		cmdat &= ~CMDAT_BUSY;
+		cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
+		if (mrq->data->flags & MMC_DATA_WRITE)
+			cmdat |= CMDAT_WRITE;
+
+		if (mrq->data->flags & MMC_DATA_STREAM)
+			cmdat |= CMDAT_STREAM;
+	}
+
+	pxamci_start_cmd(host, mrq->cmd, cmdat);
+}
+
+static int pxamci_get_ro(struct mmc_host *mmc)
+{
+	struct pxamci_host *host = mmc_priv(mmc);
+
+	if (host->pdata && host->pdata->get_ro)
+		return host->pdata->get_ro(mmc_dev(mmc));
+	/* Host doesn't support read only detection so assume writeable */
+	return 0;
+}
+
+static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct pxamci_host *host = mmc_priv(mmc);
+
+	if (ios->clock) {
+		unsigned int clk = CLOCKRATE / ios->clock;
+		if (CLOCKRATE / clk > ios->clock)
+			clk <<= 1;
+		host->clkrt = fls(clk) - 1;
+		pxa_set_cken(CKEN12_MMC, 1);
+
+		/*
+		 * we write clkrt on the next command
+		 */
+	} else {
+		pxamci_stop_clock(host);
+		pxa_set_cken(CKEN12_MMC, 0);
+	}
+
+	if (host->power_mode != ios->power_mode) {
+		host->power_mode = ios->power_mode;
+
+		if (host->pdata && host->pdata->setpower)
+			host->pdata->setpower(mmc_dev(mmc), ios->vdd);
+
+		if (ios->power_mode == MMC_POWER_ON)
+			host->cmdat |= CMDAT_INIT;
+	}
+
+	pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
+		 host->clkrt, host->cmdat);
+}
+
+static const struct mmc_host_ops pxamci_ops = {
+	.request	= pxamci_request,
+	.get_ro		= pxamci_get_ro,
+	.set_ios	= pxamci_set_ios,
+};
+
+static void pxamci_dma_irq(int dma, void *devid)
+{
+	printk(KERN_ERR "DMA%d: IRQ???\n", dma);
+	DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
+}
+
+static irqreturn_t pxamci_detect_irq(int irq, void *devid)
+{
+	struct pxamci_host *host = mmc_priv(devid);
+
+	mmc_detect_change(devid, host->pdata->detect_delay);
+	return IRQ_HANDLED;
+}
+
+static int pxamci_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc;
+	struct pxamci_host *host = NULL;
+	struct resource *r;
+	int ret, irq;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!r || irq < 0)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
+	if (!r)
+		return -EBUSY;
+
+	mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	mmc->ops = &pxamci_ops;
+	mmc->f_min = CLOCKRATE_MIN;
+	mmc->f_max = CLOCKRATE_MAX;
+
+	/*
+	 * We can do SG-DMA, but we don't because we never know how much
+	 * data we successfully wrote to the card.
+	 */
+	mmc->max_phys_segs = NR_SG;
+
+	/*
+	 * Our hardware DMA can handle a maximum of one page per SG entry.
+	 */
+	mmc->max_seg_size = PAGE_SIZE;
+
+	/*
+	 * Block length register is 10 bits.
+	 */
+	mmc->max_blk_size = 1023;
+
+	/*
+	 * Block count register is 16 bits.
+	 */
+	mmc->max_blk_count = 65535;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->dma = -1;
+	host->pdata = pdev->dev.platform_data;
+	mmc->ocr_avail = host->pdata ?
+			 host->pdata->ocr_mask :
+			 MMC_VDD_32_33|MMC_VDD_33_34;
+
+	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spin_lock_init(&host->lock);
+	host->res = r;
+	host->irq = irq;
+	host->imask = MMC_I_MASK_ALL;
+
+	host->base = ioremap(r->start, SZ_4K);
+	if (!host->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Ensure that the host controller is shut down, and setup
+	 * with our defaults.
+	 */
+	pxamci_stop_clock(host);
+	writel(0, host->base + MMC_SPI);
+	writel(64, host->base + MMC_RESTO);
+	writel(host->imask, host->base + MMC_I_MASK);
+
+	host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
+				    pxamci_dma_irq, host);
+	if (host->dma < 0) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
+	if (ret)
+		goto out;
+
+	platform_set_drvdata(pdev, mmc);
+
+	if (host->pdata && host->pdata->init)
+		host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
+
+	mmc_add_host(mmc);
+
+	return 0;
+
+ out:
+	if (host) {
+		if (host->dma >= 0)
+			pxa_free_dma(host->dma);
+		if (host->base)
+			iounmap(host->base);
+		if (host->sg_cpu)
+			dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+	}
+	if (mmc)
+		mmc_free_host(mmc);
+	release_resource(r);
+	return ret;
+}
+
+static int pxamci_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (mmc) {
+		struct pxamci_host *host = mmc_priv(mmc);
+
+		if (host->pdata && host->pdata->exit)
+			host->pdata->exit(&pdev->dev, mmc);
+
+		mmc_remove_host(mmc);
+
+		pxamci_stop_clock(host);
+		writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+		       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
+		       host->base + MMC_I_MASK);
+
+		DRCMRRXMMC = 0;
+		DRCMRTXMMC = 0;
+
+		free_irq(host->irq, host);
+		pxa_free_dma(host->dma);
+		iounmap(host->base);
+		dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+		release_resource(host->res);
+
+		mmc_free_host(mmc);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxamci_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc)
+		ret = mmc_suspend_host(mmc, state);
+
+	return ret;
+}
+
+static int pxamci_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (mmc)
+		ret = mmc_resume_host(mmc);
+
+	return ret;
+}
+#else
+#define pxamci_suspend	NULL
+#define pxamci_resume	NULL
+#endif
+
+static struct platform_driver pxamci_driver = {
+	.probe		= pxamci_probe,
+	.remove		= pxamci_remove,
+	.suspend	= pxamci_suspend,
+	.resume		= pxamci_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init pxamci_init(void)
+{
+	return platform_driver_register(&pxamci_driver);
+}
+
+static void __exit pxamci_exit(void)
+{
+	platform_driver_unregister(&pxamci_driver);
+}
+
+module_init(pxamci_init);
+module_exit(pxamci_exit);
+
+MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/pxamci.h b/drivers/mmc/host/pxamci.h
new file mode 100644
index 0000000..1b16322
--- /dev/null
+++ b/drivers/mmc/host/pxamci.h
@@ -0,0 +1,124 @@
+#undef MMC_STRPCL
+#undef MMC_STAT
+#undef MMC_CLKRT
+#undef MMC_SPI
+#undef MMC_CMDAT
+#undef MMC_RESTO
+#undef MMC_RDTO
+#undef MMC_BLKLEN
+#undef MMC_NOB
+#undef MMC_PRTBUF
+#undef MMC_I_MASK
+#undef END_CMD_RES
+#undef PRG_DONE
+#undef DATA_TRAN_DONE
+#undef MMC_I_REG
+#undef MMC_CMD
+#undef MMC_ARGH
+#undef MMC_ARGL
+#undef MMC_RES
+#undef MMC_RXFIFO
+#undef MMC_TXFIFO
+
+#define MMC_STRPCL	0x0000
+#define STOP_CLOCK		(1 << 0)
+#define START_CLOCK		(2 << 0)
+
+#define MMC_STAT	0x0004
+#define STAT_END_CMD_RES		(1 << 13)
+#define STAT_PRG_DONE			(1 << 12)
+#define STAT_DATA_TRAN_DONE		(1 << 11)
+#define STAT_CLK_EN			(1 << 8)
+#define STAT_RECV_FIFO_FULL		(1 << 7)
+#define STAT_XMIT_FIFO_EMPTY		(1 << 6)
+#define STAT_RES_CRC_ERR		(1 << 5)
+#define STAT_SPI_READ_ERROR_TOKEN	(1 << 4)
+#define STAT_CRC_READ_ERROR		(1 << 3)
+#define STAT_CRC_WRITE_ERROR		(1 << 2)
+#define STAT_TIME_OUT_RESPONSE		(1 << 1)
+#define STAT_READ_TIME_OUT		(1 << 0)
+
+#define MMC_CLKRT	0x0008		/* 3 bit */
+
+#define MMC_SPI		0x000c
+#define SPI_CS_ADDRESS		(1 << 3)
+#define SPI_CS_EN		(1 << 2)
+#define CRC_ON			(1 << 1)
+#define SPI_EN			(1 << 0)
+
+#define MMC_CMDAT	0x0010
+#define CMDAT_DMAEN		(1 << 7)
+#define CMDAT_INIT		(1 << 6)
+#define CMDAT_BUSY		(1 << 5)
+#define CMDAT_STREAM		(1 << 4)	/* 1 = stream */
+#define CMDAT_WRITE		(1 << 3)	/* 1 = write */
+#define CMDAT_DATAEN		(1 << 2)
+#define CMDAT_RESP_NONE		(0 << 0)
+#define CMDAT_RESP_SHORT	(1 << 0)
+#define CMDAT_RESP_R2		(2 << 0)
+#define CMDAT_RESP_R3		(3 << 0)
+
+#define MMC_RESTO	0x0014	/* 7 bit */
+
+#define MMC_RDTO	0x0018	/* 16 bit */
+
+#define MMC_BLKLEN	0x001c	/* 10 bit */
+
+#define MMC_NOB		0x0020	/* 16 bit */
+
+#define MMC_PRTBUF	0x0024
+#define BUF_PART_FULL		(1 << 0)
+
+#define MMC_I_MASK	0x0028
+
+/*PXA27x MMC interrupts*/
+#define SDIO_SUSPEND_ACK  	(1 << 12)
+#define SDIO_INT          	(1 << 11)
+#define RD_STALLED        	(1 << 10)
+#define RES_ERR           	(1 << 9)
+#define DAT_ERR           	(1 << 8)
+#define TINT              	(1 << 7)
+
+/*PXA2xx MMC interrupts*/
+#define TXFIFO_WR_REQ		(1 << 6)
+#define RXFIFO_RD_REQ		(1 << 5)
+#define CLK_IS_OFF		(1 << 4)
+#define STOP_CMD		(1 << 3)
+#define END_CMD_RES		(1 << 2)
+#define PRG_DONE		(1 << 1)
+#define DATA_TRAN_DONE		(1 << 0)
+
+#ifdef CONFIG_PXA27x
+#define MMC_I_MASK_ALL          0x00001fff
+#else
+#define MMC_I_MASK_ALL          0x0000007f
+#endif
+
+#define MMC_I_REG	0x002c
+/* same as MMC_I_MASK */
+
+#define MMC_CMD		0x0030
+
+#define MMC_ARGH	0x0034	/* 16 bit */
+
+#define MMC_ARGL	0x0038	/* 16 bit */
+
+#define MMC_RES		0x003c	/* 16 bit */
+
+#define MMC_RXFIFO	0x0040	/* 8 bit */
+
+#define MMC_TXFIFO	0x0044	/* 8 bit */
+
+/*
+ * The base MMC clock rate
+ */
+#ifdef CONFIG_PXA27x
+#define CLOCKRATE_MIN	304688
+#define CLOCKRATE_MAX	19500000
+#else
+#define CLOCKRATE_MIN	312500
+#define CLOCKRATE_MAX	20000000
+#endif
+
+#define CLOCKRATE	CLOCKRATE_MAX
+
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
new file mode 100644
index 0000000..579142a
--- /dev/null
+++ b/drivers/mmc/host/sdhci.c
@@ -0,0 +1,1539 @@
+/*
+ *  linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver
+ *
+ *  Copyright (C) 2005-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/scatterlist.h>
+
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci"
+
+#define DBG(f, x...) \
+	pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
+
+static unsigned int debug_nodma = 0;
+static unsigned int debug_forcedma = 0;
+static unsigned int debug_quirks = 0;
+
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET			(1<<0)
+#define SDHCI_QUIRK_FORCE_DMA				(1<<1)
+/* Controller doesn't like some resets when there is no card inserted. */
+#define SDHCI_QUIRK_NO_CARD_NO_RESET			(1<<2)
+#define SDHCI_QUIRK_SINGLE_POWER_WRITE			(1<<3)
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+	{
+		.vendor		= PCI_VENDOR_ID_RICOH,
+		.device		= PCI_DEVICE_ID_RICOH_R5C822,
+		.subvendor	= PCI_VENDOR_ID_IBM,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+				  SDHCI_QUIRK_FORCE_DMA,
+	},
+
+	{
+		.vendor		= PCI_VENDOR_ID_RICOH,
+		.device		= PCI_DEVICE_ID_RICOH_R5C822,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SDHCI_QUIRK_FORCE_DMA |
+				  SDHCI_QUIRK_NO_CARD_NO_RESET,
+	},
+
+	{
+		.vendor		= PCI_VENDOR_ID_TI,
+		.device		= PCI_DEVICE_ID_TI_XX21_XX11_SD,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SDHCI_QUIRK_FORCE_DMA,
+	},
+
+	{
+		.vendor		= PCI_VENDOR_ID_ENE,
+		.device		= PCI_DEVICE_ID_ENE_CB712_SD,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SDHCI_QUIRK_SINGLE_POWER_WRITE,
+	},
+
+	{	/* Generic SD host controller */
+		PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+	},
+
+	{ /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
+static void sdhci_finish_data(struct sdhci_host *);
+
+static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
+static void sdhci_finish_command(struct sdhci_host *);
+
+static void sdhci_dumpregs(struct sdhci_host *host)
+{
+	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));
+	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));
+	printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+		readl(host->ioaddr + SDHCI_ARGUMENT),
+		readw(host->ioaddr + 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));
+	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));
+	printk(KERN_DEBUG DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
+		readb(host->ioaddr + SDHCI_WALK_UP_CONTROL),
+		readw(host->ioaddr + 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));
+	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));
+	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));
+	printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Max curr: 0x%08x\n",
+		readl(host->ioaddr + SDHCI_CAPABILITIES),
+		readl(host->ioaddr + SDHCI_MAX_CURRENT));
+
+	printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Low level functions                                                       *
+ *                                                                           *
+\*****************************************************************************/
+
+static void sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+	unsigned long timeout;
+
+	if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+		if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+			SDHCI_CARD_PRESENT))
+			return;
+	}
+
+	writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+
+	if (mask & SDHCI_RESET_ALL)
+		host->clock = 0;
+
+	/* Wait max 100 ms */
+	timeout = 100;
+
+	/* hw clears the bit when it's done */
+	while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+		if (timeout == 0) {
+			printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+				mmc_hostname(host->mmc), (int)mask);
+			sdhci_dumpregs(host);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+}
+
+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_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;
+
+	writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
+	writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_activate_led(struct sdhci_host *host)
+{
+	u8 ctrl;
+
+	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+	ctrl |= SDHCI_CTRL_LED;
+	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+static void sdhci_deactivate_led(struct sdhci_host *host)
+{
+	u8 ctrl;
+
+	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+	ctrl &= ~SDHCI_CTRL_LED;
+	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Core functions                                                            *
+ *                                                                           *
+\*****************************************************************************/
+
+static inline char* sdhci_sg_to_buffer(struct sdhci_host* host)
+{
+	return page_address(host->cur_sg->page) + host->cur_sg->offset;
+}
+
+static inline int sdhci_next_sg(struct sdhci_host* host)
+{
+	/*
+	 * Skip to next SG entry.
+	 */
+	host->cur_sg++;
+	host->num_sg--;
+
+	/*
+	 * Any entries left?
+	 */
+	if (host->num_sg > 0) {
+		host->offset = 0;
+		host->remain = host->cur_sg->length;
+	}
+
+	return host->num_sg;
+}
+
+static void sdhci_read_block_pio(struct sdhci_host *host)
+{
+	int blksize, chunk_remain;
+	u32 data;
+	char *buffer;
+	int size;
+
+	DBG("PIO reading\n");
+
+	blksize = host->data->blksz;
+	chunk_remain = 0;
+	data = 0;
+
+	buffer = sdhci_sg_to_buffer(host) + host->offset;
+
+	while (blksize) {
+		if (chunk_remain == 0) {
+			data = readl(host->ioaddr + SDHCI_BUFFER);
+			chunk_remain = min(blksize, 4);
+		}
+
+		size = min(host->remain, chunk_remain);
+
+		chunk_remain -= size;
+		blksize -= size;
+		host->offset += size;
+		host->remain -= size;
+
+		while (size) {
+			*buffer = data & 0xFF;
+			buffer++;
+			data >>= 8;
+			size--;
+		}
+
+		if (host->remain == 0) {
+			if (sdhci_next_sg(host) == 0) {
+				BUG_ON(blksize != 0);
+				return;
+			}
+			buffer = sdhci_sg_to_buffer(host);
+		}
+	}
+}
+
+static void sdhci_write_block_pio(struct sdhci_host *host)
+{
+	int blksize, chunk_remain;
+	u32 data;
+	char *buffer;
+	int bytes, size;
+
+	DBG("PIO writing\n");
+
+	blksize = host->data->blksz;
+	chunk_remain = 4;
+	data = 0;
+
+	bytes = 0;
+	buffer = sdhci_sg_to_buffer(host) + host->offset;
+
+	while (blksize) {
+		size = min(host->remain, chunk_remain);
+
+		chunk_remain -= size;
+		blksize -= size;
+		host->offset += size;
+		host->remain -= size;
+
+		while (size) {
+			data >>= 8;
+			data |= (u32)*buffer << 24;
+			buffer++;
+			size--;
+		}
+
+		if (chunk_remain == 0) {
+			writel(data, host->ioaddr + SDHCI_BUFFER);
+			chunk_remain = min(blksize, 4);
+		}
+
+		if (host->remain == 0) {
+			if (sdhci_next_sg(host) == 0) {
+				BUG_ON(blksize != 0);
+				return;
+			}
+			buffer = sdhci_sg_to_buffer(host);
+		}
+	}
+}
+
+static void sdhci_transfer_pio(struct sdhci_host *host)
+{
+	u32 mask;
+
+	BUG_ON(!host->data);
+
+	if (host->num_sg == 0)
+		return;
+
+	if (host->data->flags & MMC_DATA_READ)
+		mask = SDHCI_DATA_AVAILABLE;
+	else
+		mask = SDHCI_SPACE_AVAILABLE;
+
+	while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+		if (host->data->flags & MMC_DATA_READ)
+			sdhci_read_block_pio(host);
+		else
+			sdhci_write_block_pio(host);
+
+		if (host->num_sg == 0)
+			break;
+	}
+
+	DBG("PIO transfer complete.\n");
+}
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
+{
+	u8 count;
+	unsigned target_timeout, current_timeout;
+
+	WARN_ON(host->data);
+
+	if (data == NULL)
+		return;
+
+	DBG("blksz %04x blks %04x flags %08x\n",
+		data->blksz, data->blocks, data->flags);
+	DBG("tsac %d ms nsac %d clk\n",
+		data->timeout_ns / 1000000, data->timeout_clks);
+
+	/* Sanity checks */
+	BUG_ON(data->blksz * data->blocks > 524288);
+	BUG_ON(data->blksz > host->mmc->max_blk_size);
+	BUG_ON(data->blocks > 65535);
+
+	/* timeout in us */
+	target_timeout = data->timeout_ns / 1000 +
+		data->timeout_clks / host->clock;
+
+	/*
+	 * Figure out needed cycles.
+	 * We do this in steps in order to fit inside a 32 bit int.
+	 * The first step is the minimum timeout, which will have a
+	 * minimum resolution of 6 bits:
+	 * (1) 2^13*1000 > 2^22,
+	 * (2) host->timeout_clk < 2^16
+	 *     =>
+	 *     (1) / (2) > 2^6
+	 */
+	count = 0;
+	current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+	while (current_timeout < target_timeout) {
+		count++;
+		current_timeout <<= 1;
+		if (count >= 0xF)
+			break;
+	}
+
+	if (count >= 0xF) {
+		printk(KERN_WARNING "%s: Too large timeout requested!\n",
+			mmc_hostname(host->mmc));
+		count = 0xE;
+	}
+
+	writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+
+	if (host->flags & SDHCI_USE_DMA) {
+		int count;
+
+		count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len,
+			(data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
+		BUG_ON(count != 1);
+
+		writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
+	} else {
+		host->cur_sg = data->sg;
+		host->num_sg = data->sg_len;
+
+		host->offset = 0;
+		host->remain = host->cur_sg->length;
+	}
+
+	/* 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);
+}
+
+static void sdhci_set_transfer_mode(struct sdhci_host *host,
+	struct mmc_data *data)
+{
+	u16 mode;
+
+	WARN_ON(host->data);
+
+	if (data == NULL)
+		return;
+
+	mode = SDHCI_TRNS_BLK_CNT_EN;
+	if (data->blocks > 1)
+		mode |= SDHCI_TRNS_MULTI;
+	if (data->flags & MMC_DATA_READ)
+		mode |= SDHCI_TRNS_READ;
+	if (host->flags & SDHCI_USE_DMA)
+		mode |= SDHCI_TRNS_DMA;
+
+	writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+}
+
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+	struct mmc_data *data;
+	u16 blocks;
+
+	BUG_ON(!host->data);
+
+	data = host->data;
+	host->data = NULL;
+
+	if (host->flags & SDHCI_USE_DMA) {
+		pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len,
+			(data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
+	}
+
+	/*
+	 * Controller doesn't count down when in single block mode.
+	 */
+	if ((data->blocks == 1) && (data->error == MMC_ERR_NONE))
+		blocks = 0;
+	else
+		blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT);
+	data->bytes_xfered = data->blksz * (data->blocks - blocks);
+
+	if ((data->error == MMC_ERR_NONE) && blocks) {
+		printk(KERN_ERR "%s: Controller signalled completion even "
+			"though there were blocks left.\n",
+			mmc_hostname(host->mmc));
+		data->error = MMC_ERR_FAILED;
+	}
+
+	DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+
+	if (data->stop) {
+		/*
+		 * The controller needs a reset of internal state machines
+		 * upon error conditions.
+		 */
+		if (data->error != MMC_ERR_NONE) {
+			sdhci_reset(host, SDHCI_RESET_CMD);
+			sdhci_reset(host, SDHCI_RESET_DATA);
+		}
+
+		sdhci_send_command(host, data->stop);
+	} else
+		tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+	int flags;
+	u32 mask;
+	unsigned long timeout;
+
+	WARN_ON(host->cmd);
+
+	DBG("Sending cmd (%x)\n", cmd->opcode);
+
+	/* Wait max 10 ms */
+	timeout = 10;
+
+	mask = SDHCI_CMD_INHIBIT;
+	if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+		mask |= SDHCI_DATA_INHIBIT;
+
+	/* We shouldn't wait for data inihibit for stop commands, even
+	   though they might use busy signaling */
+	if (host->mrq->data && (cmd == host->mrq->data->stop))
+		mask &= ~SDHCI_DATA_INHIBIT;
+
+	while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+		if (timeout == 0) {
+			printk(KERN_ERR "%s: Controller never released "
+				"inhibit bit(s).\n", mmc_hostname(host->mmc));
+			sdhci_dumpregs(host);
+			cmd->error = MMC_ERR_FAILED;
+			tasklet_schedule(&host->finish_tasklet);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	mod_timer(&host->timer, jiffies + 10 * HZ);
+
+	host->cmd = cmd;
+
+	sdhci_prepare_data(host, cmd->data);
+
+	writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+
+	sdhci_set_transfer_mode(host, cmd->data);
+
+	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+		printk(KERN_ERR "%s: Unsupported response type!\n",
+			mmc_hostname(host->mmc));
+		cmd->error = MMC_ERR_INVALID;
+		tasklet_schedule(&host->finish_tasklet);
+		return;
+	}
+
+	if (!(cmd->flags & MMC_RSP_PRESENT))
+		flags = SDHCI_CMD_RESP_NONE;
+	else if (cmd->flags & MMC_RSP_136)
+		flags = SDHCI_CMD_RESP_LONG;
+	else if (cmd->flags & MMC_RSP_BUSY)
+		flags = SDHCI_CMD_RESP_SHORT_BUSY;
+	else
+		flags = SDHCI_CMD_RESP_SHORT;
+
+	if (cmd->flags & MMC_RSP_CRC)
+		flags |= SDHCI_CMD_CRC;
+	if (cmd->flags & MMC_RSP_OPCODE)
+		flags |= SDHCI_CMD_INDEX;
+	if (cmd->data)
+		flags |= SDHCI_CMD_DATA;
+
+	writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
+		host->ioaddr + SDHCI_COMMAND);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
+	int i;
+
+	BUG_ON(host->cmd == NULL);
+
+	if (host->cmd->flags & MMC_RSP_PRESENT) {
+		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 +
+					SDHCI_RESPONSE + (3-i)*4) << 8;
+				if (i != 3)
+					host->cmd->resp[i] |=
+						readb(host->ioaddr +
+						SDHCI_RESPONSE + (3-i)*4-1);
+			}
+		} else {
+			host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+		}
+	}
+
+	host->cmd->error = MMC_ERR_NONE;
+
+	DBG("Ending cmd (%x)\n", host->cmd->opcode);
+
+	if (host->cmd->data)
+		host->data = host->cmd->data;
+	else
+		tasklet_schedule(&host->finish_tasklet);
+
+	host->cmd = NULL;
+}
+
+static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	int div;
+	u16 clk;
+	unsigned long timeout;
+
+	if (clock == host->clock)
+		return;
+
+	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		goto out;
+
+	for (div = 1;div < 256;div *= 2) {
+		if ((host->max_clk / div) <= clock)
+			break;
+	}
+	div >>= 1;
+
+	clk = div << SDHCI_DIVIDER_SHIFT;
+	clk |= SDHCI_CLOCK_INT_EN;
+	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 10 ms */
+	timeout = 10;
+	while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			printk(KERN_ERR "%s: Internal clock never "
+				"stabilised.\n", mmc_hostname(host->mmc));
+			sdhci_dumpregs(host);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+out:
+	host->clock = clock;
+}
+
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+	u8 pwr;
+
+	if (host->power == power)
+		return;
+
+	if (power == (unsigned short)-1) {
+		writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+		goto out;
+	}
+
+	/*
+	 * Spec says that we should clear the power reg before setting
+	 * a new value. Some controllers don't seem to like this though.
+	 */
+	if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
+		writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+
+	pwr = SDHCI_POWER_ON;
+
+	switch (power) {
+	case MMC_VDD_170:
+	case MMC_VDD_180:
+	case MMC_VDD_190:
+		pwr |= SDHCI_POWER_180;
+		break;
+	case MMC_VDD_290:
+	case MMC_VDD_300:
+	case MMC_VDD_310:
+		pwr |= SDHCI_POWER_300;
+		break;
+	case MMC_VDD_320:
+	case MMC_VDD_330:
+	case MMC_VDD_340:
+		pwr |= SDHCI_POWER_330;
+		break;
+	default:
+		BUG();
+	}
+
+	writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+
+out:
+	host->power = power;
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * MMC callbacks                                                             *
+ *                                                                           *
+\*****************************************************************************/
+
+static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	WARN_ON(host->mrq != NULL);
+
+	sdhci_activate_led(host);
+
+	host->mrq = mrq;
+
+	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+		host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+		tasklet_schedule(&host->finish_tasklet);
+	} else
+		sdhci_send_command(host, mrq->cmd);
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+	u8 ctrl;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/*
+	 * Reset the chip on each power off.
+	 * Should clear out any weird states.
+	 */
+	if (ios->power_mode == MMC_POWER_OFF) {
+		writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+		sdhci_init(host);
+	}
+
+	sdhci_set_clock(host, ios->clock);
+
+	if (ios->power_mode == MMC_POWER_OFF)
+		sdhci_set_power(host, -1);
+	else
+		sdhci_set_power(host, ios->vdd);
+
+	ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+
+	if (ios->bus_width == MMC_BUS_WIDTH_4)
+		ctrl |= SDHCI_CTRL_4BITBUS;
+	else
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+
+	if (ios->timing == MMC_TIMING_SD_HS)
+		ctrl |= SDHCI_CTRL_HISPD;
+	else
+		ctrl &= ~SDHCI_CTRL_HISPD;
+
+	writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+	int present;
+
+	host = mmc_priv(mmc);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	return !(present & SDHCI_WRITE_PROTECT);
+}
+
+static const struct mmc_host_ops sdhci_ops = {
+	.request	= sdhci_request,
+	.set_ios	= sdhci_set_ios,
+	.get_ro		= sdhci_get_ro,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Tasklets                                                                  *
+ *                                                                           *
+\*****************************************************************************/
+
+static void sdhci_tasklet_card(unsigned long param)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+
+	host = (struct sdhci_host*)param;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+		if (host->mrq) {
+			printk(KERN_ERR "%s: Card removed during transfer!\n",
+				mmc_hostname(host->mmc));
+			printk(KERN_ERR "%s: Resetting controller.\n",
+				mmc_hostname(host->mmc));
+
+			sdhci_reset(host, SDHCI_RESET_CMD);
+			sdhci_reset(host, SDHCI_RESET_DATA);
+
+			host->mrq->cmd->error = MMC_ERR_FAILED;
+			tasklet_schedule(&host->finish_tasklet);
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+	struct mmc_request *mrq;
+
+	host = (struct sdhci_host*)param;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	del_timer(&host->timer);
+
+	mrq = host->mrq;
+
+	DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode);
+
+	/*
+	 * The controller needs a reset of internal state machines
+	 * upon error conditions.
+	 */
+	if ((mrq->cmd->error != MMC_ERR_NONE) ||
+		(mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||
+		(mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {
+
+		/* Some controllers need this kick or reset won't work here */
+		if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+			unsigned int clock;
+
+			/* This is to force an update */
+			clock = host->clock;
+			host->clock = 0;
+			sdhci_set_clock(host, clock);
+		}
+
+		/* Spec says we should do both at the same time, but Ricoh
+		   controllers do not like that. */
+		sdhci_reset(host, SDHCI_RESET_CMD);
+		sdhci_reset(host, SDHCI_RESET_DATA);
+	}
+
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+
+	sdhci_deactivate_led(host);
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+static void sdhci_timeout_timer(unsigned long data)
+{
+	struct sdhci_host *host;
+	unsigned long flags;
+
+	host = (struct sdhci_host*)data;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (host->mrq) {
+		printk(KERN_ERR "%s: Timeout waiting for hardware "
+			"interrupt.\n", mmc_hostname(host->mmc));
+		sdhci_dumpregs(host);
+
+		if (host->data) {
+			host->data->error = MMC_ERR_TIMEOUT;
+			sdhci_finish_data(host);
+		} else {
+			if (host->cmd)
+				host->cmd->error = MMC_ERR_TIMEOUT;
+			else
+				host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+
+			tasklet_schedule(&host->finish_tasklet);
+		}
+	}
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Interrupt handling                                                        *
+ *                                                                           *
+\*****************************************************************************/
+
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
+{
+	BUG_ON(intmask == 0);
+
+	if (!host->cmd) {
+		printk(KERN_ERR "%s: Got command interrupt even though no "
+			"command operation was in progress.\n",
+			mmc_hostname(host->mmc));
+		sdhci_dumpregs(host);
+		return;
+	}
+
+	if (intmask & SDHCI_INT_RESPONSE)
+		sdhci_finish_command(host);
+	else {
+		if (intmask & SDHCI_INT_TIMEOUT)
+			host->cmd->error = MMC_ERR_TIMEOUT;
+		else if (intmask & SDHCI_INT_CRC)
+			host->cmd->error = MMC_ERR_BADCRC;
+		else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX))
+			host->cmd->error = MMC_ERR_FAILED;
+		else
+			host->cmd->error = MMC_ERR_INVALID;
+
+		tasklet_schedule(&host->finish_tasklet);
+	}
+}
+
+static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
+{
+	BUG_ON(intmask == 0);
+
+	if (!host->data) {
+		/*
+		 * A data end interrupt is sent together with the response
+		 * for the stop command.
+		 */
+		if (intmask & SDHCI_INT_DATA_END)
+			return;
+
+		printk(KERN_ERR "%s: Got data interrupt even though no "
+			"data operation was in progress.\n",
+			mmc_hostname(host->mmc));
+		sdhci_dumpregs(host);
+
+		return;
+	}
+
+	if (intmask & SDHCI_INT_DATA_TIMEOUT)
+		host->data->error = MMC_ERR_TIMEOUT;
+	else if (intmask & SDHCI_INT_DATA_CRC)
+		host->data->error = MMC_ERR_BADCRC;
+	else if (intmask & SDHCI_INT_DATA_END_BIT)
+		host->data->error = MMC_ERR_FAILED;
+
+	if (host->data->error != MMC_ERR_NONE)
+		sdhci_finish_data(host);
+	else {
+		if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
+			sdhci_transfer_pio(host);
+
+		if (intmask & SDHCI_INT_DATA_END)
+			sdhci_finish_data(host);
+	}
+}
+
+static irqreturn_t sdhci_irq(int irq, void *dev_id)
+{
+	irqreturn_t result;
+	struct sdhci_host* host = dev_id;
+	u32 intmask;
+
+	spin_lock(&host->lock);
+
+	intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+
+	if (!intmask || intmask == 0xffffffff) {
+		result = IRQ_NONE;
+		goto out;
+	}
+
+	DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, 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);
+		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_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_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+	}
+
+	intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+
+	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);
+	}
+
+	intmask &= SDHCI_INT_BUS_POWER;
+
+	if (intmask) {
+		printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+			mmc_hostname(host->mmc), intmask);
+		sdhci_dumpregs(host);
+
+		writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+	}
+
+	result = IRQ_HANDLED;
+
+	mmiowb();
+out:
+	spin_unlock(&host->lock);
+
+	return result;
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Suspend/resume                                                            *
+ *                                                                           *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+	struct sdhci_chip *chip;
+	int i, ret;
+
+	chip = pci_get_drvdata(pdev);
+	if (!chip)
+		return 0;
+
+	DBG("Suspending...\n");
+
+	for (i = 0;i < chip->num_slots;i++) {
+		if (!chip->hosts[i])
+			continue;
+		ret = mmc_suspend_host(chip->hosts[i]->mmc, state);
+		if (ret) {
+			for (i--;i >= 0;i--)
+				mmc_resume_host(chip->hosts[i]->mmc);
+			return ret;
+		}
+	}
+
+	pci_save_state(pdev);
+	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+
+	for (i = 0;i < chip->num_slots;i++) {
+		if (!chip->hosts[i])
+			continue;
+		free_irq(chip->hosts[i]->irq, chip->hosts[i]);
+	}
+
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static int sdhci_resume (struct pci_dev *pdev)
+{
+	struct sdhci_chip *chip;
+	int i, ret;
+
+	chip = pci_get_drvdata(pdev);
+	if (!chip)
+		return 0;
+
+	DBG("Resuming...\n");
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	for (i = 0;i < chip->num_slots;i++) {
+		if (!chip->hosts[i])
+			continue;
+		if (chip->hosts[i]->flags & SDHCI_USE_DMA)
+			pci_set_master(pdev);
+		ret = request_irq(chip->hosts[i]->irq, sdhci_irq,
+			IRQF_SHARED, chip->hosts[i]->slot_descr,
+			chip->hosts[i]);
+		if (ret)
+			return ret;
+		sdhci_init(chip->hosts[i]);
+		mmiowb();
+		ret = mmc_resume_host(chip->hosts[i]->mmc);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define sdhci_suspend NULL
+#define sdhci_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ *                                                                           *
+ * Device probing/removal                                                    *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
+{
+	int ret;
+	unsigned int version;
+	struct sdhci_chip *chip;
+	struct mmc_host *mmc;
+	struct sdhci_host *host;
+
+	u8 first_bar;
+	unsigned int caps;
+
+	chip = pci_get_drvdata(pdev);
+	BUG_ON(!chip);
+
+	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
+	if (ret)
+		return ret;
+
+	first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+
+	if (first_bar > 5) {
+		printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n");
+		return -ENODEV;
+	}
+
+	if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) {
+		printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n");
+		return -ENODEV;
+	}
+
+	if (pci_resource_len(pdev, first_bar + slot) != 0x100) {
+		printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. "
+			"You may experience problems.\n");
+	}
+
+	if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+		printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n");
+		return -ENODEV;
+	}
+
+	if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
+		printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n");
+		return -ENODEV;
+	}
+
+	mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+
+	host->chip = chip;
+	chip->hosts[slot] = host;
+
+	host->bar = first_bar + slot;
+
+	host->addr = pci_resource_start(pdev, host->bar);
+	host->irq = pdev->irq;
+
+	DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
+
+	snprintf(host->slot_descr, 20, "sdhci:slot%d", slot);
+
+	ret = pci_request_region(pdev, host->bar, host->slot_descr);
+	if (ret)
+		goto free;
+
+	host->ioaddr = ioremap_nocache(host->addr,
+		pci_resource_len(pdev, host->bar));
+	if (!host->ioaddr) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	sdhci_reset(host, SDHCI_RESET_ALL);
+
+	version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+	version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
+	if (version != 0) {
+		printk(KERN_ERR "%s: Unknown controller version (%d). "
+			"You may experience problems.\n", host->slot_descr,
+			version);
+	}
+
+	caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+
+	if (debug_nodma)
+		DBG("DMA forced off\n");
+	else if (debug_forcedma) {
+		DBG("DMA forced on\n");
+		host->flags |= SDHCI_USE_DMA;
+	} else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+		host->flags |= SDHCI_USE_DMA;
+	else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)
+		DBG("Controller doesn't have DMA interface\n");
+	else if (!(caps & SDHCI_CAN_DO_DMA))
+		DBG("Controller doesn't have DMA capability\n");
+	else
+		host->flags |= SDHCI_USE_DMA;
+
+	if (host->flags & SDHCI_USE_DMA) {
+		if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+			printk(KERN_WARNING "%s: No suitable DMA available. "
+				"Falling back to PIO.\n", host->slot_descr);
+			host->flags &= ~SDHCI_USE_DMA;
+		}
+	}
+
+	if (host->flags & SDHCI_USE_DMA)
+		pci_set_master(pdev);
+	else /* XXX: Hack to get MMC layer to avoid highmem */
+		pdev->dma_mask = 0;
+
+	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", host->slot_descr);
+		ret = -ENODEV;
+		goto unmap;
+	}
+	host->max_clk *= 1000000;
+
+	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", host->slot_descr);
+		ret = -ENODEV;
+		goto unmap;
+	}
+	if (caps & SDHCI_TIMEOUT_CLK_UNIT)
+		host->timeout_clk *= 1000;
+
+	/*
+	 * Set host parameters.
+	 */
+	mmc->ops = &sdhci_ops;
+	mmc->f_min = host->max_clk / 256;
+	mmc->f_max = host->max_clk;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
+
+	if (caps & SDHCI_CAN_DO_HISPD)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+	mmc->ocr_avail = 0;
+	if (caps & SDHCI_CAN_VDD_330)
+		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+	if (caps & SDHCI_CAN_VDD_300)
+		mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+	if (caps & SDHCI_CAN_VDD_180)
+		mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19;
+
+	if (mmc->ocr_avail == 0) {
+		printk(KERN_ERR "%s: Hardware doesn't report any "
+			"support voltages.\n", host->slot_descr);
+		ret = -ENODEV;
+		goto unmap;
+	}
+
+	spin_lock_init(&host->lock);
+
+	/*
+	 * Maximum number of segments. Hardware cannot do scatter lists.
+	 */
+	if (host->flags & SDHCI_USE_DMA)
+		mmc->max_hw_segs = 1;
+	else
+		mmc->max_hw_segs = 16;
+	mmc->max_phys_segs = 16;
+
+	/*
+	 * Maximum number of sectors in one transfer. Limited by DMA boundary
+	 * size (512KiB).
+	 */
+	mmc->max_req_size = 524288;
+
+	/*
+	 * Maximum segment size. Could be one segment with the maximum number
+	 * of bytes.
+	 */
+	mmc->max_seg_size = mmc->max_req_size;
+
+	/*
+	 * 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_ERR "%s: Invalid maximum block size.\n",
+			host->slot_descr);
+		ret = -ENODEV;
+		goto unmap;
+	}
+	mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+	/*
+	 * Maximum block count.
+	 */
+	mmc->max_blk_count = 65535;
+
+	/*
+	 * Init tasklets.
+	 */
+	tasklet_init(&host->card_tasklet,
+		sdhci_tasklet_card, (unsigned long)host);
+	tasklet_init(&host->finish_tasklet,
+		sdhci_tasklet_finish, (unsigned long)host);
+
+	setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+
+	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+		host->slot_descr, host);
+	if (ret)
+		goto untasklet;
+
+	sdhci_init(host);
+
+#ifdef CONFIG_MMC_DEBUG
+	sdhci_dumpregs(host);
+#endif
+
+	mmiowb();
+
+	mmc_add_host(mmc);
+
+	printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc),
+		host->addr, host->irq,
+		(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
+
+	return 0;
+
+untasklet:
+	tasklet_kill(&host->card_tasklet);
+	tasklet_kill(&host->finish_tasklet);
+unmap:
+	iounmap(host->ioaddr);
+release:
+	pci_release_region(pdev, host->bar);
+free:
+	mmc_free_host(mmc);
+
+	return ret;
+}
+
+static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
+{
+	struct sdhci_chip *chip;
+	struct mmc_host *mmc;
+	struct sdhci_host *host;
+
+	chip = pci_get_drvdata(pdev);
+	host = chip->hosts[slot];
+	mmc = host->mmc;
+
+	chip->hosts[slot] = NULL;
+
+	mmc_remove_host(mmc);
+
+	sdhci_reset(host, SDHCI_RESET_ALL);
+
+	free_irq(host->irq, host);
+
+	del_timer_sync(&host->timer);
+
+	tasklet_kill(&host->card_tasklet);
+	tasklet_kill(&host->finish_tasklet);
+
+	iounmap(host->ioaddr);
+
+	pci_release_region(pdev, host->bar);
+
+	mmc_free_host(mmc);
+}
+
+static int __devinit sdhci_probe(struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	int ret, i;
+	u8 slots, rev;
+	struct sdhci_chip *chip;
+
+	BUG_ON(pdev == NULL);
+	BUG_ON(ent == NULL);
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+	printk(KERN_INFO DRIVER_NAME
+		": SDHCI controller found at %s [%04x:%04x] (rev %x)\n",
+		pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
+		(int)rev);
+
+	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
+	if (ret)
+		return ret;
+
+	slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+	DBG("found %d slot(s)\n", slots);
+	if (slots == 0)
+		return -ENODEV;
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	chip = kzalloc(sizeof(struct sdhci_chip) +
+		sizeof(struct sdhci_host*) * slots, GFP_KERNEL);
+	if (!chip) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	chip->pdev = pdev;
+	chip->quirks = ent->driver_data;
+
+	if (debug_quirks)
+		chip->quirks = debug_quirks;
+
+	chip->num_slots = slots;
+	pci_set_drvdata(pdev, chip);
+
+	for (i = 0;i < slots;i++) {
+		ret = sdhci_probe_slot(pdev, i);
+		if (ret) {
+			for (i--;i >= 0;i--)
+				sdhci_remove_slot(pdev, i);
+			goto free;
+		}
+	}
+
+	return 0;
+
+free:
+	pci_set_drvdata(pdev, NULL);
+	kfree(chip);
+
+err:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void __devexit sdhci_remove(struct pci_dev *pdev)
+{
+	int i;
+	struct sdhci_chip *chip;
+
+	chip = pci_get_drvdata(pdev);
+
+	if (chip) {
+		for (i = 0;i < chip->num_slots;i++)
+			sdhci_remove_slot(pdev, i);
+
+		pci_set_drvdata(pdev, NULL);
+
+		kfree(chip);
+	}
+
+	pci_disable_device(pdev);
+}
+
+static struct pci_driver sdhci_driver = {
+	.name = 	DRIVER_NAME,
+	.id_table =	pci_ids,
+	.probe = 	sdhci_probe,
+	.remove =	__devexit_p(sdhci_remove),
+	.suspend =	sdhci_suspend,
+	.resume	=	sdhci_resume,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Driver init/exit                                                          *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+	printk(KERN_INFO DRIVER_NAME
+		": Secure Digital Host Controller Interface driver\n");
+	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+
+	return pci_register_driver(&sdhci_driver);
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+	DBG("Exiting\n");
+
+	pci_unregister_driver(&sdhci_driver);
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+module_param(debug_nodma, uint, 0444);
+module_param(debug_forcedma, uint, 0444);
+module_param(debug_quirks, uint, 0444);
+
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)");
+MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)");
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
new file mode 100644
index 0000000..7400f4b
--- /dev/null
+++ b/drivers/mmc/host/sdhci.h
@@ -0,0 +1,210 @@
+/*
+ *  linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver
+ *
+ *  Copyright (C) 2005-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+/*
+ * PCI registers
+ */
+
+#define PCI_SDHCI_IFPIO			0x00
+#define PCI_SDHCI_IFDMA			0x01
+#define PCI_SDHCI_IFVENDOR		0x02
+
+#define PCI_SLOT_INFO			0x40	/* 8 bits */
+#define  PCI_SLOT_INFO_SLOTS(x)		((x >> 4) & 7)
+#define  PCI_SLOT_INFO_FIRST_BAR_MASK	0x07
+
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS	0x00
+
+#define SDHCI_BLOCK_SIZE	0x04
+#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#define SDHCI_BLOCK_COUNT	0x06
+
+#define SDHCI_ARGUMENT		0x08
+
+#define SDHCI_TRANSFER_MODE	0x0C
+#define  SDHCI_TRNS_DMA		0x01
+#define  SDHCI_TRNS_BLK_CNT_EN	0x02
+#define  SDHCI_TRNS_ACMD12	0x04
+#define  SDHCI_TRNS_READ	0x10
+#define  SDHCI_TRNS_MULTI	0x20
+
+#define SDHCI_COMMAND		0x0E
+#define  SDHCI_CMD_RESP_MASK	0x03
+#define  SDHCI_CMD_CRC		0x08
+#define  SDHCI_CMD_INDEX	0x10
+#define  SDHCI_CMD_DATA		0x20
+
+#define  SDHCI_CMD_RESP_NONE	0x00
+#define  SDHCI_CMD_RESP_LONG	0x01
+#define  SDHCI_CMD_RESP_SHORT	0x02
+#define  SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+
+#define SDHCI_RESPONSE		0x10
+
+#define SDHCI_BUFFER		0x20
+
+#define SDHCI_PRESENT_STATE	0x24
+#define  SDHCI_CMD_INHIBIT	0x00000001
+#define  SDHCI_DATA_INHIBIT	0x00000002
+#define  SDHCI_DOING_WRITE	0x00000100
+#define  SDHCI_DOING_READ	0x00000200
+#define  SDHCI_SPACE_AVAILABLE	0x00000400
+#define  SDHCI_DATA_AVAILABLE	0x00000800
+#define  SDHCI_CARD_PRESENT	0x00010000
+#define  SDHCI_WRITE_PROTECT	0x00080000
+
+#define SDHCI_HOST_CONTROL 	0x28
+#define  SDHCI_CTRL_LED		0x01
+#define  SDHCI_CTRL_4BITBUS	0x02
+#define  SDHCI_CTRL_HISPD	0x04
+
+#define SDHCI_POWER_CONTROL	0x29
+#define  SDHCI_POWER_ON		0x01
+#define  SDHCI_POWER_180	0x0A
+#define  SDHCI_POWER_300	0x0C
+#define  SDHCI_POWER_330	0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL	0x2A
+
+#define SDHCI_WALK_UP_CONTROL	0x2B
+
+#define SDHCI_CLOCK_CONTROL	0x2C
+#define  SDHCI_DIVIDER_SHIFT	8
+#define  SDHCI_CLOCK_CARD_EN	0x0004
+#define  SDHCI_CLOCK_INT_STABLE	0x0002
+#define  SDHCI_CLOCK_INT_EN	0x0001
+
+#define SDHCI_TIMEOUT_CONTROL	0x2E
+
+#define SDHCI_SOFTWARE_RESET	0x2F
+#define  SDHCI_RESET_ALL	0x01
+#define  SDHCI_RESET_CMD	0x02
+#define  SDHCI_RESET_DATA	0x04
+
+#define SDHCI_INT_STATUS	0x30
+#define SDHCI_INT_ENABLE	0x34
+#define SDHCI_SIGNAL_ENABLE	0x38
+#define  SDHCI_INT_RESPONSE	0x00000001
+#define  SDHCI_INT_DATA_END	0x00000002
+#define  SDHCI_INT_DMA_END	0x00000008
+#define  SDHCI_INT_SPACE_AVAIL	0x00000010
+#define  SDHCI_INT_DATA_AVAIL	0x00000020
+#define  SDHCI_INT_CARD_INSERT	0x00000040
+#define  SDHCI_INT_CARD_REMOVE	0x00000080
+#define  SDHCI_INT_CARD_INT	0x00000100
+#define  SDHCI_INT_TIMEOUT	0x00010000
+#define  SDHCI_INT_CRC		0x00020000
+#define  SDHCI_INT_END_BIT	0x00040000
+#define  SDHCI_INT_INDEX	0x00080000
+#define  SDHCI_INT_DATA_TIMEOUT	0x00100000
+#define  SDHCI_INT_DATA_CRC	0x00200000
+#define  SDHCI_INT_DATA_END_BIT	0x00400000
+#define  SDHCI_INT_BUS_POWER	0x00800000
+#define  SDHCI_INT_ACMD12ERR	0x01000000
+
+#define  SDHCI_INT_NORMAL_MASK	0x00007FFF
+#define  SDHCI_INT_ERROR_MASK	0xFFFF8000
+
+#define  SDHCI_INT_CMD_MASK	(SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+		SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define  SDHCI_INT_DATA_MASK	(SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+		SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+		SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+		SDHCI_INT_DATA_END_BIT)
+
+#define SDHCI_ACMD12_ERR	0x3C
+
+/* 3E-3F reserved */
+
+#define SDHCI_CAPABILITIES	0x40
+#define  SDHCI_TIMEOUT_CLK_MASK	0x0000003F
+#define  SDHCI_TIMEOUT_CLK_SHIFT 0
+#define  SDHCI_TIMEOUT_CLK_UNIT	0x00000080
+#define  SDHCI_CLOCK_BASE_MASK	0x00003F00
+#define  SDHCI_CLOCK_BASE_SHIFT	8
+#define  SDHCI_MAX_BLOCK_MASK	0x00030000
+#define  SDHCI_MAX_BLOCK_SHIFT  16
+#define  SDHCI_CAN_DO_HISPD	0x00200000
+#define  SDHCI_CAN_DO_DMA	0x00400000
+#define  SDHCI_CAN_VDD_330	0x01000000
+#define  SDHCI_CAN_VDD_300	0x02000000
+#define  SDHCI_CAN_VDD_180	0x04000000
+
+/* 44-47 reserved for more caps */
+
+#define SDHCI_MAX_CURRENT	0x48
+
+/* 4C-4F reserved for more max current */
+
+/* 50-FB reserved */
+
+#define SDHCI_SLOT_INT_STATUS	0xFC
+
+#define SDHCI_HOST_VERSION	0xFE
+#define  SDHCI_VENDOR_VER_MASK	0xFF00
+#define  SDHCI_VENDOR_VER_SHIFT	8
+#define  SDHCI_SPEC_VER_MASK	0x00FF
+#define  SDHCI_SPEC_VER_SHIFT	0
+
+struct sdhci_chip;
+
+struct sdhci_host {
+	struct sdhci_chip	*chip;
+	struct mmc_host		*mmc;		/* MMC structure */
+
+	spinlock_t		lock;		/* Mutex */
+
+	int			flags;		/* Host attributes */
+#define SDHCI_USE_DMA		(1<<0)
+
+	unsigned int		max_clk;	/* Max possible freq (MHz) */
+	unsigned int		timeout_clk;	/* Timeout freq (KHz) */
+
+	unsigned int		clock;		/* Current clock (MHz) */
+	unsigned short		power;		/* Current voltage */
+
+	struct mmc_request	*mrq;		/* Current request */
+	struct mmc_command	*cmd;		/* Current command */
+	struct mmc_data		*data;		/* Current data request */
+
+	struct scatterlist	*cur_sg;	/* We're working on this */
+	int			num_sg;		/* Entries left */
+	int			offset;		/* Offset into current sg */
+	int			remain;		/* Bytes left in current */
+
+	char			slot_descr[20];	/* Name for reservations */
+
+	int			irq;		/* Device IRQ */
+	int			bar;		/* PCI BAR index */
+	unsigned long		addr;		/* Bus address */
+	void __iomem *		ioaddr;		/* Mapped address */
+
+	struct tasklet_struct	card_tasklet;	/* Tasklet structures */
+	struct tasklet_struct	finish_tasklet;
+
+	struct timer_list	timer;		/* Timer for timeouts */
+};
+
+struct sdhci_chip {
+	struct pci_dev		*pdev;
+
+	unsigned long		quirks;
+
+	int			num_slots;	/* Slots on controller */
+	struct sdhci_host	*hosts[0];	/* Pointers to hosts */
+};
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
new file mode 100644
index 0000000..b0d77d2
--- /dev/null
+++ b/drivers/mmc/host/tifm_sd.c
@@ -0,0 +1,1102 @@
+/*
+ *  tifm_sd.c - TI FlashMedia driver
+ *
+ *  Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
+ *
+ * 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.
+ *
+ * Special thanks to Brad Campbell for extensive testing of this driver.
+ *
+ */
+
+
+#include <linux/tifm.h>
+#include <linux/mmc/host.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+
+#define DRIVER_NAME "tifm_sd"
+#define DRIVER_VERSION "0.8"
+
+static int no_dma = 0;
+static int fixed_timeout = 0;
+module_param(no_dma, bool, 0644);
+module_param(fixed_timeout, bool, 0644);
+
+/* Constants here are mostly from OMAP5912 datasheet */
+#define TIFM_MMCSD_RESET      0x0002
+#define TIFM_MMCSD_CLKMASK    0x03ff
+#define TIFM_MMCSD_POWER      0x0800
+#define TIFM_MMCSD_4BBUS      0x8000
+#define TIFM_MMCSD_RXDE       0x8000   /* rx dma enable */
+#define TIFM_MMCSD_TXDE       0x0080   /* tx dma enable */
+#define TIFM_MMCSD_BUFINT     0x0c00   /* set bits: AE, AF */
+#define TIFM_MMCSD_DPE        0x0020   /* data timeout counted in kilocycles */
+#define TIFM_MMCSD_INAB       0x0080   /* abort / initialize command */
+#define TIFM_MMCSD_READ       0x8000
+
+#define TIFM_MMCSD_ERRMASK    0x01e0   /* set bits: CCRC, CTO, DCRC, DTO */
+#define TIFM_MMCSD_EOC        0x0001   /* end of command phase  */
+#define TIFM_MMCSD_CD         0x0002   /* card detect           */
+#define TIFM_MMCSD_CB         0x0004   /* card enter busy state */
+#define TIFM_MMCSD_BRS        0x0008   /* block received/sent   */
+#define TIFM_MMCSD_EOFB       0x0010   /* card exit busy state  */
+#define TIFM_MMCSD_DTO        0x0020   /* data time-out         */
+#define TIFM_MMCSD_DCRC       0x0040   /* data crc error        */
+#define TIFM_MMCSD_CTO        0x0080   /* command time-out      */
+#define TIFM_MMCSD_CCRC       0x0100   /* command crc error     */
+#define TIFM_MMCSD_AF         0x0400   /* fifo almost full      */
+#define TIFM_MMCSD_AE         0x0800   /* fifo almost empty     */
+#define TIFM_MMCSD_OCRB       0x1000   /* OCR busy              */
+#define TIFM_MMCSD_CIRQ       0x2000   /* card irq (cmd40/sdio) */
+#define TIFM_MMCSD_CERR       0x4000   /* card status error     */
+
+#define TIFM_MMCSD_ODTO       0x0040   /* open drain / extended timeout */
+#define TIFM_MMCSD_CARD_RO    0x0200   /* card is read-only     */
+
+#define TIFM_MMCSD_FIFO_SIZE  0x0020
+
+#define TIFM_MMCSD_RSP_R0     0x0000
+#define TIFM_MMCSD_RSP_R1     0x0100
+#define TIFM_MMCSD_RSP_R2     0x0200
+#define TIFM_MMCSD_RSP_R3     0x0300
+#define TIFM_MMCSD_RSP_R4     0x0400
+#define TIFM_MMCSD_RSP_R5     0x0500
+#define TIFM_MMCSD_RSP_R6     0x0600
+
+#define TIFM_MMCSD_RSP_BUSY   0x0800
+
+#define TIFM_MMCSD_CMD_BC     0x0000
+#define TIFM_MMCSD_CMD_BCR    0x1000
+#define TIFM_MMCSD_CMD_AC     0x2000
+#define TIFM_MMCSD_CMD_ADTC   0x3000
+
+#define TIFM_MMCSD_MAX_BLOCK_SIZE  0x0800UL
+
+enum {
+	CMD_READY    = 0x0001,
+	FIFO_READY   = 0x0002,
+	BRS_READY    = 0x0004,
+	SCMD_ACTIVE  = 0x0008,
+	SCMD_READY   = 0x0010,
+	CARD_BUSY    = 0x0020,
+	DATA_CARRY   = 0x0040
+};
+
+struct tifm_sd {
+	struct tifm_dev       *dev;
+
+	unsigned short        eject:1,
+			      open_drain:1,
+			      no_dma:1;
+	unsigned short        cmd_flags;
+
+	unsigned int          clk_freq;
+	unsigned int          clk_div;
+	unsigned long         timeout_jiffies;
+
+	struct tasklet_struct finish_tasklet;
+	struct timer_list     timer;
+	struct mmc_request    *req;
+
+	int                   sg_len;
+	int                   sg_pos;
+	unsigned int          block_pos;
+	struct scatterlist    bounce_buf;
+	unsigned char         bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE];
+};
+
+/* for some reason, host won't respond correctly to readw/writew */
+static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
+			      unsigned int off, unsigned int cnt)
+{
+	struct tifm_dev *sock = host->dev;
+	unsigned char *buf;
+	unsigned int pos = 0, val;
+
+	buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
+	if (host->cmd_flags & DATA_CARRY) {
+		buf[pos++] = host->bounce_buf_data[0];
+		host->cmd_flags &= ~DATA_CARRY;
+	}
+
+	while (pos < cnt) {
+		val = readl(sock->addr + SOCK_MMCSD_DATA);
+		buf[pos++] = val & 0xff;
+		if (pos == cnt) {
+			host->bounce_buf_data[0] = (val >> 8) & 0xff;
+			host->cmd_flags |= DATA_CARRY;
+			break;
+		}
+		buf[pos++] = (val >> 8) & 0xff;
+	}
+	kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
+}
+
+static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
+			       unsigned int off, unsigned int cnt)
+{
+	struct tifm_dev *sock = host->dev;
+	unsigned char *buf;
+	unsigned int pos = 0, val;
+
+	buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off;
+	if (host->cmd_flags & DATA_CARRY) {
+		val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
+		writel(val, sock->addr + SOCK_MMCSD_DATA);
+		host->cmd_flags &= ~DATA_CARRY;
+	}
+
+	while (pos < cnt) {
+		val = buf[pos++];
+		if (pos == cnt) {
+			host->bounce_buf_data[0] = val & 0xff;
+			host->cmd_flags |= DATA_CARRY;
+			break;
+		}
+		val |= (buf[pos++] << 8) & 0xff00;
+		writel(val, sock->addr + SOCK_MMCSD_DATA);
+	}
+	kunmap_atomic(buf - off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_sd_transfer_data(struct tifm_sd *host)
+{
+	struct mmc_data *r_data = host->req->cmd->data;
+	struct scatterlist *sg = r_data->sg;
+	unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2;
+	unsigned int p_off, p_cnt;
+	struct page *pg;
+
+	if (host->sg_pos == host->sg_len)
+		return;
+	while (t_size) {
+		cnt = sg[host->sg_pos].length - host->block_pos;
+		if (!cnt) {
+			host->block_pos = 0;
+			host->sg_pos++;
+			if (host->sg_pos == host->sg_len) {
+				if ((r_data->flags & MMC_DATA_WRITE)
+				    && DATA_CARRY)
+					writel(host->bounce_buf_data[0],
+					       host->dev->addr
+					       + SOCK_MMCSD_DATA);
+
+				return;
+			}
+			cnt = sg[host->sg_pos].length;
+		}
+		off = sg[host->sg_pos].offset + host->block_pos;
+
+		pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT);
+		p_off = offset_in_page(off);
+		p_cnt = PAGE_SIZE - p_off;
+		p_cnt = min(p_cnt, cnt);
+		p_cnt = min(p_cnt, t_size);
+
+		if (r_data->flags & MMC_DATA_READ)
+			tifm_sd_read_fifo(host, pg, p_off, p_cnt);
+		else if (r_data->flags & MMC_DATA_WRITE)
+			tifm_sd_write_fifo(host, pg, p_off, p_cnt);
+
+		t_size -= p_cnt;
+		host->block_pos += p_cnt;
+	}
+}
+
+static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
+			      struct page *src, unsigned int src_off,
+			      unsigned int count)
+{
+	unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off;
+	unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off;
+
+	memcpy(dst_buf, src_buf, count);
+
+	kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ);
+	kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
+{
+	struct scatterlist *sg = r_data->sg;
+	unsigned int t_size = r_data->blksz;
+	unsigned int off, cnt;
+	unsigned int p_off, p_cnt;
+	struct page *pg;
+
+	dev_dbg(&host->dev->dev, "bouncing block\n");
+	while (t_size) {
+		cnt = sg[host->sg_pos].length - host->block_pos;
+		if (!cnt) {
+			host->block_pos = 0;
+			host->sg_pos++;
+			if (host->sg_pos == host->sg_len)
+				return;
+			cnt = sg[host->sg_pos].length;
+		}
+		off = sg[host->sg_pos].offset + host->block_pos;
+
+		pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT);
+		p_off = offset_in_page(off);
+		p_cnt = PAGE_SIZE - p_off;
+		p_cnt = min(p_cnt, cnt);
+		p_cnt = min(p_cnt, t_size);
+
+		if (r_data->flags & MMC_DATA_WRITE)
+			tifm_sd_copy_page(host->bounce_buf.page,
+					  r_data->blksz - t_size,
+					  pg, p_off, p_cnt);
+		else if (r_data->flags & MMC_DATA_READ)
+			tifm_sd_copy_page(pg, p_off, host->bounce_buf.page,
+					  r_data->blksz - t_size, p_cnt);
+
+		t_size -= p_cnt;
+		host->block_pos += p_cnt;
+	}
+}
+
+int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data)
+{
+	struct tifm_dev *sock = host->dev;
+	unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz;
+	unsigned int dma_len, dma_blk_cnt, dma_off;
+	struct scatterlist *sg = NULL;
+	unsigned long flags;
+
+	if (host->sg_pos == host->sg_len)
+		return 1;
+
+	if (host->cmd_flags & DATA_CARRY) {
+		host->cmd_flags &= ~DATA_CARRY;
+		local_irq_save(flags);
+		tifm_sd_bounce_block(host, r_data);
+		local_irq_restore(flags);
+		if (host->sg_pos == host->sg_len)
+			return 1;
+	}
+
+	dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos;
+	if (!dma_len) {
+		host->block_pos = 0;
+		host->sg_pos++;
+		if (host->sg_pos == host->sg_len)
+			return 1;
+		dma_len = sg_dma_len(&r_data->sg[host->sg_pos]);
+	}
+
+	if (dma_len < t_size) {
+		dma_blk_cnt = dma_len / r_data->blksz;
+		dma_off = host->block_pos;
+		host->block_pos += dma_blk_cnt * r_data->blksz;
+	} else {
+		dma_blk_cnt = TIFM_DMA_TSIZE;
+		dma_off = host->block_pos;
+		host->block_pos += t_size;
+	}
+
+	if (dma_blk_cnt)
+		sg = &r_data->sg[host->sg_pos];
+	else if (dma_len) {
+		if (r_data->flags & MMC_DATA_WRITE) {
+			local_irq_save(flags);
+			tifm_sd_bounce_block(host, r_data);
+			local_irq_restore(flags);
+		} else
+			host->cmd_flags |= DATA_CARRY;
+
+		sg = &host->bounce_buf;
+		dma_off = 0;
+		dma_blk_cnt = 1;
+	} else
+		return 1;
+
+	dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt);
+	writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS);
+	if (r_data->flags & MMC_DATA_WRITE)
+		writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
+		       sock->addr + SOCK_DMA_CONTROL);
+	else
+		writel((dma_blk_cnt << 8) | TIFM_DMA_EN,
+		       sock->addr + SOCK_DMA_CONTROL);
+
+	return 0;
+}
+
+static unsigned int tifm_sd_op_flags(struct mmc_command *cmd)
+{
+	unsigned int rc = 0;
+
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_NONE:
+		rc |= TIFM_MMCSD_RSP_R0;
+		break;
+	case MMC_RSP_R1B:
+		rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through
+	case MMC_RSP_R1:
+		rc |= TIFM_MMCSD_RSP_R1;
+		break;
+	case MMC_RSP_R2:
+		rc |= TIFM_MMCSD_RSP_R2;
+		break;
+	case MMC_RSP_R3:
+		rc |= TIFM_MMCSD_RSP_R3;
+		break;
+	default:
+		BUG();
+	}
+
+	switch (mmc_cmd_type(cmd)) {
+	case MMC_CMD_BC:
+		rc |= TIFM_MMCSD_CMD_BC;
+		break;
+	case MMC_CMD_BCR:
+		rc |= TIFM_MMCSD_CMD_BCR;
+		break;
+	case MMC_CMD_AC:
+		rc |= TIFM_MMCSD_CMD_AC;
+		break;
+	case MMC_CMD_ADTC:
+		rc |= TIFM_MMCSD_CMD_ADTC;
+		break;
+	default:
+		BUG();
+	}
+	return rc;
+}
+
+static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd)
+{
+	struct tifm_dev *sock = host->dev;
+	unsigned int cmd_mask = tifm_sd_op_flags(cmd);
+
+	if (host->open_drain)
+		cmd_mask |= TIFM_MMCSD_ODTO;
+
+	if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
+		cmd_mask |= TIFM_MMCSD_READ;
+
+	dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n",
+		cmd->opcode, cmd->arg, cmd_mask);
+
+	writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH);
+	writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW);
+	writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND);
+}
+
+static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock)
+{
+	cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16)
+		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18);
+	cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16)
+		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10);
+	cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16)
+		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08);
+	cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16)
+		       | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00);
+}
+
+static void tifm_sd_check_status(struct tifm_sd *host)
+{
+	struct tifm_dev *sock = host->dev;
+	struct mmc_command *cmd = host->req->cmd;
+
+	if (cmd->error != MMC_ERR_NONE)
+		goto finish_request;
+
+	if (!(host->cmd_flags & CMD_READY))
+		return;
+
+	if (cmd->data) {
+		if (cmd->data->error != MMC_ERR_NONE) {
+			if ((host->cmd_flags & SCMD_ACTIVE)
+			    && !(host->cmd_flags & SCMD_READY))
+				return;
+
+			goto finish_request;
+		}
+
+		if (!(host->cmd_flags & BRS_READY))
+			return;
+
+		if (!(host->no_dma || (host->cmd_flags & FIFO_READY)))
+			return;
+
+		if (cmd->data->flags & MMC_DATA_WRITE) {
+			if (host->req->stop) {
+				if (!(host->cmd_flags & SCMD_ACTIVE)) {
+					host->cmd_flags |= SCMD_ACTIVE;
+					writel(TIFM_MMCSD_EOFB
+					       | readl(sock->addr
+						       + SOCK_MMCSD_INT_ENABLE),
+					       sock->addr
+					       + SOCK_MMCSD_INT_ENABLE);
+					tifm_sd_exec(host, host->req->stop);
+					return;
+				} else {
+					if (!(host->cmd_flags & SCMD_READY)
+					    || (host->cmd_flags & CARD_BUSY))
+						return;
+					writel((~TIFM_MMCSD_EOFB)
+					       & readl(sock->addr
+						       + SOCK_MMCSD_INT_ENABLE),
+					       sock->addr
+					       + SOCK_MMCSD_INT_ENABLE);
+				}
+			} else {
+				if (host->cmd_flags & CARD_BUSY)
+					return;
+				writel((~TIFM_MMCSD_EOFB)
+				       & readl(sock->addr
+					       + SOCK_MMCSD_INT_ENABLE),
+				       sock->addr + SOCK_MMCSD_INT_ENABLE);
+			}
+		} else {
+			if (host->req->stop) {
+				if (!(host->cmd_flags & SCMD_ACTIVE)) {
+					host->cmd_flags |= SCMD_ACTIVE;
+					tifm_sd_exec(host, host->req->stop);
+					return;
+				} else {
+					if (!(host->cmd_flags & SCMD_READY))
+						return;
+				}
+			}
+		}
+	}
+finish_request:
+	tasklet_schedule(&host->finish_tasklet);
+}
+
+/* Called from interrupt handler */
+static void tifm_sd_data_event(struct tifm_dev *sock)
+{
+	struct tifm_sd *host;
+	unsigned int fifo_status = 0;
+	struct mmc_data *r_data = NULL;
+
+	spin_lock(&sock->lock);
+	host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
+	fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
+	dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
+		fifo_status, host->cmd_flags);
+
+	if (host->req) {
+		r_data = host->req->cmd->data;
+
+		if (r_data && (fifo_status & TIFM_FIFO_READY)) {
+			if (tifm_sd_set_dma_data(host, r_data)) {
+				host->cmd_flags |= FIFO_READY;
+				tifm_sd_check_status(host);
+			}
+		}
+	}
+
+	writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+	spin_unlock(&sock->lock);
+}
+
+/* Called from interrupt handler */
+static void tifm_sd_card_event(struct tifm_dev *sock)
+{
+	struct tifm_sd *host;
+	unsigned int host_status = 0;
+	int cmd_error = MMC_ERR_NONE;
+	struct mmc_command *cmd = NULL;
+	unsigned long flags;
+
+	spin_lock(&sock->lock);
+	host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
+	host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
+	dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
+		host_status, host->cmd_flags);
+
+	if (host->req) {
+		cmd = host->req->cmd;
+
+		if (host_status & TIFM_MMCSD_ERRMASK) {
+			writel(host_status & TIFM_MMCSD_ERRMASK,
+			       sock->addr + SOCK_MMCSD_STATUS);
+			if (host_status & TIFM_MMCSD_CTO)
+				cmd_error = MMC_ERR_TIMEOUT;
+			else if (host_status & TIFM_MMCSD_CCRC)
+				cmd_error = MMC_ERR_BADCRC;
+
+			if (cmd->data) {
+				if (host_status & TIFM_MMCSD_DTO)
+					cmd->data->error = MMC_ERR_TIMEOUT;
+				else if (host_status & TIFM_MMCSD_DCRC)
+					cmd->data->error = MMC_ERR_BADCRC;
+			}
+
+			writel(TIFM_FIFO_INT_SETALL,
+			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+			writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+
+			if (host->req->stop) {
+				if (host->cmd_flags & SCMD_ACTIVE) {
+					host->req->stop->error = cmd_error;
+					host->cmd_flags |= SCMD_READY;
+				} else {
+					cmd->error = cmd_error;
+					host->cmd_flags |= SCMD_ACTIVE;
+					tifm_sd_exec(host, host->req->stop);
+					goto done;
+				}
+			} else
+				cmd->error = cmd_error;
+		} else {
+			if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
+				if (!(host->cmd_flags & CMD_READY)) {
+					host->cmd_flags |= CMD_READY;
+					tifm_sd_fetch_resp(cmd, sock);
+				} else if (host->cmd_flags & SCMD_ACTIVE) {
+					host->cmd_flags |= SCMD_READY;
+					tifm_sd_fetch_resp(host->req->stop,
+							   sock);
+				}
+			}
+			if (host_status & TIFM_MMCSD_BRS)
+				host->cmd_flags |= BRS_READY;
+		}
+
+		if (host->no_dma && cmd->data) {
+			if (host_status & TIFM_MMCSD_AE)
+				writel(host_status & TIFM_MMCSD_AE,
+				       sock->addr + SOCK_MMCSD_STATUS);
+
+			if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF
+					   | TIFM_MMCSD_BRS)) {
+				local_irq_save(flags);
+				tifm_sd_transfer_data(host);
+				local_irq_restore(flags);
+				host_status &= ~TIFM_MMCSD_AE;
+			}
+		}
+
+		if (host_status & TIFM_MMCSD_EOFB)
+			host->cmd_flags &= ~CARD_BUSY;
+		else if (host_status & TIFM_MMCSD_CB)
+			host->cmd_flags |= CARD_BUSY;
+
+		tifm_sd_check_status(host);
+	}
+done:
+	writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
+	spin_unlock(&sock->lock);
+}
+
+static void tifm_sd_set_data_timeout(struct tifm_sd *host,
+				     struct mmc_data *data)
+{
+	struct tifm_dev *sock = host->dev;
+	unsigned int data_timeout = data->timeout_clks;
+
+	if (fixed_timeout)
+		return;
+
+	data_timeout += data->timeout_ns /
+			((1000000000UL / host->clk_freq) * host->clk_div);
+
+	if (data_timeout < 0xffff) {
+		writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO);
+		writel((~TIFM_MMCSD_DPE)
+		       & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG),
+		       sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG);
+	} else {
+		data_timeout = (data_timeout >> 10) + 1;
+		if (data_timeout > 0xffff)
+			data_timeout = 0;	/* set to unlimited */
+		writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO);
+		writel(TIFM_MMCSD_DPE
+		       | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG),
+		       sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG);
+	}
+}
+
+static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct tifm_sd *host = mmc_priv(mmc);
+	struct tifm_dev *sock = host->dev;
+	unsigned long flags;
+	struct mmc_data *r_data = mrq->cmd->data;
+
+	spin_lock_irqsave(&sock->lock, flags);
+	if (host->eject) {
+		spin_unlock_irqrestore(&sock->lock, flags);
+		goto err_out;
+	}
+
+	if (host->req) {
+		printk(KERN_ERR "%s : unfinished request detected\n",
+		       sock->dev.bus_id);
+		spin_unlock_irqrestore(&sock->lock, flags);
+		goto err_out;
+	}
+
+	host->cmd_flags = 0;
+	host->block_pos = 0;
+	host->sg_pos = 0;
+
+	if (r_data) {
+		tifm_sd_set_data_timeout(host, r_data);
+
+		if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop)
+			 writel(TIFM_MMCSD_EOFB
+				| readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+				sock->addr + SOCK_MMCSD_INT_ENABLE);
+
+		if (host->no_dma) {
+			writel(TIFM_MMCSD_BUFINT
+			       | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+			       sock->addr + SOCK_MMCSD_INT_ENABLE);
+			writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
+			       | (TIFM_MMCSD_FIFO_SIZE - 1),
+			       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+			host->sg_len = r_data->sg_len;
+		} else {
+			sg_init_one(&host->bounce_buf, host->bounce_buf_data,
+				    r_data->blksz);
+
+			if(1 != tifm_map_sg(sock, &host->bounce_buf, 1,
+					    r_data->flags & MMC_DATA_WRITE
+					    ? PCI_DMA_TODEVICE
+					    : PCI_DMA_FROMDEVICE)) {
+				printk(KERN_ERR "%s : scatterlist map failed\n",
+				       sock->dev.bus_id);
+				spin_unlock_irqrestore(&sock->lock, flags);
+				goto err_out;
+			}
+			host->sg_len = tifm_map_sg(sock, r_data->sg,
+						   r_data->sg_len,
+						   r_data->flags
+						   & MMC_DATA_WRITE
+						   ? PCI_DMA_TODEVICE
+						   : PCI_DMA_FROMDEVICE);
+			if (host->sg_len < 1) {
+				printk(KERN_ERR "%s : scatterlist map failed\n",
+				       sock->dev.bus_id);
+				tifm_unmap_sg(sock, &host->bounce_buf, 1,
+					      r_data->flags & MMC_DATA_WRITE
+					      ? PCI_DMA_TODEVICE
+					      : PCI_DMA_FROMDEVICE);
+				spin_unlock_irqrestore(&sock->lock, flags);
+				goto err_out;
+			}
+
+			writel(TIFM_FIFO_INT_SETALL,
+			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+			writel(ilog2(r_data->blksz) - 2,
+			       sock->addr + SOCK_FIFO_PAGE_SIZE);
+			writel(TIFM_FIFO_ENABLE,
+			       sock->addr + SOCK_FIFO_CONTROL);
+			writel(TIFM_FIFO_INTMASK,
+			       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+
+			if (r_data->flags & MMC_DATA_WRITE)
+				writel(TIFM_MMCSD_TXDE,
+				       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+			else
+				writel(TIFM_MMCSD_RXDE,
+				       sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+			tifm_sd_set_dma_data(host, r_data);
+		}
+
+		writel(r_data->blocks - 1,
+		       sock->addr + SOCK_MMCSD_NUM_BLOCKS);
+		writel(r_data->blksz - 1,
+		       sock->addr + SOCK_MMCSD_BLOCK_LEN);
+	}
+
+	host->req = mrq;
+	mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+	writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
+	       sock->addr + SOCK_CONTROL);
+	tifm_sd_exec(host, mrq->cmd);
+	spin_unlock_irqrestore(&sock->lock, flags);
+	return;
+
+err_out:
+	mrq->cmd->error = MMC_ERR_TIMEOUT;
+	mmc_request_done(mmc, mrq);
+}
+
+static void tifm_sd_end_cmd(unsigned long data)
+{
+	struct tifm_sd *host = (struct tifm_sd*)data;
+	struct tifm_dev *sock = host->dev;
+	struct mmc_host *mmc = tifm_get_drvdata(sock);
+	struct mmc_request *mrq;
+	struct mmc_data *r_data = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sock->lock, flags);
+
+	del_timer(&host->timer);
+	mrq = host->req;
+	host->req = NULL;
+
+	if (!mrq) {
+		printk(KERN_ERR " %s : no request to complete?\n",
+		       sock->dev.bus_id);
+		spin_unlock_irqrestore(&sock->lock, flags);
+		return;
+	}
+
+	r_data = mrq->cmd->data;
+	if (r_data) {
+		if (host->no_dma) {
+			writel((~TIFM_MMCSD_BUFINT)
+			       & readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+			       sock->addr + SOCK_MMCSD_INT_ENABLE);
+		} else {
+			tifm_unmap_sg(sock, &host->bounce_buf, 1,
+				      (r_data->flags & MMC_DATA_WRITE)
+				      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+			tifm_unmap_sg(sock, r_data->sg, r_data->sg_len,
+				      (r_data->flags & MMC_DATA_WRITE)
+				      ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+		}
+
+		r_data->bytes_xfered = r_data->blocks
+			- readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1;
+		r_data->bytes_xfered *= r_data->blksz;
+		r_data->bytes_xfered += r_data->blksz
+			- readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1;
+	}
+
+	writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
+	       sock->addr + SOCK_CONTROL);
+
+	spin_unlock_irqrestore(&sock->lock, flags);
+	mmc_request_done(mmc, mrq);
+}
+
+static void tifm_sd_abort(unsigned long data)
+{
+	struct tifm_sd *host = (struct tifm_sd*)data;
+
+	printk(KERN_ERR
+	       "%s : card failed to respond for a long period of time "
+	       "(%x, %x)\n",
+	       host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags);
+
+	tifm_eject(host->dev);
+}
+
+static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct tifm_sd *host = mmc_priv(mmc);
+	struct tifm_dev *sock = host->dev;
+	unsigned int clk_div1, clk_div2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sock->lock, flags);
+
+	dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, "
+		"chip_select = %x, power_mode = %x, bus_width = %x\n",
+		ios->clock, ios->vdd, ios->bus_mode, ios->chip_select,
+		ios->power_mode, ios->bus_width);
+
+	if (ios->bus_width == MMC_BUS_WIDTH_4) {
+		writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG),
+		       sock->addr + SOCK_MMCSD_CONFIG);
+	} else {
+		writel((~TIFM_MMCSD_4BBUS)
+		       & readl(sock->addr + SOCK_MMCSD_CONFIG),
+		       sock->addr + SOCK_MMCSD_CONFIG);
+	}
+
+	if (ios->clock) {
+		clk_div1 = 20000000 / ios->clock;
+		if (!clk_div1)
+			clk_div1 = 1;
+
+		clk_div2 = 24000000 / ios->clock;
+		if (!clk_div2)
+			clk_div2 = 1;
+
+		if ((20000000 / clk_div1) > ios->clock)
+			clk_div1++;
+		if ((24000000 / clk_div2) > ios->clock)
+			clk_div2++;
+		if ((20000000 / clk_div1) > (24000000 / clk_div2)) {
+			host->clk_freq = 20000000;
+			host->clk_div = clk_div1;
+			writel((~TIFM_CTRL_FAST_CLK)
+			       & readl(sock->addr + SOCK_CONTROL),
+			       sock->addr + SOCK_CONTROL);
+		} else {
+			host->clk_freq = 24000000;
+			host->clk_div = clk_div2;
+			writel(TIFM_CTRL_FAST_CLK
+			       | readl(sock->addr + SOCK_CONTROL),
+			       sock->addr + SOCK_CONTROL);
+		}
+	} else {
+		host->clk_div = 0;
+	}
+	host->clk_div &= TIFM_MMCSD_CLKMASK;
+	writel(host->clk_div
+	       | ((~TIFM_MMCSD_CLKMASK)
+		  & readl(sock->addr + SOCK_MMCSD_CONFIG)),
+	       sock->addr + SOCK_MMCSD_CONFIG);
+
+	host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN);
+
+	/* chip_select : maybe later */
+	//vdd
+	//power is set before probe / after remove
+
+	spin_unlock_irqrestore(&sock->lock, flags);
+}
+
+static int tifm_sd_ro(struct mmc_host *mmc)
+{
+	int rc = 0;
+	struct tifm_sd *host = mmc_priv(mmc);
+	struct tifm_dev *sock = host->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sock->lock, flags);
+	if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE))
+		rc = 1;
+	spin_unlock_irqrestore(&sock->lock, flags);
+	return rc;
+}
+
+static const struct mmc_host_ops tifm_sd_ops = {
+	.request = tifm_sd_request,
+	.set_ios = tifm_sd_ios,
+	.get_ro  = tifm_sd_ro
+};
+
+static int tifm_sd_initialize_host(struct tifm_sd *host)
+{
+	int rc;
+	unsigned int host_status = 0;
+	struct tifm_dev *sock = host->dev;
+
+	writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
+	mmiowb();
+	host->clk_div = 61;
+	host->clk_freq = 20000000;
+	writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL);
+	writel(host->clk_div | TIFM_MMCSD_POWER,
+	       sock->addr + SOCK_MMCSD_CONFIG);
+
+	/* wait up to 0.51 sec for reset */
+	for (rc = 32; rc <= 256; rc <<= 1) {
+		if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) {
+			rc = 0;
+			break;
+		}
+		msleep(rc);
+	}
+
+	if (rc) {
+		printk(KERN_ERR "%s : controller failed to reset\n",
+		       sock->dev.bus_id);
+		return -ENODEV;
+	}
+
+	writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS);
+	writel(host->clk_div | TIFM_MMCSD_POWER,
+	       sock->addr + SOCK_MMCSD_CONFIG);
+	writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+	// command timeout fixed to 64 clocks for now
+	writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO);
+	writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND);
+
+	for (rc = 16; rc <= 64; rc <<= 1) {
+		host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
+		writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
+		if (!(host_status & TIFM_MMCSD_ERRMASK)
+		    && (host_status & TIFM_MMCSD_EOC)) {
+			rc = 0;
+			break;
+		}
+		msleep(rc);
+	}
+
+	if (rc) {
+		printk(KERN_ERR
+		       "%s : card not ready - probe failed on initialization\n",
+		       sock->dev.bus_id);
+		return -ENODEV;
+	}
+
+	writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC
+	       | TIFM_MMCSD_ERRMASK,
+	       sock->addr + SOCK_MMCSD_INT_ENABLE);
+	mmiowb();
+
+	return 0;
+}
+
+static int tifm_sd_probe(struct tifm_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct tifm_sd *host;
+	int rc = -EIO;
+
+	if (!(TIFM_SOCK_STATE_OCCUPIED
+	      & readl(sock->addr + SOCK_PRESENT_STATE))) {
+		printk(KERN_WARNING "%s : card gone, unexpectedly\n",
+		       sock->dev.bus_id);
+		return rc;
+	}
+
+	mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	host->no_dma = no_dma;
+	tifm_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->timeout_jiffies = msecs_to_jiffies(1000);
+
+	tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
+		     (unsigned long)host);
+	setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host);
+
+	mmc->ops = &tifm_sd_ops;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
+	mmc->f_min = 20000000 / 60;
+	mmc->f_max = 24000000;
+
+	mmc->max_blk_count = 2048;
+	mmc->max_hw_segs = mmc->max_blk_count;
+	mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE);
+	mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
+	mmc->max_req_size = mmc->max_seg_size;
+	mmc->max_phys_segs = mmc->max_hw_segs;
+
+	sock->card_event = tifm_sd_card_event;
+	sock->data_event = tifm_sd_data_event;
+	rc = tifm_sd_initialize_host(host);
+
+	if (!rc)
+		rc = mmc_add_host(mmc);
+	if (!rc)
+		return 0;
+
+	mmc_free_host(mmc);
+	return rc;
+}
+
+static void tifm_sd_remove(struct tifm_dev *sock)
+{
+	struct mmc_host *mmc = tifm_get_drvdata(sock);
+	struct tifm_sd *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&sock->lock, flags);
+	host->eject = 1;
+	writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
+	mmiowb();
+	spin_unlock_irqrestore(&sock->lock, flags);
+
+	tasklet_kill(&host->finish_tasklet);
+
+	spin_lock_irqsave(&sock->lock, flags);
+	if (host->req) {
+		writel(TIFM_FIFO_INT_SETALL,
+		       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+		writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+		host->req->cmd->error = MMC_ERR_TIMEOUT;
+		if (host->req->stop)
+			host->req->stop->error = MMC_ERR_TIMEOUT;
+		tasklet_schedule(&host->finish_tasklet);
+	}
+	spin_unlock_irqrestore(&sock->lock, flags);
+	mmc_remove_host(mmc);
+	dev_dbg(&sock->dev, "after remove\n");
+
+	/* The meaning of the bit majority in this constant is unknown. */
+	writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
+	       sock->addr + SOCK_CONTROL);
+
+	mmc_free_host(mmc);
+}
+
+#ifdef CONFIG_PM
+
+static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
+{
+	struct mmc_host *mmc = tifm_get_drvdata(sock);
+	int rc;
+
+	rc = mmc_suspend_host(mmc, state);
+	/* The meaning of the bit majority in this constant is unknown. */
+	writel(0xfff8 & readl(sock->addr + SOCK_CONTROL),
+	       sock->addr + SOCK_CONTROL);
+	return rc;
+}
+
+static int tifm_sd_resume(struct tifm_dev *sock)
+{
+	struct mmc_host *mmc = tifm_get_drvdata(sock);
+	struct tifm_sd *host = mmc_priv(mmc);
+	int rc;
+
+	rc = tifm_sd_initialize_host(host);
+	dev_dbg(&sock->dev, "resume initialize %d\n", rc);
+
+	if (rc)
+		host->eject = 1;
+	else
+		rc = mmc_resume_host(mmc);
+
+	return rc;
+}
+
+#else
+
+#define tifm_sd_suspend NULL
+#define tifm_sd_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct tifm_device_id tifm_sd_id_tbl[] = {
+	{ TIFM_TYPE_SD }, { }
+};
+
+static struct tifm_driver tifm_sd_driver = {
+	.driver = {
+		.name  = DRIVER_NAME,
+		.owner = THIS_MODULE
+	},
+	.id_table = tifm_sd_id_tbl,
+	.probe    = tifm_sd_probe,
+	.remove   = tifm_sd_remove,
+	.suspend  = tifm_sd_suspend,
+	.resume   = tifm_sd_resume
+};
+
+static int __init tifm_sd_init(void)
+{
+	return tifm_register_driver(&tifm_sd_driver);
+}
+
+static void __exit tifm_sd_exit(void)
+{
+	tifm_unregister_driver(&tifm_sd_driver);
+}
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("TI FlashMedia SD driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tifm_sd_init);
+module_exit(tifm_sd_exit);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
new file mode 100644
index 0000000..9f7518b
--- /dev/null
+++ b/drivers/mmc/host/wbsd.c
@@ -0,0 +1,2062 @@
+/*
+ *  linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver
+ *
+ *  Copyright (C) 2004-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ *
+ * Warning!
+ *
+ * Changes to the FIFO system should be done with extreme care since
+ * the hardware is full of bugs related to the FIFO. Known issues are:
+ *
+ * - FIFO size field in FSR is always zero.
+ *
+ * - FIFO interrupts tend not to work as they should. Interrupts are
+ *   triggered only for full/empty events, not for threshold values.
+ *
+ * - On APIC systems the FIFO empty interrupt is sometimes lost.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/pnp.h>
+#include <linux/highmem.h>
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/scatterlist.h>
+
+#include "wbsd.h"
+
+#define DRIVER_NAME "wbsd"
+
+#define DBG(x...) \
+	pr_debug(DRIVER_NAME ": " x)
+#define DBGF(f, x...) \
+	pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
+
+/*
+ * Device resources
+ */
+
+#ifdef CONFIG_PNP
+
+static const struct pnp_device_id pnp_dev_table[] = {
+	{ "WEC0517", 0 },
+	{ "WEC0518", 0 },
+	{ "", 0 },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+#endif /* CONFIG_PNP */
+
+static const int config_ports[] = { 0x2E, 0x4E };
+static const int unlock_codes[] = { 0x83, 0x87 };
+
+static const int valid_ids[] = {
+	0x7112,
+	};
+
+#ifdef CONFIG_PNP
+static unsigned int nopnp = 0;
+#else
+static const unsigned int nopnp = 1;
+#endif
+static unsigned int io = 0x248;
+static unsigned int irq = 6;
+static int dma = 2;
+
+/*
+ * Basic functions
+ */
+
+static inline void wbsd_unlock_config(struct wbsd_host *host)
+{
+	BUG_ON(host->config == 0);
+
+	outb(host->unlock_code, host->config);
+	outb(host->unlock_code, host->config);
+}
+
+static inline void wbsd_lock_config(struct wbsd_host *host)
+{
+	BUG_ON(host->config == 0);
+
+	outb(LOCK_CODE, host->config);
+}
+
+static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
+{
+	BUG_ON(host->config == 0);
+
+	outb(reg, host->config);
+	outb(value, host->config + 1);
+}
+
+static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
+{
+	BUG_ON(host->config == 0);
+
+	outb(reg, host->config);
+	return inb(host->config + 1);
+}
+
+static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
+{
+	outb(index, host->base + WBSD_IDXR);
+	outb(value, host->base + WBSD_DATAR);
+}
+
+static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
+{
+	outb(index, host->base + WBSD_IDXR);
+	return inb(host->base + WBSD_DATAR);
+}
+
+/*
+ * Common routines
+ */
+
+static void wbsd_init_device(struct wbsd_host *host)
+{
+	u8 setup, ier;
+
+	/*
+	 * Reset chip (SD/MMC part) and fifo.
+	 */
+	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+	setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+	/*
+	 * Set DAT3 to input
+	 */
+	setup &= ~WBSD_DAT3_H;
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+	host->flags &= ~WBSD_FIGNORE_DETECT;
+
+	/*
+	 * Read back default clock.
+	 */
+	host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
+
+	/*
+	 * Power down port.
+	 */
+	outb(WBSD_POWER_N, host->base + WBSD_CSR);
+
+	/*
+	 * Set maximum timeout.
+	 */
+	wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
+
+	/*
+	 * Test for card presence
+	 */
+	if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
+		host->flags |= WBSD_FCARD_PRESENT;
+	else
+		host->flags &= ~WBSD_FCARD_PRESENT;
+
+	/*
+	 * Enable interesting interrupts.
+	 */
+	ier = 0;
+	ier |= WBSD_EINT_CARD;
+	ier |= WBSD_EINT_FIFO_THRE;
+	ier |= WBSD_EINT_CRC;
+	ier |= WBSD_EINT_TIMEOUT;
+	ier |= WBSD_EINT_TC;
+
+	outb(ier, host->base + WBSD_EIR);
+
+	/*
+	 * Clear interrupts.
+	 */
+	inb(host->base + WBSD_ISR);
+}
+
+static void wbsd_reset(struct wbsd_host *host)
+{
+	u8 setup;
+
+	printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
+
+	/*
+	 * Soft reset of chip (SD/MMC part).
+	 */
+	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+	setup |= WBSD_SOFT_RESET;
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+}
+
+static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
+{
+	unsigned long dmaflags;
+
+	DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
+
+	if (host->dma >= 0) {
+		/*
+		 * Release ISA DMA controller.
+		 */
+		dmaflags = claim_dma_lock();
+		disable_dma(host->dma);
+		clear_dma_ff(host->dma);
+		release_dma_lock(dmaflags);
+
+		/*
+		 * Disable DMA on host.
+		 */
+		wbsd_write_index(host, WBSD_IDX_DMA, 0);
+	}
+
+	host->mrq = NULL;
+
+	/*
+	 * MMC layer might call back into the driver so first unlock.
+	 */
+	spin_unlock(&host->lock);
+	mmc_request_done(host->mmc, mrq);
+	spin_lock(&host->lock);
+}
+
+/*
+ * Scatter/gather functions
+ */
+
+static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
+{
+	/*
+	 * Get info. about SG list from data structure.
+	 */
+	host->cur_sg = data->sg;
+	host->num_sg = data->sg_len;
+
+	host->offset = 0;
+	host->remain = host->cur_sg->length;
+}
+
+static inline int wbsd_next_sg(struct wbsd_host *host)
+{
+	/*
+	 * Skip to next SG entry.
+	 */
+	host->cur_sg++;
+	host->num_sg--;
+
+	/*
+	 * Any entries left?
+	 */
+	if (host->num_sg > 0) {
+		host->offset = 0;
+		host->remain = host->cur_sg->length;
+	}
+
+	return host->num_sg;
+}
+
+static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
+{
+	return page_address(host->cur_sg->page) + host->cur_sg->offset;
+}
+
+static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
+{
+	unsigned int len, i;
+	struct scatterlist *sg;
+	char *dmabuf = host->dma_buffer;
+	char *sgbuf;
+
+	sg = data->sg;
+	len = data->sg_len;
+
+	for (i = 0; i < len; i++) {
+		sgbuf = page_address(sg[i].page) + sg[i].offset;
+		memcpy(dmabuf, sgbuf, sg[i].length);
+		dmabuf += sg[i].length;
+	}
+}
+
+static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
+{
+	unsigned int len, i;
+	struct scatterlist *sg;
+	char *dmabuf = host->dma_buffer;
+	char *sgbuf;
+
+	sg = data->sg;
+	len = data->sg_len;
+
+	for (i = 0; i < len; i++) {
+		sgbuf = page_address(sg[i].page) + sg[i].offset;
+		memcpy(sgbuf, dmabuf, sg[i].length);
+		dmabuf += sg[i].length;
+	}
+}
+
+/*
+ * Command handling
+ */
+
+static inline void wbsd_get_short_reply(struct wbsd_host *host,
+					struct mmc_command *cmd)
+{
+	/*
+	 * Correct response type?
+	 */
+	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
+		cmd->error = MMC_ERR_INVALID;
+		return;
+	}
+
+	cmd->resp[0]  = wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
+	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
+	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
+	cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
+	cmd->resp[1]  = wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
+}
+
+static inline void wbsd_get_long_reply(struct wbsd_host *host,
+	struct mmc_command *cmd)
+{
+	int i;
+
+	/*
+	 * Correct response type?
+	 */
+	if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
+		cmd->error = MMC_ERR_INVALID;
+		return;
+	}
+
+	for (i = 0; i < 4; i++) {
+		cmd->resp[i] =
+			wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
+		cmd->resp[i] |=
+			wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16;
+		cmd->resp[i] |=
+			wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8;
+		cmd->resp[i] |=
+			wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0;
+	}
+}
+
+static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
+{
+	int i;
+	u8 status, isr;
+
+	DBGF("Sending cmd (%x)\n", cmd->opcode);
+
+	/*
+	 * Clear accumulated ISR. The interrupt routine
+	 * will fill this one with events that occur during
+	 * transfer.
+	 */
+	host->isr = 0;
+
+	/*
+	 * Send the command (CRC calculated by host).
+	 */
+	outb(cmd->opcode, host->base + WBSD_CMDR);
+	for (i = 3; i >= 0; i--)
+		outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
+
+	cmd->error = MMC_ERR_NONE;
+
+	/*
+	 * Wait for the request to complete.
+	 */
+	do {
+		status = wbsd_read_index(host, WBSD_IDX_STATUS);
+	} while (status & WBSD_CARDTRAFFIC);
+
+	/*
+	 * Do we expect a reply?
+	 */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		/*
+		 * Read back status.
+		 */
+		isr = host->isr;
+
+		/* Card removed? */
+		if (isr & WBSD_INT_CARD)
+			cmd->error = MMC_ERR_TIMEOUT;
+		/* Timeout? */
+		else if (isr & WBSD_INT_TIMEOUT)
+			cmd->error = MMC_ERR_TIMEOUT;
+		/* CRC? */
+		else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
+			cmd->error = MMC_ERR_BADCRC;
+		/* All ok */
+		else {
+			if (cmd->flags & MMC_RSP_136)
+				wbsd_get_long_reply(host, cmd);
+			else
+				wbsd_get_short_reply(host, cmd);
+		}
+	}
+
+	DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
+}
+
+/*
+ * Data functions
+ */
+
+static void wbsd_empty_fifo(struct wbsd_host *host)
+{
+	struct mmc_data *data = host->mrq->cmd->data;
+	char *buffer;
+	int i, fsr, fifo;
+
+	/*
+	 * Handle excessive data.
+	 */
+	if (host->num_sg == 0)
+		return;
+
+	buffer = wbsd_sg_to_buffer(host) + host->offset;
+
+	/*
+	 * Drain the fifo. This has a tendency to loop longer
+	 * than the FIFO length (usually one block).
+	 */
+	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
+		/*
+		 * The size field in the FSR is broken so we have to
+		 * do some guessing.
+		 */
+		if (fsr & WBSD_FIFO_FULL)
+			fifo = 16;
+		else if (fsr & WBSD_FIFO_FUTHRE)
+			fifo = 8;
+		else
+			fifo = 1;
+
+		for (i = 0; i < fifo; i++) {
+			*buffer = inb(host->base + WBSD_DFR);
+			buffer++;
+			host->offset++;
+			host->remain--;
+
+			data->bytes_xfered++;
+
+			/*
+			 * End of scatter list entry?
+			 */
+			if (host->remain == 0) {
+				/*
+				 * Get next entry. Check if last.
+				 */
+				if (!wbsd_next_sg(host))
+					return;
+
+				buffer = wbsd_sg_to_buffer(host);
+			}
+		}
+	}
+
+	/*
+	 * This is a very dirty hack to solve a
+	 * hardware problem. The chip doesn't trigger
+	 * FIFO threshold interrupts properly.
+	 */
+	if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
+		tasklet_schedule(&host->fifo_tasklet);
+}
+
+static void wbsd_fill_fifo(struct wbsd_host *host)
+{
+	struct mmc_data *data = host->mrq->cmd->data;
+	char *buffer;
+	int i, fsr, fifo;
+
+	/*
+	 * Check that we aren't being called after the
+	 * entire buffer has been transfered.
+	 */
+	if (host->num_sg == 0)
+		return;
+
+	buffer = wbsd_sg_to_buffer(host) + host->offset;
+
+	/*
+	 * Fill the fifo. This has a tendency to loop longer
+	 * than the FIFO length (usually one block).
+	 */
+	while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
+		/*
+		 * The size field in the FSR is broken so we have to
+		 * do some guessing.
+		 */
+		if (fsr & WBSD_FIFO_EMPTY)
+			fifo = 0;
+		else if (fsr & WBSD_FIFO_EMTHRE)
+			fifo = 8;
+		else
+			fifo = 15;
+
+		for (i = 16; i > fifo; i--) {
+			outb(*buffer, host->base + WBSD_DFR);
+			buffer++;
+			host->offset++;
+			host->remain--;
+
+			data->bytes_xfered++;
+
+			/*
+			 * End of scatter list entry?
+			 */
+			if (host->remain == 0) {
+				/*
+				 * Get next entry. Check if last.
+				 */
+				if (!wbsd_next_sg(host))
+					return;
+
+				buffer = wbsd_sg_to_buffer(host);
+			}
+		}
+	}
+
+	/*
+	 * The controller stops sending interrupts for
+	 * 'FIFO empty' under certain conditions. So we
+	 * need to be a bit more pro-active.
+	 */
+	tasklet_schedule(&host->fifo_tasklet);
+}
+
+static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
+{
+	u16 blksize;
+	u8 setup;
+	unsigned long dmaflags;
+	unsigned int size;
+
+	DBGF("blksz %04x blks %04x flags %08x\n",
+		data->blksz, data->blocks, data->flags);
+	DBGF("tsac %d ms nsac %d clk\n",
+		data->timeout_ns / 1000000, data->timeout_clks);
+
+	/*
+	 * Calculate size.
+	 */
+	size = data->blocks * data->blksz;
+
+	/*
+	 * Check timeout values for overflow.
+	 * (Yes, some cards cause this value to overflow).
+	 */
+	if (data->timeout_ns > 127000000)
+		wbsd_write_index(host, WBSD_IDX_TAAC, 127);
+	else {
+		wbsd_write_index(host, WBSD_IDX_TAAC,
+			data->timeout_ns / 1000000);
+	}
+
+	if (data->timeout_clks > 255)
+		wbsd_write_index(host, WBSD_IDX_NSAC, 255);
+	else
+		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
+
+	/*
+	 * Inform the chip of how large blocks will be
+	 * sent. It needs this to determine when to
+	 * calculate CRC.
+	 *
+	 * Space for CRC must be included in the size.
+	 * Two bytes are needed for each data line.
+	 */
+	if (host->bus_width == MMC_BUS_WIDTH_1) {
+		blksize = data->blksz + 2;
+
+		wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
+		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+	} else if (host->bus_width == MMC_BUS_WIDTH_4) {
+		blksize = data->blksz + 2 * 4;
+
+		wbsd_write_index(host, WBSD_IDX_PBSMSB,
+			((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
+		wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+	} else {
+		data->error = MMC_ERR_INVALID;
+		return;
+	}
+
+	/*
+	 * Clear the FIFO. This is needed even for DMA
+	 * transfers since the chip still uses the FIFO
+	 * internally.
+	 */
+	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+	setup |= WBSD_FIFO_RESET;
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+	/*
+	 * DMA transfer?
+	 */
+	if (host->dma >= 0) {
+		/*
+		 * The buffer for DMA is only 64 kB.
+		 */
+		BUG_ON(size > 0x10000);
+		if (size > 0x10000) {
+			data->error = MMC_ERR_INVALID;
+			return;
+		}
+
+		/*
+		 * Transfer data from the SG list to
+		 * the DMA buffer.
+		 */
+		if (data->flags & MMC_DATA_WRITE)
+			wbsd_sg_to_dma(host, data);
+
+		/*
+		 * Initialise the ISA DMA controller.
+		 */
+		dmaflags = claim_dma_lock();
+		disable_dma(host->dma);
+		clear_dma_ff(host->dma);
+		if (data->flags & MMC_DATA_READ)
+			set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
+		else
+			set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
+		set_dma_addr(host->dma, host->dma_addr);
+		set_dma_count(host->dma, size);
+
+		enable_dma(host->dma);
+		release_dma_lock(dmaflags);
+
+		/*
+		 * Enable DMA on the host.
+		 */
+		wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
+	} else {
+		/*
+		 * This flag is used to keep printk
+		 * output to a minimum.
+		 */
+		host->firsterr = 1;
+
+		/*
+		 * Initialise the SG list.
+		 */
+		wbsd_init_sg(host, data);
+
+		/*
+		 * Turn off DMA.
+		 */
+		wbsd_write_index(host, WBSD_IDX_DMA, 0);
+
+		/*
+		 * Set up FIFO threshold levels (and fill
+		 * buffer if doing a write).
+		 */
+		if (data->flags & MMC_DATA_READ) {
+			wbsd_write_index(host, WBSD_IDX_FIFOEN,
+				WBSD_FIFOEN_FULL | 8);
+		} else {
+			wbsd_write_index(host, WBSD_IDX_FIFOEN,
+				WBSD_FIFOEN_EMPTY | 8);
+			wbsd_fill_fifo(host);
+		}
+	}
+
+	data->error = MMC_ERR_NONE;
+}
+
+static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
+{
+	unsigned long dmaflags;
+	int count;
+	u8 status;
+
+	WARN_ON(host->mrq == NULL);
+
+	/*
+	 * Send a stop command if needed.
+	 */
+	if (data->stop)
+		wbsd_send_command(host, data->stop);
+
+	/*
+	 * Wait for the controller to leave data
+	 * transfer state.
+	 */
+	do {
+		status = wbsd_read_index(host, WBSD_IDX_STATUS);
+	} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
+
+	/*
+	 * DMA transfer?
+	 */
+	if (host->dma >= 0) {
+		/*
+		 * Disable DMA on the host.
+		 */
+		wbsd_write_index(host, WBSD_IDX_DMA, 0);
+
+		/*
+		 * Turn of ISA DMA controller.
+		 */
+		dmaflags = claim_dma_lock();
+		disable_dma(host->dma);
+		clear_dma_ff(host->dma);
+		count = get_dma_residue(host->dma);
+		release_dma_lock(dmaflags);
+
+		data->bytes_xfered = host->mrq->data->blocks *
+			host->mrq->data->blksz - count;
+		data->bytes_xfered -= data->bytes_xfered % data->blksz;
+
+		/*
+		 * Any leftover data?
+		 */
+		if (count) {
+			printk(KERN_ERR "%s: Incomplete DMA transfer. "
+				"%d bytes left.\n",
+				mmc_hostname(host->mmc), count);
+
+			if (data->error == MMC_ERR_NONE)
+				data->error = MMC_ERR_FAILED;
+		} else {
+			/*
+			 * Transfer data from DMA buffer to
+			 * SG list.
+			 */
+			if (data->flags & MMC_DATA_READ)
+				wbsd_dma_to_sg(host, data);
+		}
+
+		if (data->error != MMC_ERR_NONE) {
+			if (data->bytes_xfered)
+				data->bytes_xfered -= data->blksz;
+		}
+	}
+
+	DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+
+	wbsd_request_end(host, host->mrq);
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * MMC layer callbacks                                                       *
+ *                                                                           *
+\*****************************************************************************/
+
+static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct wbsd_host *host = mmc_priv(mmc);
+	struct mmc_command *cmd;
+
+	/*
+	 * Disable tasklets to avoid a deadlock.
+	 */
+	spin_lock_bh(&host->lock);
+
+	BUG_ON(host->mrq != NULL);
+
+	cmd = mrq->cmd;
+
+	host->mrq = mrq;
+
+	/*
+	 * If there is no card in the slot then
+	 * timeout immediatly.
+	 */
+	if (!(host->flags & WBSD_FCARD_PRESENT)) {
+		cmd->error = MMC_ERR_TIMEOUT;
+		goto done;
+	}
+
+	/*
+	 * Does the request include data?
+	 */
+	if (cmd->data) {
+		wbsd_prepare_data(host, cmd->data);
+
+		if (cmd->data->error != MMC_ERR_NONE)
+			goto done;
+	}
+
+	wbsd_send_command(host, cmd);
+
+	/*
+	 * If this is a data transfer the request
+	 * will be finished after the data has
+	 * transfered.
+	 */
+	if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
+		/*
+		 * The hardware is so delightfully stupid that it has a list
+		 * of "data" commands. If a command isn't on this list, it'll
+		 * just go back to the idle state and won't send any data
+		 * interrupts.
+		 */
+		switch (cmd->opcode) {
+		case 11:
+		case 17:
+		case 18:
+		case 20:
+		case 24:
+		case 25:
+		case 26:
+		case 27:
+		case 30:
+		case 42:
+		case 56:
+			break;
+
+		/* ACMDs. We don't keep track of state, so we just treat them
+		 * like any other command. */
+		case 51:
+			break;
+
+		default:
+#ifdef CONFIG_MMC_DEBUG
+			printk(KERN_WARNING "%s: Data command %d is not "
+				"supported by this controller.\n",
+				mmc_hostname(host->mmc), cmd->opcode);
+#endif
+			cmd->data->error = MMC_ERR_INVALID;
+
+			if (cmd->data->stop)
+				wbsd_send_command(host, cmd->data->stop);
+
+			goto done;
+		};
+
+		/*
+		 * Dirty fix for hardware bug.
+		 */
+		if (host->dma == -1)
+			tasklet_schedule(&host->fifo_tasklet);
+
+		spin_unlock_bh(&host->lock);
+
+		return;
+	}
+
+done:
+	wbsd_request_end(host, mrq);
+
+	spin_unlock_bh(&host->lock);
+}
+
+static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct wbsd_host *host = mmc_priv(mmc);
+	u8 clk, setup, pwr;
+
+	spin_lock_bh(&host->lock);
+
+	/*
+	 * Reset the chip on each power off.
+	 * Should clear out any weird states.
+	 */
+	if (ios->power_mode == MMC_POWER_OFF)
+		wbsd_init_device(host);
+
+	if (ios->clock >= 24000000)
+		clk = WBSD_CLK_24M;
+	else if (ios->clock >= 16000000)
+		clk = WBSD_CLK_16M;
+	else if (ios->clock >= 12000000)
+		clk = WBSD_CLK_12M;
+	else
+		clk = WBSD_CLK_375K;
+
+	/*
+	 * Only write to the clock register when
+	 * there is an actual change.
+	 */
+	if (clk != host->clk) {
+		wbsd_write_index(host, WBSD_IDX_CLK, clk);
+		host->clk = clk;
+	}
+
+	/*
+	 * Power up card.
+	 */
+	if (ios->power_mode != MMC_POWER_OFF) {
+		pwr = inb(host->base + WBSD_CSR);
+		pwr &= ~WBSD_POWER_N;
+		outb(pwr, host->base + WBSD_CSR);
+	}
+
+	/*
+	 * MMC cards need to have pin 1 high during init.
+	 * It wreaks havoc with the card detection though so
+	 * that needs to be disabled.
+	 */
+	setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+	if (ios->chip_select == MMC_CS_HIGH) {
+		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
+		setup |= WBSD_DAT3_H;
+		host->flags |= WBSD_FIGNORE_DETECT;
+	} else {
+		if (setup & WBSD_DAT3_H) {
+			setup &= ~WBSD_DAT3_H;
+
+			/*
+			 * We cannot resume card detection immediatly
+			 * because of capacitance and delays in the chip.
+			 */
+			mod_timer(&host->ignore_timer, jiffies + HZ / 100);
+		}
+	}
+	wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+	/*
+	 * Store bus width for later. Will be used when
+	 * setting up the data transfer.
+	 */
+	host->bus_width = ios->bus_width;
+
+	spin_unlock_bh(&host->lock);
+}
+
+static int wbsd_get_ro(struct mmc_host *mmc)
+{
+	struct wbsd_host *host = mmc_priv(mmc);
+	u8 csr;
+
+	spin_lock_bh(&host->lock);
+
+	csr = inb(host->base + WBSD_CSR);
+	csr |= WBSD_MSLED;
+	outb(csr, host->base + WBSD_CSR);
+
+	mdelay(1);
+
+	csr = inb(host->base + WBSD_CSR);
+	csr &= ~WBSD_MSLED;
+	outb(csr, host->base + WBSD_CSR);
+
+	spin_unlock_bh(&host->lock);
+
+	return csr & WBSD_WRPT;
+}
+
+static const struct mmc_host_ops wbsd_ops = {
+	.request	= wbsd_request,
+	.set_ios	= wbsd_set_ios,
+	.get_ro		= wbsd_get_ro,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Interrupt handling                                                        *
+ *                                                                           *
+\*****************************************************************************/
+
+/*
+ * Helper function to reset detection ignore
+ */
+
+static void wbsd_reset_ignore(unsigned long data)
+{
+	struct wbsd_host *host = (struct wbsd_host *)data;
+
+	BUG_ON(host == NULL);
+
+	DBG("Resetting card detection ignore\n");
+
+	spin_lock_bh(&host->lock);
+
+	host->flags &= ~WBSD_FIGNORE_DETECT;
+
+	/*
+	 * Card status might have changed during the
+	 * blackout.
+	 */
+	tasklet_schedule(&host->card_tasklet);
+
+	spin_unlock_bh(&host->lock);
+}
+
+/*
+ * Tasklets
+ */
+
+static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
+{
+	WARN_ON(!host->mrq);
+	if (!host->mrq)
+		return NULL;
+
+	WARN_ON(!host->mrq->cmd);
+	if (!host->mrq->cmd)
+		return NULL;
+
+	WARN_ON(!host->mrq->cmd->data);
+	if (!host->mrq->cmd->data)
+		return NULL;
+
+	return host->mrq->cmd->data;
+}
+
+static void wbsd_tasklet_card(unsigned long param)
+{
+	struct wbsd_host *host = (struct wbsd_host *)param;
+	u8 csr;
+	int delay = -1;
+
+	spin_lock(&host->lock);
+
+	if (host->flags & WBSD_FIGNORE_DETECT) {
+		spin_unlock(&host->lock);
+		return;
+	}
+
+	csr = inb(host->base + WBSD_CSR);
+	WARN_ON(csr == 0xff);
+
+	if (csr & WBSD_CARDPRESENT) {
+		if (!(host->flags & WBSD_FCARD_PRESENT)) {
+			DBG("Card inserted\n");
+			host->flags |= WBSD_FCARD_PRESENT;
+
+			delay = 500;
+		}
+	} else if (host->flags & WBSD_FCARD_PRESENT) {
+		DBG("Card removed\n");
+		host->flags &= ~WBSD_FCARD_PRESENT;
+
+		if (host->mrq) {
+			printk(KERN_ERR "%s: Card removed during transfer!\n",
+				mmc_hostname(host->mmc));
+			wbsd_reset(host);
+
+			host->mrq->cmd->error = MMC_ERR_FAILED;
+			tasklet_schedule(&host->finish_tasklet);
+		}
+
+		delay = 0;
+	}
+
+	/*
+	 * Unlock first since we might get a call back.
+	 */
+
+	spin_unlock(&host->lock);
+
+	if (delay != -1)
+		mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
+}
+
+static void wbsd_tasklet_fifo(unsigned long param)
+{
+	struct wbsd_host *host = (struct wbsd_host *)param;
+	struct mmc_data *data;
+
+	spin_lock(&host->lock);
+
+	if (!host->mrq)
+		goto end;
+
+	data = wbsd_get_data(host);
+	if (!data)
+		goto end;
+
+	if (data->flags & MMC_DATA_WRITE)
+		wbsd_fill_fifo(host);
+	else
+		wbsd_empty_fifo(host);
+
+	/*
+	 * Done?
+	 */
+	if (host->num_sg == 0) {
+		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
+		tasklet_schedule(&host->finish_tasklet);
+	}
+
+end:
+	spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_crc(unsigned long param)
+{
+	struct wbsd_host *host = (struct wbsd_host *)param;
+	struct mmc_data *data;
+
+	spin_lock(&host->lock);
+
+	if (!host->mrq)
+		goto end;
+
+	data = wbsd_get_data(host);
+	if (!data)
+		goto end;
+
+	DBGF("CRC error\n");
+
+	data->error = MMC_ERR_BADCRC;
+
+	tasklet_schedule(&host->finish_tasklet);
+
+end:
+	spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_timeout(unsigned long param)
+{
+	struct wbsd_host *host = (struct wbsd_host *)param;
+	struct mmc_data *data;
+
+	spin_lock(&host->lock);
+
+	if (!host->mrq)
+		goto end;
+
+	data = wbsd_get_data(host);
+	if (!data)
+		goto end;
+
+	DBGF("Timeout\n");
+
+	data->error = MMC_ERR_TIMEOUT;
+
+	tasklet_schedule(&host->finish_tasklet);
+
+end:
+	spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_finish(unsigned long param)
+{
+	struct wbsd_host *host = (struct wbsd_host *)param;
+	struct mmc_data *data;
+
+	spin_lock(&host->lock);
+
+	WARN_ON(!host->mrq);
+	if (!host->mrq)
+		goto end;
+
+	data = wbsd_get_data(host);
+	if (!data)
+		goto end;
+
+	wbsd_finish_data(host, data);
+
+end:
+	spin_unlock(&host->lock);
+}
+
+/*
+ * Interrupt handling
+ */
+
+static irqreturn_t wbsd_irq(int irq, void *dev_id)
+{
+	struct wbsd_host *host = dev_id;
+	int isr;
+
+	isr = inb(host->base + WBSD_ISR);
+
+	/*
+	 * Was it actually our hardware that caused the interrupt?
+	 */
+	if (isr == 0xff || isr == 0x00)
+		return IRQ_NONE;
+
+	host->isr |= isr;
+
+	/*
+	 * Schedule tasklets as needed.
+	 */
+	if (isr & WBSD_INT_CARD)
+		tasklet_schedule(&host->card_tasklet);
+	if (isr & WBSD_INT_FIFO_THRE)
+		tasklet_schedule(&host->fifo_tasklet);
+	if (isr & WBSD_INT_CRC)
+		tasklet_hi_schedule(&host->crc_tasklet);
+	if (isr & WBSD_INT_TIMEOUT)
+		tasklet_hi_schedule(&host->timeout_tasklet);
+	if (isr & WBSD_INT_TC)
+		tasklet_schedule(&host->finish_tasklet);
+
+	return IRQ_HANDLED;
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Device initialisation and shutdown                                        *
+ *                                                                           *
+\*****************************************************************************/
+
+/*
+ * Allocate/free MMC structure.
+ */
+
+static int __devinit wbsd_alloc_mmc(struct device *dev)
+{
+	struct mmc_host *mmc;
+	struct wbsd_host *host;
+
+	/*
+	 * Allocate MMC structure.
+	 */
+	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+
+	host->dma = -1;
+
+	/*
+	 * Set host parameters.
+	 */
+	mmc->ops = &wbsd_ops;
+	mmc->f_min = 375000;
+	mmc->f_max = 24000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
+
+	spin_lock_init(&host->lock);
+
+	/*
+	 * Set up timers
+	 */
+	init_timer(&host->ignore_timer);
+	host->ignore_timer.data = (unsigned long)host;
+	host->ignore_timer.function = wbsd_reset_ignore;
+
+	/*
+	 * Maximum number of segments. Worst case is one sector per segment
+	 * so this will be 64kB/512.
+	 */
+	mmc->max_hw_segs = 128;
+	mmc->max_phys_segs = 128;
+
+	/*
+	 * Maximum request size. Also limited by 64KiB buffer.
+	 */
+	mmc->max_req_size = 65536;
+
+	/*
+	 * Maximum segment size. Could be one segment with the maximum number
+	 * of bytes.
+	 */
+	mmc->max_seg_size = mmc->max_req_size;
+
+	/*
+	 * Maximum block size. We have 12 bits (= 4095) but have to subtract
+	 * space for CRC. So the maximum is 4095 - 4*2 = 4087.
+	 */
+	mmc->max_blk_size = 4087;
+
+	/*
+	 * Maximum block count. There is no real limit so the maximum
+	 * request size will be the only restriction.
+	 */
+	mmc->max_blk_count = mmc->max_req_size;
+
+	dev_set_drvdata(dev, mmc);
+
+	return 0;
+}
+
+static void __devexit wbsd_free_mmc(struct device *dev)
+{
+	struct mmc_host *mmc;
+	struct wbsd_host *host;
+
+	mmc = dev_get_drvdata(dev);
+	if (!mmc)
+		return;
+
+	host = mmc_priv(mmc);
+	BUG_ON(host == NULL);
+
+	del_timer_sync(&host->ignore_timer);
+
+	mmc_free_host(mmc);
+
+	dev_set_drvdata(dev, NULL);
+}
+
+/*
+ * Scan for known chip id:s
+ */
+
+static int __devinit wbsd_scan(struct wbsd_host *host)
+{
+	int i, j, k;
+	int id;
+
+	/*
+	 * Iterate through all ports, all codes to
+	 * find hardware that is in our known list.
+	 */
+	for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
+		if (!request_region(config_ports[i], 2, DRIVER_NAME))
+			continue;
+
+		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
+			id = 0xFFFF;
+
+			host->config = config_ports[i];
+			host->unlock_code = unlock_codes[j];
+
+			wbsd_unlock_config(host);
+
+			outb(WBSD_CONF_ID_HI, config_ports[i]);
+			id = inb(config_ports[i] + 1) << 8;
+
+			outb(WBSD_CONF_ID_LO, config_ports[i]);
+			id |= inb(config_ports[i] + 1);
+
+			wbsd_lock_config(host);
+
+			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
+				if (id == valid_ids[k]) {
+					host->chip_id = id;
+
+					return 0;
+				}
+			}
+
+			if (id != 0xFFFF) {
+				DBG("Unknown hardware (id %x) found at %x\n",
+					id, config_ports[i]);
+			}
+		}
+
+		release_region(config_ports[i], 2);
+	}
+
+	host->config = 0;
+	host->unlock_code = 0;
+
+	return -ENODEV;
+}
+
+/*
+ * Allocate/free io port ranges
+ */
+
+static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
+{
+	if (base & 0x7)
+		return -EINVAL;
+
+	if (!request_region(base, 8, DRIVER_NAME))
+		return -EIO;
+
+	host->base = base;
+
+	return 0;
+}
+
+static void __devexit wbsd_release_regions(struct wbsd_host *host)
+{
+	if (host->base)
+		release_region(host->base, 8);
+
+	host->base = 0;
+
+	if (host->config)
+		release_region(host->config, 2);
+
+	host->config = 0;
+}
+
+/*
+ * Allocate/free DMA port and buffer
+ */
+
+static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
+{
+	if (dma < 0)
+		return;
+
+	if (request_dma(dma, DRIVER_NAME))
+		goto err;
+
+	/*
+	 * We need to allocate a special buffer in
+	 * order for ISA to be able to DMA to it.
+	 */
+	host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
+		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
+	if (!host->dma_buffer)
+		goto free;
+
+	/*
+	 * Translate the address to a physical address.
+	 */
+	host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
+		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+
+	/*
+	 * ISA DMA must be aligned on a 64k basis.
+	 */
+	if ((host->dma_addr & 0xffff) != 0)
+		goto kfree;
+	/*
+	 * ISA cannot access memory above 16 MB.
+	 */
+	else if (host->dma_addr >= 0x1000000)
+		goto kfree;
+
+	host->dma = dma;
+
+	return;
+
+kfree:
+	/*
+	 * If we've gotten here then there is some kind of alignment bug
+	 */
+	BUG_ON(1);
+
+	dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
+		WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+	host->dma_addr = (dma_addr_t)NULL;
+
+	kfree(host->dma_buffer);
+	host->dma_buffer = NULL;
+
+free:
+	free_dma(dma);
+
+err:
+	printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
+		"Falling back on FIFO.\n", dma);
+}
+
+static void __devexit wbsd_release_dma(struct wbsd_host *host)
+{
+	if (host->dma_addr) {
+		dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
+			WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+	}
+	kfree(host->dma_buffer);
+	if (host->dma >= 0)
+		free_dma(host->dma);
+
+	host->dma = -1;
+	host->dma_buffer = NULL;
+	host->dma_addr = (dma_addr_t)NULL;
+}
+
+/*
+ * Allocate/free IRQ.
+ */
+
+static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
+{
+	int ret;
+
+	/*
+	 * Allocate interrupt.
+	 */
+
+	ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
+	if (ret)
+		return ret;
+
+	host->irq = irq;
+
+	/*
+	 * Set up tasklets.
+	 */
+	tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
+			(unsigned long)host);
+	tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
+			(unsigned long)host);
+	tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
+			(unsigned long)host);
+	tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
+			(unsigned long)host);
+	tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
+			(unsigned long)host);
+
+	return 0;
+}
+
+static void __devexit wbsd_release_irq(struct wbsd_host *host)
+{
+	if (!host->irq)
+		return;
+
+	free_irq(host->irq, host);
+
+	host->irq = 0;
+
+	tasklet_kill(&host->card_tasklet);
+	tasklet_kill(&host->fifo_tasklet);
+	tasklet_kill(&host->crc_tasklet);
+	tasklet_kill(&host->timeout_tasklet);
+	tasklet_kill(&host->finish_tasklet);
+}
+
+/*
+ * Allocate all resources for the host.
+ */
+
+static int __devinit wbsd_request_resources(struct wbsd_host *host,
+	int base, int irq, int dma)
+{
+	int ret;
+
+	/*
+	 * Allocate I/O ports.
+	 */
+	ret = wbsd_request_region(host, base);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allocate interrupt.
+	 */
+	ret = wbsd_request_irq(host, irq);
+	if (ret)
+		return ret;
+
+	/*
+	 * Allocate DMA.
+	 */
+	wbsd_request_dma(host, dma);
+
+	return 0;
+}
+
+/*
+ * Release all resources for the host.
+ */
+
+static void __devexit wbsd_release_resources(struct wbsd_host *host)
+{
+	wbsd_release_dma(host);
+	wbsd_release_irq(host);
+	wbsd_release_regions(host);
+}
+
+/*
+ * Configure the resources the chip should use.
+ */
+
+static void wbsd_chip_config(struct wbsd_host *host)
+{
+	wbsd_unlock_config(host);
+
+	/*
+	 * Reset the chip.
+	 */
+	wbsd_write_config(host, WBSD_CONF_SWRST, 1);
+	wbsd_write_config(host, WBSD_CONF_SWRST, 0);
+
+	/*
+	 * Select SD/MMC function.
+	 */
+	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+
+	/*
+	 * Set up card detection.
+	 */
+	wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
+
+	/*
+	 * Configure chip
+	 */
+	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
+	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
+
+	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+
+	if (host->dma >= 0)
+		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+
+	/*
+	 * Enable and power up chip.
+	 */
+	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+
+	wbsd_lock_config(host);
+}
+
+/*
+ * Check that configured resources are correct.
+ */
+
+static int wbsd_chip_validate(struct wbsd_host *host)
+{
+	int base, irq, dma;
+
+	wbsd_unlock_config(host);
+
+	/*
+	 * Select SD/MMC function.
+	 */
+	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+
+	/*
+	 * Read configuration.
+	 */
+	base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
+	base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
+
+	irq = wbsd_read_config(host, WBSD_CONF_IRQ);
+
+	dma = wbsd_read_config(host, WBSD_CONF_DRQ);
+
+	wbsd_lock_config(host);
+
+	/*
+	 * Validate against given configuration.
+	 */
+	if (base != host->base)
+		return 0;
+	if (irq != host->irq)
+		return 0;
+	if ((dma != host->dma) && (host->dma != -1))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Powers down the SD function
+ */
+
+static void wbsd_chip_poweroff(struct wbsd_host *host)
+{
+	wbsd_unlock_config(host);
+
+	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+	wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
+
+	wbsd_lock_config(host);
+}
+
+/*****************************************************************************\
+ *                                                                           *
+ * Devices setup and shutdown                                                *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
+	int pnp)
+{
+	struct wbsd_host *host = NULL;
+	struct mmc_host *mmc = NULL;
+	int ret;
+
+	ret = wbsd_alloc_mmc(dev);
+	if (ret)
+		return ret;
+
+	mmc = dev_get_drvdata(dev);
+	host = mmc_priv(mmc);
+
+	/*
+	 * Scan for hardware.
+	 */
+	ret = wbsd_scan(host);
+	if (ret) {
+		if (pnp && (ret == -ENODEV)) {
+			printk(KERN_WARNING DRIVER_NAME
+				": Unable to confirm device presence. You may "
+				"experience lock-ups.\n");
+		} else {
+			wbsd_free_mmc(dev);
+			return ret;
+		}
+	}
+
+	/*
+	 * Request resources.
+	 */
+	ret = wbsd_request_resources(host, base, irq, dma);
+	if (ret) {
+		wbsd_release_resources(host);
+		wbsd_free_mmc(dev);
+		return ret;
+	}
+
+	/*
+	 * See if chip needs to be configured.
+	 */
+	if (pnp) {
+		if ((host->config != 0) && !wbsd_chip_validate(host)) {
+			printk(KERN_WARNING DRIVER_NAME
+				": PnP active but chip not configured! "
+				"You probably have a buggy BIOS. "
+				"Configuring chip manually.\n");
+			wbsd_chip_config(host);
+		}
+	} else
+		wbsd_chip_config(host);
+
+	/*
+	 * Power Management stuff. No idea how this works.
+	 * Not tested.
+	 */
+#ifdef CONFIG_PM
+	if (host->config) {
+		wbsd_unlock_config(host);
+		wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+		wbsd_lock_config(host);
+	}
+#endif
+	/*
+	 * Allow device to initialise itself properly.
+	 */
+	mdelay(5);
+
+	/*
+	 * Reset the chip into a known state.
+	 */
+	wbsd_init_device(host);
+
+	mmc_add_host(mmc);
+
+	printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
+	if (host->chip_id != 0)
+		printk(" id %x", (int)host->chip_id);
+	printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
+	if (host->dma >= 0)
+		printk(" dma %d", (int)host->dma);
+	else
+		printk(" FIFO");
+	if (pnp)
+		printk(" PnP");
+	printk("\n");
+
+	return 0;
+}
+
+static void __devexit wbsd_shutdown(struct device *dev, int pnp)
+{
+	struct mmc_host *mmc = dev_get_drvdata(dev);
+	struct wbsd_host *host;
+
+	if (!mmc)
+		return;
+
+	host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+
+	/*
+	 * Power down the SD/MMC function.
+	 */
+	if (!pnp)
+		wbsd_chip_poweroff(host);
+
+	wbsd_release_resources(host);
+
+	wbsd_free_mmc(dev);
+}
+
+/*
+ * Non-PnP
+ */
+
+static int __devinit wbsd_probe(struct platform_device *dev)
+{
+	/* Use the module parameters for resources */
+	return wbsd_init(&dev->dev, io, irq, dma, 0);
+}
+
+static int __devexit wbsd_remove(struct platform_device *dev)
+{
+	wbsd_shutdown(&dev->dev, 0);
+
+	return 0;
+}
+
+/*
+ * PnP
+ */
+
+#ifdef CONFIG_PNP
+
+static int __devinit
+wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
+{
+	int io, irq, dma;
+
+	/*
+	 * Get resources from PnP layer.
+	 */
+	io = pnp_port_start(pnpdev, 0);
+	irq = pnp_irq(pnpdev, 0);
+	if (pnp_dma_valid(pnpdev, 0))
+		dma = pnp_dma(pnpdev, 0);
+	else
+		dma = -1;
+
+	DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
+
+	return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
+}
+
+static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
+{
+	wbsd_shutdown(&dev->dev, 1);
+}
+
+#endif /* CONFIG_PNP */
+
+/*
+ * Power management
+ */
+
+#ifdef CONFIG_PM
+
+static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
+{
+	BUG_ON(host == NULL);
+
+	return mmc_suspend_host(host->mmc, state);
+}
+
+static int wbsd_resume(struct wbsd_host *host)
+{
+	BUG_ON(host == NULL);
+
+	wbsd_init_device(host);
+
+	return mmc_resume_host(host->mmc);
+}
+
+static int wbsd_platform_suspend(struct platform_device *dev,
+				 pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	struct wbsd_host *host;
+	int ret;
+
+	if (mmc == NULL)
+		return 0;
+
+	DBGF("Suspending...\n");
+
+	host = mmc_priv(mmc);
+
+	ret = wbsd_suspend(host, state);
+	if (ret)
+		return ret;
+
+	wbsd_chip_poweroff(host);
+
+	return 0;
+}
+
+static int wbsd_platform_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	struct wbsd_host *host;
+
+	if (mmc == NULL)
+		return 0;
+
+	DBGF("Resuming...\n");
+
+	host = mmc_priv(mmc);
+
+	wbsd_chip_config(host);
+
+	/*
+	 * Allow device to initialise itself properly.
+	 */
+	mdelay(5);
+
+	return wbsd_resume(host);
+}
+
+#ifdef CONFIG_PNP
+
+static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+	struct wbsd_host *host;
+
+	if (mmc == NULL)
+		return 0;
+
+	DBGF("Suspending...\n");
+
+	host = mmc_priv(mmc);
+
+	return wbsd_suspend(host, state);
+}
+
+static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+	struct wbsd_host *host;
+
+	if (mmc == NULL)
+		return 0;
+
+	DBGF("Resuming...\n");
+
+	host = mmc_priv(mmc);
+
+	/*
+	 * See if chip needs to be configured.
+	 */
+	if (host->config != 0) {
+		if (!wbsd_chip_validate(host)) {
+			printk(KERN_WARNING DRIVER_NAME
+				": PnP active but chip not configured! "
+				"You probably have a buggy BIOS. "
+				"Configuring chip manually.\n");
+			wbsd_chip_config(host);
+		}
+	}
+
+	/*
+	 * Allow device to initialise itself properly.
+	 */
+	mdelay(5);
+
+	return wbsd_resume(host);
+}
+
+#endif /* CONFIG_PNP */
+
+#else /* CONFIG_PM */
+
+#define wbsd_platform_suspend NULL
+#define wbsd_platform_resume NULL
+
+#define wbsd_pnp_suspend NULL
+#define wbsd_pnp_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct platform_device *wbsd_device;
+
+static struct platform_driver wbsd_driver = {
+	.probe		= wbsd_probe,
+	.remove		= __devexit_p(wbsd_remove),
+
+	.suspend	= wbsd_platform_suspend,
+	.resume		= wbsd_platform_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+	},
+};
+
+#ifdef CONFIG_PNP
+
+static struct pnp_driver wbsd_pnp_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= pnp_dev_table,
+	.probe		= wbsd_pnp_probe,
+	.remove		= __devexit_p(wbsd_pnp_remove),
+
+	.suspend	= wbsd_pnp_suspend,
+	.resume		= wbsd_pnp_resume,
+};
+
+#endif /* CONFIG_PNP */
+
+/*
+ * Module loading/unloading
+ */
+
+static int __init wbsd_drv_init(void)
+{
+	int result;
+
+	printk(KERN_INFO DRIVER_NAME
+		": Winbond W83L51xD SD/MMC card interface driver\n");
+	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+
+#ifdef CONFIG_PNP
+
+	if (!nopnp) {
+		result = pnp_register_driver(&wbsd_pnp_driver);
+		if (result < 0)
+			return result;
+	}
+#endif /* CONFIG_PNP */
+
+	if (nopnp) {
+		result = platform_driver_register(&wbsd_driver);
+		if (result < 0)
+			return result;
+
+		wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
+		if (!wbsd_device) {
+			platform_driver_unregister(&wbsd_driver);
+			return -ENOMEM;
+		}
+
+		result = platform_device_add(wbsd_device);
+		if (result) {
+			platform_device_put(wbsd_device);
+			platform_driver_unregister(&wbsd_driver);
+			return result;
+		}
+	}
+
+	return 0;
+}
+
+static void __exit wbsd_drv_exit(void)
+{
+#ifdef CONFIG_PNP
+
+	if (!nopnp)
+		pnp_unregister_driver(&wbsd_pnp_driver);
+
+#endif /* CONFIG_PNP */
+
+	if (nopnp) {
+		platform_device_unregister(wbsd_device);
+
+		platform_driver_unregister(&wbsd_driver);
+	}
+
+	DBG("unloaded\n");
+}
+
+module_init(wbsd_drv_init);
+module_exit(wbsd_drv_exit);
+#ifdef CONFIG_PNP
+module_param(nopnp, uint, 0444);
+#endif
+module_param(io, uint, 0444);
+module_param(irq, uint, 0444);
+module_param(dma, int, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
+
+#ifdef CONFIG_PNP
+MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
+#endif
+MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
+MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
+MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h
new file mode 100644
index 0000000..873bda1
--- /dev/null
+++ b/drivers/mmc/host/wbsd.h
@@ -0,0 +1,185 @@
+/*
+ *  linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver
+ *
+ *  Copyright (C) 2004-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#define LOCK_CODE		0xAA
+
+#define WBSD_CONF_SWRST		0x02
+#define WBSD_CONF_DEVICE	0x07
+#define WBSD_CONF_ID_HI		0x20
+#define WBSD_CONF_ID_LO		0x21
+#define WBSD_CONF_POWER		0x22
+#define WBSD_CONF_PME		0x23
+#define WBSD_CONF_PMES		0x24
+
+#define WBSD_CONF_ENABLE	0x30
+#define WBSD_CONF_PORT_HI	0x60
+#define WBSD_CONF_PORT_LO	0x61
+#define WBSD_CONF_IRQ		0x70
+#define WBSD_CONF_DRQ		0x74
+
+#define WBSD_CONF_PINS		0xF0
+
+#define DEVICE_SD		0x03
+
+#define WBSD_PINS_DAT3_HI	0x20
+#define WBSD_PINS_DAT3_OUT	0x10
+#define WBSD_PINS_GP11_HI	0x04
+#define WBSD_PINS_DETECT_GP11	0x02
+#define WBSD_PINS_DETECT_DAT3	0x01
+
+#define WBSD_CMDR		0x00
+#define WBSD_DFR		0x01
+#define WBSD_EIR		0x02
+#define WBSD_ISR		0x03
+#define WBSD_FSR		0x04
+#define WBSD_IDXR		0x05
+#define WBSD_DATAR		0x06
+#define WBSD_CSR		0x07
+
+#define WBSD_EINT_CARD		0x40
+#define WBSD_EINT_FIFO_THRE	0x20
+#define WBSD_EINT_CRC		0x10
+#define WBSD_EINT_TIMEOUT	0x08
+#define WBSD_EINT_PROGEND	0x04
+#define WBSD_EINT_BUSYEND	0x02
+#define WBSD_EINT_TC		0x01
+
+#define WBSD_INT_PENDING	0x80
+#define WBSD_INT_CARD		0x40
+#define WBSD_INT_FIFO_THRE	0x20
+#define WBSD_INT_CRC		0x10
+#define WBSD_INT_TIMEOUT	0x08
+#define WBSD_INT_PROGEND	0x04
+#define WBSD_INT_BUSYEND	0x02
+#define WBSD_INT_TC		0x01
+
+#define WBSD_FIFO_EMPTY		0x80
+#define WBSD_FIFO_FULL		0x40
+#define WBSD_FIFO_EMTHRE	0x20
+#define WBSD_FIFO_FUTHRE	0x10
+#define WBSD_FIFO_SZMASK	0x0F
+
+#define WBSD_MSLED		0x20
+#define WBSD_POWER_N		0x10
+#define WBSD_WRPT		0x04
+#define WBSD_CARDPRESENT	0x01
+
+#define WBSD_IDX_CLK		0x01
+#define WBSD_IDX_PBSMSB		0x02
+#define WBSD_IDX_TAAC		0x03
+#define WBSD_IDX_NSAC		0x04
+#define WBSD_IDX_PBSLSB		0x05
+#define WBSD_IDX_SETUP		0x06
+#define WBSD_IDX_DMA		0x07
+#define WBSD_IDX_FIFOEN		0x08
+#define WBSD_IDX_STATUS		0x10
+#define WBSD_IDX_RSPLEN		0x1E
+#define WBSD_IDX_RESP0		0x1F
+#define WBSD_IDX_RESP1		0x20
+#define WBSD_IDX_RESP2		0x21
+#define WBSD_IDX_RESP3		0x22
+#define WBSD_IDX_RESP4		0x23
+#define WBSD_IDX_RESP5		0x24
+#define WBSD_IDX_RESP6		0x25
+#define WBSD_IDX_RESP7		0x26
+#define WBSD_IDX_RESP8		0x27
+#define WBSD_IDX_RESP9		0x28
+#define WBSD_IDX_RESP10		0x29
+#define WBSD_IDX_RESP11		0x2A
+#define WBSD_IDX_RESP12		0x2B
+#define WBSD_IDX_RESP13		0x2C
+#define WBSD_IDX_RESP14		0x2D
+#define WBSD_IDX_RESP15		0x2E
+#define WBSD_IDX_RESP16		0x2F
+#define WBSD_IDX_CRCSTATUS	0x30
+#define WBSD_IDX_ISR		0x3F
+
+#define WBSD_CLK_375K		0x00
+#define WBSD_CLK_12M		0x01
+#define WBSD_CLK_16M		0x02
+#define WBSD_CLK_24M		0x03
+
+#define WBSD_DATA_WIDTH		0x01
+
+#define WBSD_DAT3_H		0x08
+#define WBSD_FIFO_RESET		0x04
+#define WBSD_SOFT_RESET		0x02
+#define WBSD_INC_INDEX		0x01
+
+#define WBSD_DMA_SINGLE		0x02
+#define WBSD_DMA_ENABLE		0x01
+
+#define WBSD_FIFOEN_EMPTY	0x20
+#define WBSD_FIFOEN_FULL	0x10
+#define WBSD_FIFO_THREMASK	0x0F
+
+#define WBSD_BLOCK_READ		0x80
+#define WBSD_BLOCK_WRITE	0x40
+#define WBSD_BUSY		0x20
+#define WBSD_CARDTRAFFIC	0x04
+#define WBSD_SENDCMD		0x02
+#define WBSD_RECVRES		0x01
+
+#define WBSD_RSP_SHORT		0x00
+#define WBSD_RSP_LONG		0x01
+
+#define WBSD_CRC_MASK		0x1F
+#define WBSD_CRC_OK		0x05 /* S010E (00101) */
+#define WBSD_CRC_FAIL		0x0B /* S101E (01011) */
+
+#define WBSD_DMA_SIZE		65536
+
+struct wbsd_host
+{
+	struct mmc_host*	mmc;		/* MMC structure */
+
+	spinlock_t		lock;		/* Mutex */
+
+	int			flags;		/* Driver states */
+
+#define WBSD_FCARD_PRESENT	(1<<0)		/* Card is present */
+#define WBSD_FIGNORE_DETECT	(1<<1)		/* Ignore card detection */
+
+	struct mmc_request*	mrq;		/* Current request */
+
+	u8			isr;		/* Accumulated ISR */
+
+	struct scatterlist*	cur_sg;		/* Current SG entry */
+	unsigned int		num_sg;		/* Number of entries left */
+
+	unsigned int		offset;		/* Offset into current entry */
+	unsigned int		remain;		/* Data left in curren entry */
+
+	char*			dma_buffer;	/* ISA DMA buffer */
+	dma_addr_t		dma_addr;	/* Physical address for same */
+
+	int			firsterr;	/* See fifo functions */
+
+	u8			clk;		/* Current clock speed */
+	unsigned char		bus_width;	/* Current bus width */
+
+	int			config;		/* Config port */
+	u8			unlock_code;	/* Code to unlock config */
+
+	int			chip_id;	/* ID of controller */
+
+	int			base;		/* I/O port base */
+	int			irq;		/* Interrupt */
+	int			dma;		/* DMA channel */
+
+	struct tasklet_struct	card_tasklet;	/* Tasklet structures */
+	struct tasklet_struct	fifo_tasklet;
+	struct tasklet_struct	crc_tasklet;
+	struct tasklet_struct	timeout_tasklet;
+	struct tasklet_struct	finish_tasklet;
+
+	struct timer_list	ignore_timer;	/* Ignore detection timer */
+};