From: Hyunsung Jang <hs79.j...@samsung.com>

This patch adds support a new sdmmc controller which name is
'Mobile Storage Host Controller'. Its base version is 2.30a.

The 'mshci.c' is based on 'linux/drivers/mmc/host/sdhci.c' and
the role is exactly same with that.

NOTE:
This patch covers 1bit, 4bit and 8bit bus width with SDR/DDR
but it has some hardware limititation with 8bit DDR mode now.
So, needs later version of mshci so that can be work it with
this code.

Signed-off-by: Hyunsung Jang <hs79.j...@samsung.com>
Signed-off-by: Hyuk Lee <hyuk1....@samsung.com>
Signed-off-by: Kukjin Kim <kgene....@samsung.com>
---
 drivers/mmc/host/Kconfig  |   11 +
 drivers/mmc/host/Makefile |    1 +
 drivers/mmc/host/mshci.c  | 1718 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/mshci.h  |  292 ++++++++
 include/linux/mmc/host.h  |    3 +
 5 files changed, 2025 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/mshci.c
 create mode 100644 drivers/mmc/host/mshci.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5ec02a5..2a7fbf0 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -24,6 +24,17 @@ config MMC_PXA
 
          If unsure, say N.
 
+config MMC_MSHCI
+       tristate "Mobile Storage Host Controller Interface support"
+       depends on HAS_DMA
+       help
+         This selects the Mobile Storage Host Controller Interface.
+
+         If you have a controller with this interface, say Y or M here. You
+         also need to enable an appropriate bus interface.
+
+         If unsure, say N.
+
 config MMC_SDHCI
        tristate "Secure Digital Host Controller Interface support"
        depends on HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e834fb2..ee69f75 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MMC_SDHCI_PCI)   += sdhci-pci.o
 obj-$(CONFIG_MMC_SDHCI_PXA)    += sdhci-pxa.o
 obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)  += sdhci-spear.o
+obj-$(CONFIG_MMC_MSHCI)        += mshci.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/mshci.c b/drivers/mmc/host/mshci.c
new file mode 100644
index 0000000..39493e1
--- /dev/null
+++ b/drivers/mmc/host/mshci.c
@@ -0,0 +1,1718 @@
+/*
+ * linux/drivers/mmc/host/mshci.c
+ *
+ * Mobile Storage Host Controller Interface driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Based on linux/drivers/mmc/host/sdhci.c
+ *
+ * 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/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/leds.h>
+#include <linux/mmc/host.h>
+
+#include "mshci.h"
+
+#define DRIVER_NAME "mshci"
+
+#define DBG(f, x...) \
+       pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+#define SDHC_CLK_ON 1
+#define SDHC_CLK_OFF 0
+
+static unsigned int debug_quirks;
+
+static void mshci_prepare_data(struct mshci_host *, struct mmc_data *);
+static void mshci_finish_data(struct mshci_host *);
+
+static void mshci_send_command(struct mshci_host *, struct mmc_command *);
+static void mshci_finish_command(struct mshci_host *);
+static void mshci_set_clock(struct mshci_host *host, unsigned int clock);
+
+static void mshci_dumpregs(struct mshci_host *host)
+{
+       printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP 
==============\n");
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CTRL:      0x%08x\n",
+               mshci_readl(host, MSHCI_CTRL));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_PWREN:     0x%08x\n",
+               mshci_readl(host, MSHCI_PWREN));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CLKDIV:    0x%08x\n",
+               mshci_readl(host, MSHCI_CLKDIV));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CLKSRC:    0x%08x\n",
+               mshci_readl(host, MSHCI_CLKSRC));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CLKENA:    0x%08x\n",
+               mshci_readl(host, MSHCI_CLKENA));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_TMOUT:     0x%08x\n",
+               mshci_readl(host, MSHCI_TMOUT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CTYPE:     0x%08x\n",
+               mshci_readl(host, MSHCI_CTYPE));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_BLKSIZ:    0x%08x\n",
+               mshci_readl(host, MSHCI_BLKSIZ));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_BYTCNT:    0x%08x\n",
+               mshci_readl(host, MSHCI_BYTCNT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_INTMSK:    0x%08x\n",
+               mshci_readl(host, MSHCI_INTMSK));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CMDARG:    0x%08x\n",
+               mshci_readl(host, MSHCI_CMDARG));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CMD:       0x%08x\n",
+               mshci_readl(host, MSHCI_CMD));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_MINTSTS:   0x%08x\n",
+               mshci_readl(host, MSHCI_MINTSTS));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_RINTSTS:   0x%08x\n",
+               mshci_readl(host, MSHCI_RINTSTS));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_STATUS:    0x%08x\n",
+               mshci_readl(host, MSHCI_STATUS));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_FIFOTH:    0x%08x\n",
+               mshci_readl(host, MSHCI_FIFOTH));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CDETECT:   0x%08x\n",
+               mshci_readl(host, MSHCI_CDETECT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_WRTPRT:    0x%08x\n",
+               mshci_readl(host, MSHCI_WRTPRT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_GPIO:      0x%08x\n",
+               mshci_readl(host, MSHCI_GPIO));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_TCBCNT:    0x%08x\n",
+               mshci_readl(host, MSHCI_TCBCNT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_TBBCNT:    0x%08x\n",
+               mshci_readl(host, MSHCI_TBBCNT));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_DEBNCE:    0x%08x\n",
+               mshci_readl(host, MSHCI_DEBNCE));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_USRID:     0x%08x\n",
+               mshci_readl(host, MSHCI_USRID));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_VERID:     0x%08x\n",
+               mshci_readl(host, MSHCI_VERID));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_HCON:      0x%08x\n",
+               mshci_readl(host, MSHCI_HCON));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_UHS_REG:   0x%08x\n",
+               mshci_readl(host, MSHCI_UHS_REG));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_BMOD:      0x%08x\n",
+               mshci_readl(host, MSHCI_BMOD));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_PLDMND:    0x%08x\n",
+               mshci_readl(host, MSHCI_PLDMND));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_DBADDR:    0x%08x\n",
+               mshci_readl(host, MSHCI_DBADDR));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_IDSTS:     0x%08x\n",
+               mshci_readl(host, MSHCI_IDSTS));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_IDINTEN:   0x%08x\n",
+               mshci_readl(host, MSHCI_IDINTEN));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_DSCADDR:   0x%08x\n",
+               mshci_readl(host, MSHCI_DSCADDR));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_BUFADDR:   0x%08x\n",
+               mshci_readl(host, MSHCI_BUFADDR));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_WAKEUPCON: 0x%08x\n",
+               mshci_readl(host, MSHCI_WAKEUPCON));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_CLOCKCON:  0x%08x\n",
+               mshci_readl(host, MSHCI_CLOCKCON));
+       printk(KERN_DEBUG DRIVER_NAME ": MSHCI_FIFODAT:   0x%08x\n",
+               mshci_readl(host, MSHCI_FIFODAT));
+       printk(KERN_DEBUG DRIVER_NAME ": 
===========================================\n");
+}
+
+/* Low level functions */
+
+static void mshci_clear_set_irqs(struct mshci_host *host, u32 clear, u32 set)
+{
+       u32 ier;
+
+       ier = mshci_readl(host, MSHCI_INTMSK);
+       ier &= ~clear;
+       ier |= set;
+       mshci_writel(host, ier, MSHCI_INTMSK);
+}
+
+static void mshci_unmask_irqs(struct mshci_host *host, u32 irqs)
+{
+       mshci_clear_set_irqs(host, 0, irqs);
+}
+
+static void mshci_mask_irqs(struct mshci_host *host, u32 irqs)
+{
+       mshci_clear_set_irqs(host, irqs, 0);
+}
+
+static void mshci_set_card_detection(struct mshci_host *host, bool enable)
+{
+       u32 irqs = INTMSK_CDETECT;
+
+       if (enable)
+               mshci_unmask_irqs(host, irqs);
+       else
+               mshci_mask_irqs(host, irqs);
+}
+
+static void mshci_enable_card_detection(struct mshci_host *host)
+{
+       mshci_set_card_detection(host, true);
+}
+
+static void mshci_disable_card_detection(struct mshci_host *host)
+{
+       mshci_set_card_detection(host, false);
+}
+
+static void mshci_reset_ciu(struct mshci_host *host)
+{
+       u32 timeout = 100;
+       u32 ier;
+
+       ier = mshci_readl(host, MSHCI_CTRL);
+       ier |= CTRL_RESET;
+
+       mshci_writel(host, ier, MSHCI_CTRL);
+       while (mshci_readl(host, MSHCI_CTRL) & CTRL_RESET) {
+               if (timeout == 0) {
+                       printk(KERN_ERR "%s: Reset CTRL never completed.\n",
+                               mmc_hostname(host->mmc));
+                       mshci_dumpregs(host);
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+}
+
+static void mshci_reset_fifo(struct mshci_host *host)
+{
+       u32 timeout = 100;
+       u32 ier;
+
+       ier = mshci_readl(host, MSHCI_CTRL);
+       ier |= FIFO_RESET;
+
+       mshci_writel(host, ier, MSHCI_CTRL);
+       while (mshci_readl(host, MSHCI_CTRL) & FIFO_RESET) {
+               if (timeout == 0) {
+                       printk(KERN_ERR "%s: Reset FIFO never completed.\n",
+                               mmc_hostname(host->mmc));
+                       mshci_dumpregs(host);
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+}
+
+static void mshci_reset_dma(struct mshci_host *host)
+{
+       u32 timeout = 100;
+       u32 ier;
+
+       ier = mshci_readl(host, MSHCI_CTRL);
+       ier |= DMA_RESET;
+
+       mshci_writel(host, ier, MSHCI_CTRL);
+       while (mshci_readl(host, MSHCI_CTRL) & DMA_RESET) {
+               if (timeout == 0) {
+                       printk(KERN_ERR "%s: Reset DMA never completed.\n",
+                               mmc_hostname(host->mmc));
+                       mshci_dumpregs(host);
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+}
+
+static void mshci_reset_all(struct mshci_host *host)
+{
+       mshci_reset_ciu(host);
+       mshci_reset_fifo(host);
+       mshci_reset_dma(host);
+}
+
+static void mshci_init(struct mshci_host *host)
+{
+       mshci_reset_all(host);
+
+       /* clear interrupt status */
+       mshci_writel(host, INTMSK_ALL, MSHCI_RINTSTS);
+
+       mshci_clear_set_irqs(host, INTMSK_ALL,
+               INTMSK_CDETECT | INTMSK_RE |
+               INTMSK_CDONE | INTMSK_DTO | INTMSK_TXDR | INTMSK_RXDR |
+               INTMSK_RCRC | INTMSK_DCRC | INTMSK_RTO | INTMSK_DRTO |
+               INTMSK_HTO | INTMSK_FRUN | INTMSK_HLE | INTMSK_SBE |
+               INTMSK_EBE);
+}
+
+static void mshci_reinit(struct mshci_host *host)
+{
+       mshci_init(host);
+       mshci_enable_card_detection(host);
+}
+
+/* Core functions */
+
+static void mshci_read_block_pio(struct mshci_host *host)
+{
+       unsigned long flags;
+       size_t fifo_cnt, len, chunk;
+       u32 uninitialized_var(scratch);
+       u8 *buf;
+
+       DBG("PIO reading\n");
+
+       fifo_cnt = (mshci_readl(host, MSHCI_STATUS) & FIFO_COUNT) >> 17;
+       fifo_cnt *= FIFO_WIDTH;
+       chunk = 0;
+
+       local_irq_save(flags);
+
+       while (fifo_cnt) {
+               if (!sg_miter_next(&host->sg_miter))
+                       BUG();
+
+               len = min(host->sg_miter.length, fifo_cnt);
+
+               fifo_cnt -= len;
+               host->sg_miter.consumed = len;
+
+               buf = host->sg_miter.addr;
+
+               while (len) {
+                       if (chunk == 0) {
+                               scratch = mshci_readl(host, MSHCI_FIFODAT);
+                               chunk = 4;
+                       }
+
+                       *buf = scratch & 0xFF;
+
+                       buf++;
+                       scratch >>= 8;
+                       chunk--;
+                       len--;
+               }
+       }
+
+       sg_miter_stop(&host->sg_miter);
+
+       local_irq_restore(flags);
+}
+
+static void mshci_write_block_pio(struct mshci_host *host)
+{
+       unsigned long flags;
+       size_t fifo_cnt, len, chunk;
+       u32 scratch;
+       u8 *buf;
+
+       DBG("PIO writing\n");
+
+       fifo_cnt = 8;
+
+       fifo_cnt *= FIFO_WIDTH;
+       chunk = 0;
+       scratch = 0;
+
+       local_irq_save(flags);
+
+       while (fifo_cnt) {
+               if (!sg_miter_next(&host->sg_miter)) {
+
+                       /*
+                        * Even though transfer is complete,
+                        * TXDR interrupt occurs again.
+                        * So, it has to check that it has really
+                        * no next sg buffer or just DTO interrupt
+                        * has not occured yet.
+                        */
+
+                       if ((host->data->blocks * host->data->blksz) ==
+                                       host->data_transfered)
+                               /* transfer done but DTO not yet */
+                               break;
+                       BUG();
+               }
+               len = min(host->sg_miter.length, fifo_cnt);
+
+               fifo_cnt -= len;
+               host->sg_miter.consumed = len;
+               host->data_transfered += len;
+
+               buf = (host->sg_miter.addr);
+
+               while (len) {
+                       scratch |= (u32)*buf << (chunk * 8);
+
+                       buf++;
+                       chunk++;
+                       len--;
+
+                       if ((chunk == 4) || ((len == 0) && (fifo_cnt == 0))) {
+                               mshci_writel(host, scratch, MSHCI_FIFODAT);
+                               chunk = 0;
+                               scratch = 0;
+                       }
+               }
+       }
+
+       sg_miter_stop(&host->sg_miter);
+
+       local_irq_restore(flags);
+}
+
+static void mshci_transfer_pio(struct mshci_host *host)
+{
+       BUG_ON(!host->data);
+
+       if (host->blocks == 0)
+               return;
+
+       if (host->data->flags & MMC_DATA_READ)
+               mshci_read_block_pio(host);
+       else
+               mshci_write_block_pio(host);
+
+       DBG("PIO transfer complete.\n");
+}
+
+static void mshci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy,
+                               u32 des0, u32 des1, u32 des2)
+{
+       ((struct mshci_idmac *)(desc_vir))->des0 = des0;
+       ((struct mshci_idmac *)(desc_vir))->des1 = des1;
+       ((struct mshci_idmac *)(desc_vir))->des2 = des2;
+       ((struct mshci_idmac *)(desc_vir))->des3 = (u32)desc_phy +
+                                       sizeof(struct mshci_idmac);
+}
+
+static int mshci_mdma_table_pre(struct mshci_host *host,
+                               struct mmc_data *data)
+{
+       int direction;
+
+       u8 *desc_vir, *desc_phy;
+       dma_addr_t addr;
+       int len;
+
+       struct scatterlist *sg;
+       int i;
+       u32 des_flag;
+       u32 size_idmac = sizeof(struct mshci_idmac);
+
+       if (data->flags & MMC_DATA_READ)
+               direction = DMA_FROM_DEVICE;
+       else
+               direction = DMA_TO_DEVICE;
+
+       host->sg_count = dma_map_sg(mmc_dev(host->mmc),
+               data->sg, data->sg_len, direction);
+       if (host->sg_count == 0)
+               goto fail;
+
+       desc_vir = host->idma_desc;
+
+       /* to know phy address */
+       host->idma_addr = dma_map_single(mmc_dev(host->mmc),
+                               host->idma_desc,
+                               128 * size_idmac,
+                               DMA_TO_DEVICE);
+       if (dma_mapping_error(mmc_dev(host->mmc), host->idma_addr))
+               goto unmap_entries;
+       BUG_ON(host->idma_addr & 0x3);
+
+       desc_phy = (u8 *)host->idma_addr;
+
+       for_each_sg(data->sg, sg, host->sg_count, i) {
+               addr = sg_dma_address(sg);
+               len = sg_dma_len(sg);
+
+               /* tran, valid */
+               des_flag = (MSHCI_IDMAC_OWN | MSHCI_IDMAC_CH);
+               des_flag |= (i == 0) ? MSHCI_IDMAC_FS : 0;
+
+               mshci_set_mdma_desc(desc_vir, desc_phy, des_flag, len, addr);
+               desc_vir += size_idmac;
+               desc_phy += size_idmac;
+
+               /* If this triggers then we have a calculation bug somewhere */
+               WARN_ON((desc_vir - host->idma_desc) > 128 * size_idmac);
+       }
+
+       /* Add a terminating flag */
+       ((struct mshci_idmac *)(desc_vir-size_idmac))->des0 |= MSHCI_IDMAC_LD;
+
+       /* It has to dma map again to resync vir data to phy data */
+       host->idma_addr = dma_map_single(mmc_dev(host->mmc),
+                               host->idma_desc,
+                               128 * size_idmac,
+                               DMA_TO_DEVICE);
+       if (dma_mapping_error(mmc_dev(host->mmc), host->idma_addr))
+               goto unmap_entries;
+       BUG_ON(host->idma_addr & 0x3);
+
+       return 0;
+
+unmap_entries:
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
+fail:
+       return -EINVAL;
+}
+
+static void mshci_idma_table_post(struct mshci_host *host,
+                                 struct mmc_data *data)
+{
+       int direction;
+
+       if (data->flags & MMC_DATA_READ)
+               direction = DMA_FROM_DEVICE;
+       else
+               direction = DMA_TO_DEVICE;
+
+       dma_unmap_single(mmc_dev(host->mmc), host->idma_addr,
+                        128 * sizeof(struct mshci_idmac), DMA_TO_DEVICE);
+
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
+}
+
+static u32 mshci_calc_timeout(struct mshci_host *host, struct mmc_data *data)
+{
+       /* this value SHOULD be optimized */
+       return 0xffffffff;
+}
+
+static void mshci_set_transfer_irqs(struct mshci_host *host)
+{
+       u32 uhs_reg;
+       u32 dma_irqs = INTMSK_DMA;
+       u32 pio_irqs = INTMSK_TXDR | INTMSK_RXDR;
+
+       uhs_reg = mshci_readl(host, MSHCI_UHS_REG);
+
+       if (host->flags & MSHCI_REQ_USE_DMA) {
+               /* Next codes are the W/A for DDR */
+               if ((uhs_reg & (0x1 << 16))
+                       && (host->data->flags & MMC_DATA_WRITE))
+                       dma_irqs |= INTMSK_DCRC;
+
+               /* clear interrupts for PIO */
+               mshci_clear_set_irqs(host, dma_irqs, 0);
+       } else {
+               /* Next codes are the W/A for DDR */
+               if ((uhs_reg & (0x1 << 16))
+                       && (host->data->flags & MMC_DATA_WRITE))
+                       mshci_clear_set_irqs(host, INTMSK_DCRC, pio_irqs);
+               else
+                       mshci_clear_set_irqs(host, 0, pio_irqs);
+       }
+}
+
+static void mshci_prepare_data(struct mshci_host *host, struct mmc_data *data)
+{
+       u32 count;
+       u32 ret;
+
+       WARN_ON(host->data);
+
+       if (data == NULL)
+               return;
+
+       BUG_ON(data->blksz * data->blocks > (host->mmc->max_req_size *
+                                       host->mmc->max_segs));
+       BUG_ON(data->blksz > host->mmc->max_blk_size);
+       BUG_ON(data->blocks > 400000);
+
+       host->data = data;
+       host->data_early = 0;
+
+       count = mshci_calc_timeout(host, data);
+       mshci_writel(host, count, MSHCI_TMOUT);
+
+       mshci_reset_fifo(host);
+
+       if (host->flags & (MSHCI_USE_IDMA))
+               host->flags |= MSHCI_REQ_USE_DMA;
+
+       /*
+        * FIXME: This doesn't account for merging when mapping the
+        * scatterlist.
+        */
+       if (host->flags & MSHCI_REQ_USE_DMA) {
+               /*
+                *  mshc's IDMAC can't transfer data that is not aligned
+                * or has length not divided by 4 byte.
+                */
+               int i;
+               struct scatterlist *sg;
+
+                       for_each_sg(data->sg, sg, data->sg_len, i) {
+                               if (sg->length & 0x3) {
+                                       DBG("Reverting to PIO because of "
+                                               "transfer size (%d)\n",
+                                               sg->length);
+                                       host->flags &= ~MSHCI_REQ_USE_DMA;
+                                       break;
+                       } else if (sg->offset & 0x3) {
+                               DBG("Reverting to PIO because of "
+                                       "bad alignment\n");
+                               host->flags &= ~MSHCI_REQ_USE_DMA;
+                               break;
+                       }
+               }
+       }
+
+       if (host->flags & MSHCI_REQ_USE_DMA) {
+               ret = mshci_mdma_table_pre(host, data);
+               if (ret) {
+                       /*
+                        * This only happens when someone fed
+                        * us an invalid request.
+                        */
+                       WARN_ON(1);
+                       host->flags &= ~MSHCI_REQ_USE_DMA;
+               } else {
+                       mshci_writel(host, host->idma_addr,
+                               MSHCI_DBADDR);
+               }
+       }
+
+       if (host->flags & MSHCI_REQ_USE_DMA) {
+               /* enable DMA, IDMA interrupts and IDMAC */
+               mshci_writel(host, (mshci_readl(host, MSHCI_CTRL) |
+                                       ENABLE_IDMAC|DMA_ENABLE), MSHCI_CTRL);
+               mshci_writel(host, (mshci_readl(host, MSHCI_BMOD) |
+                                       (BMOD_IDMAC_ENABLE|BMOD_IDMAC_FB)),
+                                       MSHCI_BMOD);
+               mshci_writel(host, INTMSK_IDMAC_ERROR, MSHCI_IDINTEN);
+       }
+
+       if (!(host->flags & MSHCI_REQ_USE_DMA)) {
+               int flags;
+
+               flags = SG_MITER_ATOMIC;
+               if (host->data->flags & MMC_DATA_READ)
+                       flags |= SG_MITER_TO_SG;
+               else
+                       flags |= SG_MITER_FROM_SG;
+
+               sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
+               host->blocks = data->blocks;
+
+               printk(KERN_ERR "it starts transfer on PIO\n");
+       }
+       /* set transfered data as 0. this value only uses for PIO write */
+       host->data_transfered = 0;
+       mshci_set_transfer_irqs(host);
+
+       mshci_writel(host, data->blksz, MSHCI_BLKSIZ);
+       mshci_writel(host, (data->blocks * data->blksz), MSHCI_BYTCNT);
+}
+
+static u32 mshci_set_transfer_mode(struct mshci_host *host,
+                                  struct mmc_data *data)
+{
+       u32 ret = 0;
+
+       if (data == NULL)
+               return ret;
+
+       WARN_ON(!host->data);
+
+       /* this cmd has data to transmit */
+       ret |= CMD_DATA_EXP_BIT;
+
+       if (data->flags & MMC_DATA_WRITE)
+               ret |= CMD_RW_BIT;
+       if (data->flags & MMC_DATA_STREAM)
+               ret |= CMD_TRANSMODE_BIT;
+
+       return ret;
+}
+
+static void mshci_finish_data(struct mshci_host *host)
+{
+       struct mmc_data *data;
+
+       BUG_ON(!host->data);
+
+       data = host->data;
+       host->data = NULL;
+
+       if (host->flags & MSHCI_REQ_USE_DMA) {
+               mshci_idma_table_post(host, data);
+               /* disable IDMAC and DMA interrupt */
+               mshci_writel(host, (mshci_readl(host, MSHCI_CTRL) &
+                               ~(DMA_ENABLE|ENABLE_IDMAC)), MSHCI_CTRL);
+
+               /* mask all interrupt source of IDMAC */
+               mshci_writel(host, 0x0, MSHCI_IDINTEN);
+       }
+
+       if (data->error) {
+               mshci_reset_dma(host);
+               data->bytes_xfered = 0;
+       } else
+               data->bytes_xfered = data->blksz * data->blocks;
+
+       if (data->stop)
+               mshci_send_command(host, data->stop);
+       else
+               tasklet_schedule(&host->finish_tasklet);
+}
+
+static void mshci_clock_onoff(struct mshci_host *host, bool val)
+{
+       u32 loop_count = 0x100000;
+
+       if (val) {
+               mshci_writel(host, (0x1<<0), MSHCI_CLKENA);
+               mshci_writel(host, 0, MSHCI_CMD);
+               mshci_writel(host, CMD_ONLY_CLK, MSHCI_CMD);
+               do {
+                       if (!(mshci_readl(host, MSHCI_CMD) & CMD_STRT_BIT))
+                               break;
+                       loop_count--;
+               } while (loop_count);
+       } else {
+               mshci_writel(host, (0x0<<0), MSHCI_CLKENA);
+               mshci_writel(host, 0, MSHCI_CMD);
+               mshci_writel(host, CMD_ONLY_CLK, MSHCI_CMD);
+               do {
+                       if (!(mshci_readl(host, MSHCI_CMD) & CMD_STRT_BIT))
+                               break;
+                       loop_count--;
+               } while (loop_count);
+       }
+       if (loop_count == 0) {
+               printk(KERN_ERR "%s: Clock %s has been failed.\n ",
+                      mmc_hostname(host->mmc), val ? "ON" : "OFF");
+       }
+}
+
+static void mshci_send_command(struct mshci_host *host, struct mmc_command 
*cmd)
+{
+       int flags, ret;
+       unsigned long timeout;
+
+       WARN_ON(host->cmd);
+
+       /* Wait max 1000 ms */
+       timeout = 100000;
+
+       /*
+        * 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)) {
+               /* nothing to do */
+       } else {
+               while (mshci_readl(host, MSHCI_STATUS) & (1 << 9)) {
+                       if (timeout == 0) {
+                               printk(KERN_ERR "%s: Controller never released "
+                                       "data0.\n", mmc_hostname(host->mmc));
+                               mshci_dumpregs(host);
+                               cmd->error = -EIO;
+                               tasklet_schedule(&host->finish_tasklet);
+                               return;
+                       }
+                       timeout--;
+                       udelay(10);
+               }
+       }
+
+       if (host->mmc->caps & MMC_CAP_CLOCK_GATING) {
+               del_timer(&host->clock_timer);
+               if (host->clock_to_restore != 0 && host->clock == 0)
+                       mshci_set_clock(host, host->clock_to_restore);
+       }
+
+       /* disable interrupt before issuing cmd to the card. */
+       mshci_writel(host, (mshci_readl(host, MSHCI_CTRL) & ~INT_ENABLE),
+                                       MSHCI_CTRL);
+
+       mod_timer(&host->timer, jiffies + 10 * HZ);
+
+       host->cmd = cmd;
+
+       mshci_prepare_data(host, cmd->data);
+
+       mshci_writel(host, cmd->arg, MSHCI_CMDARG);
+
+       flags = mshci_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 = -EINVAL;
+               tasklet_schedule(&host->finish_tasklet);
+               return;
+       }
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               flags |= CMD_RESP_EXP_BIT;
+               if (cmd->flags & MMC_RSP_136)
+                       flags |= CMD_RESP_LENGTH_BIT;
+       }
+       if (cmd->flags & MMC_RSP_CRC)
+               flags |= CMD_CHECK_CRC_BIT;
+       flags |= (cmd->opcode | CMD_STRT_BIT | CMD_WAIT_PRV_DAT_BIT);
+
+       ret = mshci_readl(host, MSHCI_CMD);
+       if (ret & CMD_STRT_BIT)
+               printk(KERN_ERR "CMD busy. current cmd %d. last cmd reg 0x%x\n",
+                       cmd->opcode, ret);
+
+       mshci_writel(host, flags, MSHCI_CMD);
+
+       /* enable interrupt upon it sends a command to the card. */
+       mshci_writel(host, (mshci_readl(host, MSHCI_CTRL) | INT_ENABLE),
+                                       MSHCI_CTRL);
+}
+
+static void mshci_finish_command(struct mshci_host *host)
+{
+       int i;
+
+       BUG_ON(host->cmd == NULL);
+
+       if (host->cmd->flags & MMC_RSP_PRESENT) {
+               if (host->cmd->flags & MMC_RSP_136) {
+                       /* response data are overturned */
+                       for (i = 0; i < 4; i++) {
+                               host->cmd->resp[0] = mshci_readl(host,
+                                                               MSHCI_RESP3);
+                               host->cmd->resp[1] = mshci_readl(host,
+                                                               MSHCI_RESP2);
+                               host->cmd->resp[2] = mshci_readl(host,
+                                                               MSHCI_RESP1);
+                               host->cmd->resp[3] = mshci_readl(host,
+                                                               MSHCI_RESP0);
+                       }
+               } else {
+                       host->cmd->resp[0] = mshci_readl(host, MSHCI_RESP0);
+               }
+       }
+
+       host->cmd->error = 0;
+
+       /* if data interrupt occurs earlier than command interrupt */
+       if (host->data && host->data_early)
+               mshci_finish_data(host);
+
+       if (!host->cmd->data)
+               tasklet_schedule(&host->finish_tasklet);
+
+       host->cmd = NULL;
+}
+
+static void mshci_set_clock(struct mshci_host *host, unsigned int clock)
+{
+       int div;
+       u32 loop_count;
+
+       if (clock == host->clock)
+               return;
+
+       /* before changing clock. clock needs to be off */
+       mshci_clock_onoff(host, CLK_DISABLE);
+
+       if (clock == 0)
+               goto out;
+
+       if (clock >= host->max_clk) {
+               div = 0;
+       } else {
+               for (div = 1; div < 255; div++) {
+                       if ((host->max_clk / (div<<1)) <= clock)
+                               break;
+               }
+       }
+
+       mshci_writel(host, div, MSHCI_CLKDIV);
+
+       mshci_writel(host, 0, MSHCI_CMD);
+       mshci_writel(host, CMD_ONLY_CLK, MSHCI_CMD);
+       loop_count = 0x100000;
+
+       do {
+               if (!(mshci_readl(host, MSHCI_CMD) & CMD_STRT_BIT))
+                       break;
+               loop_count--;
+       } while (loop_count);
+
+       if (loop_count == 0) {
+               printk(KERN_ERR "%s: Changing clock has been failed.\n "
+                               , mmc_hostname(host->mmc));
+       }
+       mshci_writel(host, mshci_readl(host, MSHCI_CMD)&(~CMD_SEND_CLK_ONLY),
+                                       MSHCI_CMD);
+
+       mshci_clock_onoff(host, CLK_ENABLE);
+
+out:
+       host->clock = clock;
+}
+
+static void mshci_set_power(struct mshci_host *host, unsigned short power)
+{
+       u8 pwr = power;
+
+       if (power == (unsigned short)-1)
+               pwr = 0;
+
+       if (host->pwr == pwr)
+               return;
+
+       host->pwr = pwr;
+
+       if (pwr == 0)
+               mshci_writel(host, 0, MSHCI_PWREN);
+       else
+               mshci_writel(host, 0x1, MSHCI_PWREN);
+}
+
+/* MMC callbacks */
+
+static void mshci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct mshci_host *host;
+       bool present;
+       unsigned long flags;
+
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       WARN_ON(host->mrq != NULL);
+
+       host->mrq = mrq;
+
+       /* If polling, assume that the card is always present. */
+       if (host->quirks & MSHCI_QUIRK_BROKEN_CARD_DETECTION ||
+                       host->quirks & MSHCI_QUIRK_BROKEN_PRESENT_BIT)
+               present = true;
+       else
+               present = !(mshci_readl(host, MSHCI_CDETECT) & CARD_PRESENT);
+
+       if (!present || host->flags & MSHCI_DEVICE_DEAD) {
+               host->mrq->cmd->error = -ENOMEDIUM;
+               tasklet_schedule(&host->finish_tasklet);
+       } else {
+               mshci_send_command(host, mrq->cmd);
+       }
+
+       mmiowb();
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void mshci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (host->flags & MSHCI_DEVICE_DEAD)
+               goto out;
+
+       if (ios->power_mode == MMC_POWER_OFF)
+               mshci_reinit(host);
+
+       if (host->ops->set_ios)
+               host->ops->set_ios(host, ios);
+
+       mshci_set_clock(host, ios->clock);
+
+       if (ios->power_mode == MMC_POWER_OFF)
+               mshci_set_power(host, -1);
+       else
+               mshci_set_power(host, ios->vdd);
+
+       if (ios->bus_width == MMC_BUS_WIDTH_8) {
+               mshci_writel(host, (0x1<<16), MSHCI_CTYPE);
+               mshci_writel(host, 0, MSHCI_UHS_REG);
+       } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
+               mshci_writel(host, (0x1<<0), MSHCI_CTYPE);
+               mshci_writel(host, 0, MSHCI_UHS_REG);
+       } else if (ios->bus_width == MMC_BUS_WIDTH_8_DDR) {
+               mshci_writel(host, (0x1<<16), MSHCI_CTYPE);
+               mshci_writel(host, (0x1<<16), MSHCI_UHS_REG);
+       } else if (ios->bus_width == MMC_BUS_WIDTH_4_DDR) {
+               mshci_writel(host, (0x1<<0), MSHCI_CTYPE);
+               mshci_writel(host, (0x1<<16), MSHCI_UHS_REG);
+       } else {
+               mshci_writel(host, 0, MSHCI_CTYPE);
+               mshci_writel(host, 0, MSHCI_UHS_REG);
+       }
+out:
+       mmiowb();
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mshci_get_ro(struct mmc_host *mmc)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+       int wrtprt;
+
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (host->quirks & MSHCI_QUIRK_ALWAYS_WRITABLE)
+               wrtprt = 0;
+       else if (host->quirks & MSHCI_QUIRK_NO_WP_BIT)
+               wrtprt = host->ops->get_ro(mmc) ? 0 : WRTPRT_ON;
+       else if (host->flags & MSHCI_DEVICE_DEAD)
+               wrtprt = 0;
+       else
+               wrtprt = mshci_readl(host, MSHCI_WRTPRT);
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       return wrtprt & WRTPRT_ON;
+}
+
+static void mshci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (host->flags & MSHCI_DEVICE_DEAD)
+               goto out;
+
+       if (enable)
+               mshci_unmask_irqs(host, SDIO_INT_ENABLE);
+       else
+               mshci_mask_irqs(host, SDIO_INT_ENABLE);
+out:
+       mmiowb();
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static struct mmc_host_ops mshci_ops = {
+       .request        = mshci_request,
+       .set_ios        = mshci_set_ios,
+       .get_ro         = mshci_get_ro,
+       .enable_sdio_irq = mshci_enable_sdio_irq,
+};
+
+/* Tasklets */
+
+static void mshci_tasklet_card(unsigned long param)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+
+       host = (struct mshci_host *)param;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (mshci_readl(host, MSHCI_CDETECT) & 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));
+
+                       host->mrq->cmd->error = -ENOMEDIUM;
+                       tasklet_schedule(&host->finish_tasklet);
+               }
+       }
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+}
+
+static void mshci_tasklet_finish(unsigned long param)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+       struct mmc_request *mrq;
+
+       host = (struct mshci_host *)param;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       del_timer(&host->timer);
+
+       mrq = host->mrq;
+
+       /*
+        * The controller needs a reset of internal state machines
+        * upon error conditions.
+        */
+       if (!(host->flags & MSHCI_DEVICE_DEAD) &&
+               (mrq->cmd->error ||
+                (mrq->data && (mrq->data->error ||
+                 (mrq->data->stop && mrq->data->stop->error))))) {
+
+               /*
+                * Spec says we should do both at the same time,
+                * but Ricoh controllers do not like that
+                */
+               mshci_reset_fifo(host);
+       }
+
+       if (host->mmc->caps & MMC_CAP_CLOCK_GATING) {
+               /* Disable the clock for power saving */
+               if (host->clock != 0) {
+                       mod_timer(&host->clock_timer,
+                                 jiffies + msecs_to_jiffies(10));
+               }
+       }
+
+       host->mrq = NULL;
+       host->cmd = NULL;
+       host->data = NULL;
+
+       mmiowb();
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       mmc_request_done(host->mmc, mrq);
+}
+
+static void mshci_timeout_timer(unsigned long data)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+
+       host = (struct mshci_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));
+               mshci_dumpregs(host);
+
+               if (host->data) {
+                       host->data->error = -ETIMEDOUT;
+                       mshci_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 mshci_clock_gate_timer(unsigned long data)
+{
+       struct mshci_host *host;
+       unsigned long flags;
+
+       host = (struct mshci_host *)data;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       /*
+        * if data line is busy or cmd, data and mrq exist,
+        * don't turn clock off
+        */
+       if ((mshci_readl(host, MSHCI_STATUS) & (1 << 9))
+               || host->cmd || host->data || host->mrq) {
+               mod_timer(&host->clock_timer, jiffies + msecs_to_jiffies(10));
+       } else {
+               host->clock_to_restore = host->clock;
+               mshci_set_clock(host, 0);
+       }
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/* Interrupt handling */
+
+static void mshci_cmd_irq(struct mshci_host *host, u32 intmask)
+{
+       BUG_ON(intmask == 0);
+
+       if (!host->cmd) {
+               printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+                       "though no command operation was in progress.\n",
+                       mmc_hostname(host->mmc), (unsigned)intmask);
+               mshci_dumpregs(host);
+               return;
+       }
+
+       if (intmask & INTMSK_RTO)
+               host->cmd->error = -ETIMEDOUT;
+       else if (intmask & (INTMSK_RCRC | INTMSK_RE))
+               host->cmd->error = -EILSEQ;
+
+       if (host->cmd->error) {
+               tasklet_schedule(&host->finish_tasklet);
+               return;
+       }
+
+       if (intmask & INTMSK_CDONE)
+               mshci_finish_command(host);
+}
+
+static void mshci_data_irq(struct mshci_host *host, u32 intmask, u8 intr_src)
+{
+       BUG_ON(intmask == 0);
+
+       if (!host->data) {
+               /*
+                * The "data complete" interrupt is also used to
+                * indicate that a busy state has ended. See comment
+                * above in mshci_cmd_irq().
+                */
+               if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+                       if (intmask & INTMSK_DTO) {
+                               mshci_finish_command(host);
+                               return;
+                       }
+               }
+
+               printk(KERN_ERR "%s: Got data interrupt 0x%08x from %s "
+                       "even though no data operation was in progress.\n",
+                       mmc_hostname(host->mmc), (unsigned)intmask,
+                       intr_src ? "MINT" : "IDMAC");
+               mshci_dumpregs(host);
+
+               return;
+       }
+       if (intr_src == INT_SRC_MINT) {
+               if (intmask & INTMSK_HTO) {
+                       printk(KERN_ERR "%s: Host timeout error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -ETIMEDOUT;
+               } else if (intmask & INTMSK_DRTO) {
+                       printk(KERN_ERR "%s: Data read timeout error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -ETIMEDOUT;
+               } else if (intmask & INTMSK_SBE) {
+                       printk(KERN_ERR "%s: Start bit error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               } else if (intmask & INTMSK_EBE) {
+                       printk(KERN_ERR "%s: Endbit/Write no CRC error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               } else if (intmask & INTMSK_DCRC) {
+                       printk(KERN_ERR "%s: Data CRC error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               } else if (intmask & INTMSK_FRUN) {
+                       printk(KERN_ERR "%s: FIFO underrun/overrun error\n",
+                                               mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               }
+       } else {
+               if (intmask & IDSTS_FBE) {
+                       printk(KERN_ERR "%s: Fatal Bus error on DMA\n",
+                                       mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               } else if (intmask & IDSTS_CES) {
+                       printk(KERN_ERR "%s: Card error on DMA\n",
+                                       mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               } else if (intmask & IDSTS_DU) {
+                       printk(KERN_ERR "%s: Description error on DMA\n",
+                                       mmc_hostname(host->mmc));
+                       host->data->error = -EIO;
+               }
+       }
+
+       if (host->data->error) {
+               mshci_finish_data(host);
+       } else {
+               if (!(host->flags & MSHCI_REQ_USE_DMA) &&
+                               (((host->data->flags & MMC_DATA_READ) &&
+                               (intmask & (INTMSK_RXDR | INTMSK_DTO))) ||
+                               ((host->data->flags & MMC_DATA_WRITE) &&
+                                       (intmask & (INTMSK_TXDR)))))
+                       mshci_transfer_pio(host);
+
+               if (intmask & INTMSK_DTO) {
+                       if (host->cmd) {
+                               /*
+                                * Data managed to finish before the
+                                * command completed. Make sure we do
+                                * things in the proper order.
+                                */
+                               host->data_early = 1;
+                       } else {
+                               mshci_finish_data(host);
+                       }
+               }
+       }
+}
+
+static irqreturn_t mshci_irq(int irq, void *dev_id)
+{
+       irqreturn_t result;
+       struct mshci_host *host = dev_id;
+       u32 intmask;
+       int cardint = 0;
+       int timeout = 0x10000;
+
+       spin_lock(&host->lock);
+
+       intmask = mshci_readl(host, MSHCI_MINTSTS);
+
+       if (!intmask || intmask == 0xffffffff) {
+               /* check if there is a interrupt for IDMAC  */
+               intmask = mshci_readl(host, MSHCI_IDSTS);
+               if (intmask) {
+                       mshci_writel(host, intmask, MSHCI_IDSTS);
+                       mshci_data_irq(host, intmask, INT_SRC_IDMAC);
+                       result = IRQ_HANDLED;
+                       goto out;
+                       }
+               result = IRQ_NONE;
+               goto out;
+       }
+       DBG("*** %s got interrupt: 0x%08x\n", mmc_hostname(host->mmc), intmask);
+
+       mshci_writel(host, intmask, MSHCI_RINTSTS);
+
+       if (intmask & (INTMSK_CDETECT))
+               tasklet_schedule(&host->card_tasklet);
+
+       intmask &= ~INTMSK_CDETECT;
+
+       if (intmask & CMD_STATUS) {
+               if (!(intmask & INTMSK_CDONE) && (intmask & INTMSK_RTO)) {
+                       /*
+                        * when a error about command timeout occurs,
+                        * cmd done intr comes together.
+                        * cmd done intr comes later than error intr.
+                        * so, it has to wait for cmd done intr.
+                        */
+                       while (--timeout && !(mshci_readl(host, MSHCI_MINTSTS)
+                               & INTMSK_CDONE))
+                               ; /* Nothing to do */
+                       if (!timeout)
+                               printk(KERN_ERR"*** %s time out for\
+                                       CDONE intr\n",
+                                       mmc_hostname(host->mmc));
+                       else
+                               mshci_writel(host, INTMSK_CDONE,
+                                       MSHCI_RINTSTS);
+                       mshci_cmd_irq(host, intmask & CMD_STATUS);
+               } else {
+                       mshci_cmd_irq(host, intmask & CMD_STATUS);
+               }
+       }
+
+       if (intmask & DATA_STATUS) {
+               if (!(intmask & INTMSK_DTO) && (intmask & INTMSK_DRTO)) {
+                       /*
+                        * when a error about data timout occurs,
+                        * DTO intr comes together.
+                        * DTO intr comes later than error intr.
+                        * so, it has to wait for DTO intr.
+                        */
+                       while (--timeout && !(mshci_readl(host, MSHCI_MINTSTS)
+                               & INTMSK_DTO))
+                               ; /* Nothing to do */
+                       if (!timeout)
+                               printk(KERN_ERR"*** %s time out for\
+                                       CDONE intr\n",
+                                       mmc_hostname(host->mmc));
+                       else
+                               mshci_writel(host, INTMSK_DTO,
+                                       MSHCI_RINTSTS);
+                       mshci_data_irq(host, intmask & DATA_STATUS,
+                                                       INT_SRC_MINT);
+               } else {
+                       mshci_data_irq(host, intmask & DATA_STATUS,
+                                                       INT_SRC_MINT);
+               }
+       }
+
+       intmask &= ~(CMD_STATUS | DATA_STATUS);
+
+       if (intmask & SDIO_INT_ENABLE)
+               cardint = 1;
+
+       intmask &= ~SDIO_INT_ENABLE;
+
+       if (intmask) {
+               printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+                       mmc_hostname(host->mmc), intmask);
+               mshci_dumpregs(host);
+       }
+
+       result = IRQ_HANDLED;
+
+       mmiowb();
+out:
+       spin_unlock(&host->lock);
+
+       /* We have to delay this as it calls back into the driver */
+       if (cardint)
+               mmc_signal_sdio_irq(host->mmc);
+
+       return result;
+}
+
+/* Suspend and Resume */
+
+#ifdef CONFIG_PM
+
+int mshci_suspend_host(struct mshci_host *host, pm_message_t state)
+{
+       int ret;
+
+       mshci_disable_card_detection(host);
+
+       ret = mmc_suspend_host(host->mmc);
+       if (ret)
+               return ret;
+
+       free_irq(host->irq, host);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mshci_suspend_host);
+
+int mshci_resume_host(struct mshci_host *host)
+{
+       int ret;
+
+       if (host->flags & (MSHCI_USE_IDMA)) {
+               if (host->ops->enable_dma)
+                       host->ops->enable_dma(host);
+       }
+
+       ret = request_irq(host->irq, mshci_irq, IRQF_SHARED,
+                         mmc_hostname(host->mmc), host);
+       if (ret)
+               return ret;
+
+       mshci_init(host);
+       mmiowb();
+
+       ret = mmc_resume_host(host->mmc);
+       if (ret)
+               return ret;
+
+       mshci_enable_card_detection(host);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mshci_resume_host);
+
+#endif /* CONFIG_PM */
+
+/* Device allocation and registration */
+
+struct mshci_host *mshci_alloc_host(struct device *dev,
+                                   size_t priv_size)
+{
+       struct mmc_host *mmc;
+       struct mshci_host *host;
+
+       WARN_ON(dev == NULL);
+
+       mmc = mmc_alloc_host(sizeof(struct mshci_host) + priv_size, dev);
+       if (!mmc)
+               return ERR_PTR(-ENOMEM);
+
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+
+       return host;
+}
+
+static void mshci_fifo_init(struct mshci_host *host)
+{
+       int fifo_val, fifo_depth, fifo_threshold;
+
+       fifo_val = mshci_readl(host, MSHCI_FIFOTH);
+       fifo_depth = ((fifo_val & RX_WMARK)>>16)+1;
+       fifo_threshold = fifo_depth/2;
+       host->fifo_threshold = fifo_threshold;
+       host->fifo_depth = fifo_threshold*2;
+
+       printk(KERN_INFO "%s: FIFO WMARK FOR RX 0x%x WX 0x%x.\n",
+               mmc_hostname(host->mmc), fifo_depth,
+               ((fifo_val & TX_WMARK) >> 16) + 1);
+
+       fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK);
+
+       fifo_val |= (fifo_threshold | (fifo_threshold << 16));
+       fifo_val |= MSIZE_8;
+
+       mshci_writel(host, fifo_val, MSHCI_FIFOTH);
+}
+EXPORT_SYMBOL_GPL(mshci_alloc_host);
+
+int mshci_add_host(struct mshci_host *host)
+{
+       struct mmc_host *mmc;
+       int ret, count;
+
+       WARN_ON(host == NULL);
+       if (host == NULL)
+               return -EINVAL;
+
+       mmc = host->mmc;
+
+       if (debug_quirks)
+               host->quirks = debug_quirks;
+
+       mshci_reset_all(host);
+
+       host->version = mshci_readl(host, MSHCI_VERID);
+
+       /* there are no reasons not to use DMA */
+       host->flags |= MSHCI_USE_IDMA;
+
+       if (host->flags & MSHCI_USE_IDMA) {
+               /* We need to allocate descriptors for all sg entries
+                * 128 transfer for each of those entries. */
+               host->idma_desc = kmalloc(128 * sizeof(struct mshci_idmac),
+                                       GFP_KERNEL);
+               if (!host->idma_desc) {
+                       kfree(host->idma_desc);
+                       printk(KERN_WARNING "%s: Unable to allocate IDMA "
+                               "buffers. Falling back to standard DMA.\n",
+                               mmc_hostname(mmc));
+                       host->flags &= ~MSHCI_USE_IDMA;
+               }
+       }
+
+       /*
+        * If we use DMA, then it's up to the caller to set the DMA
+        * mask, but PIO does not need the hw shim so we set a new
+        * mask here in that case.
+        */
+       if (!(host->flags & (MSHCI_USE_IDMA))) {
+               host->dma_mask = DMA_BIT_MASK(64);
+               mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+       }
+
+       printk(KERN_ERR "%s: Version ID 0x%x.\n",
+               mmc_hostname(host->mmc), host->version);
+
+       host->max_clk = 0;
+
+       if (host->max_clk == 0) {
+               if (!host->ops->get_max_clock) {
+                       printk(KERN_ERR
+                              "%s: Hardware doesn't specify base clock "
+                              "frequency.\n", mmc_hostname(mmc));
+                       return -ENODEV;
+               }
+               host->max_clk = host->ops->get_max_clock(host);
+       }
+
+       /* Set host parameters */
+       if (host->ops->get_ro)
+               mshci_ops.get_ro = host->ops->get_ro;
+
+       mmc->ops = &mshci_ops;
+       mmc->f_min = host->max_clk / 510;
+       mmc->f_max = host->max_clk;
+       mmc->caps |= MMC_CAP_SDIO_IRQ;
+
+       mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+       mmc->ocr_avail = 0;
+       mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+       mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+
+       if (mmc->ocr_avail == 0) {
+               printk(KERN_ERR "%s: Hardware doesn't report any "
+                       "support voltages.\n", mmc_hostname(mmc));
+               return -ENODEV;
+       }
+
+       spin_lock_init(&host->lock);
+
+       /* Maximum number of segments */
+       mmc->max_segs = 128;
+
+       /*
+        * Maximum number of sectors in one transfer. Limited by DMA boundary
+        * size (4KiB).
+        * Limited by CPU I/O boundry size (0xfffff000 KiB)
+        */
+
+       /*
+        * to prevent starvation of a process that want to access SD device
+        * it should limit size that transfer at one time.
+        */
+       mmc->max_req_size = 0x80000  ;
+
+       /*
+        * Maximum segment size. Could be one segment with the maximum number
+        * of bytes. When doing hardware scatter/gather, each entry cannot
+        * be larger than 4 KiB though.
+        */
+       if (host->flags & MSHCI_USE_IDMA)
+               mmc->max_seg_size = 0x1000;
+       else
+               mmc->max_seg_size = mmc->max_req_size;
+
+       /*
+        * from SD spec 2.0 and MMC spec 4.2, block size has been
+        * fixed to 512 byte
+        */
+       mmc->max_blk_size = 0;
+
+       mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+       /* Maximum block count */
+       mmc->max_blk_count = 0xffff;
+
+       /* Init tasklets */
+       tasklet_init(&host->card_tasklet,
+               mshci_tasklet_card, (unsigned long)host);
+       tasklet_init(&host->finish_tasklet,
+               mshci_tasklet_finish, (unsigned long)host);
+
+       setup_timer(&host->timer, mshci_timeout_timer, (unsigned long)host);
+       if (host->mmc->caps & MMC_CAP_CLOCK_GATING)
+               setup_timer(&host->clock_timer, mshci_clock_gate_timer,
+                           (unsigned long)host);
+
+       ret = request_irq(host->irq, mshci_irq, IRQF_SHARED,
+               mmc_hostname(mmc), host);
+       if (ret)
+               goto untasklet;
+
+       mshci_init(host);
+
+       mshci_writel(host, (mshci_readl(host, MSHCI_CTRL) | INT_ENABLE),
+                                       MSHCI_CTRL);
+
+       mshci_fifo_init(host);
+
+       /* set debounce filter value */
+       mshci_writel(host, 0xfffff, MSHCI_DEBNCE);
+
+       /* clear card type. set 1bit mode */
+       mshci_writel(host, 0x0, MSHCI_CTYPE);
+
+       /* set bus mode register for IDMAC */
+       if (host->flags & MSHCI_USE_IDMA) {
+               mshci_writel(host, BMOD_IDMAC_RESET, MSHCI_BMOD);
+               count = 100;
+               while ((mshci_readl(host, MSHCI_BMOD) & BMOD_IDMAC_RESET)
+                       && --count)
+                       ; /* nothing to do */
+
+               mshci_writel(host, (mshci_readl(host, MSHCI_BMOD) |
+                               (BMOD_IDMAC_ENABLE|BMOD_IDMAC_FB)), MSHCI_BMOD);
+       }
+#ifdef CONFIG_MMC_DEBUG
+       mshci_dumpregs(host);
+#endif
+
+       mmiowb();
+
+       mmc_add_host(mmc);
+
+       printk(KERN_INFO "%s: MSHCI controller on %s [%s] using %s\n",
+               mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
+               (host->flags & MSHCI_USE_IDMA) ? "IDMA" : "PIO");
+
+       mshci_enable_card_detection(host);
+
+       return 0;
+
+untasklet:
+       tasklet_kill(&host->card_tasklet);
+       tasklet_kill(&host->finish_tasklet);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mshci_add_host);
+
+void mshci_remove_host(struct mshci_host *host, int dead)
+{
+       unsigned long flags;
+
+       if (dead) {
+               spin_lock_irqsave(&host->lock, flags);
+
+               host->flags |= MSHCI_DEVICE_DEAD;
+
+               if (host->mrq) {
+                       printk(KERN_ERR "%s: Controller removed during "
+                               " transfer!\n", mmc_hostname(host->mmc));
+
+                       host->mrq->cmd->error = -ENOMEDIUM;
+                       tasklet_schedule(&host->finish_tasklet);
+               }
+
+               spin_unlock_irqrestore(&host->lock, flags);
+       }
+
+       mshci_disable_card_detection(host);
+
+       mmc_remove_host(host->mmc);
+
+       if (!dead)
+               mshci_reset_all(host);
+
+       free_irq(host->irq, host);
+
+       del_timer_sync(&host->timer);
+       if (host->mmc->caps & MMC_CAP_CLOCK_GATING)
+               del_timer_sync(&host->clock_timer);
+
+       tasklet_kill(&host->card_tasklet);
+       tasklet_kill(&host->finish_tasklet);
+
+       kfree(host->idma_desc);
+
+       host->idma_desc = NULL;
+       host->align_buffer = NULL;
+}
+EXPORT_SYMBOL_GPL(mshci_remove_host);
+
+void mshci_free_host(struct mshci_host *host)
+{
+       mmc_free_host(host->mmc);
+}
+EXPORT_SYMBOL_GPL(mshci_free_host);
+
+/* Driver init and exit */
+
+static int __init mshci_drv_init(void)
+{
+       printk(KERN_INFO DRIVER_NAME
+               ": Mobile Storage Host Controller Interface driver\n");
+       printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+
+       return 0;
+}
+
+static void __exit mshci_drv_exit(void)
+{
+}
+
+module_init(mshci_drv_init);
+module_exit(mshci_drv_exit);
+
+module_param(debug_quirks, uint, 0444);
+
+MODULE_AUTHOR("Hyunsung Jang <hs79.j...@samsung.com>");
+MODULE_AUTHOR("Hyuk Lee <hyuk1....@samsung.com>");
+MODULE_DESCRIPTION("Mobile Storage Host Controller Interface core driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
diff --git a/drivers/mmc/host/mshci.h b/drivers/mmc/host/mshci.h
new file mode 100644
index 0000000..7730381
--- /dev/null
+++ b/drivers/mmc/host/mshci.h
@@ -0,0 +1,292 @@
+/*
+ * linux/drivers/mmc/host/mshci.h
+ *
+ * Mobile Storage Host Controller Interface driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Based on linux/drivers/mmc/host/sdhci.h
+ *
+ * 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 __MSHCI_H
+#define __MSHCI_H
+
+#include <linux/scatterlist.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+/* MSHC Internal Registers */
+
+#define MSHCI_CTRL     0x00    /* Control */
+#define MSHCI_PWREN    0x04    /* Power-enable */
+#define MSHCI_CLKDIV   0x08    /* Clock divider */
+#define MSHCI_CLKSRC   0x0C    /* Clock source */
+#define MSHCI_CLKENA   0x10    /* Clock enable */
+#define MSHCI_TMOUT    0x14    /* Timeout */
+#define MSHCI_CTYPE    0x18    /* Card type */
+#define MSHCI_BLKSIZ   0x1C    /* Block Size */
+#define MSHCI_BYTCNT   0x20    /* Byte count */
+#define MSHCI_INTMSK   0x24    /* Interrupt Mask */
+#define MSHCI_CMDARG   0x28    /* Command Argument */
+#define MSHCI_CMD      0x2C    /* Command */
+#define MSHCI_RESP0    0x30    /* Response 0 */
+#define MSHCI_RESP1    0x34    /* Response 1 */
+#define MSHCI_RESP2    0x38    /* Response 2 */
+#define MSHCI_RESP3    0x3C    /* Response 3 */
+#define MSHCI_MINTSTS  0x40    /* Masked interrupt status */
+#define MSHCI_RINTSTS  0x44    /* Raw interrupt status */
+#define MSHCI_STATUS   0x48    /* Status */
+#define MSHCI_FIFOTH   0x4C    /* FIFO threshold */
+#define MSHCI_CDETECT  0x50    /* Card detect */
+#define MSHCI_WRTPRT   0x54    /* Write protect */
+#define MSHCI_GPIO     0x58    /* General Purpose IO */
+#define MSHCI_TCBCNT   0x5C    /* Transferred CIU byte count */
+#define MSHCI_TBBCNT   0x60    /* Transferred host/DMA to/from byte count */
+#define MSHCI_DEBNCE   0x64    /* Card detect debounce */
+#define MSHCI_USRID    0x68    /* User ID */
+#define MSHCI_VERID    0x6C    /* Version ID */
+#define MSHCI_HCON     0x70    /* Hardware Configuration */
+#define MSHCI_UHS_REG  0x74    /* UHS and DDR setting */
+#define MSHCI_BMOD     0x80    /* Bus mode register */
+#define MSHCI_PLDMND   0x84    /* Poll demand */
+#define MSHCI_DBADDR   0x88    /* Descriptor list base address */
+#define MSHCI_IDSTS    0x8C    /* Internal DMAC status */
+#define MSHCI_IDINTEN  0x90    /* Internal DMAC interrupt enable */
+#define MSHCI_DSCADDR  0x94    /* Current host descriptor address */
+#define MSHCI_BUFADDR  0x98    /* Current host buffer address */
+#define MSHCI_WAKEUPCON        0xA0    /* Wakeup control register */
+#define MSHCI_CLOCKCON 0xA4    /* Clock (delay) control register */
+#define MSHCI_FIFODAT  0x100   /* FIFO data read write */
+
+/* Control Register MSHCI_CTRL(offset 0x00) */
+
+#define CTRL_RESET     (0x1 << 0) /* Reset DWC_mobile_storage controller */
+#define FIFO_RESET     (0x1 << 1) /* Reset FIFO */
+#define DMA_RESET      (0x1 << 2) /* Reset DMA interface */
+#define INT_ENABLE     (0x1 << 4) /* Global interrupt enable/disable bit */
+#define DMA_ENABLE     (0x1 << 5) /* DMA transfer mode enable/disable bit */
+#define ENABLE_IDMAC   (0x1 << 25)
+
+/* Clock Enable Register MSHCI_CLKENA(offset 0x10) */
+
+#define CLK_ENABLE     (0x1 << 0)
+#define CLK_DISABLE    (0x0 << 0)
+
+/* Interrupt Mask Register MSHCI_INTMSK(offset 0x24) */
+
+#define SDIO_INT_ENABLE        (0x1 << 16)
+
+/* Interrupt bits */
+
+#define INTMSK_ALL     0xFFFFFFFF
+#define INTMSK_CDETECT (0x1 << 0)
+#define INTMSK_RE      (0x1 << 1)
+#define INTMSK_CDONE   (0x1 << 2)
+#define INTMSK_DTO     (0x1 << 3)
+#define INTMSK_TXDR    (0x1 << 4)
+#define INTMSK_RXDR    (0x1 << 5)
+#define INTMSK_RCRC    (0x1 << 6)
+#define INTMSK_DCRC    (0x1 << 7)
+#define INTMSK_RTO     (0x1 << 8)
+#define INTMSK_DRTO    (0x1 << 9)
+#define INTMSK_HTO     (0x1 << 10)
+#define INTMSK_FRUN    (0x1 << 11)
+#define INTMSK_HLE     (0x1 << 12)
+#define INTMSK_SBE     (0x1 << 13)
+#define INTMSK_ACD     (0x1 << 14)
+#define INTMSK_EBE     (0x1 << 15)
+#define INTMSK_DMA     (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR)
+
+#define INT_SRC_IDMAC  (0x0)
+#define INT_SRC_MINT   (0x1)
+
+/* Command Register MSHCI_CMD(offset 0x2C) */
+
+#define CMD_RESP_EXP_BIT       (0x1 << 6)
+#define CMD_RESP_LENGTH_BIT    (0x1 << 7)
+#define CMD_CHECK_CRC_BIT      (0x1 << 8)
+#define CMD_DATA_EXP_BIT       (0x1 << 9)
+#define CMD_RW_BIT             (0x1 << 10)
+#define CMD_TRANSMODE_BIT      (0x1 << 11)
+#define CMD_WAIT_PRV_DAT_BIT   (0x1 << 13)
+#define CMD_SEND_CLK_ONLY      (0x1 << 21)
+#define CMD_STRT_BIT           (0x1 << 31)
+#define CMD_ONLY_CLK           (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \
+                                               CMD_WAIT_PRV_DAT_BIT)
+
+/* Raw Interrupt Register MSHCI_RINTSTS(offset 0x44) */
+
+#define DATA_ERR       (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE |         \
+                        INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC)
+#define DATA_TOUT      (INTMSK_HTO | INTMSK_DRTO)
+#define DATA_STATUS    (DATA_ERR | DATA_TOUT | INTMSK_RXDR |           \
+                        INTMSK_TXDR | INTMSK_DTO)
+#define CMD_STATUS     (INTMSK_RTO | INTMSK_RCRC | INTMSK_CDONE |      \
+                        INTMSK_RE)
+
+/* Status Register MSHCI_STATUS(offset 0x48) */
+
+#define FIFO_COUNT     (0x1FFF << 17)
+#define FIFO_WIDTH     (0x4)
+
+/* FIFO Threshold Watermark Register MSHCI_FIFOTH(offset 0x4C) */
+
+#define TX_WMARK               (0xFFF << 0)
+#define RX_WMARK               (0xFFF << 16)
+#define MSIZE_MASK             (0x7 << 28)
+
+/* DW DMA Mutiple Transaction Size */
+#define MSIZE_8                        (2 << 28)
+
+/*
+ * Card Detect Register MSHCI_CDETECT(offset 0x50)
+ * It assumes there is only one SD slot
+ */
+#define CARD_PRESENT           (0x1 << 0)
+
+/*
+ * Write Protect Register MSHCI_WRTPRT(offset 0x54)
+ * It assumes there is only one SD slot
+ */
+#define WRTPRT_ON              (0x1 << 0)
+
+/* Bus Mode Register MSHCI_BMOD(offset 0x80) */
+
+#define BMOD_IDMAC_RESET       (0x1 << 1)
+#define BMOD_IDMAC_FB          (0x1 << 1)
+#define BMOD_IDMAC_ENABLE      (0x1 << 7)
+
+/* Hardware Configuration  Register MSHCI_IDSTS(offset 0x8c) */
+
+#define IDSTS_CES              (0x1 << 5)
+#define IDSTS_DU               (0x1 << 4)
+#define IDSTS_FBE              (0x1 << 2)
+
+struct mshci_ops;
+
+struct mshci_idmac {
+       u32     des0;
+       u32     des1;
+       u32     des2;
+       u32     des3;
+#define MSHCI_IDMAC_OWN        (1 << 31)
+#define MSHCI_IDMAC_CH         (1 << 4)
+#define MSHCI_IDMAC_FS         (1 << 3)
+#define MSHCI_IDMAC_LD         (1 << 2)
+#define INTMSK_IDMAC_ERROR     (0x214)
+};
+
+struct mshci_host {
+       /* Data set by hardware interface driver */
+       const char              *hw_name;       /* Hardware bus name */
+
+       unsigned int            quirks;         /* Deviations from spec. */
+
+/* Controller has no write-protect pin connected with SD card */
+
+#define MSHCI_QUIRK_NO_WP_BIT                  (1 << 0)
+#define MSHCI_QUIRK_BROKEN_CARD_DETECTION      (1 << 1)
+#define MSHCI_QUIRK_ALWAYS_WRITABLE            (1 << 2)
+#define MSHCI_QUIRK_BROKEN_PRESENT_BIT         (1 << 3)
+
+       int                     irq;            /* Device IRQ */
+       void __iomem            *ioaddr;        /* Mapped address */
+
+       const struct mshci_ops  *ops;           /* Low level hw interface */
+
+       /* Internal data */
+       struct mmc_host         *mmc;           /* MMC structure */
+       u64                     dma_mask;       /* custom DMA mask */
+
+       spinlock_t              lock;           /* Mutex */
+
+       int                     flags;          /* Host attributes */
+#define MSHCI_USE_IDMA         (1 << 1)        /* Host is ADMA capable */
+#define MSHCI_REQ_USE_DMA      (1 << 2)        /* Use DMA for this req. */
+#define MSHCI_DEVICE_DEAD      (1 << 3)        /* Device unresponsive */
+
+       unsigned int            version;        /* SDHCI spec. version */
+
+       unsigned int            max_clk;        /* Max possible freq (MHz) */
+       unsigned int            timeout_clk;    /* Timeout freq (KHz) */
+
+       unsigned int            clock;          /* Current clock (MHz) */
+       unsigned int            clock_to_restore; /* Saved clock for dynamic 
clock gating (MHz) */
+       u8                      pwr;            /* Current voltage */
+
+       struct mmc_request      *mrq;           /* Current request */
+       struct mmc_command      *cmd;           /* Current command */
+       struct mmc_data         *data;          /* Current data request */
+       unsigned int            data_early:1;   /* Data finished before cmd */
+
+       struct sg_mapping_iter  sg_miter;       /* SG state for PIO */
+       unsigned int            blocks;         /* remaining PIO blocks */
+
+       int                     sg_count;       /* Mapped sg entries */
+
+       u8                      *idma_desc;     /* ADMA descriptor table */
+       u8                      *align_buffer;  /* Bounce buffer */
+
+       dma_addr_t              idma_addr;      /* Mapped ADMA descr. table */
+       dma_addr_t              align_addr;     /* Mapped bounce buffer */
+
+       struct tasklet_struct   card_tasklet;   /* Tasklet structures */
+       struct tasklet_struct   finish_tasklet;
+
+       struct timer_list       timer;          /* Timer for timeouts */
+       struct timer_list       clock_timer;    /* Timer for clock gating */
+
+       u32                     fifo_depth;
+       u32                     fifo_threshold;
+       u32                     data_transfered;
+       unsigned long           private[0] ____cacheline_aligned;
+};
+
+struct mshci_ops {
+       void    (*set_clock)(struct mshci_host *host, unsigned int clock);
+
+       int             (*enable_dma)(struct mshci_host *host);
+       unsigned int    (*get_max_clock)(struct mshci_host *host);
+       unsigned int    (*get_min_clock)(struct mshci_host *host);
+       unsigned int    (*get_timeout_clock)(struct mshci_host *host);
+       void            (*set_ios)(struct mshci_host *host,
+                                  struct mmc_ios *ios);
+       int             (*get_ro)(struct mmc_host *mmc);
+       void            (*init_issue_cmd)(struct mshci_host *host);
+};
+
+static inline void mshci_writel(struct mshci_host *host, u32 val, int reg)
+{
+       writel(val, host->ioaddr + reg);
+}
+
+static inline u32 mshci_readl(struct mshci_host *host, int reg)
+{
+       return readl(host->ioaddr + reg);
+}
+
+extern struct mshci_host *mshci_alloc_host(struct device *dev,
+                                          size_t priv_size);
+extern void mshci_free_host(struct mshci_host *host);
+
+static inline void *mshci_priv(struct mshci_host *host)
+{
+       return (void *)host->private;
+}
+
+extern int mshci_add_host(struct mshci_host *host);
+extern void mshci_remove_host(struct mshci_host *host, int dead);
+
+#ifdef CONFIG_PM
+extern int mshci_suspend_host(struct mshci_host *host, pm_message_t state);
+extern int mshci_resume_host(struct mshci_host *host);
+#endif
+
+#endif /* __MSHCI_H */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bcb793e..7b4c5b5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -44,6 +44,8 @@ struct mmc_ios {
 #define MMC_BUS_WIDTH_1                0
 #define MMC_BUS_WIDTH_4                2
 #define MMC_BUS_WIDTH_8                3
+#define MMC_BUS_WIDTH_4_DDR    4
+#define MMC_BUS_WIDTH_8_DDR    5
 
        unsigned char   timing;                 /* timing specification used */
 
@@ -173,6 +175,7 @@ struct mmc_host {
                                                /* DDR mode at 1.2V */
 #define MMC_CAP_POWER_OFF_CARD (1 << 13)       /* Can power off after boot */
 #define MMC_CAP_BUS_WIDTH_TEST (1 << 14)       /* CMD14/CMD19 bus width ok */
+#define MMC_CAP_CLOCK_GATING   (1 << 15)       /* Can do clock gating 
dynamically */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
 
-- 
1.6.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to