diff mbox

mmc: Add driver for Freescale eSDHC controllers

Message ID 20090114194631.GA25978@oksana.dev.rtsoft.ru (mailing list archive)
State Superseded, archived
Delegated to: Kumar Gala
Headers show

Commit Message

Anton Vorontsov Jan. 14, 2009, 7:46 p.m. UTC
From: Xie Xiaobo <X.Xie@freescale.com>

This patch adds support for the Freescale Enhanced Secure Digital
Host Controller Interface as found in some Freescale PowerPC
microprocessors (e.g. MPC837x SOCs).

Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Signed-off-by: Konjin Lai <Konjin.Lai@freescale.com>
Signed-off-by: Joe D'Abbraccio <Joe.D'abbraccio@freescale.com>
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/mmc/host/Kconfig  |    9 +
 drivers/mmc/host/Makefile |    1 +
 drivers/mmc/host/esdhc.c  | 1321 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/esdhc.h  |  255 +++++++++
 4 files changed, 1586 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/esdhc.c
 create mode 100644 drivers/mmc/host/esdhc.h

Comments

Dave Liu Jan. 14, 2009, 11:37 p.m. UTC | #1
> This patch adds support for the Freescale Enhanced Secure Digital
> Host Controller Interface as found in some Freescale PowerPC
> microprocessors (e.g. MPC837x SOCs).

The Freescale ESDHC controller is basically compatible with the
standard SDHC controller. but the eshdc expand some bits.

The esdhc.c is much like the orignal sdhci.c.
it is possible to merge them.

Thanks, Dave
Anton Vorontsov Jan. 14, 2009, 11:56 p.m. UTC | #2
On Thu, Jan 15, 2009 at 07:37:00AM +0800, Liu Dave wrote:
> > This patch adds support for the Freescale Enhanced Secure Digital
> > Host Controller Interface as found in some Freescale PowerPC
> > microprocessors (e.g. MPC837x SOCs).
> 
> The Freescale ESDHC controller is basically compatible with the
> standard SDHC controller. but the eshdc expand some bits.
> 
> The esdhc.c is much like the orignal sdhci.c.
> it is possible to merge them.

Ah. I wonder why Freescale just didn't write some patches for sdhci,
but copied the code instead...

Thanks for the info, I'll look into merging the driver into sdhci.
Kumar Gala Jan. 15, 2009, 3:43 a.m. UTC | #3
On Jan 14, 2009, at 1:46 PM, Anton Vorontsov wrote:

> From: Xie Xiaobo <X.Xie@freescale.com>
>
> This patch adds support for the Freescale Enhanced Secure Digital
> Host Controller Interface as found in some Freescale PowerPC
> microprocessors (e.g. MPC837x SOCs).
>
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Konjin Lai <Konjin.Lai@freescale.com>
> Signed-off-by: Joe D'Abbraccio <Joe.D'abbraccio@freescale.com>
> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
> ---
> drivers/mmc/host/Kconfig  |    9 +
> drivers/mmc/host/Makefile |    1 +
> drivers/mmc/host/esdhc.c  | 1321 ++++++++++++++++++++++++++++++++++++ 
> +++++++++
> drivers/mmc/host/esdhc.h  |  255 +++++++++
> 4 files changed, 1586 insertions(+), 0 deletions(-)
> create mode 100644 drivers/mmc/host/esdhc.c
> create mode 100644 drivers/mmc/host/esdhc.h

Want to work some device tree docs for this.

- k
Pierre Ossman Jan. 15, 2009, 5:55 a.m. UTC | #4
On Thu, 15 Jan 2009 02:56:05 +0300
Anton Vorontsov <avorontsov@ru.mvista.com> wrote:

> 
> Ah. I wonder why Freescale just didn't write some patches for sdhci,
> but copied the code instead...
> 

Probably because it was quicker. Samsung did the same thing, but now
Ben Dooks has patches to properly support it via sdhci.

Rgds
Dave Liu Jan. 15, 2009, 6:11 a.m. UTC | #5
> Probably because it was quicker. Samsung did the same thing, but now
> Ben Dooks has patches to properly support it via sdhci.

Pierre,
What samsung driver are you talking? Could you point it to me?

Thanks, Dave
Pierre Ossman Jan. 15, 2009, 6:39 a.m. UTC | #6
On Thu, 15 Jan 2009 14:11:09 +0800
"Liu Dave" <DaveLiu@freescale.com> wrote:

> > Probably because it was quicker. Samsung did the same thing, but now
> > Ben Dooks has patches to properly support it via sdhci.
> 
> Pierre,
> What samsung driver are you talking? Could you point it to me?
> 

I'm afraid I haven't seen any readily available sources for their
driver. If you meant Ben's version, you can find the latest revision
here:

http://marc.info/?t=122823266100005

Unfortunately marc.info didn't sort out the threading, but you should
be able to find all the patches there or some other LKML archive.

Rgds
Dave Liu Jan. 15, 2009, 6:56 a.m. UTC | #7
> I'm afraid I haven't seen any readily available sources for their
> driver. If you meant Ben's version, you can find the latest revision
> here:
> 
> http://marc.info/?t=122823266100005
> 
> Unfortunately marc.info didn't sort out the threading, but you should
> be able to find all the patches there or some other LKML archive.

Thanks Pierre, That is my needs.
Ben Dooks Jan. 15, 2009, 2:35 p.m. UTC | #8
On Thu, Jan 15, 2009 at 02:56:05AM +0300, Anton Vorontsov wrote:
> On Thu, Jan 15, 2009 at 07:37:00AM +0800, Liu Dave wrote:
> > > This patch adds support for the Freescale Enhanced Secure Digital
> > > Host Controller Interface as found in some Freescale PowerPC
> > > microprocessors (e.g. MPC837x SOCs).
> > 
> > The Freescale ESDHC controller is basically compatible with the
> > standard SDHC controller. but the eshdc expand some bits.
> > 
> > The esdhc.c is much like the orignal sdhci.c.
> > it is possible to merge them.
> 
> Ah. I wonder why Freescale just didn't write some patches for sdhci,
> but copied the code instead...

For the same reasons earthmen like tea.
Arnd Bergmann Jan. 15, 2009, 9:52 p.m. UTC | #9
On Wednesday 14 January 2009, Anton Vorontsov wrote:
> 
> +config MMC_ESDHC
> +       tristate "Freescale Enhanced SD Host Controller Interface support"
> +       depends on FSL_SOC
> +       help
> +         This selects the Freescale Enhanced SD Host Controller Interface
> +         as found in some Freescale PowerPC microprocessors.
> +
> +         If unsure, say N.


The naming could be improved, calling it ESDHC instead of ESDHCI is very
confusing: SDHCI is the controller chip while SDHC is a type of card (high
capacity SD). This driver clearly does not refer to the card type but
rather to the controller.

	Arnd <><
Anton Vorontsov Jan. 22, 2009, 1:35 a.m. UTC | #10
On Wed, Jan 14, 2009 at 09:43:50PM -0600, Kumar Gala wrote:
>
> On Jan 14, 2009, at 1:46 PM, Anton Vorontsov wrote:
>
>> From: Xie Xiaobo <X.Xie@freescale.com>
>>
>> This patch adds support for the Freescale Enhanced Secure Digital
>> Host Controller Interface as found in some Freescale PowerPC
>> microprocessors (e.g. MPC837x SOCs).
>>
>> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
>> Signed-off-by: Konjin Lai <Konjin.Lai@freescale.com>
>> Signed-off-by: Joe D'Abbraccio <Joe.D'abbraccio@freescale.com>
>> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
>> ---
>> drivers/mmc/host/Kconfig  |    9 +
>> drivers/mmc/host/Makefile |    1 +
>> drivers/mmc/host/esdhc.c  | 1321 ++++++++++++++++++++++++++++++++++++ 
>> +++++++++
>> drivers/mmc/host/esdhc.h  |  255 +++++++++
>> 4 files changed, 1586 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/mmc/host/esdhc.c
>> create mode 100644 drivers/mmc/host/esdhc.h
>
> Want to work some device tree docs for this.

Will do. Though all the needed nodes already in the .dts files. ;-)

Thanks,
diff mbox

Patch

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index dfa585f..941975c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -65,6 +65,15 @@  config MMC_RICOH_MMC
 
 	  If unsure, say Y.
 
+config MMC_ESDHC
+	tristate "Freescale Enhanced SD Host Controller Interface support"
+	depends on FSL_SOC
+	help
+	  This selects the Freescale Enhanced SD Host Controller Interface
+	  as found in some Freescale PowerPC microprocessors.
+
+	  If unsure, say N.
+
 config MMC_OMAP
 	tristate "TI OMAP Multimedia Card Interface support"
 	depends on ARCH_OMAP
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f485328..031489f 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -12,6 +12,7 @@  obj-$(CONFIG_MMC_IMX)		+= imxmmc.o
 obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
 obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o
+obj-$(CONFIG_MMC_ESDHC)		+= esdhc.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
diff --git a/drivers/mmc/host/esdhc.c b/drivers/mmc/host/esdhc.c
new file mode 100644
index 0000000..c97689d
--- /dev/null
+++ b/drivers/mmc/host/esdhc.c
@@ -0,0 +1,1321 @@ 
+/*
+ * Freescale Enhanced Secure Digital Host Controller driver.
+ *
+ * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ * Copyright (C) 2007-2009 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. All rights reserved.
+ *
+ * Author: Xiaobo Xie <X.Xie@freescale.com>
+ *
+ * Derived from sdhci driver by Pierre Ossman <drzeus@drzeus.cx>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/uaccess.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include "esdhc.h"
+
+#define DBG(fmt, args...) pr_debug("[%s]  " fmt "\n", __func__, ## args)
+
+static unsigned int debug_nodma;
+module_param(debug_nodma, uint, 0444);
+MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers.");
+
+static unsigned int debug_forcedma;
+module_param(debug_forcedma, uint, 0444);
+MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers.");
+
+#define ESDHC_QUIRK_CLOCK_BEFORE_RESET	(1 << 0)
+#define ESDHC_QUIRK_FORCE_DMA		(1 << 1)
+#define ESDHC_QUIRK_NO_CARD_NO_RESET	(1 << 2)
+#define ESDHC_QUIRK_SINGLE_POWER_WRITE	(1 << 3)
+
+static unsigned int debug_quirks;
+module_param(debug_quirks, uint, 0444);
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
+
+static void esdhc_dumpregs(struct esdhc_host *host)
+{
+	DBG("========= REGISTER DUMP ==========");
+	DBG("Sysaddr: 0x%08x | Blkattr: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_DMA_ADDRESS),
+		fsl_readl(host->ioaddr + ESDHC_BLOCK_ATTR));
+	DBG("Argument: 0x%08x | COMMAND: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_ARGUMENT),
+		fsl_readl(host->ioaddr + ESDHC_COMMAND));
+	DBG("Present: 0x%08x | DMA ctl: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE),
+		fsl_readl(host->ioaddr + ESDHC_DMA_SYSCTL));
+	DBG("PROCTL: 0x%08x | SYSCTL: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_PROTOCOL_CONTROL),
+		fsl_readl(host->ioaddr + ESDHC_SYSTEM_CONTROL));
+	DBG("Int stat: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_INT_STATUS));
+	DBG("Intenab: 0x%08x | Sigenab: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_INT_ENABLE),
+		fsl_readl(host->ioaddr + ESDHC_SIGNAL_ENABLE));
+	DBG("AC12 err: 0x%08x | Version: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_ACMD12_ERR),
+		fsl_readl(host->ioaddr + ESDHC_HOST_VERSION));
+	DBG("Caps: 0x%08x | Watermark: 0x%08x",
+		fsl_readl(host->ioaddr + ESDHC_CAPABILITIES),
+		fsl_readl(host->ioaddr + ESDHC_WML));
+	DBG("==================================");
+}
+
+static void esdhc_reset(struct esdhc_host *host, u8 mask)
+{
+	unsigned long timeout;
+	unsigned int sysctl;
+
+	if (host->quirks & ESDHC_QUIRK_NO_CARD_NO_RESET) {
+		if (!(fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) &
+					ESDHC_CARD_PRESENT))
+			return;
+	}
+
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL,
+		  mask << ESDHC_RESET_SHIFT);
+
+	if (mask & ESDHC_RESET_ALL) {
+		host->clock = 0;
+		host->bus_width = 0;
+	}
+
+	/* Wait max 100 ms */
+	timeout = 100;
+
+	/* hw clears the bit when it's done */
+	sysctl = (mask << ESDHC_RESET_SHIFT);
+	while (fsl_readl(host->ioaddr + ESDHC_SYSTEM_CONTROL) & sysctl) {
+		if (timeout == 0) {
+			dev_err(host->mmc->parent, "Reset 0x%x never "
+				"completed.\n", (int)mask);
+			esdhc_dumpregs(host);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+}
+
+static void esdhc_init(struct esdhc_host *host)
+{
+	u32 intmask;
+
+	esdhc_reset(host, ESDHC_RESET_ALL);
+
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL,
+			(ESDHC_CLOCK_INT_EN | ESDHC_CLOCK_INT_STABLE));
+
+	intmask = fsl_readl(host->ioaddr + ESDHC_INT_STATUS);
+	fsl_writel(host->ioaddr + ESDHC_INT_STATUS, intmask);
+
+	intmask = ESDHC_INT_DATA_END_BIT | ESDHC_INT_DATA_CRC |
+		ESDHC_INT_DATA_TIMEOUT | ESDHC_INT_INDEX |
+		ESDHC_INT_END_BIT | ESDHC_INT_CRC | ESDHC_INT_TIMEOUT |
+		ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL |
+		ESDHC_INT_DMA_END | ESDHC_INT_DATA_END | ESDHC_INT_RESPONSE;
+
+	if (host->card_insert)
+		intmask |= ESDHC_INT_CARD_REMOVE;
+	else
+		intmask |= ESDHC_INT_CARD_INSERT;
+
+	fsl_writel(host->ioaddr + ESDHC_INT_ENABLE, intmask);
+	fsl_writel(host->ioaddr + ESDHC_SIGNAL_ENABLE, intmask);
+
+	setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+}
+
+static void reset_regs(struct esdhc_host *host)
+{
+	u32 intmask;
+
+	intmask = fsl_readl(host->ioaddr + ESDHC_INT_STATUS);
+	fsl_writel(host->ioaddr + ESDHC_INT_STATUS, intmask);
+
+	intmask = ESDHC_INT_DATA_END_BIT | ESDHC_INT_DATA_CRC |
+		  ESDHC_INT_DATA_TIMEOUT | ESDHC_INT_INDEX |
+		  ESDHC_INT_END_BIT | ESDHC_INT_CRC | ESDHC_INT_TIMEOUT |
+		  ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL |
+		  ESDHC_INT_DMA_END | ESDHC_INT_DATA_END | ESDHC_INT_RESPONSE;
+
+	if (host->card_insert)
+		intmask |= ESDHC_INT_CARD_REMOVE;
+	else
+		intmask |= ESDHC_INT_CARD_INSERT;
+
+	fsl_writel(host->ioaddr + ESDHC_INT_ENABLE, intmask);
+	fsl_writel(host->ioaddr + ESDHC_SIGNAL_ENABLE, intmask);
+
+	if (host->bus_width == MMC_BUS_WIDTH_4) {
+		intmask = fsl_readl(host->ioaddr + ESDHC_PROTOCOL_CONTROL);
+		intmask |= ESDHC_CTRL_4BITBUS;
+		fsl_writel(host->ioaddr + ESDHC_PROTOCOL_CONTROL, intmask);
+	}
+}
+
+/* Return the SG's virtual address */
+static inline char *esdhc_sg_to_buffer(struct esdhc_host *host)
+{
+	return sg_virt(host->cur_sg);
+}
+
+static inline int esdhc_next_sg(struct esdhc_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 esdhc_read_block_pio(struct esdhc_host *host)
+{
+	int blksize;
+	int chunk_remain;
+	u32 data;
+	char *buffer;
+	int size;
+
+	DBG("PIO reading\n");
+
+	blksize = host->data->blksz;
+	chunk_remain = 0;
+	data = 0;
+
+	buffer = esdhc_sg_to_buffer(host) + host->offset;
+
+	while (blksize) {
+		if (chunk_remain == 0) {
+			data = fsl_readl(host->ioaddr + ESDHC_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 (esdhc_next_sg(host) == 0) {
+				BUG_ON(blksize != 0);
+				return;
+			}
+			buffer = esdhc_sg_to_buffer(host);
+		}
+	}
+}
+
+static void esdhc_write_block_pio(struct esdhc_host *host)
+{
+	int blksize;
+	int 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 = esdhc_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 + ESDHC_BUFFER);
+			chunk_remain = min(blksize, 4);
+		}
+
+		if (host->remain == 0) {
+			if (esdhc_next_sg(host) == 0) {
+				BUG_ON(blksize != 0);
+				return;
+			}
+			buffer = esdhc_sg_to_buffer(host);
+		}
+	}
+}
+
+static void esdhc_transfer_pio(struct esdhc_host *host)
+{
+	u32 mask;
+
+	BUG_ON(!host->data);
+
+	if (host->num_sg == 0)
+		return;
+
+	if (host->data->flags & MMC_DATA_READ)
+		mask = ESDHC_DATA_AVAILABLE;
+	else
+		mask = ESDHC_SPACE_AVAILABLE;
+
+	while (fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) & mask) {
+		if (host->data->flags & MMC_DATA_READ)
+			esdhc_read_block_pio(host);
+		else
+			esdhc_write_block_pio(host);
+
+		if (host->num_sg == 0)
+			break;
+	}
+
+	DBG("PIO transfer complete.\n");
+}
+
+static void esdhc_prepare_data(struct esdhc_host *host, struct mmc_data *data)
+{
+	u8 count;
+	unsigned blkattr = 0;
+	unsigned target_timeout, current_timeout;
+	unsigned int sysctl;
+
+	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);
+
+	if (host->clock == 0) {
+		dev_err(host->mmc->parent, "The SD_CLK isn't set\n");
+		return;
+	}
+	/* timeout in us */
+	target_timeout = data->timeout_ns / 1000 +
+		(data->timeout_clks * 1000000) / 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;
+	host->timeout_clk = host->clock/1000;
+
+	current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+	while (current_timeout < target_timeout) {
+		count++;
+		current_timeout <<= 1;
+		if (count >= 0xF)
+			break;
+	}
+
+	if (count >= 0xF) {
+		dev_warn(host->mmc->parent, "Too large timeout requested!\n");
+		count = 0xE;
+	}
+	if (data->blocks >= 0x50)
+		count = 0xE;
+
+	sysctl = fsl_readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
+	sysctl &= (~ESDHC_TIMEOUT_MASK);
+	fsl_writel(host->ioaddr + ESDHC_SYSTEM_CONTROL,
+		   sysctl | (count << ESDHC_TIMEOUT_SHIFT));
+
+	if (host->flags & ESDHC_USE_DMA) {
+		int sg_count;
+		unsigned int wml;
+		unsigned int wml_value;
+
+		sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
+				      data->sg_len,
+				      (data->flags & MMC_DATA_READ) ?
+					DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		BUG_ON(sg_count != 1);
+
+		fsl_writel(host->ioaddr + ESDHC_DMA_ADDRESS,
+				sg_dma_address(data->sg));
+
+		/* Disable the BRR and BWR interrupt */
+		clrbits32(host->ioaddr + ESDHC_INT_ENABLE,
+				(ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL));
+		clrbits32(host->ioaddr + ESDHC_SIGNAL_ENABLE,
+				(ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL));
+
+		wml_value = data->blksz/4;
+		if (data->flags & MMC_DATA_READ) {
+			if (wml_value > 0x10)
+				wml_value = 0x10;
+			wml = (wml_value & ESDHC_WML_MASK) |
+				((0x10 & ESDHC_WML_MASK)
+				 << ESDHC_WML_WRITE_SHIFT);
+		} else {
+			if (wml_value > 0x80)
+				wml_value = 0x80;
+			wml = (0x10 & ESDHC_WML_MASK) |
+				(((wml_value) & ESDHC_WML_MASK)
+				 << ESDHC_WML_WRITE_SHIFT);
+		}
+
+		fsl_writel(host->ioaddr + ESDHC_WML, wml);
+	} else {
+		host->cur_sg = data->sg;
+		host->num_sg = data->sg_len;
+
+		host->offset = 0;
+		host->remain = host->cur_sg->length;
+
+		setbits32(host->ioaddr + ESDHC_INT_ENABLE,
+				(ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL));
+		setbits32(host->ioaddr + ESDHC_SIGNAL_ENABLE,
+				(ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL));
+	}
+
+	/* We do not handle DMA boundaries */
+	blkattr = data->blksz;
+	blkattr |= data->blocks << 16;
+	fsl_writel(host->ioaddr + ESDHC_BLOCK_ATTR, blkattr);
+}
+
+static unsigned int esdhc_set_transfer_mode(struct esdhc_host *host,
+					    struct mmc_data *data)
+{
+	u32 mode = 0;
+
+	WARN_ON(host->data);
+
+	if (data == NULL)
+		return 0;
+
+	mode = ESDHC_TRNS_BLK_CNT_EN;
+	if (data->blocks > 1)
+		mode |= ESDHC_TRNS_MULTI;
+	if (data->flags & MMC_DATA_READ)
+		mode |= ESDHC_TRNS_READ;
+	if (host->flags & ESDHC_USE_DMA)
+		mode |= ESDHC_TRNS_DMA;
+
+	return mode;
+}
+
+static void esdhc_send_command(struct esdhc_host *host, struct mmc_command *cmd)
+{
+	unsigned int flags;
+	u32 mask;
+	unsigned long timeout;
+
+	WARN_ON(host->cmd);
+
+	DBG("Sending cmd (%d)", cmd->opcode);
+
+	/* Wait max 10 ms. */
+	timeout = 10;
+
+	mask = ESDHC_CMD_INHIBIT;
+	if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+		mask |= ESDHC_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 &= ~ESDHC_DATA_INHIBIT;
+
+	while (fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) & mask) {
+		if (timeout == 0) {
+			dev_err(host->mmc->parent, "Controller never "
+				"released inhibit bit(s).\n");
+			esdhc_dumpregs(host);
+			cmd->error = -EIO;
+			tasklet_schedule(&host->finish_tasklet);
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	mod_timer(&host->timer, jiffies + 10 * HZ);
+
+	host->cmd = cmd;
+
+	esdhc_prepare_data(host, cmd->data);
+
+	fsl_writel(host->ioaddr + ESDHC_ARGUMENT, cmd->arg);
+
+	flags = esdhc_set_transfer_mode(host, cmd->data);
+
+	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+		dev_err(host->mmc->parent, "Unsupported response type!\n");
+		cmd->error = -EINVAL;
+		tasklet_schedule(&host->finish_tasklet);
+		return;
+	}
+
+	if (!(cmd->flags & MMC_RSP_PRESENT))
+		flags |= ESDHC_CMD_RESP_NONE;
+	else if (cmd->flags & MMC_RSP_136)
+		flags |= ESDHC_CMD_RESP_LONG;
+	else if (cmd->flags & MMC_RSP_BUSY)
+		flags |= ESDHC_CMD_RESP_SHORT_BUSY;
+	else
+		flags |= ESDHC_CMD_RESP_SHORT;
+
+	if (cmd->flags & MMC_RSP_CRC)
+		flags |= ESDHC_CMD_CRC_EN;
+	if (cmd->flags & MMC_RSP_OPCODE)
+		flags |= ESDHC_CMD_INDEX_EN;
+	if (cmd->data)
+		flags |= ESDHC_CMD_DATA;
+
+	fsl_writel(host->ioaddr + ESDHC_COMMAND,
+		 ESDHC_MAKE_CMD(cmd->opcode, flags));
+}
+
+static void esdhc_finish_data(struct esdhc_host *host)
+{
+	struct mmc_data *data;
+	u16 blocks;
+
+	BUG_ON(!host->data);
+
+	data = host->data;
+	host->data = NULL;
+
+	if (host->flags & ESDHC_USE_DMA) {
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+			     (data->flags & MMC_DATA_READ) ?
+				DMA_FROM_DEVICE : DMA_TO_DEVICE);
+	}
+
+	/*
+	 * Controller doesn't count down when in single block mode.
+	 */
+	if (data->blocks == 1 && data->error == 0)
+		blocks = 0;
+	else
+		blocks = fsl_readl(host->ioaddr + ESDHC_BLOCK_ATTR) >> 16;
+
+	data->bytes_xfered = data->blksz * (data->blocks - blocks);
+
+	if (data->error == 0 && blocks) {
+		dev_err(host->mmc->parent, "Controller signalled completion "
+			"even though there were blocks left.\n");
+		data->error = -EIO;
+	}
+
+	if (blocks == 0 && data->error == -ETIMEDOUT) {
+		dev_err(host->mmc->parent, "Controller transmitted completion "
+			"even though there were timeout error.\n");
+		data->error = 0;
+	}
+
+	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 != 0) {
+			esdhc_reset(host, ESDHC_RESET_CMD);
+			esdhc_reset(host, ESDHC_RESET_DATA);
+			reset_regs(host);
+		}
+
+		esdhc_send_command(host, data->stop);
+	} else {
+		tasklet_schedule(&host->finish_tasklet);
+	}
+}
+
+static void esdhc_finish_command(struct esdhc_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] = fsl_readl(host->ioaddr +
+						ESDHC_RESPONSE + (3-i)*4) << 8;
+				if (i != 3) {
+					host->cmd->resp[i] |=
+						fsl_readl(host->ioaddr +
+							  ESDHC_RESPONSE +
+							  (2 - i) * 4) >> 24;
+				}
+			}
+		} else {
+			host->cmd->resp[0] = fsl_readl(host->ioaddr +
+						       ESDHC_RESPONSE);
+		}
+	}
+
+	host->cmd->error = 0;
+
+	DBG("Ending cmd (%d)", host->cmd->opcode);
+
+	if (host->cmd->data)
+		host->data = host->cmd->data;
+	else
+		tasklet_schedule(&host->finish_tasklet);
+
+	host->cmd = NULL;
+}
+
+static void esdhc_set_clock(struct esdhc_host *host, unsigned int clock)
+{
+	int pre_div;
+	int div;
+	u16 clk;
+	unsigned long timeout;
+
+	if (clock == host->clock)
+		return;
+
+	clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_MASK);
+
+	if (clock == 0)
+		goto out;
+
+	if (host->max_clk / 16 > clock) {
+		for (pre_div = 1; pre_div < 256; pre_div *= 2) {
+			if ((host->max_clk / pre_div) < (clock*16))
+				break;
+		}
+	} else {
+		pre_div = 1;
+	}
+
+	for (div = 1; div <= 16; div++) {
+		if ((host->max_clk / (div * pre_div)) <= clock)
+			break;
+	}
+
+	pre_div >>= 1;
+	div -= 1;
+
+	clk = (div << ESDHC_DIVIDER_SHIFT) | (pre_div << ESDHC_PREDIV_SHIFT);
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, clk);
+
+	/* Wait max 10 ms */
+	timeout = 10;
+	while (timeout) {
+		timeout--;
+		mdelay(1);
+	}
+
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_CARD_EN);
+	esdhc_dumpregs(host);
+
+out:
+	host->clock = clock;
+	if (host->clock == 0)
+		setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL,
+					ESDHC_CLOCK_DEFAULT);
+}
+
+static void esdhc_set_power(struct esdhc_host *host, unsigned short power)
+{
+	if (host->power == power)
+		return;
+	if (power == (unsigned short)-1)
+		host->power = power;
+}
+
+static void esdhc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct esdhc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	WARN_ON(host->mrq != NULL);
+
+	host->mrq = mrq;
+
+	if (!(fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) &
+			ESDHC_CARD_PRESENT)) {
+		host->mrq->cmd->error = -ETIMEDOUT;
+		tasklet_schedule(&host->finish_tasklet);
+	} else {
+		esdhc_send_command(host, mrq->cmd);
+	}
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void esdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct esdhc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	u32 ctrl;
+
+	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) {
+		fsl_writel(host->ioaddr + ESDHC_SIGNAL_ENABLE, 0);
+		esdhc_init(host);
+	}
+
+	esdhc_set_clock(host, ios->clock);
+
+	if (ios->power_mode == MMC_POWER_OFF)
+		esdhc_set_power(host, -1);
+	else
+		esdhc_set_power(host, ios->vdd);
+
+	ctrl = fsl_readl(host->ioaddr + ESDHC_PROTOCOL_CONTROL);
+
+	if (ios->bus_width == MMC_BUS_WIDTH_4) {
+		ctrl |= ESDHC_CTRL_4BITBUS;
+		host->bus_width = MMC_BUS_WIDTH_4;
+	} else {
+		ctrl &= ~ESDHC_CTRL_4BITBUS;
+		host->bus_width = MMC_BUS_WIDTH_1;
+	}
+
+	fsl_writel(host->ioaddr + ESDHC_PROTOCOL_CONTROL, ctrl);
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int esdhc_get_ro(struct mmc_host *mmc)
+{
+	struct esdhc_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	int present;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	present = fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	/* esdhc is different form sdhc */
+	return present & ESDHC_WRITE_PROTECT;
+}
+
+static const struct mmc_host_ops esdhc_ops = {
+	.request	= esdhc_request,
+	.set_ios	= esdhc_set_ios,
+	.get_ro		= esdhc_get_ro,
+};
+
+static void esdhc_tasklet_card(unsigned long param)
+{
+	struct esdhc_host *host = (struct esdhc_host *)param;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (!(fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) &
+				ESDHC_CARD_PRESENT)) {
+		if (host->mrq) {
+			dev_err(host->mmc->parent,
+				"Card removed during transfer! "
+				"Resetting the controller.\n");
+
+			esdhc_reset(host, ESDHC_RESET_CMD);
+			esdhc_reset(host, ESDHC_RESET_DATA);
+
+			host->mrq->cmd->error = -EIO;
+			tasklet_schedule(&host->finish_tasklet);
+		}
+		host->card_insert = 0;
+	} else {
+		esdhc_reset(host, ESDHC_INIT_CARD);
+		host->card_insert = 1;
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+}
+
+static void esdhc_tasklet_finish(unsigned long param)
+{
+	struct esdhc_host *host = (struct esdhc_host *)param;
+	unsigned long flags;
+	struct mmc_request *mrq;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	del_timer(&host->timer);
+
+	mrq = host->mrq;
+
+	DBG("Ending request, cmd (%d)", mrq->cmd->opcode);
+
+	/*
+	 * The controller needs a reset of internal state machines
+	 * upon error conditions.
+	 */
+	if ((mrq->cmd->error != 0) ||
+		(mrq->data && ((mrq->data->error != 0) ||
+		(mrq->data->stop &&
+			(mrq->data->stop->error != 0))))) {
+
+		/* Some controllers need this kick or reset won't work here */
+		if (host->quirks & ESDHC_QUIRK_CLOCK_BEFORE_RESET) {
+			unsigned int clock;
+
+			/* This is to force an update */
+			clock = host->clock;
+			host->clock = 0;
+			esdhc_set_clock(host, clock);
+		}
+
+		/* Spec says we should do both at the same time, but Ricoh
+		   controllers do not like that. */
+		if (mrq->cmd->error != -ETIMEDOUT) {
+			esdhc_reset(host, ESDHC_RESET_CMD);
+			esdhc_reset(host, ESDHC_RESET_DATA);
+			reset_regs(host);
+			esdhc_dumpregs(host);
+		}
+	}
+
+	host->mrq = NULL;
+	host->cmd = NULL;
+	host->data = NULL;
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+static void esdhc_timeout_timer(unsigned long data)
+{
+	struct esdhc_host *host = (struct esdhc_host *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (host->mrq) {
+		dev_err(host->mmc->parent, "Timeout waiting for hardware "
+			"interrupt.\n");
+		esdhc_dumpregs(host);
+
+		if (host->data) {
+			host->data->error = -ETIMEDOUT;
+			esdhc_finish_data(host);
+		} else {
+			if (host->cmd)
+				host->cmd->error = -ETIMEDOUT;
+			else
+				host->mrq->cmd->error = -ETIMEDOUT;
+
+			tasklet_schedule(&host->finish_tasklet);
+		}
+	}
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void esdhc_cmd_irq(struct esdhc_host *host, u32 intmask)
+{
+	BUG_ON(intmask == 0);
+
+	if (!host->cmd) {
+		dev_err(host->mmc->parent, "Got command interrupt even "
+			"though no command operation was in progress.\n");
+		esdhc_dumpregs(host);
+		return;
+	}
+
+	if (intmask & ESDHC_INT_TIMEOUT) {
+		host->cmd->error = -ETIMEDOUT;
+		tasklet_schedule(&host->finish_tasklet);
+	} else if (intmask & ESDHC_INT_RESPONSE) {
+		esdhc_finish_command(host);
+	} else {
+		if (intmask & ESDHC_INT_CRC)
+			host->cmd->error = -EILSEQ;
+		else if (intmask & (ESDHC_INT_END_BIT | ESDHC_INT_INDEX))
+			host->cmd->error = -EIO;
+		else
+			host->cmd->error = -EINVAL;
+
+		tasklet_schedule(&host->finish_tasklet);
+	}
+}
+
+static void esdhc_data_irq(struct esdhc_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 & ESDHC_INT_DATA_END ||
+				intmask & ESDHC_INT_DMA_END)
+			return;
+
+		dev_err(host->mmc->parent, "Got data interrupt even though "
+			"no data operation was in progress.\n");
+		esdhc_dumpregs(host);
+		return;
+	}
+
+	if (intmask & ESDHC_INT_DATA_TIMEOUT)
+		host->data->error = -ETIMEDOUT;
+	else if (intmask & ESDHC_INT_DATA_CRC)
+		host->data->error = -EILSEQ;
+	else if (intmask & ESDHC_INT_DATA_END_BIT)
+		host->data->error = -EIO;
+
+	if (host->data->error != 0) {
+		esdhc_finish_data(host);
+	} else {
+		if (intmask & (ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL))
+			esdhc_transfer_pio(host);
+
+		/*
+		 * We currently don't do anything fancy with DMA
+		 * boundaries, but as we can't disable the feature
+		 * we need to at least restart the transfer.
+		 */
+		if (intmask & ESDHC_INT_DMA_END)
+			fsl_writel(host->ioaddr + ESDHC_DMA_ADDRESS,
+				fsl_readl(host->ioaddr + ESDHC_DMA_ADDRESS));
+
+		if (intmask & ESDHC_INT_DATA_END)
+			esdhc_finish_data(host);
+	}
+}
+
+static irqreturn_t esdhc_irq(int irq, void *dev_id)
+{
+	struct esdhc_host *host = dev_id;
+	irqreturn_t result;
+	u32 status;
+
+	spin_lock_irq(&host->lock);
+
+	status = fsl_readl(host->ioaddr + ESDHC_INT_STATUS);
+
+	if (!status || status == 0xffffffff) {
+		result = IRQ_NONE;
+		goto out;
+	}
+
+	if (status & (ESDHC_INT_CARD_INSERT | ESDHC_INT_CARD_REMOVE)) {
+		if (status & ESDHC_INT_CARD_INSERT) {
+			if (fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) &
+					ESDHC_CARD_PRESENT) {
+				DBG("***  got card-insert interrupt");
+				fsl_writel(host->ioaddr + ESDHC_INT_ENABLE,
+					ESDHC_INT_INSERT_MASK);
+				fsl_writel(host->ioaddr + ESDHC_SIGNAL_ENABLE,
+					ESDHC_INT_INSERT_MASK);
+			}
+		}
+		if (status & ESDHC_INT_CARD_REMOVE) {
+			if (!(fsl_readl(host->ioaddr + ESDHC_PRESENT_STATE) &
+					ESDHC_CARD_PRESENT)) {
+				DBG("***  got card-remove interrupt");
+				fsl_writel(host->ioaddr + ESDHC_INT_ENABLE,
+					ESDHC_INT_REMOVE_MASK);
+				fsl_writel(host->ioaddr + ESDHC_SIGNAL_ENABLE,
+					ESDHC_INT_REMOVE_MASK);
+			}
+		}
+
+		tasklet_schedule(&host->card_tasklet);
+	}
+
+	status &= ~(ESDHC_INT_CARD_INSERT | ESDHC_INT_CARD_REMOVE);
+
+	if (status & ESDHC_INT_CMD_MASK) {
+		fsl_writel(host->ioaddr + ESDHC_INT_STATUS,
+			status & ESDHC_INT_CMD_MASK);
+		esdhc_cmd_irq(host, status & ESDHC_INT_CMD_MASK);
+	}
+
+	if (status & ESDHC_INT_DATA_MASK) {
+		fsl_writel(host->ioaddr + ESDHC_INT_STATUS,
+			status & ESDHC_INT_DATA_MASK);
+		esdhc_data_irq(host, status & ESDHC_INT_DATA_MASK);
+	}
+
+	status &= ~(ESDHC_INT_CMD_MASK | ESDHC_INT_DATA_MASK);
+
+	if (status) {
+		dev_err(host->mmc->parent, "Unexpected interrupt 0x%08x.\n",
+			status);
+		esdhc_dumpregs(host);
+		fsl_writel(host->ioaddr + ESDHC_INT_STATUS, status);
+	}
+
+	result = IRQ_HANDLED;
+
+	mmiowb();
+out:
+	spin_unlock_irq(&host->lock);
+
+	return result;
+}
+
+#ifdef CONFIG_PM
+
+static int esdhc_suspend(struct of_device *ofdev, pm_message_t state)
+{
+	struct esdhc_host *host = dev_get_drvdata(&ofdev->dev);
+	int ret;
+
+	DBG("Suspending...");
+
+	ret = mmc_suspend_host(host->mmc, state);
+	if (ret)
+		return ret;
+
+	disable_irq(host->irq);
+	return 0;
+}
+
+static int esdhc_resume(struct of_device *ofdev)
+{
+	struct esdhc_host *host = dev_get_drvdata(&ofdev->dev);
+	int ret;
+
+	DBG("Resuming...");
+
+	esdhc_init(host);
+	enable_irq(host->irq);
+	mmiowb();
+
+	ret = mmc_resume_host(host->mmc);
+	if (ret)
+		return ret;
+	return 0;
+}
+
+#else
+
+#define esdhc_suspend NULL
+#define esdhc_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int __devinit esdhc_probe(struct of_device *ofdev,
+				 const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct esdhc_host *host;
+	struct mmc_host *mmc;
+	struct resource res;
+	unsigned int version;
+	unsigned int caps;
+	const u32 *clk;
+	int size;
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(struct esdhc_host), &ofdev->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	dev_set_drvdata(&ofdev->dev, host);
+
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret)
+		goto err_no_addr;
+
+	host->addr = res.start;
+	host->size = res.end - res.start + 1;
+
+	if (!request_mem_region(host->addr, host->size,
+				dev_name(&ofdev->dev))) {
+		ret = -EBUSY;
+		goto err_addr_request;
+	}
+
+	host->ioaddr = ioremap_nocache(host->addr, host->size);
+	if (!host->ioaddr) {
+		ret = -ENOMEM;
+		goto err_addr_map;
+	}
+
+	host->irq = irq_of_parse_and_map(np, 0);
+	if (!host->irq) {
+		ret = -EINVAL;
+		goto err_no_irq;
+	}
+
+	esdhc_reset(host, ESDHC_RESET_ALL);
+
+	version = fsl_readl(host->ioaddr + ESDHC_HOST_VERSION);
+	if (version != 0x01) {
+		dev_err(host->mmc->parent, "Unknown controller version "
+			"(%d). You may experience problems.\n", version);
+	}
+
+
+	host->quirks = ESDHC_QUIRK_NO_CARD_NO_RESET;
+	if (debug_quirks)
+		host->quirks = debug_quirks;
+
+	caps = fsl_readl(host->ioaddr + ESDHC_CAPABILITIES);
+
+	if (debug_nodma) {
+		DBG("DMA forced off\n");
+	} else if (debug_forcedma) {
+		DBG("DMA forced on\n");
+		host->flags |= ESDHC_USE_DMA;
+	} else if (host->quirks & ESDHC_QUIRK_FORCE_DMA) {
+		host->flags |= ESDHC_USE_DMA;
+	} else if (!(caps & ESDHC_CAN_DO_DMA)) {
+		DBG("Controller doesn't have DMA capability\n");
+	} else {
+		host->flags |= ESDHC_USE_DMA;
+	}
+
+	clk = of_get_property(np, "clock-frequency", &size);
+	if (!clk || size != sizeof(*clk) || !*clk) {
+		ret = -EINVAL;
+		goto err_no_clk;
+	}
+	host->max_clk = *clk;
+
+	/*
+	 * Set host parameters.
+	 */
+	mmc->ops = &esdhc_ops;
+	mmc->f_min = 400000;
+	mmc->f_max = min((int)*clk, 50000000);
+	mmc->caps = MMC_CAP_4_BIT_DATA;
+
+	if (caps & ESDHC_CAN_DO_HISPD)
+		mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+	mmc->ocr_avail = 0;
+	if (caps & ESDHC_CAN_VDD_330)
+		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+	if (caps & ESDHC_CAN_VDD_300)
+		mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+	if (caps & ESDHC_CAN_VDD_180)
+		mmc->ocr_avail |= MMC_VDD_165_195;
+
+	if (mmc->ocr_avail == 0) {
+		dev_err(host->mmc->parent, "Hardware doesn't report any "
+			"support voltages.\n");
+		ret = -ENODEV;
+		goto err_no_ocr;
+	}
+
+	spin_lock_init(&host->lock);
+
+	/*
+	 * Maximum number of segments. Hardware cannot do scatter lists.
+	 */
+	if (host->flags & ESDHC_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 & ESDHC_MAX_BLOCK_MASK) >>
+					ESDHC_MAX_BLOCK_SHIFT;
+	if (mmc->max_blk_size > 3) {
+		dev_err(host->mmc->parent, "Invalid maximum block size.\n");
+		ret = -ENODEV;
+		goto err_blk_size;
+	}
+	mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+	/*
+	 * Maximum block count.
+	 */
+	mmc->max_blk_count = 65535;
+
+	/*
+	 * Init tasklets.
+	 */
+	tasklet_init(&host->card_tasklet,
+		esdhc_tasklet_card, (unsigned long)host);
+	tasklet_init(&host->finish_tasklet,
+		esdhc_tasklet_finish, (unsigned long)host);
+
+	setup_timer(&host->timer, esdhc_timeout_timer, (unsigned long)host);
+
+	esdhc_init(host);
+
+#ifdef CONFIG_MMC_DEBUG
+	esdhc_dumpregs(host);
+#endif
+
+	ret = request_irq(host->irq, esdhc_irq, IRQF_SHARED,
+			  mmc_hostname(mmc), host);
+	if (ret)
+		goto err_request_irq;
+
+	mmiowb();
+
+	mmc_add_host(mmc);
+
+	dev_info(host->mmc->parent, "probed at 0x%08lx, irq %d, %s\n",
+		host->addr, host->irq,
+		(host->flags & ESDHC_USE_DMA) ? "DMA" : "PIO");
+
+	return 0;
+
+err_request_irq:
+	tasklet_kill(&host->card_tasklet);
+	tasklet_kill(&host->finish_tasklet);
+err_blk_size:
+err_no_ocr:
+err_no_clk:
+	irq_dispose_mapping(host->irq);
+err_no_irq:
+	iounmap(host->ioaddr);
+err_addr_map:
+	release_mem_region(host->addr, host->size);
+err_addr_request:
+err_no_addr:
+	mmc_free_host(mmc);
+	return ret;
+}
+
+static int __devexit esdhc_remove(struct of_device *ofdev)
+{
+
+	struct esdhc_host *host = dev_get_drvdata(&ofdev->dev);
+	struct mmc_host *mmc = host->mmc;
+
+	mmc_remove_host(mmc);
+
+	esdhc_reset(host, ESDHC_RESET_ALL);
+
+	free_irq(host->irq, host);
+	irq_dispose_mapping(host->irq);
+
+	del_timer_sync(&host->timer);
+
+	tasklet_kill(&host->card_tasklet);
+	tasklet_kill(&host->finish_tasklet);
+
+	iounmap(host->ioaddr);
+
+	release_mem_region(host->addr, host->size);
+
+	mmc_free_host(mmc);
+	return 0;
+}
+
+static const struct of_device_id fsl_esdhc_match[] = {
+	{ .compatible = "fsl,esdhc", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, fsl_esdhc_match);
+
+static struct of_platform_driver esdhc_driver = {
+	.driver.name = "esdhc",
+	.match_table = fsl_esdhc_match,
+	.probe = esdhc_probe,
+	.remove = __devexit_p(esdhc_remove),
+	.suspend = esdhc_suspend,
+	.resume	= esdhc_resume,
+};
+
+static int __init esdhc_drv_init(void)
+{
+	pr_info("%s: Freescale Enhanced Secure Digital Host Controller\n",
+		esdhc_driver.driver.name);
+	return of_register_platform_driver(&esdhc_driver);
+}
+module_init(esdhc_drv_init);
+
+static void __exit esdhc_drv_exit(void)
+{
+	of_unregister_platform_driver(&esdhc_driver);
+}
+module_exit(esdhc_drv_exit);
+
+MODULE_DESCRIPTION("Enhanced Secure Digital Host Controller driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/esdhc.h b/drivers/mmc/host/esdhc.h
new file mode 100644
index 0000000..0e259c6
--- /dev/null
+++ b/drivers/mmc/host/esdhc.h
@@ -0,0 +1,255 @@ 
+/*
+ * Freescale Enhanced Secure Digital Host Controller driver.
+ *
+ * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ * Copyright (C) 2007-2009 Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright (C) 2008-2009 MontaVista Software, Inc. All rights reserved.
+ *
+ * Author: Xiaobo Xie <X.Xie@freescale.com>
+ *
+ * Derived from sdhci driver by Pierre Ossman <drzeus@drzeus.cx>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __MMC_HOST_ESDHC_H
+#define __MMC_HOST_ESDHC_H
+
+#include <linux/io.h>
+
+/*
+ * Controller registers (Big Endian)
+ */
+/* DMA System Address Register */
+#define ESDHC_DMA_ADDRESS	0x00
+
+/* Block Attributes Register */
+#define ESDHC_BLOCK_ATTR	0x04
+#define ESDHC_BLOCK_SIZE_MASK	0x00000fff
+#define ESDHC_BLCOK_CNT_MASK	0xffff0000
+#define ESDHC_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+/* Command Argument */
+#define ESDHC_ARGUMENT		0x08
+
+/* Transfer Type Register */
+#define ESDHC_COMMAND		0x0C
+
+#define ESDHC_TRNS_DMA		0x00000001
+#define ESDHC_TRNS_BLK_CNT_EN	0x00000002
+#define ESDHC_TRNS_ACMD12	0x00000004
+#define ESDHC_TRNS_READ		0x00000010
+#define ESDHC_TRNS_MULTI	0x00000020
+
+#define ESDHC_CMD_RESP_MASK	0x00030000
+#define ESDHC_CMD_CRC_EN	0x00080000
+#define ESDHC_CMD_INDEX_EN	0x00100000
+#define ESDHC_CMD_DATA		0x00200000
+#define ESDHC_CMD_TYPE_MASK	0x00c00000
+#define ESDHC_CMD_INDEX		0x3f000000
+
+#define ESDHC_CMD_RESP_NONE	0x00000000
+#define ESDHC_CMD_RESP_LONG	0x00010000
+#define ESDHC_CMD_RESP_SHORT	0x00020000
+#define ESDHC_CMD_RESP_SHORT_BUSY 0x00030000
+
+#define ESDHC_MAKE_CMD(c, f) (((c & 0xff) << 24) | (f & 0xfb0037))
+
+/* Response Register */
+#define ESDHC_RESPONSE		0x10
+
+/* Buffer Data Port Register */
+#define ESDHC_BUFFER		0x20
+
+/* Present State Register */
+#define ESDHC_PRESENT_STATE	0x24
+#define  ESDHC_CMD_INHIBIT	0x00000001
+#define  ESDHC_DATA_INHIBIT	0x00000002
+#define  ESDHC_DOING_WRITE	0x00000100
+#define  ESDHC_DOING_READ	0x00000200
+#define  ESDHC_SPACE_AVAILABLE	0x00000400
+#define  ESDHC_DATA_AVAILABLE	0x00000800
+#define  ESDHC_CARD_PRESENT	0x00010000
+#define  ESDHC_WRITE_PROTECT	0x00080000
+
+/* Protocol control Register */
+#define ESDHC_PROTOCOL_CONTROL 	0x28
+
+#define ESDHC_CTRL_BUS_MASK	0x00000006
+#define ESDHC_CTRL_4BITBUS	0x00000002
+#define ESDHC_CTRL_D3_DETEC	0x00000008
+#define ESDHC_CTRL_DTCT_EN	0x00000080
+#define ESDHC_CTRL_DTCT_STATUS	0x00000040
+#define ESDHC_CTRL_WU_CRM	0x04000000
+#define ESDHC_CTRL_WU_CINS	0x02000000
+#define ESDHC_CTRL_WU_CINT	0x01000000
+
+/* System Control Register */
+#define ESDHC_SYSTEM_CONTROL	0x2C
+
+#define ESDHC_CLOCK_MASK	0x0000fff0
+#define ESDHC_CLOCK_DEFAULT	0x00008000
+#define ESDHC_PREDIV_SHIFT	8
+#define ESDHC_DIVIDER_SHIFT	4
+#define ESDHC_CLOCK_CARD_EN	0x00000004
+#define ESDHC_CLOCK_INT_STABLE	0x00000002
+#define ESDHC_CLOCK_INT_EN	0x00000001
+
+#define ESDHC_TIMEOUT_MASK	0x000f0000
+#define ESDHC_TIMEOUT_SHIFT	16
+
+#define ESDHC_RESET_SHIFT	24
+#define ESDHC_RESET_ALL		0x01
+#define ESDHC_RESET_CMD		0x02
+#define ESDHC_RESET_DATA	0x04
+#define ESDHC_INIT_CARD		0x08
+
+/* Interrupt Register */
+#define ESDHC_INT_STATUS	0x30
+#define ESDHC_INT_ENABLE	0x34
+#define ESDHC_SIGNAL_ENABLE	0x38
+
+#define ESDHC_INT_RESPONSE	0x00000001
+#define ESDHC_INT_DATA_END	0x00000002
+#define ESDHC_INT_DMA_END	0x00000008
+#define ESDHC_INT_SPACE_AVAIL	0x00000010
+#define ESDHC_INT_DATA_AVAIL	0x00000020
+#define ESDHC_INT_CARD_INSERT	0x00000040
+#define ESDHC_INT_CARD_REMOVE	0x00000080
+#define ESDHC_INT_CARD_INT	0x00000100
+
+#define ESDHC_INT_TIMEOUT	0x00010000
+#define ESDHC_INT_CRC		0x00020000
+#define ESDHC_INT_END_BIT	0x00040000
+#define ESDHC_INT_INDEX		0x00080000
+#define ESDHC_INT_DATA_TIMEOUT	0x00100000
+#define ESDHC_INT_DATA_CRC	0x00200000
+#define ESDHC_INT_DATA_END_BIT	0x00400000
+#define ESDHC_INT_ACMD12ERR	0x01000000
+#define ESDHC_INT_DMAERR	0x10000000
+
+#define ESDHC_INT_NORMAL_MASK	0x00007FFF
+#define ESDHC_INT_ERROR_MASK	0xFFFF8000
+
+#define ESDHC_INT_CMD_MASK	(ESDHC_INT_RESPONSE | ESDHC_INT_TIMEOUT | \
+		ESDHC_INT_CRC | ESDHC_INT_END_BIT | ESDHC_INT_INDEX)
+#define ESDHC_INT_DATA_MASK	(ESDHC_INT_DATA_END | ESDHC_INT_DMA_END | \
+		ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL | \
+		ESDHC_INT_DATA_TIMEOUT | ESDHC_INT_DATA_CRC | \
+		ESDHC_INT_DATA_END_BIT)
+
+#define ESDHC_INT_INSERT_MASK (ESDHC_INT_DATA_END_BIT | ESDHC_INT_DATA_CRC | \
+		ESDHC_INT_DATA_TIMEOUT | ESDHC_INT_INDEX | \
+		ESDHC_INT_END_BIT | ESDHC_INT_CRC | ESDHC_INT_TIMEOUT | \
+		ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL | \
+		ESDHC_INT_DMA_END | ESDHC_INT_DATA_END | \
+		ESDHC_INT_RESPONSE | ESDHC_INT_CARD_REMOVE)
+
+#define ESDHC_INT_REMOVE_MASK (ESDHC_INT_DATA_END_BIT | ESDHC_INT_DATA_CRC | \
+		ESDHC_INT_DATA_TIMEOUT | ESDHC_INT_INDEX | \
+		ESDHC_INT_END_BIT | ESDHC_INT_CRC | ESDHC_INT_TIMEOUT | \
+		ESDHC_INT_DATA_AVAIL | ESDHC_INT_SPACE_AVAIL | \
+		ESDHC_INT_DMA_END | ESDHC_INT_DATA_END | \
+		ESDHC_INT_RESPONSE | ESDHC_INT_CARD_INSERT)
+
+/* Auto CMD12 Error Status Register */
+#define ESDHC_ACMD12_ERR	0x3C
+
+/* 3E-3F reserved */
+/* Host Controller Capabilities */
+#define ESDHC_CAPABILITIES	0x40
+
+#define ESDHC_MAX_BLOCK_MASK	0x00070000
+#define ESDHC_MAX_BLOCK_SHIFT	16
+#define ESDHC_CAN_DO_HISPD	0x00200000
+#define ESDHC_CAN_DO_DMA	0x00400000
+#define ESDHC_CAN_DO_SUSPEND	0x00800000
+#define ESDHC_CAN_VDD_330	0x01000000
+#define ESDHC_CAN_VDD_300	0x02000000
+#define ESDHC_CAN_VDD_180	0x04000000
+
+/* Watermark Level Register */
+#define ESDHC_WML		0x44
+#define ESDHC_WML_MASK		0xff
+#define ESDHC_WML_READ_SHIFT	0
+#define ESDHC_WML_WRITE_SHIFT	16
+
+/* 45-4F reserved for more caps and max curren*/
+
+/* Force Event Register */
+#define ESDHC_FORCE_EVENT	0x50
+
+/* 54-FB reserved */
+
+/* Host Controller Version Register */
+#define ESDHC_HOST_VERSION	0xFC
+
+#define ESDHC_VENDOR_VER_MASK	0xFF00
+#define ESDHC_VENDOR_VER_SHIFT	8
+#define ESDHC_SPEC_VER_MASK	0x00FF
+#define ESDHC_SPEC_VER_SHIFT	0
+
+#define ESDHC_DMA_SYSCTL	0x40C
+#define ESDHC_DMA_SNOOP		0x00000040
+
+#define ESDHC_SLOTS_NUMBER	1
+
+/* The SCCR[SDHCCM] Register */
+#define MPC837X_SCCR_OFFS	0xA08
+#define MPC837X_SDHCCM_MASK	0x0c000000
+#define MPC837X_SDHCCM_SHIFT	26
+
+static inline u32 fsl_readl(void __iomem *addr)
+{
+	return in_be32(addr);
+}
+
+static inline void fsl_writel(void __iomem *addr, u32 val)
+{
+	out_be32(addr, val);
+}
+
+struct mmc_host;
+
+struct esdhc_host {
+	struct mmc_host		*mmc;		/* MMC structure */
+
+	spinlock_t		lock;		/* Mutex */
+
+	unsigned long		quirks;		/* Host quirks */
+	int			flags;		/* Host attributes */
+#define ESDHC_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 */
+	unsigned short		bus_width;	/* current bus width */
+
+	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 */
+
+	int			card_insert;
+
+	int			irq;		/* Device IRQ */
+	unsigned long		addr;		/* Bus address */
+	unsigned int		size;		/* IO size */
+	void __iomem		*ioaddr;	/* Mapped address */
+
+	struct tasklet_struct	card_tasklet;	/* Tasklet structures */
+	struct tasklet_struct	finish_tasklet;
+
+	struct timer_list	timer;		/* Timer for timeouts */
+};
+
+#endif /* __MMC_HOST_ESDHC_H */