From patchwork Sat Jan 8 00:13:42 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russell King - ARM Linux X-Patchwork-Id: 465381 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p080DuMj031157 for ; Sat, 8 Jan 2011 00:13:56 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751649Ab1AHANz (ORCPT ); Fri, 7 Jan 2011 19:13:55 -0500 Received: from caramon.arm.linux.org.uk ([78.32.30.218]:38638 "EHLO caramon.arm.linux.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751315Ab1AHANz (ORCPT ); Fri, 7 Jan 2011 19:13:55 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=arm.linux.org.uk; s=caramon; h=Sender:Content-Type:MIME-Version:Message-ID:Subject:Cc:To:From:Date; bh=LkfKTcb7x1lJazSuE2HjpodnI9u2/ILc7b0cX9+XDxE=; b=JeAI1Bb1WPMfY1Sy/B62eLBVRmVuHphtiRak4v4n8eOSOMdQq3dUDYbHdr1Zt1Kyn+67flggu5sOxgtTXkC3triMrfu/GWRE6+cGYQexWIj31WTyXWXP5MlHaEYW75Url56VFIdozKfYvfbVAL4HggZtd39PEuEKn5gCj5hfF4M=; Received: from n2100.arm.linux.org.uk ([2002:4e20:1eda:1:214:fdff:fe10:4f86]) by caramon.arm.linux.org.uk with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.72) (envelope-from ) id 1PbMRE-00026b-BQ; Sat, 08 Jan 2011 00:13:45 +0000 Received: from linux by n2100.arm.linux.org.uk with local (Exim 4.72) (envelope-from ) id 1PbMRD-0001cD-3g; Sat, 08 Jan 2011 00:13:43 +0000 Date: Sat, 8 Jan 2011 00:13:42 +0000 From: Russell King - ARM Linux To: Chris Ball Cc: linux-mmc@vger.kernel.org Subject: RFC: improved error recovery Message-ID: <20110108001342.GA31708@n2100.arm.linux.org.uk> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.19 (2009-01-05) Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Sat, 08 Jan 2011 00:13:57 +0000 (UTC) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 217f820..5a8c749 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -245,6 +245,20 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) return result; } +static void send_stop(struct mmc_card *card, struct request *req) +{ + struct mmc_command cmd; + int err; + + memset(&cmd, 0, sizeof(struct mmc_command)); + 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, 0); + if (err) + pr_err("%s: error %d sending stop command\n", + req->rq_disk->disk_name, err); +} + static u32 get_card_status(struct mmc_card *card, struct request *req) { struct mmc_command cmd; @@ -255,9 +269,9 @@ static u32 get_card_status(struct mmc_card *card, struct request *req) if (!mmc_host_is_spi(card->host)) cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 0); + err = mmc_wait_for_cmd(card->host, &cmd, 2); if (err) - printk(KERN_ERR "%s: error %d sending status comand", + pr_err("%s: error %d sending status command\n", req->rq_disk->disk_name, err); return cmd.resp[0]; } @@ -336,7 +350,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) 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; mmc_claim_host(card->host); @@ -432,6 +446,53 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) * programming mode even when things go wrong. */ if (brq.cmd.error || brq.data.error || brq.stop.error) { + status = get_card_status(card, req); + + /* First print what's up */ + if (brq.cmd.error) + pr_err("%s: error %d sending read/write command, card status %#x\n", + req->rq_disk->disk_name, brq.cmd.error, + status); + + 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], status); + + if (brq.stop.error) + 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); + + /* + * Now 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) + send_stop(card, req); + + /* + * r/w cmd failure - get_card_status() should + * tell us why the command was not accepted + */ + if (brq.cmd.error && retry < 2) { + /* + * 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)) { + retry++; + continue; + } + } + if (brq.data.blocks > 1 && rq_data_dir(req) == READ) { /* Redo read one sector at a time */ printk(KERN_WARNING "%s: retrying using single " @@ -439,32 +500,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) disable_multi = 1; continue; } - status = get_card_status(card, req); - } - - 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) { @@ -486,7 +521,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req) * indication and the card state. */ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || - (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)); #if 0 if (cmd.resp[0] & ~0x00000900) diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 956fbd8..8b34bbc 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -131,6 +131,16 @@ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ #define R1_APP_CMD (1 << 5) /* sr, c */ +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + /* * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS * R1 is the low order byte; R2 is the next highest byte, when present.