Merge 3.5-rc7 into char-misc-next.
[firefly-linux-kernel-4.4.55.git] / drivers / mmc / host / atmel-mci.c
index e94476beca181287b39c3af3a0b542a519bef353..f2c115e064387715a3c2f2cf796c5ae8b70bddd0 100644 (file)
 #define ATMCI_DMA_THRESHOLD    16
 
 enum {
-       EVENT_CMD_COMPLETE = 0,
+       EVENT_CMD_RDY = 0,
        EVENT_XFER_COMPLETE,
-       EVENT_DATA_COMPLETE,
+       EVENT_NOTBUSY,
        EVENT_DATA_ERROR,
 };
 
 enum atmel_mci_state {
        STATE_IDLE = 0,
        STATE_SENDING_CMD,
-       STATE_SENDING_DATA,
-       STATE_DATA_BUSY,
+       STATE_DATA_XFER,
+       STATE_WAITING_NOTBUSY,
        STATE_SENDING_STOP,
-       STATE_DATA_ERROR,
+       STATE_END_REQUEST,
 };
 
 enum atmci_xfer_dir {
@@ -78,6 +78,9 @@ struct atmel_mci_caps {
        bool    has_highspeed;
        bool    has_rwproof;
        bool    has_odd_clk_div;
+       bool    has_bad_data_ordering;
+       bool    need_reset_after_xfer;
+       bool    need_blksz_mul_4;
 };
 
 struct atmel_mci_dma {
@@ -91,6 +94,11 @@ struct atmel_mci_dma {
  * @regs: Pointer to MMIO registers.
  * @sg: Scatterlist entry currently being processed by PIO or PDC code.
  * @pio_offset: Offset into the current scatterlist entry.
+ * @buffer: Buffer used if we don't have the r/w proof capability. We
+ *      don't have the time to switch pdc buffers so we have to use only
+ *      one buffer for the full transaction.
+ * @buf_size: size of the buffer.
+ * @phys_buf_addr: buffer address needed for pdc.
  * @cur_slot: The slot which is currently using the controller.
  * @mrq: The request currently being processed on @cur_slot,
  *     or NULL if the controller is idle.
@@ -116,6 +124,7 @@ struct atmel_mci_dma {
  * @queue: List of slots waiting for access to the controller.
  * @need_clock_update: Update the clock rate before the next request.
  * @need_reset: Reset controller before next request.
+ * @timer: Timer to balance the data timeout error flag which cannot rise.
  * @mode_reg: Value of the MR register.
  * @cfg_reg: Value of the CFG register.
  * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
@@ -166,6 +175,9 @@ struct atmel_mci {
 
        struct scatterlist      *sg;
        unsigned int            pio_offset;
+       unsigned int            *buffer;
+       unsigned int            buf_size;
+       dma_addr_t              buf_phys_addr;
 
        struct atmel_mci_slot   *cur_slot;
        struct mmc_request      *mrq;
@@ -189,6 +201,7 @@ struct atmel_mci {
 
        bool                    need_clock_update;
        bool                    need_reset;
+       struct timer_list       timer;
        u32                     mode_reg;
        u32                     cfg_reg;
        unsigned long           bus_hz;
@@ -480,6 +493,32 @@ err:
        dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
 }
 
+static inline unsigned int atmci_get_version(struct atmel_mci *host)
+{
+       return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
+}
+
+static void atmci_timeout_timer(unsigned long data)
+{
+       struct atmel_mci *host;
+
+       host = (struct atmel_mci *)data;
+
+       dev_dbg(&host->pdev->dev, "software timeout\n");
+
+       if (host->mrq->cmd->data) {
+               host->mrq->cmd->data->error = -ETIMEDOUT;
+               host->data = NULL;
+       } else {
+               host->mrq->cmd->error = -ETIMEDOUT;
+               host->cmd = NULL;
+       }
+       host->need_reset = 1;
+       host->state = STATE_END_REQUEST;
+       smp_wmb();
+       tasklet_schedule(&host->tasklet);
+}
+
 static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host,
                                        unsigned int ns)
 {
@@ -591,6 +630,7 @@ static void atmci_send_command(struct atmel_mci *host,
 
 static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
 {
+       dev_dbg(&host->pdev->dev, "send stop command\n");
        atmci_send_command(host, data->stop, host->stop_cmdr);
        atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
 }
@@ -603,6 +643,7 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
        enum atmci_xfer_dir dir, enum atmci_pdc_buf buf_nb)
 {
        u32 pointer_reg, counter_reg;
+       unsigned int buf_size;
 
        if (dir == XFER_RECEIVE) {
                pointer_reg = ATMEL_PDC_RPR;
@@ -617,8 +658,15 @@ static void atmci_pdc_set_single_buf(struct atmel_mci *host,
                counter_reg += ATMEL_PDC_SCND_BUF_OFF;
        }
 
-       atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
-       if (host->data_size <= sg_dma_len(host->sg)) {
+       if (!host->caps.has_rwproof) {
+               buf_size = host->buf_size;
+               atmci_writel(host, pointer_reg, host->buf_phys_addr);
+       } else {
+               buf_size = sg_dma_len(host->sg);
+               atmci_writel(host, pointer_reg, sg_dma_address(host->sg));
+       }
+
+       if (host->data_size <= buf_size) {
                if (host->data_size & 0x3) {
                        /* If size is different from modulo 4, transfer bytes */
                        atmci_writel(host, counter_reg, host->data_size);
@@ -670,7 +718,20 @@ static void atmci_pdc_cleanup(struct atmel_mci *host)
  */
 static void atmci_pdc_complete(struct atmel_mci *host)
 {
+       int transfer_size = host->data->blocks * host->data->blksz;
+       int i;
+
        atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+       if ((!host->caps.has_rwproof)
+           && (host->data->flags & MMC_DATA_READ)) {
+               if (host->caps.has_bad_data_ordering)
+                       for (i = 0; i < transfer_size; i++)
+                               host->buffer[i] = swab32(host->buffer[i]);
+               sg_copy_from_buffer(host->data->sg, host->data->sg_len,
+                                   host->buffer, transfer_size);
+       }
+
        atmci_pdc_cleanup(host);
 
        /*
@@ -678,9 +739,10 @@ static void atmci_pdc_complete(struct atmel_mci *host)
         * to send the stop command or waiting for NBUSY in this case.
         */
        if (host->data) {
+               dev_dbg(&host->pdev->dev,
+                       "(%s) set pending xfer complete\n", __func__);
                atmci_set_pending(host, EVENT_XFER_COMPLETE);
                tasklet_schedule(&host->tasklet);
-               atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
        }
 }
 
@@ -716,6 +778,8 @@ static void atmci_dma_complete(void *arg)
         * to send the stop command or waiting for NBUSY in this case.
         */
        if (data) {
+               dev_dbg(&host->pdev->dev,
+                       "(%s) set pending xfer complete\n", __func__);
                atmci_set_pending(host, EVENT_XFER_COMPLETE);
                tasklet_schedule(&host->tasklet);
 
@@ -791,6 +855,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
        u32 iflags, tmp;
        unsigned int sg_len;
        enum dma_data_direction dir;
+       int i;
 
        data->error = -EINPROGRESS;
 
@@ -806,7 +871,7 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
                iflags |= ATMCI_ENDRX | ATMCI_RXBUFF;
        } else {
                dir = DMA_TO_DEVICE;
-               iflags |= ATMCI_ENDTX | ATMCI_TXBUFE;
+               iflags |= ATMCI_ENDTX | ATMCI_TXBUFE | ATMCI_BLKE;
        }
 
        /* Set BLKLEN */
@@ -818,6 +883,16 @@ atmci_prepare_data_pdc(struct atmel_mci *host, struct mmc_data *data)
        /* Configure PDC */
        host->data_size = data->blocks * data->blksz;
        sg_len = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir);
+
+       if ((!host->caps.has_rwproof)
+           && (host->data->flags & MMC_DATA_WRITE)) {
+               sg_copy_to_buffer(host->data->sg, host->data->sg_len,
+                                 host->buffer, host->data_size);
+               if (host->caps.has_bad_data_ordering)
+                       for (i = 0; i < host->data_size; i++)
+                               host->buffer[i] = swab32(host->buffer[i]);
+       }
+
        if (host->data_size)
                atmci_pdc_set_both_buf(host,
                        ((dir == DMA_FROM_DEVICE) ? XFER_RECEIVE : XFER_TRANSMIT));
@@ -835,6 +910,7 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
        enum dma_data_direction         direction;
        enum dma_transfer_direction     slave_dirn;
        unsigned int                    sglen;
+       u32                             maxburst;
        u32 iflags;
 
        data->error = -EINPROGRESS;
@@ -868,17 +944,18 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
        if (!chan)
                return -ENODEV;
 
-       if (host->caps.has_dma)
-               atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(3) | ATMCI_DMAEN);
-
        if (data->flags & MMC_DATA_READ) {
                direction = DMA_FROM_DEVICE;
                host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM;
+               maxburst = atmci_convert_chksize(host->dma_conf.src_maxburst);
        } else {
                direction = DMA_TO_DEVICE;
                host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV;
+               maxburst = atmci_convert_chksize(host->dma_conf.dst_maxburst);
        }
 
+       atmci_writel(host, ATMCI_DMA, ATMCI_DMA_CHKSIZE(maxburst) | ATMCI_DMAEN);
+
        sglen = dma_map_sg(chan->device->dev, data->sg,
                        data->sg_len, direction);
 
@@ -931,6 +1008,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
 
 static void atmci_stop_transfer(struct atmel_mci *host)
 {
+       dev_dbg(&host->pdev->dev,
+               "(%s) set pending xfer complete\n", __func__);
        atmci_set_pending(host, EVENT_XFER_COMPLETE);
        atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
 }
@@ -940,8 +1019,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
  */
 static void atmci_stop_transfer_pdc(struct atmel_mci *host)
 {
-       atmci_set_pending(host, EVENT_XFER_COMPLETE);
-       atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+       atmci_writel(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
 }
 
 static void atmci_stop_transfer_dma(struct atmel_mci *host)
@@ -953,6 +1031,8 @@ static void atmci_stop_transfer_dma(struct atmel_mci *host)
                atmci_dma_cleanup(host);
        } else {
                /* Data transfer was stopped by the interrupt handler */
+               dev_dbg(&host->pdev->dev,
+                       "(%s) set pending xfer complete\n", __func__);
                atmci_set_pending(host, EVENT_XFER_COMPLETE);
                atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
        }
@@ -977,9 +1057,12 @@ static void atmci_start_request(struct atmel_mci *host,
 
        host->pending_events = 0;
        host->completed_events = 0;
+       host->cmd_status = 0;
        host->data_status = 0;
 
-       if (host->need_reset) {
+       dev_dbg(&host->pdev->dev, "start request: cmd %u\n", mrq->cmd->opcode);
+
+       if (host->need_reset || host->caps.need_reset_after_xfer) {
                iflags = atmci_readl(host, ATMCI_IMR);
                iflags &= (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB);
                atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
@@ -994,7 +1077,7 @@ static void atmci_start_request(struct atmel_mci *host,
 
        iflags = atmci_readl(host, ATMCI_IMR);
        if (iflags & ~(ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
-               dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+               dev_dbg(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
                                iflags);
 
        if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
@@ -1043,6 +1126,8 @@ static void atmci_start_request(struct atmel_mci *host,
         * prepared yet.)
         */
        atmci_writel(host, ATMCI_IER, iflags);
+
+       mod_timer(&host->timer, jiffies +  msecs_to_jiffies(2000));
 }
 
 static void atmci_queue_request(struct atmel_mci *host,
@@ -1057,6 +1142,7 @@ static void atmci_queue_request(struct atmel_mci *host,
                host->state = STATE_SENDING_CMD;
                atmci_start_request(host, slot);
        } else {
+               dev_dbg(&host->pdev->dev, "queue request\n");
                list_add_tail(&slot->queue_node, &host->queue);
        }
        spin_unlock_bh(&host->lock);
@@ -1069,6 +1155,7 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        struct mmc_data         *data;
 
        WARN_ON(slot->mrq);
+       dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
 
        /*
         * We may "know" the card is gone even though there's still an
@@ -1308,6 +1395,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
                host->state = STATE_IDLE;
        }
 
+       del_timer(&host->timer);
+
        spin_unlock(&host->lock);
        mmc_request_done(prev_mmc, mrq);
        spin_lock(&host->lock);
@@ -1330,21 +1419,13 @@ static void atmci_command_complete(struct atmel_mci *host,
                cmd->error = -EILSEQ;
        else if (status & (ATMCI_RINDE | ATMCI_RDIRE | ATMCI_RENDE))
                cmd->error = -EIO;
-       else
-               cmd->error = 0;
-
-       if (cmd->error) {
-               dev_dbg(&host->pdev->dev,
-                       "command error: status=0x%08x\n", status);
-
-               if (cmd->data) {
-                       host->stop_transfer(host);
-                       host->data = NULL;
-                       atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY
-                                       | ATMCI_TXRDY | ATMCI_RXRDY
-                                       | ATMCI_DATA_ERROR_FLAGS);
+       else if (host->mrq->data && (host->mrq->data->blksz & 3)) {
+               if (host->caps.need_blksz_mul_4) {
+                       cmd->error = -EINVAL;
+                       host->need_reset = 1;
                }
-       }
+       } else
+               cmd->error = 0;
 }
 
 static void atmci_detect_change(unsigned long data)
@@ -1407,23 +1488,21 @@ static void atmci_detect_change(unsigned long data)
                                        break;
                                case STATE_SENDING_CMD:
                                        mrq->cmd->error = -ENOMEDIUM;
-                                       if (!mrq->data)
-                                               break;
-                                       /* fall through */
-                               case STATE_SENDING_DATA:
+                                       if (mrq->data)
+                                               host->stop_transfer(host);
+                                       break;
+                               case STATE_DATA_XFER:
                                        mrq->data->error = -ENOMEDIUM;
                                        host->stop_transfer(host);
                                        break;
-                               case STATE_DATA_BUSY:
-                               case STATE_DATA_ERROR:
-                                       if (mrq->data->error == -EINPROGRESS)
-                                               mrq->data->error = -ENOMEDIUM;
-                                       if (!mrq->stop)
-                                               break;
-                                       /* fall through */
+                               case STATE_WAITING_NOTBUSY:
+                                       mrq->data->error = -ENOMEDIUM;
+                                       break;
                                case STATE_SENDING_STOP:
                                        mrq->stop->error = -ENOMEDIUM;
                                        break;
+                               case STATE_END_REQUEST:
+                                       break;
                                }
 
                                atmci_request_end(host, mrq);
@@ -1451,7 +1530,6 @@ static void atmci_tasklet_func(unsigned long priv)
        struct atmel_mci        *host = (struct atmel_mci *)priv;
        struct mmc_request      *mrq = host->mrq;
        struct mmc_data         *data = host->data;
-       struct mmc_command      *cmd = host->cmd;
        enum atmel_mci_state    state = host->state;
        enum atmel_mci_state    prev_state;
        u32                     status;
@@ -1467,107 +1545,186 @@ static void atmci_tasklet_func(unsigned long priv)
 
        do {
                prev_state = state;
+               dev_dbg(&host->pdev->dev, "FSM: state=%d\n", state);
 
                switch (state) {
                case STATE_IDLE:
                        break;
 
                case STATE_SENDING_CMD:
+                       /*
+                        * Command has been sent, we are waiting for command
+                        * ready. Then we have three next states possible:
+                        * END_REQUEST by default, WAITING_NOTBUSY if it's a
+                        * command needing it or DATA_XFER if there is data.
+                        */
+                       dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
                        if (!atmci_test_and_clear_pending(host,
-                                               EVENT_CMD_COMPLETE))
+                                               EVENT_CMD_RDY))
                                break;
 
+                       dev_dbg(&host->pdev->dev, "set completed cmd ready\n");
                        host->cmd = NULL;
-                       atmci_set_completed(host, EVENT_CMD_COMPLETE);
+                       atmci_set_completed(host, EVENT_CMD_RDY);
                        atmci_command_complete(host, mrq->cmd);
-                       if (!mrq->data || cmd->error) {
-                               atmci_request_end(host, host->mrq);
-                               goto unlock;
-                       }
+                       if (mrq->data) {
+                               dev_dbg(&host->pdev->dev,
+                                       "command with data transfer");
+                               /*
+                                * If there is a command error don't start
+                                * data transfer.
+                                */
+                               if (mrq->cmd->error) {
+                                       host->stop_transfer(host);
+                                       host->data = NULL;
+                                       atmci_writel(host, ATMCI_IDR,
+                                                    ATMCI_TXRDY | ATMCI_RXRDY
+                                                    | ATMCI_DATA_ERROR_FLAGS);
+                                       state = STATE_END_REQUEST;
+                               } else
+                                       state = STATE_DATA_XFER;
+                       } else if ((!mrq->data) && (mrq->cmd->flags & MMC_RSP_BUSY)) {
+                               dev_dbg(&host->pdev->dev,
+                                       "command response need waiting notbusy");
+                               atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+                               state = STATE_WAITING_NOTBUSY;
+                       } else
+                               state = STATE_END_REQUEST;
 
-                       prev_state = state = STATE_SENDING_DATA;
-                       /* fall through */
+                       break;
 
-               case STATE_SENDING_DATA:
+               case STATE_DATA_XFER:
                        if (atmci_test_and_clear_pending(host,
                                                EVENT_DATA_ERROR)) {
-                               host->stop_transfer(host);
-                               if (data->stop)
-                                       atmci_send_stop_cmd(host, data);
-                               state = STATE_DATA_ERROR;
+                               dev_dbg(&host->pdev->dev, "set completed data error\n");
+                               atmci_set_completed(host, EVENT_DATA_ERROR);
+                               state = STATE_END_REQUEST;
                                break;
                        }
 
+                       /*
+                        * A data transfer is in progress. The event expected
+                        * to move to the next state depends of data transfer
+                        * type (PDC or DMA). Once transfer done we can move
+                        * to the next step which is WAITING_NOTBUSY in write
+                        * case and directly SENDING_STOP in read case.
+                        */
+                       dev_dbg(&host->pdev->dev, "FSM: xfer complete?\n");
                        if (!atmci_test_and_clear_pending(host,
                                                EVENT_XFER_COMPLETE))
                                break;
 
+                       dev_dbg(&host->pdev->dev,
+                               "(%s) set completed xfer complete\n",
+                               __func__);
                        atmci_set_completed(host, EVENT_XFER_COMPLETE);
-                       prev_state = state = STATE_DATA_BUSY;
-                       /* fall through */
 
-               case STATE_DATA_BUSY:
-                       if (!atmci_test_and_clear_pending(host,
-                                               EVENT_DATA_COMPLETE))
-                               break;
-
-                       host->data = NULL;
-                       atmci_set_completed(host, EVENT_DATA_COMPLETE);
-                       status = host->data_status;
-                       if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
-                               if (status & ATMCI_DTOE) {
-                                       dev_dbg(&host->pdev->dev,
-                                                       "data timeout error\n");
-                                       data->error = -ETIMEDOUT;
-                               } else if (status & ATMCI_DCRCE) {
-                                       dev_dbg(&host->pdev->dev,
-                                                       "data CRC error\n");
-                                       data->error = -EILSEQ;
-                               } else {
-                                       dev_dbg(&host->pdev->dev,
-                                               "data FIFO error (status=%08x)\n",
-                                               status);
-                                       data->error = -EIO;
-                               }
+                       if (host->data->flags & MMC_DATA_WRITE) {
+                               atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+                               state = STATE_WAITING_NOTBUSY;
+                       } else if (host->mrq->stop) {
+                               atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
+                               atmci_send_stop_cmd(host, data);
+                               state = STATE_SENDING_STOP;
                        } else {
+                               host->data = NULL;
                                data->bytes_xfered = data->blocks * data->blksz;
                                data->error = 0;
-                               atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS);
+                               state = STATE_END_REQUEST;
                        }
+                       break;
 
-                       if (!data->stop) {
-                               atmci_request_end(host, host->mrq);
-                               goto unlock;
-                       }
+               case STATE_WAITING_NOTBUSY:
+                       /*
+                        * We can be in the state for two reasons: a command
+                        * requiring waiting not busy signal (stop command
+                        * included) or a write operation. In the latest case,
+                        * we need to send a stop command.
+                        */
+                       dev_dbg(&host->pdev->dev, "FSM: not busy?\n");
+                       if (!atmci_test_and_clear_pending(host,
+                                               EVENT_NOTBUSY))
+                               break;
 
-                       prev_state = state = STATE_SENDING_STOP;
-                       if (!data->error)
-                               atmci_send_stop_cmd(host, data);
-                       /* fall through */
+                       dev_dbg(&host->pdev->dev, "set completed not busy\n");
+                       atmci_set_completed(host, EVENT_NOTBUSY);
+
+                       if (host->data) {
+                               /*
+                                * For some commands such as CMD53, even if
+                                * there is data transfer, there is no stop
+                                * command to send.
+                                */
+                               if (host->mrq->stop) {
+                                       atmci_writel(host, ATMCI_IER,
+                                                    ATMCI_CMDRDY);
+                                       atmci_send_stop_cmd(host, data);
+                                       state = STATE_SENDING_STOP;
+                               } else {
+                                       host->data = NULL;
+                                       data->bytes_xfered = data->blocks
+                                                            * data->blksz;
+                                       data->error = 0;
+                                       state = STATE_END_REQUEST;
+                               }
+                       } else
+                               state = STATE_END_REQUEST;
+                       break;
 
                case STATE_SENDING_STOP:
+                       /*
+                        * In this state, it is important to set host->data to
+                        * NULL (which is tested in the waiting notbusy state)
+                        * in order to go to the end request state instead of
+                        * sending stop again.
+                        */
+                       dev_dbg(&host->pdev->dev, "FSM: cmd ready?\n");
                        if (!atmci_test_and_clear_pending(host,
-                                               EVENT_CMD_COMPLETE))
+                                               EVENT_CMD_RDY))
                                break;
 
+                       dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
                        host->cmd = NULL;
+                       host->data = NULL;
+                       data->bytes_xfered = data->blocks * data->blksz;
+                       data->error = 0;
                        atmci_command_complete(host, mrq->stop);
-                       atmci_request_end(host, host->mrq);
-                       goto unlock;
+                       if (mrq->stop->error) {
+                               host->stop_transfer(host);
+                               atmci_writel(host, ATMCI_IDR,
+                                            ATMCI_TXRDY | ATMCI_RXRDY
+                                            | ATMCI_DATA_ERROR_FLAGS);
+                               state = STATE_END_REQUEST;
+                       } else {
+                               atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
+                               state = STATE_WAITING_NOTBUSY;
+                       }
+                       break;
 
-               case STATE_DATA_ERROR:
-                       if (!atmci_test_and_clear_pending(host,
-                                               EVENT_XFER_COMPLETE))
-                               break;
+               case STATE_END_REQUEST:
+                       atmci_writel(host, ATMCI_IDR, ATMCI_TXRDY | ATMCI_RXRDY
+                                          | ATMCI_DATA_ERROR_FLAGS);
+                       status = host->data_status;
+                       if (unlikely(status)) {
+                               host->stop_transfer(host);
+                               host->data = NULL;
+                               if (status & ATMCI_DTOE) {
+                                       data->error = -ETIMEDOUT;
+                               } else if (status & ATMCI_DCRCE) {
+                                       data->error = -EILSEQ;
+                               } else {
+                                       data->error = -EIO;
+                               }
+                       }
 
-                       state = STATE_DATA_BUSY;
+                       atmci_request_end(host, host->mrq);
+                       state = STATE_IDLE;
                        break;
                }
        } while (state != prev_state);
 
        host->state = state;
 
-unlock:
        spin_unlock(&host->lock);
 }
 
@@ -1620,9 +1777,6 @@ static void atmci_read_data_pio(struct atmel_mci *host)
                                                | ATMCI_DATA_ERROR_FLAGS));
                        host->data_status = status;
                        data->bytes_xfered += nbytes;
-                       smp_wmb();
-                       atmci_set_pending(host, EVENT_DATA_ERROR);
-                       tasklet_schedule(&host->tasklet);
                        return;
                }
        } while (status & ATMCI_RXRDY);
@@ -1691,9 +1845,6 @@ static void atmci_write_data_pio(struct atmel_mci *host)
                                                | ATMCI_DATA_ERROR_FLAGS));
                        host->data_status = status;
                        data->bytes_xfered += nbytes;
-                       smp_wmb();
-                       atmci_set_pending(host, EVENT_DATA_ERROR);
-                       tasklet_schedule(&host->tasklet);
                        return;
                }
        } while (status & ATMCI_TXRDY);
@@ -1711,16 +1862,6 @@ done:
        atmci_set_pending(host, EVENT_XFER_COMPLETE);
 }
 
-static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
-{
-       atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
-
-       host->cmd_status = status;
-       smp_wmb();
-       atmci_set_pending(host, EVENT_CMD_COMPLETE);
-       tasklet_schedule(&host->tasklet);
-}
-
 static void atmci_sdio_interrupt(struct atmel_mci *host, u32 status)
 {
        int     i;
@@ -1748,17 +1889,21 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
                        break;
 
                if (pending & ATMCI_DATA_ERROR_FLAGS) {
+                       dev_dbg(&host->pdev->dev, "IRQ: data error\n");
                        atmci_writel(host, ATMCI_IDR, ATMCI_DATA_ERROR_FLAGS
-                                       | ATMCI_RXRDY | ATMCI_TXRDY);
-                       pending &= atmci_readl(host, ATMCI_IMR);
+                                       | ATMCI_RXRDY | ATMCI_TXRDY
+                                       | ATMCI_ENDRX | ATMCI_ENDTX
+                                       | ATMCI_RXBUFF | ATMCI_TXBUFE);
 
                        host->data_status = status;
+                       dev_dbg(&host->pdev->dev, "set pending data error\n");
                        smp_wmb();
                        atmci_set_pending(host, EVENT_DATA_ERROR);
                        tasklet_schedule(&host->tasklet);
                }
 
                if (pending & ATMCI_TXBUFE) {
+                       dev_dbg(&host->pdev->dev, "IRQ: tx buffer empty\n");
                        atmci_writel(host, ATMCI_IDR, ATMCI_TXBUFE);
                        atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
                        /*
@@ -1774,6 +1919,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
                                atmci_pdc_complete(host);
                        }
                } else if (pending & ATMCI_ENDTX) {
+                       dev_dbg(&host->pdev->dev, "IRQ: end of tx buffer\n");
                        atmci_writel(host, ATMCI_IDR, ATMCI_ENDTX);
 
                        if (host->data_size) {
@@ -1784,6 +1930,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
                }
 
                if (pending & ATMCI_RXBUFF) {
+                       dev_dbg(&host->pdev->dev, "IRQ: rx buffer full\n");
                        atmci_writel(host, ATMCI_IDR, ATMCI_RXBUFF);
                        atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
                        /*
@@ -1799,6 +1946,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
                                atmci_pdc_complete(host);
                        }
                } else if (pending & ATMCI_ENDRX) {
+                       dev_dbg(&host->pdev->dev, "IRQ: end of rx buffer\n");
                        atmci_writel(host, ATMCI_IDR, ATMCI_ENDRX);
 
                        if (host->data_size) {
@@ -1808,23 +1956,44 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
                        }
                }
 
+               /*
+                * First mci IPs, so mainly the ones having pdc, have some
+                * issues with the notbusy signal. You can't get it after
+                * data transmission if you have not sent a stop command.
+                * The appropriate workaround is to use the BLKE signal.
+                */
+               if (pending & ATMCI_BLKE) {
+                       dev_dbg(&host->pdev->dev, "IRQ: blke\n");
+                       atmci_writel(host, ATMCI_IDR, ATMCI_BLKE);
+                       smp_wmb();
+                       dev_dbg(&host->pdev->dev, "set pending notbusy\n");
+                       atmci_set_pending(host, EVENT_NOTBUSY);
+                       tasklet_schedule(&host->tasklet);
+               }
 
                if (pending & ATMCI_NOTBUSY) {
-                       atmci_writel(host, ATMCI_IDR,
-                                       ATMCI_DATA_ERROR_FLAGS | ATMCI_NOTBUSY);
-                       if (!host->data_status)
-                               host->data_status = status;
+                       dev_dbg(&host->pdev->dev, "IRQ: not_busy\n");
+                       atmci_writel(host, ATMCI_IDR, ATMCI_NOTBUSY);
                        smp_wmb();
-                       atmci_set_pending(host, EVENT_DATA_COMPLETE);
+                       dev_dbg(&host->pdev->dev, "set pending notbusy\n");
+                       atmci_set_pending(host, EVENT_NOTBUSY);
                        tasklet_schedule(&host->tasklet);
                }
+
                if (pending & ATMCI_RXRDY)
                        atmci_read_data_pio(host);
                if (pending & ATMCI_TXRDY)
                        atmci_write_data_pio(host);
 
-               if (pending & ATMCI_CMDRDY)
-                       atmci_cmd_interrupt(host, status);
+               if (pending & ATMCI_CMDRDY) {
+                       dev_dbg(&host->pdev->dev, "IRQ: cmd ready\n");
+                       atmci_writel(host, ATMCI_IDR, ATMCI_CMDRDY);
+                       host->cmd_status = status;
+                       smp_wmb();
+                       dev_dbg(&host->pdev->dev, "set pending cmd rdy\n");
+                       atmci_set_pending(host, EVENT_CMD_RDY);
+                       tasklet_schedule(&host->tasklet);
+               }
 
                if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB))
                        atmci_sdio_interrupt(host, status);
@@ -1877,13 +2046,26 @@ static int __init atmci_init_slot(struct atmel_mci *host,
                mmc->caps |= MMC_CAP_SDIO_IRQ;
        if (host->caps.has_highspeed)
                mmc->caps |= MMC_CAP_SD_HIGHSPEED;
-       if (slot_data->bus_width >= 4)
+       /*
+        * Without the read/write proof capability, it is strongly suggested to
+        * use only one bit for data to prevent fifo underruns and overruns
+        * which will corrupt data.
+        */
+       if ((slot_data->bus_width >= 4) && host->caps.has_rwproof)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
-       mmc->max_segs = 64;
-       mmc->max_req_size = 32768 * 512;
-       mmc->max_blk_size = 32768;
-       mmc->max_blk_count = 512;
+       if (atmci_get_version(host) < 0x200) {
+               mmc->max_segs = 256;
+               mmc->max_blk_size = 4095;
+               mmc->max_blk_count = 256;
+               mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+               mmc->max_seg_size = mmc->max_blk_size * mmc->max_segs;
+       } else {
+               mmc->max_segs = 64;
+               mmc->max_req_size = 32768 * 512;
+               mmc->max_blk_size = 32768;
+               mmc->max_blk_count = 512;
+       }
 
        /* Assume card is present initially */
        set_bit(ATMCI_CARD_PRESENT, &slot->flags);
@@ -2007,11 +2189,6 @@ static bool atmci_configure_dma(struct atmel_mci *host)
        }
 }
 
-static inline unsigned int atmci_get_version(struct atmel_mci *host)
-{
-       return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
-}
-
 /*
  * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
  * HSMCI provides DMA support and a new config register but no more supports
@@ -2032,6 +2209,9 @@ static void __init atmci_get_cap(struct atmel_mci *host)
        host->caps.has_highspeed = 0;
        host->caps.has_rwproof = 0;
        host->caps.has_odd_clk_div = 0;
+       host->caps.has_bad_data_ordering = 1;
+       host->caps.need_reset_after_xfer = 1;
+       host->caps.need_blksz_mul_4 = 1;
 
        /* keep only major version number */
        switch (version & 0xf00) {
@@ -2051,7 +2231,11 @@ static void __init atmci_get_cap(struct atmel_mci *host)
                host->caps.has_highspeed = 1;
        case 0x200:
                host->caps.has_rwproof = 1;
+               host->caps.need_blksz_mul_4 = 0;
        case 0x100:
+               host->caps.has_bad_data_ordering = 0;
+               host->caps.need_reset_after_xfer = 0;
+       case 0x0:
                break;
        default:
                host->caps.has_pdc = 0;
@@ -2132,20 +2316,28 @@ static int __init atmci_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, host);
 
+       setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
+
        /* We need at least one slot to succeed */
        nr_slots = 0;
        ret = -ENODEV;
        if (pdata->slot[0].bus_width) {
                ret = atmci_init_slot(host, &pdata->slot[0],
                                0, ATMCI_SDCSEL_SLOT_A, ATMCI_SDIOIRQA);
-               if (!ret)
+               if (!ret) {
                        nr_slots++;
+                       host->buf_size = host->slot[0]->mmc->max_req_size;
+               }
        }
        if (pdata->slot[1].bus_width) {
                ret = atmci_init_slot(host, &pdata->slot[1],
                                1, ATMCI_SDCSEL_SLOT_B, ATMCI_SDIOIRQB);
-               if (!ret)
+               if (!ret) {
                        nr_slots++;
+                       if (host->slot[1]->mmc->max_req_size > host->buf_size)
+                               host->buf_size =
+                                       host->slot[1]->mmc->max_req_size;
+               }
        }
 
        if (!nr_slots) {
@@ -2153,6 +2345,17 @@ static int __init atmci_probe(struct platform_device *pdev)
                goto err_init_slot;
        }
 
+       if (!host->caps.has_rwproof) {
+               host->buffer = dma_alloc_coherent(&pdev->dev, host->buf_size,
+                                                 &host->buf_phys_addr,
+                                                 GFP_KERNEL);
+               if (!host->buffer) {
+                       ret = -ENOMEM;
+                       dev_err(&pdev->dev, "buffer allocation failed\n");
+                       goto err_init_slot;
+               }
+       }
+
        dev_info(&pdev->dev,
                        "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
                        host->mapbase, irq, nr_slots);
@@ -2179,6 +2382,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, NULL);
 
+       if (host->buffer)
+               dma_free_coherent(&pdev->dev, host->buf_size,
+                                 host->buffer, host->buf_phys_addr);
+
        for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
                if (host->slot[i])
                        atmci_cleanup_slot(host->slot[i], i);