return result;
 }
 
+static int send_stop(struct mmc_card *card, u32 *status)
+{
+       struct mmc_command cmd = {0};
+       int err;
+
+       cmd.opcode = MMC_STOP_TRANSMISSION;
+       cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, 5);
+       if (err == 0)
+               *status = cmd.resp[0];
+       return err;
+}
+
 static int get_card_status(struct mmc_card *card, u32 *status, int retries)
 {
        struct mmc_command cmd = {0};
        return err;
 }
 
+#define ERR_RETRY      2
+#define ERR_ABORT      1
+#define ERR_CONTINUE   0
+
+static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
+       bool status_valid, u32 status)
+{
+       switch (error) {
+       case -EILSEQ:
+               /* response crc error, retry the r/w cmd */
+               pr_err("%s: %s sending %s command, card status %#x\n",
+                       req->rq_disk->disk_name, "response CRC error",
+                       name, status);
+               return ERR_RETRY;
+
+       case -ETIMEDOUT:
+               pr_err("%s: %s sending %s command, card status %#x\n",
+                       req->rq_disk->disk_name, "timed out", name, status);
+
+               /* If the status cmd initially failed, retry the r/w cmd */
+               if (!status_valid)
+                       return ERR_RETRY;
+
+               /*
+                * If it was a r/w cmd crc error, or illegal command
+                * (eg, issued in wrong state) then retry - we should
+                * have corrected the state problem above.
+                */
+               if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND))
+                       return ERR_RETRY;
+
+               /* Otherwise abort the command */
+               return ERR_ABORT;
+
+       default:
+               /* We don't understand the error code the driver gave us */
+               pr_err("%s: unknown error %d sending read/write command, card status %#x\n",
+                      req->rq_disk->disk_name, error, status);
+               return ERR_ABORT;
+       }
+}
+
+/*
+ * Initial r/w and stop cmd error recovery.
+ * We don't know whether the card received the r/w cmd or not, so try to
+ * restore things back to a sane state.  Essentially, we do this as follows:
+ * - Obtain card status.  If the first attempt to obtain card status fails,
+ *   the status word will reflect the failed status cmd, not the failed
+ *   r/w cmd.  If we fail to obtain card status, it suggests we can no
+ *   longer communicate with the card.
+ * - Check the card state.  If the card received the cmd but there was a
+ *   transient problem with the response, it might still be in a data transfer
+ *   mode.  Try to send it a stop command.  If this fails, we can't recover.
+ * - If the r/w cmd failed due to a response CRC error, it was probably
+ *   transient, so retry the cmd.
+ * - If the r/w cmd timed out, but we didn't get the r/w cmd status, retry.
+ * - If the r/w cmd timed out, and the r/w cmd failed due to CRC error or
+ *   illegal cmd, retry.
+ * Otherwise we don't understand what happened, so abort.
+ */
+static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
+       struct mmc_blk_request *brq)
+{
+       bool prev_cmd_status_valid = true;
+       u32 status, stop_status = 0;
+       int err, retry;
+
+       /*
+        * Try to get card status which indicates both the card state
+        * and why there was no response.  If the first attempt fails,
+        * we can't be sure the returned status is for the r/w command.
+        */
+       for (retry = 2; retry >= 0; retry--) {
+               err = get_card_status(card, &status, 0);
+               if (!err)
+                       break;
+
+               prev_cmd_status_valid = false;
+               pr_err("%s: error %d sending status command, %sing\n",
+                      req->rq_disk->disk_name, err, retry ? "retry" : "abort");
+       }
+
+       /* We couldn't get a response from the card.  Give up. */
+       if (err)
+               return ERR_ABORT;
+
+       /*
+        * Check the current card state.  If it is in some data transfer
+        * mode, tell it to stop (and hopefully transition back to TRAN.)
+        */
+       if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
+           R1_CURRENT_STATE(status) == R1_STATE_RCV) {
+               err = send_stop(card, &stop_status);
+               if (err)
+                       pr_err("%s: error %d sending stop command\n",
+                              req->rq_disk->disk_name, err);
+
+               /*
+                * If the stop cmd also timed out, the card is probably
+                * not present, so abort.  Other errors are bad news too.
+                */
+               if (err)
+                       return ERR_ABORT;
+       }
+
+       /* Check for set block count errors */
+       if (brq->sbc.error)
+               return mmc_blk_cmd_error(req, "SET_BLOCK_COUNT", brq->sbc.error,
+                               prev_cmd_status_valid, status);
+
+       /* Check for r/w command errors */
+       if (brq->cmd.error)
+               return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error,
+                               prev_cmd_status_valid, status);
+
+       /* Now for stop errors.  These aren't fatal to the transfer. */
+       pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
+              req->rq_disk->disk_name, brq->stop.error,
+              brq->cmd.resp[0], status);
+
+       /*
+        * Subsitute in our own stop status as this will give the error
+        * state which happened during the execution of the r/w command.
+        */
+       if (stop_status) {
+               brq->stop.resp[0] = stop_status;
+               brq->stop.error = 0;
+       }
+       return ERR_CONTINUE;
+}
+
 static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
 {
        struct mmc_blk_data *md = mq->data;
        struct mmc_blk_data *md = mq->data;
        struct mmc_card *card = md->queue.card;
        struct mmc_blk_request brq;
-       int ret = 1, disable_multi = 0;
+       int ret = 1, disable_multi = 0, retry = 0;
 
        /*
         * Reliable writes are used to implement Forced Unit Access and
                (md->flags & MMC_BLK_REL_WR);
 
        do {
-               u32 readcmd, writecmd, status = 0;
+               u32 readcmd, writecmd;
 
                memset(&brq, 0, sizeof(struct mmc_blk_request));
                brq.mrq.cmd = &brq.cmd;
                mmc_queue_bounce_post(mq);
 
                /*
-                * Check for errors here, but don't jump to cmd_err
-                * until later as we need to wait for the card to leave
-                * programming mode even when things go wrong.
+                * sbc.error indicates a problem with the set block count
+                * command.  No data will have been transferred.
+                *
+                * cmd.error indicates a problem with the r/w command.  No
+                * data will have been transferred.
+                *
+                * stop.error indicates a problem with the stop command.  Data
+                * may have been transferred, or may still be transferring.
                 */
-               if (brq.sbc.error || brq.cmd.error ||
-                   brq.data.error || brq.stop.error) {
-                       if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
-                               /* Redo read one sector at a time */
-                               printk(KERN_WARNING "%s: retrying using single "
-                                      "block read\n", req->rq_disk->disk_name);
-                               disable_multi = 1;
-                               continue;
+               if (brq.sbc.error || brq.cmd.error || brq.stop.error) {
+                       switch (mmc_blk_cmd_recovery(card, req, &brq)) {
+                       case ERR_RETRY:
+                               if (retry++ < 5)
+                                       continue;
+                       case ERR_ABORT:
+                               goto cmd_abort;
+                       case ERR_CONTINUE:
+                               break;
                        }
-                       get_card_status(card, &status, 0);
-               }
-
-               if (brq.sbc.error) {
-                       printk(KERN_ERR "%s: error %d sending SET_BLOCK_COUNT "
-                              "command, response %#x, card status %#x\n",
-                              req->rq_disk->disk_name, brq.sbc.error,
-                              brq.sbc.resp[0], status);
-               }
-
-               if (brq.cmd.error) {
-                       printk(KERN_ERR "%s: error %d sending read/write "
-                              "command, response %#x, card status %#x\n",
-                              req->rq_disk->disk_name, brq.cmd.error,
-                              brq.cmd.resp[0], status);
-               }
-
-               if (brq.data.error) {
-                       if (brq.data.error == -ETIMEDOUT && brq.mrq.stop)
-                               /* 'Stop' response contains card status */
-                               status = brq.mrq.stop->resp[0];
-                       printk(KERN_ERR "%s: error %d transferring data,"
-                              " sector %u, nr %u, card status %#x\n",
-                              req->rq_disk->disk_name, brq.data.error,
-                              (unsigned)blk_rq_pos(req),
-                              (unsigned)blk_rq_sectors(req), status);
-               }
-
-               if (brq.stop.error) {
-                       printk(KERN_ERR "%s: error %d sending stop command, "
-                              "response %#x, card status %#x\n",
-                              req->rq_disk->disk_name, brq.stop.error,
-                              brq.stop.resp[0], status);
                }
 
                if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+                       u32 status;
                        do {
                                int err = get_card_status(card, &status, 5);
                                if (err) {
                                 (R1_CURRENT_STATE(status) == R1_STATE_PRG));
                }
 
-               if (brq.cmd.error || brq.stop.error || brq.data.error) {
+               if (brq.data.error) {
+                       pr_err("%s: error %d transferring data, sector %u nr %u, cmd response %#x card status %#x\n",
+                               req->rq_disk->disk_name, brq.data.error,
+                               (unsigned)blk_rq_pos(req),
+                               (unsigned)blk_rq_sectors(req),
+                               brq.cmd.resp[0], brq.stop.resp[0]);
+
                        if (rq_data_dir(req) == READ) {
+                               if (brq.data.blocks > 1) {
+                                       /* Redo read one sector at a time */
+                                       pr_warning("%s: retrying using single block read\n",
+                                               req->rq_disk->disk_name);
+                                       disable_multi = 1;
+                                       continue;
+                               }
+
                                /*
                                 * After an error, we redo I/O one sector at a
                                 * time, so we only reach here after trying to
                                ret = __blk_end_request(req, -EIO, brq.data.blksz);
                                spin_unlock_irq(&md->lock);
                                continue;
+                       } else {
+                               goto cmd_err;
                        }
-                       goto cmd_err;
                }
 
                /*
                spin_unlock_irq(&md->lock);
        }
 
+ cmd_abort:
        spin_lock_irq(&md->lock);
        while (ret)
                ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));