[PATCH 1/1] CMD12 error recovery support for SD cards

Mahadev Cholachagudda
2009-09-16T11:05:50Z

Dear All,

I've a kernel code base of 2.6.28.9, where in I find that CMD12 error
recovery is not been implemented. It is a wise idea to implement CMD12
error recovery, that involves sending CMD13 to get the status of the
card. If the card is in "trans" state, it means CMD12 error shall be
treated as successfull. If card state is not in "trans", then we have
to send CMD12 again to stop the data transfer and then checking of the
card status through CMD13. This sequence is detailed in the
"Simplified Host Controller specification" on SDCard website. I
believe this is the case for any controller, though I may be wrong.

With the modification done to the code which is attached as patch to
the latest mmc git, it is working at our site. And we do see some
errors with the SDIO controller (proprietary but meets Standard SD
host controller specification) that we are using.

Request you to comment on the patch.

diff -urNd linux-orig/drivers/mmc/card/block.c
linux-current/drivers/mmc/card/block.c
--- linux-orig/drivers/mmc/card/block.c    2009-09-16 15:43:03.667274139 +0530
+++ linux-current/drivers/mmc/card/block.c    2009-09-16
15:36:24.722272962 +0530
@@ -335,6 +335,76 @@
* until later as we need to wait for the card to leave
* programming mode even when things go wrong.
*/
+
+    /* cmd12 error recovery fix */
+    if( (brq.stop.error) && (brq.data.blocks > 1) &&
!mmc_host_is_spi(card->host) &&
+        mmc_card_sd(card) ) {
+
+      struct mmc_request my_mrq;
+      struct mmc_command my_cmd;
+
+      /*
+       * if there is an error for CMD12, get the state of the card by
+       * sending cmd13 and then check the state of the card.
+       * If the card state is "tran", consider the cmd12 is successfull
+       * If not, then send again CMD12 to stop the data transfer. This time,
+       * any failure to get response, will result in to the card being
+       * non-recoverable.
+       */
+
+      memset(&my_mrq, 0, sizeof(struct mmc_request));
+      memset(&my_cmd, 0, sizeof(struct mmc_command));
+      my_cmd.opcode = MMC_SEND_STATUS;
+      my_cmd.arg = card->rca << 16;
+      my_cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+      my_mrq.cmd = &my_cmd;
+      mmc_wait_for_req(card->host, &my_mrq);
+
+      if(!my_mrq.cmd->error) {
+        if(R1_CURRENT_STATE(my_cmd.resp[0]) == 4) {
+          /* card is in "trans" state, so treat the CMD12 as succesfull */
+          brq.stop.error = 0;
+        }
+        else {
+          /*
+           * card is not still in trans state. Now send CMD12 again and repeat
+           * the process of checking the card status using CMD13
+           */
+          memset(&my_cmd, 0, sizeof(struct mmc_command));
+          my_cmd.opcode = MMC_STOP_TRANSMISSION;
+          my_cmd.arg = 0;
+          my_cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+          my_mrq.cmd = &my_cmd;
+          mmc_wait_for_req(card->host, &my_mrq);
+
+          /* get the card status */
+          memset(&my_cmd, 0, sizeof(struct mmc_command));
+          my_cmd.opcode = MMC_SEND_STATUS;
+          my_cmd.arg = card->rca << 16;
+          my_cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+          my_mrq.cmd = &my_cmd;
+          mmc_wait_for_req(card->host, &my_mrq);
+
+          if(!my_mrq.cmd->error) {
+            if(R1_CURRENT_STATE(my_cmd.resp[0]) == 4) {
+              /* card is in "trans" state, so treat the CMD12 as succesfull */
+              brq.stop.error = 0;
+            }
+            else {
+              /*
+               * card is not in "trans" state yet. Just post an error and
+               * error is not recoverable
+               */
+            }
+          }
+          else {
+            /* error for cmd13!!, then something is wrong with the card */
+          }
+        }
+      }
+    }
+    /* cmd12 error recovery fix */
+
if (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 */

Regards,
Mahadev Cholachagudda