Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 255220
b: refs/heads/master
c: d78d4a8
h: refs/heads/master
v: v3
  • Loading branch information
Per Forlin authored and Chris Ball committed Jul 20, 2011
1 parent a369ebe commit 365de4c
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 90 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 54d49d77628bed77e5491b8a2a1158a492843a19
refs/heads/master: d78d4a8ad53f345dd3c0bb5f8d377baa523739f7
220 changes: 131 additions & 89 deletions trunk/drivers/mmc/card/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ struct mmc_blk_data {

static DEFINE_MUTEX(open_lock);

enum mmc_blk_status {
MMC_BLK_SUCCESS = 0,
MMC_BLK_PARTIAL,
MMC_BLK_RETRY,
MMC_BLK_RETRY_SINGLE,
MMC_BLK_DATA_ERR,
MMC_BLK_CMD_ERR,
MMC_BLK_ABORT,
};

module_param(perdev_minors, int, 0444);
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");

Expand Down Expand Up @@ -812,6 +822,95 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
R1_CC_ERROR | /* Card controller error */ \
R1_ERROR) /* General/unknown error */

int mmc_blk_err_check(struct mmc_blk_request *brq,
struct request *req,
struct mmc_card *card,
struct mmc_blk_data *md)
{
int ret = MMC_BLK_SUCCESS;

/*
* 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->stop.error) {
switch (mmc_blk_cmd_recovery(card, req, brq)) {
case ERR_RETRY:
return MMC_BLK_RETRY;
case ERR_ABORT:
return MMC_BLK_ABORT;
case ERR_CONTINUE:
break;
}
}

/*
* Check for errors relating to the execution of the
* initial command - such as address errors. No data
* has been transferred.
*/
if (brq->cmd.resp[0] & CMD_ERRORS) {
pr_err("%s: r/w command failed, status = %#x\n",
req->rq_disk->disk_name, brq->cmd.resp[0]);
return MMC_BLK_ABORT;
}

/*
* Everything else is either success, or a data error of some
* kind. If it was a write, we may have transitioned to
* program mode, which we have to wait for it to complete.
*/
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) {
printk(KERN_ERR "%s: error %d requesting status\n",
req->rq_disk->disk_name, err);
return MMC_BLK_CMD_ERR;
}
/*
* Some cards mishandle the status bits,
* so make sure to check both the busy
* indication and the card state.
*/
} while (!(status & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(status) == R1_STATE_PRG));
}

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);
return MMC_BLK_RETRY_SINGLE;
}
return MMC_BLK_DATA_ERR;
} else {
return MMC_BLK_CMD_ERR;
}
}

if (ret == MMC_BLK_SUCCESS &&
blk_rq_bytes(req) != brq->data.bytes_xfered)
ret = MMC_BLK_PARTIAL;

return ret;
}

static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
struct mmc_card *card,
int disable_multi,
Expand Down Expand Up @@ -948,106 +1047,49 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
int ret = 1, disable_multi = 0, retry = 0;
enum mmc_blk_status status;

do {
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, disable_multi, mq);
mmc_wait_for_req(card->host, &brq->mrq);

mmc_queue_bounce_post(mq->mqrq_cur);

/*
* 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->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:
status = mmc_blk_err_check(brq, req, card, md);
switch (status) {
case MMC_BLK_SUCCESS:
case MMC_BLK_PARTIAL:
/*
* A block was successfully transferred.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0,
brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
break;
case MMC_BLK_CMD_ERR:
goto cmd_err;
case MMC_BLK_RETRY_SINGLE:
disable_multi = 1;
break;
case MMC_BLK_RETRY:
if (retry++ < 5)
break;
}
}

/*
* Check for errors relating to the execution of the
* initial command - such as address errors. No data
* has been transferred.
*/
if (brq->cmd.resp[0] & CMD_ERRORS) {
pr_err("%s: r/w command failed, status = %#x\n",
req->rq_disk->disk_name, brq->cmd.resp[0]);
case MMC_BLK_ABORT:
goto cmd_abort;
case MMC_BLK_DATA_ERR:
/*
* After an error, we redo I/O one sector at a
* time, so we only reach here after trying to
* read a single sector.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, -EIO,
brq->data.blksz);
spin_unlock_irq(&md->lock);
break;
}

/*
* Everything else is either success, or a data error of some
* kind. If it was a write, we may have transitioned to
* program mode, which we have to wait for it to complete.
*/
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) {
printk(KERN_ERR "%s: error %d requesting status\n",
req->rq_disk->disk_name, err);
goto cmd_err;
}
/*
* Some cards mishandle the status bits,
* so make sure to check both the busy
* indication and the card state.
*/
} while (!(status & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(status) == R1_STATE_PRG));
}

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
* read a single sector.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, -EIO,
brq->data.blksz);
spin_unlock_irq(&md->lock);
continue;
} else {
goto cmd_err;
}
}

/*
* A block was successfully transferred.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
} while (ret);

return 1;
Expand Down

0 comments on commit 365de4c

Please sign in to comment.