]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/mmc/host/tmio_mmc.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / mmc / host / tmio_mmc.c
index e7765a89593e31e3377c5a09b7a5440190ff46ed..e3c6ef20839193462150eadb2cfea55b0234a7d3 100644 (file)
  *   double buffer support
  *
  */
-#include <linux/module.h>
-#include <linux/irq.h>
-#include <linux/device.h>
+
 #include <linux/delay.h>
+#include <linux/device.h>
 #include <linux/dmaengine.h>
-#include <linux/mmc/host.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/tmio.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+#define CTL_SD_CMD 0x00
+#define CTL_ARG_REG 0x04
+#define CTL_STOP_INTERNAL_ACTION 0x08
+#define CTL_XFER_BLK_COUNT 0xa
+#define CTL_RESPONSE 0x0c
+#define CTL_STATUS 0x1c
+#define CTL_IRQ_MASK 0x20
+#define CTL_SD_CARD_CLK_CTL 0x24
+#define CTL_SD_XFER_LEN 0x26
+#define CTL_SD_MEM_CARD_OPT 0x28
+#define CTL_SD_ERROR_DETAIL_STATUS 0x2c
+#define CTL_SD_DATA_PORT 0x30
+#define CTL_TRANSACTION_CTL 0x34
+#define CTL_SDIO_STATUS 0x36
+#define CTL_SDIO_IRQ_MASK 0x38
+#define CTL_RESET_SD 0xe0
+#define CTL_SDIO_REGS 0x100
+#define CTL_CLK_AND_WAIT_CTL 0x138
+#define CTL_RESET_SDIO 0x1e0
+
+/* Definitions for values the CTRL_STATUS register can take. */
+#define TMIO_STAT_CMDRESPEND    0x00000001
+#define TMIO_STAT_DATAEND       0x00000004
+#define TMIO_STAT_CARD_REMOVE   0x00000008
+#define TMIO_STAT_CARD_INSERT   0x00000010
+#define TMIO_STAT_SIGSTATE      0x00000020
+#define TMIO_STAT_WRPROTECT     0x00000080
+#define TMIO_STAT_CARD_REMOVE_A 0x00000100
+#define TMIO_STAT_CARD_INSERT_A 0x00000200
+#define TMIO_STAT_SIGSTATE_A    0x00000400
+#define TMIO_STAT_CMD_IDX_ERR   0x00010000
+#define TMIO_STAT_CRCFAIL       0x00020000
+#define TMIO_STAT_STOPBIT_ERR   0x00040000
+#define TMIO_STAT_DATATIMEOUT   0x00080000
+#define TMIO_STAT_RXOVERFLOW    0x00100000
+#define TMIO_STAT_TXUNDERRUN    0x00200000
+#define TMIO_STAT_CMDTIMEOUT    0x00400000
+#define TMIO_STAT_RXRDY         0x01000000
+#define TMIO_STAT_TXRQ          0x02000000
+#define TMIO_STAT_ILL_FUNC      0x20000000
+#define TMIO_STAT_CMD_BUSY      0x40000000
+#define TMIO_STAT_ILL_ACCESS    0x80000000
+
+/* Definitions for values the CTRL_SDIO_STATUS register can take. */
+#define TMIO_SDIO_STAT_IOIRQ   0x0001
+#define TMIO_SDIO_STAT_EXPUB52 0x4000
+#define TMIO_SDIO_STAT_EXWT    0x8000
+#define TMIO_SDIO_MASK_ALL     0xc007
+
+/* Define some IRQ masks */
+/* This is the mask used at reset by the chip */
+#define TMIO_MASK_ALL           0x837f031d
+#define TMIO_MASK_READOP  (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND)
+#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND)
+#define TMIO_MASK_CMD     (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \
+               TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
+#define TMIO_MASK_IRQ     (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
+
+#define enable_mmc_irqs(host, i) \
+       do { \
+               u32 mask;\
+               mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \
+               mask &= ~((i) & TMIO_MASK_IRQ); \
+               sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \
+       } while (0)
+
+#define disable_mmc_irqs(host, i) \
+       do { \
+               u32 mask;\
+               mask  = sd_ctrl_read32((host), CTL_IRQ_MASK); \
+               mask |= ((i) & TMIO_MASK_IRQ); \
+               sd_ctrl_write32((host), CTL_IRQ_MASK, mask); \
+       } while (0)
+
+#define ack_mmc_irqs(host, i) \
+       do { \
+               sd_ctrl_write32((host), CTL_STATUS, ~(i)); \
+       } while (0)
+
+/* This is arbitrary, just noone needed any higher alignment yet */
+#define MAX_ALIGN 4
+
+struct tmio_mmc_host {
+       void __iomem *ctl;
+       unsigned long bus_shift;
+       struct mmc_command      *cmd;
+       struct mmc_request      *mrq;
+       struct mmc_data         *data;
+       struct mmc_host         *mmc;
+       int                     irq;
+       unsigned int            sdio_irq_enabled;
+
+       /* Callbacks for clock / power control */
+       void (*set_pwr)(struct platform_device *host, int state);
+       void (*set_clk_div)(struct platform_device *host, int state);
+
+       /* pio related stuff */
+       struct scatterlist      *sg_ptr;
+       struct scatterlist      *sg_orig;
+       unsigned int            sg_len;
+       unsigned int            sg_off;
+
+       struct platform_device *pdev;
+
+       /* DMA support */
+       struct dma_chan         *chan_rx;
+       struct dma_chan         *chan_tx;
+       struct tasklet_struct   dma_complete;
+       struct tasklet_struct   dma_issue;
+#ifdef CONFIG_TMIO_MMC_DMA
+       unsigned int            dma_sglen;
+       u8                      bounce_buf[PAGE_CACHE_SIZE] __attribute__((aligned(MAX_ALIGN)));
+       struct scatterlist      bounce_sg;
+#endif
+
+       /* Track lost interrupts */
+       struct delayed_work     delayed_reset_work;
+       spinlock_t              lock;
+       unsigned long           last_req_ts;
+};
+
+static void tmio_check_bounce_buffer(struct tmio_mmc_host *host);
+
+static u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
+{
+       return readw(host->ctl + (addr << host->bus_shift));
+}
+
+static void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
+               u16 *buf, int count)
+{
+       readsw(host->ctl + (addr << host->bus_shift), buf, count);
+}
 
-#include "tmio_mmc.h"
+static u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
+{
+       return readw(host->ctl + (addr << host->bus_shift)) |
+              readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
+}
+
+static void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
+{
+       writew(val, host->ctl + (addr << host->bus_shift));
+}
+
+static void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
+               u16 *buf, int count)
+{
+       writesw(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
+static void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
+{
+       writew(val, host->ctl + (addr << host->bus_shift));
+       writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
+}
+
+static void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data)
+{
+       host->sg_len = data->sg_len;
+       host->sg_ptr = data->sg;
+       host->sg_orig = data->sg;
+       host->sg_off = 0;
+}
+
+static int tmio_mmc_next_sg(struct tmio_mmc_host *host)
+{
+       host->sg_ptr = sg_next(host->sg_ptr);
+       host->sg_off = 0;
+       return --host->sg_len;
+}
+
+static char *tmio_mmc_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
+{
+       local_irq_save(*flags);
+       return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static void tmio_mmc_kunmap_atomic(void *virt, unsigned long *flags)
+{
+       kunmap_atomic(virt, KM_BIO_SRC_IRQ);
+       local_irq_restore(*flags);
+}
+
+#ifdef CONFIG_MMC_DEBUG
+
+#define STATUS_TO_TEXT(a) \
+       do { \
+               if (status & TMIO_STAT_##a) \
+                       printk(#a); \
+       } while (0)
+
+void pr_debug_status(u32 status)
+{
+       printk(KERN_DEBUG "status: %08x = ", status);
+       STATUS_TO_TEXT(CARD_REMOVE);
+       STATUS_TO_TEXT(CARD_INSERT);
+       STATUS_TO_TEXT(SIGSTATE);
+       STATUS_TO_TEXT(WRPROTECT);
+       STATUS_TO_TEXT(CARD_REMOVE_A);
+       STATUS_TO_TEXT(CARD_INSERT_A);
+       STATUS_TO_TEXT(SIGSTATE_A);
+       STATUS_TO_TEXT(CMD_IDX_ERR);
+       STATUS_TO_TEXT(STOPBIT_ERR);
+       STATUS_TO_TEXT(ILL_FUNC);
+       STATUS_TO_TEXT(CMD_BUSY);
+       STATUS_TO_TEXT(CMDRESPEND);
+       STATUS_TO_TEXT(DATAEND);
+       STATUS_TO_TEXT(CRCFAIL);
+       STATUS_TO_TEXT(DATATIMEOUT);
+       STATUS_TO_TEXT(CMDTIMEOUT);
+       STATUS_TO_TEXT(RXOVERFLOW);
+       STATUS_TO_TEXT(TXUNDERRUN);
+       STATUS_TO_TEXT(RXRDY);
+       STATUS_TO_TEXT(TXRQ);
+       STATUS_TO_TEXT(ILL_ACCESS);
+       printk("\n");
+}
+
+#else
+#define pr_debug_status(s)  do { } while (0)
+#endif
+
+static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct tmio_mmc_host *host = mmc_priv(mmc);
+
+       if (enable) {
+               host->sdio_irq_enabled = 1;
+               sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
+               sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK,
+                       (TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ));
+       } else {
+               sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, TMIO_SDIO_MASK_ALL);
+               sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
+               host->sdio_irq_enabled = 0;
+       }
+}
 
 static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
 {
@@ -55,8 +300,23 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
 
 static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
 {
+       struct mfd_cell *cell = host->pdev->dev.platform_data;
+       struct tmio_mmc_data *pdata = cell->driver_data;
+
+       /*
+        * Testing on sh-mobile showed that SDIO IRQs are unmasked when
+        * CTL_CLK_AND_WAIT_CTL gets written, so we have to disable the
+        * device IRQ here and restore the SDIO IRQ mask before
+        * re-enabling the device IRQ.
+        */
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ)
+               disable_irq(host->irq);
        sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
        msleep(10);
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
+               tmio_mmc_enable_sdio_irq(host->mmc, host->sdio_irq_enabled);
+               enable_irq(host->irq);
+       }
        sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
                sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
        msleep(10);
@@ -64,11 +324,21 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
 
 static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
 {
+       struct mfd_cell *cell = host->pdev->dev.platform_data;
+       struct tmio_mmc_data *pdata = cell->driver_data;
+
        sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
                sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
        msleep(10);
+       /* see comment in tmio_mmc_clk_stop above */
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ)
+               disable_irq(host->irq);
        sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
        msleep(10);
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
+               tmio_mmc_enable_sdio_irq(host->mmc, host->sdio_irq_enabled);
+               enable_irq(host->irq);
+       }
 }
 
 static void reset(struct tmio_mmc_host *host)
@@ -82,15 +352,60 @@ static void reset(struct tmio_mmc_host *host)
        msleep(10);
 }
 
+static void tmio_mmc_reset_work(struct work_struct *work)
+{
+       struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host,
+                                                 delayed_reset_work.work);
+       struct mmc_request *mrq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       mrq = host->mrq;
+
+       /* request already finished */
+       if (!mrq
+           || time_is_after_jiffies(host->last_req_ts +
+               msecs_to_jiffies(2000))) {
+               spin_unlock_irqrestore(&host->lock, flags);
+               return;
+       }
+
+       dev_warn(&host->pdev->dev,
+               "timeout waiting for hardware interrupt (CMD%u)\n",
+               mrq->cmd->opcode);
+
+       if (host->data)
+               host->data->error = -ETIMEDOUT;
+       else if (host->cmd)
+               host->cmd->error = -ETIMEDOUT;
+       else
+               mrq->cmd->error = -ETIMEDOUT;
+
+       host->cmd = NULL;
+       host->data = NULL;
+       host->mrq = NULL;
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       reset(host);
+
+       mmc_request_done(host->mmc, mrq);
+}
+
 static void
 tmio_mmc_finish_request(struct tmio_mmc_host *host)
 {
        struct mmc_request *mrq = host->mrq;
 
+       if (!mrq)
+               return;
+
        host->mrq = NULL;
        host->cmd = NULL;
        host->data = NULL;
 
+       cancel_delayed_work(&host->delayed_reset_work);
+
        mmc_request_done(host->mmc, mrq);
 }
 
@@ -200,6 +515,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
        return;
 }
 
+/* needs to be called with host->lock held */
 static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
 {
        struct mmc_data *data = host->data;
@@ -233,6 +549,8 @@ static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
        if (data->flags & MMC_DATA_READ) {
                if (!host->chan_rx)
                        disable_mmc_irqs(host, TMIO_MASK_READOP);
+               else
+                       tmio_check_bounce_buffer(host);
                dev_dbg(&host->pdev->dev, "Complete Rx request %p\n",
                        host->mrq);
        } else {
@@ -254,10 +572,12 @@ static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
 
 static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
 {
-       struct mmc_data *data = host->data;
+       struct mmc_data *data;
+       spin_lock(&host->lock);
+       data = host->data;
 
        if (!data)
-               return;
+               goto out;
 
        if (host->chan_tx && (data->flags & MMC_DATA_WRITE)) {
                /*
@@ -278,6 +598,8 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
        } else {
                tmio_mmc_do_data_irq(host);
        }
+out:
+       spin_unlock(&host->lock);
 }
 
 static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
@@ -286,9 +608,11 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
        struct mmc_command *cmd = host->cmd;
        int i, addr;
 
+       spin_lock(&host->lock);
+
        if (!host->cmd) {
                pr_debug("Spurious CMD irq\n");
-               return;
+               goto out;
        }
 
        host->cmd = NULL;
@@ -324,8 +648,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
                        if (!host->chan_rx)
                                enable_mmc_irqs(host, TMIO_MASK_READOP);
                } else {
-                       struct dma_chan *chan = host->chan_tx;
-                       if (!chan)
+                       if (!host->chan_tx)
                                enable_mmc_irqs(host, TMIO_MASK_WRITEOP);
                        else
                                tasklet_schedule(&host->dma_issue);
@@ -334,13 +657,19 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
                tmio_mmc_finish_request(host);
        }
 
+out:
+       spin_unlock(&host->lock);
+
        return;
 }
 
 static irqreturn_t tmio_mmc_irq(int irq, void *devid)
 {
        struct tmio_mmc_host *host = devid;
+       struct mfd_cell *cell = host->pdev->dev.platform_data;
+       struct tmio_mmc_data *pdata = cell->driver_data;
        unsigned int ireg, irq_mask, status;
+       unsigned int sdio_ireg, sdio_irq_mask, sdio_status;
 
        pr_debug("MMC IRQ begin\n");
 
@@ -348,6 +677,29 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
        irq_mask = sd_ctrl_read32(host, CTL_IRQ_MASK);
        ireg = status & TMIO_MASK_IRQ & ~irq_mask;
 
+       sdio_ireg = 0;
+       if (!ireg && pdata->flags & TMIO_MMC_SDIO_IRQ) {
+               sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
+               sdio_irq_mask = sd_ctrl_read16(host, CTL_SDIO_IRQ_MASK);
+               sdio_ireg = sdio_status & TMIO_SDIO_MASK_ALL & ~sdio_irq_mask;
+
+               sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status & ~TMIO_SDIO_MASK_ALL);
+
+               if (sdio_ireg && !host->sdio_irq_enabled) {
+                       pr_warning("tmio_mmc: Spurious SDIO IRQ, disabling! 0x%04x 0x%04x 0x%04x\n",
+                                  sdio_status, sdio_irq_mask, sdio_ireg);
+                       tmio_mmc_enable_sdio_irq(host->mmc, 0);
+                       goto out;
+               }
+
+               if (host->mmc->caps & MMC_CAP_SDIO_IRQ &&
+                       sdio_ireg & TMIO_SDIO_STAT_IOIRQ)
+                       mmc_signal_sdio_irq(host->mmc);
+
+               if (sdio_ireg)
+                       goto out;
+       }
+
        pr_debug_status(status);
        pr_debug_status(ireg);
 
@@ -375,8 +727,10 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
  */
 
                /* Command completion */
-               if (ireg & TMIO_MASK_CMD) {
-                       ack_mmc_irqs(host, TMIO_MASK_CMD);
+               if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) {
+                       ack_mmc_irqs(host,
+                                    TMIO_STAT_CMDRESPEND |
+                                    TMIO_STAT_CMDTIMEOUT);
                        tmio_mmc_cmd_irq(host, status);
                }
 
@@ -407,6 +761,16 @@ out:
 }
 
 #ifdef CONFIG_TMIO_MMC_DMA
+static void tmio_check_bounce_buffer(struct tmio_mmc_host *host)
+{
+       if (host->sg_ptr == &host->bounce_sg) {
+               unsigned long flags;
+               void *sg_vaddr = tmio_mmc_kmap_atomic(host->sg_orig, &flags);
+               memcpy(sg_vaddr, host->bounce_buf, host->bounce_sg.length);
+               tmio_mmc_kunmap_atomic(sg_vaddr, &flags);
+       }
+}
+
 static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
 {
 #if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE)
@@ -427,12 +791,39 @@ static void tmio_dma_complete(void *arg)
                enable_mmc_irqs(host, TMIO_STAT_DATAEND);
 }
 
-static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
+static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
 {
-       struct scatterlist *sg = host->sg_ptr;
+       struct scatterlist *sg = host->sg_ptr, *sg_tmp;
        struct dma_async_tx_descriptor *desc = NULL;
        struct dma_chan *chan = host->chan_rx;
-       int ret;
+       struct mfd_cell *cell = host->pdev->dev.platform_data;
+       struct tmio_mmc_data *pdata = cell->driver_data;
+       dma_cookie_t cookie;
+       int ret, i;
+       bool aligned = true, multiple = true;
+       unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
+
+       for_each_sg(sg, sg_tmp, host->sg_len, i) {
+               if (sg_tmp->offset & align)
+                       aligned = false;
+               if (sg_tmp->length & align) {
+                       multiple = false;
+                       break;
+               }
+       }
+
+       if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE ||
+                         align >= MAX_ALIGN)) || !multiple) {
+               ret = -EINVAL;
+               goto pio;
+       }
+
+       /* The only sg element can be unaligned, use our bounce buffer then */
+       if (!aligned) {
+               sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
+               host->sg_ptr = &host->bounce_sg;
+               sg = host->sg_ptr;
+       }
 
        ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_FROM_DEVICE);
        if (ret > 0) {
@@ -442,21 +833,21 @@ static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
        }
 
        if (desc) {
-               host->desc = desc;
                desc->callback = tmio_dma_complete;
                desc->callback_param = host;
-               host->cookie = desc->tx_submit(desc);
-               if (host->cookie < 0) {
-                       host->desc = NULL;
-                       ret = host->cookie;
+               cookie = desc->tx_submit(desc);
+               if (cookie < 0) {
+                       desc = NULL;
+                       ret = cookie;
                } else {
                        chan->device->device_issue_pending(chan);
                }
        }
        dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n",
-               __func__, host->sg_len, ret, host->cookie, host->mrq);
+               __func__, host->sg_len, ret, cookie, host->mrq);
 
-       if (!host->desc) {
+pio:
+       if (!desc) {
                /* DMA failed, fall back to PIO */
                if (ret >= 0)
                        ret = -EIO;
@@ -471,24 +862,49 @@ static int tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
                dev_warn(&host->pdev->dev,
                         "DMA failed: %d, falling back to PIO\n", ret);
                tmio_mmc_enable_dma(host, false);
-               reset(host);
-               /* Fail this request, let above layers recover */
-               host->mrq->cmd->error = ret;
-               tmio_mmc_finish_request(host);
        }
 
        dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
-               desc, host->cookie, host->sg_len);
-
-       return ret > 0 ? 0 : ret;
+               desc, cookie, host->sg_len);
 }
 
-static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
+static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
 {
-       struct scatterlist *sg = host->sg_ptr;
+       struct scatterlist *sg = host->sg_ptr, *sg_tmp;
        struct dma_async_tx_descriptor *desc = NULL;
        struct dma_chan *chan = host->chan_tx;
-       int ret;
+       struct mfd_cell *cell = host->pdev->dev.platform_data;
+       struct tmio_mmc_data *pdata = cell->driver_data;
+       dma_cookie_t cookie;
+       int ret, i;
+       bool aligned = true, multiple = true;
+       unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
+
+       for_each_sg(sg, sg_tmp, host->sg_len, i) {
+               if (sg_tmp->offset & align)
+                       aligned = false;
+               if (sg_tmp->length & align) {
+                       multiple = false;
+                       break;
+               }
+       }
+
+       if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE ||
+                         align >= MAX_ALIGN)) || !multiple) {
+               ret = -EINVAL;
+               goto pio;
+       }
+
+       /* The only sg element can be unaligned, use our bounce buffer then */
+       if (!aligned) {
+               unsigned long flags;
+               void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags);
+               sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
+               memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length);
+               tmio_mmc_kunmap_atomic(sg_vaddr, &flags);
+               host->sg_ptr = &host->bounce_sg;
+               sg = host->sg_ptr;
+       }
 
        ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_TO_DEVICE);
        if (ret > 0) {
@@ -498,19 +914,19 @@ static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
        }
 
        if (desc) {
-               host->desc = desc;
                desc->callback = tmio_dma_complete;
                desc->callback_param = host;
-               host->cookie = desc->tx_submit(desc);
-               if (host->cookie < 0) {
-                       host->desc = NULL;
-                       ret = host->cookie;
+               cookie = desc->tx_submit(desc);
+               if (cookie < 0) {
+                       desc = NULL;
+                       ret = cookie;
                }
        }
        dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n",
-               __func__, host->sg_len, ret, host->cookie, host->mrq);
+               __func__, host->sg_len, ret, cookie, host->mrq);
 
-       if (!host->desc) {
+pio:
+       if (!desc) {
                /* DMA failed, fall back to PIO */
                if (ret >= 0)
                        ret = -EIO;
@@ -525,30 +941,22 @@ static int tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
                dev_warn(&host->pdev->dev,
                         "DMA failed: %d, falling back to PIO\n", ret);
                tmio_mmc_enable_dma(host, false);
-               reset(host);
-               /* Fail this request, let above layers recover */
-               host->mrq->cmd->error = ret;
-               tmio_mmc_finish_request(host);
        }
 
        dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d\n", __func__,
-               desc, host->cookie);
-
-       return ret > 0 ? 0 : ret;
+               desc, cookie);
 }
 
-static int tmio_mmc_start_dma(struct tmio_mmc_host *host,
+static void tmio_mmc_start_dma(struct tmio_mmc_host *host,
                               struct mmc_data *data)
 {
        if (data->flags & MMC_DATA_READ) {
                if (host->chan_rx)
-                       return tmio_mmc_start_dma_rx(host);
+                       tmio_mmc_start_dma_rx(host);
        } else {
                if (host->chan_tx)
-                       return tmio_mmc_start_dma_tx(host);
+                       tmio_mmc_start_dma_tx(host);
        }
-
-       return 0;
 }
 
 static void tmio_issue_tasklet_fn(unsigned long priv)
@@ -562,6 +970,12 @@ static void tmio_issue_tasklet_fn(unsigned long priv)
 static void tmio_tasklet_fn(unsigned long arg)
 {
        struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (!host->data)
+               goto out;
 
        if (host->data->flags & MMC_DATA_READ)
                dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->dma_sglen,
@@ -571,6 +985,8 @@ static void tmio_tasklet_fn(unsigned long arg)
                             DMA_TO_DEVICE);
 
        tmio_mmc_do_data_irq(host);
+out:
+       spin_unlock_irqrestore(&host->lock, flags);
 }
 
 /* It might be necessary to make filter MFD specific */
@@ -584,9 +1000,6 @@ static bool tmio_mmc_filter(struct dma_chan *chan, void *arg)
 static void tmio_mmc_request_dma(struct tmio_mmc_host *host,
                                 struct tmio_mmc_data *pdata)
 {
-       host->cookie = -EINVAL;
-       host->desc = NULL;
-
        /* We can only either use DMA for both Tx and Rx or not use it at all */
        if (pdata->dma) {
                dma_cap_mask_t mask;
@@ -632,15 +1045,15 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host)
                host->chan_rx = NULL;
                dma_release_channel(chan);
        }
-
-       host->cookie = -EINVAL;
-       host->desc = NULL;
 }
 #else
-static int tmio_mmc_start_dma(struct tmio_mmc_host *host,
+static void tmio_check_bounce_buffer(struct tmio_mmc_host *host)
+{
+}
+
+static void tmio_mmc_start_dma(struct tmio_mmc_host *host,
                               struct mmc_data *data)
 {
-       return 0;
 }
 
 static void tmio_mmc_request_dma(struct tmio_mmc_host *host,
@@ -682,7 +1095,9 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
        sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz);
        sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks);
 
-       return tmio_mmc_start_dma(host, data);
+       tmio_mmc_start_dma(host, data);
+
+       return 0;
 }
 
 /* Process requests from the MMC layer */
@@ -694,6 +1109,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
        if (host->mrq)
                pr_debug("request not null\n");
 
+       host->last_req_ts = jiffies;
+       wmb();
        host->mrq = mrq;
 
        if (mrq->data) {
@@ -703,10 +1120,14 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
        }
 
        ret = tmio_mmc_start_command(host, mrq->cmd);
-       if (!ret)
+       if (!ret) {
+               schedule_delayed_work(&host->delayed_reset_work,
+                                     msecs_to_jiffies(2000));
                return;
+       }
 
 fail:
+       host->mrq = NULL;
        mrq->cmd->error = ret;
        mmc_request_done(mmc, mrq);
 }
@@ -780,6 +1201,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
        .set_ios        = tmio_mmc_set_ios,
        .get_ro         = tmio_mmc_get_ro,
        .get_cd         = tmio_mmc_get_cd,
+       .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
 };
 
 #ifdef CONFIG_PM
@@ -864,10 +1286,15 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
                goto host_free;
 
        mmc->ops = &tmio_mmc_ops;
-       mmc->caps = MMC_CAP_4_BIT_DATA;
-       mmc->caps |= pdata->capabilities;
+       mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities;
        mmc->f_max = pdata->hclk;
        mmc->f_min = mmc->f_max / 512;
+       mmc->max_segs = 32;
+       mmc->max_blk_size = 512;
+       mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
+               mmc->max_segs;
+       mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+       mmc->max_seg_size = mmc->max_req_size;
        if (pdata->ocr_mask)
                mmc->ocr_avail = pdata->ocr_mask;
        else
@@ -890,12 +1317,19 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
                goto cell_disable;
 
        disable_mmc_irqs(host, TMIO_MASK_ALL);
+       if (pdata->flags & TMIO_MMC_SDIO_IRQ)
+               tmio_mmc_enable_sdio_irq(mmc, 0);
 
        ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED |
                IRQF_TRIGGER_FALLING, dev_name(&dev->dev), host);
        if (ret)
                goto cell_disable;
 
+       spin_lock_init(&host->lock);
+
+       /* Init delayed work for request timeouts */
+       INIT_DELAYED_WORK(&host->delayed_reset_work, tmio_mmc_reset_work);
+
        /* See if we also get DMA */
        tmio_mmc_request_dma(host, pdata);
 
@@ -934,6 +1368,7 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev)
        if (mmc) {
                struct tmio_mmc_host *host = mmc_priv(mmc);
                mmc_remove_host(mmc);
+               cancel_delayed_work_sync(&host->delayed_reset_work);
                tmio_mmc_release_dma(host);
                free_irq(host->irq, host);
                if (cell->disable)