qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 1/2] Fix SPI SD card command responses


From: Paul Brook
Subject: [Qemu-devel] [PATCH 1/2] Fix SPI SD card command responses
Date: Mon, 30 Apr 2012 13:01:29 +0100

When in SPI mode, we give a bogus response to CMD8 (part of the SD physical
spec v2).  This command should return both the status byte and the
register value.

The current code returns "long" status words from sd.c, then parses translates
those to SPI status bytes ssi-sd.c.  For CMD8 (and CMD58 to follow)
this gets messy, with both parts requiring command specific knowledge.
We already have magic SPI-mode behavior in sd.c, so may as well just
generate the correct response there.

Signed-off-by: Paul Brook <address@hidden>
---
 hw/sd.c     |  125 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 hw/sd.h     |   17 ++++++++
 hw/ssi-sd.c |   83 +++------------------------------------
 3 files changed, 124 insertions(+), 101 deletions(-)

diff --git a/hw/sd.c b/hw/sd.c
index 07eb263..220562e 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -52,6 +52,7 @@ typedef enum {
     sd_r7,        /* Operating voltage */
     sd_r1b = -1,
     sd_illegal = -2,
+    sd_r1_long = -3, /* Two byte status in SPI mode.  */
 } sd_rsp_type_t;
 
 struct SDState {
@@ -342,24 +343,93 @@ static int sd_req_crc_validate(SDRequest *req)
     return sd_crc7(buffer, 5) != req->crc;     /* TODO */
 }
 
-static void sd_response_r1_make(SDState *sd, uint8_t *response)
+
+/* Make SPI status word from full card status.  Most commands only use
+   the high byte.  */
+static uint16_t sd_get_spi_status(SDState *sd, uint32_t cardstatus)
+{
+    uint16_t status = 0;
+
+    if (((cardstatus >> 9) & 0xf) < 4)
+        status |= SPI_SDR_IDLE;
+    if (cardstatus & ERASE_RESET)
+        status |= SPI_SDR_ERASE_RESET;
+    if (cardstatus & ILLEGAL_COMMAND)
+        status |= SPI_SDR_ILLEGAL_COMMAND;
+    if (cardstatus & COM_CRC_ERROR)
+        status |= SPI_SDR_COM_CRC_ERROR;
+    if (cardstatus & ERASE_SEQ_ERROR)
+        status |= SPI_SDR_ERASE_SEQ_ERROR;
+    if (cardstatus & ADDRESS_ERROR)
+        status |= SPI_SDR_ADDRESS_ERROR;
+    if (cardstatus & CARD_IS_LOCKED)
+        status |= SPI_SDR_LOCKED;
+    if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP))
+        status |= SPI_SDR_WP_ERASE;
+    if (cardstatus & SD_ERROR)
+        status |= SPI_SDR_ERROR;
+    if (cardstatus & CC_ERROR)
+        status |= SPI_SDR_CC_ERROR;
+    if (cardstatus & CARD_ECC_FAILED)
+        status |= SPI_SDR_ECC_FAILED;
+    if (cardstatus & WP_VIOLATION)
+        status |= SPI_SDR_WP_VIOLATION;
+    if (cardstatus & ERASE_PARAM)
+        status |= SPI_SDR_ERASE_PARAM;
+    if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE))
+        status |= SPI_SDR_OUT_OF_RANGE;
+    /* ??? Don't know what Parameter Error really means, so
+       assume it's set if the second byte is nonzero.  */
+    if (status & 0xff)
+        status |= SPI_SDR_PARAMETER_ERROR;
+
+    return status;
+}
+
+static int sd_response_r1_make(SDState *sd, uint8_t *response)
 {
     uint32_t status = sd->card_status;
     /* Clear the "clear on read" status bits */
     sd->card_status &= ~CARD_STATUS_C;
 
-    response[0] = (status >> 24) & 0xff;
-    response[1] = (status >> 16) & 0xff;
-    response[2] = (status >> 8) & 0xff;
-    response[3] = (status >> 0) & 0xff;
+    if (sd->spi) {
+        response[0] = sd_get_spi_status(sd, status) >> 8;
+        return 1;
+    } else {
+        response[0] = (status >> 24) & 0xff;
+        response[1] = (status >> 16) & 0xff;
+        response[2] = (status >> 8) & 0xff;
+        response[3] = (status >> 0) & 0xff;
+        return 4;
+    }
+}
+
+/* Only used in SPI mode.  */
+static int sd_response_r1_long_make(SDState *sd, uint8_t *response)
+{
+    uint32_t status = sd->card_status;
+    /* Clear the "clear on read" status bits */
+    sd->card_status &= ~CARD_STATUS_C;
+    status = sd_get_spi_status(sd, status);
+    response[0] = status >> 8;
+    response[1] = status & 0xff;
+    return 2;
 }
 
-static void sd_response_r3_make(SDState *sd, uint8_t *response)
+static int sd_response_r3_make(SDState *sd, uint8_t *response)
 {
-    response[0] = (sd->ocr >> 24) & 0xff;
-    response[1] = (sd->ocr >> 16) & 0xff;
-    response[2] = (sd->ocr >> 8) & 0xff;
-    response[3] = (sd->ocr >> 0) & 0xff;
+    int len = 4;
+
+    if (sd->spi) {
+        len = 5;
+        *(response++) = sd_get_spi_status(sd, sd->card_status) >> 8;
+    }
+    *(response++) = (sd->ocr >> 24) & 0xff;
+    *(response++) = (sd->ocr >> 16) & 0xff;
+    *(response++) = (sd->ocr >> 8) & 0xff;
+    *(response++) = (sd->ocr >> 0) & 0xff;
+
+    return len;
 }
 
 static void sd_response_r6_make(SDState *sd, uint8_t *response)
@@ -379,12 +449,20 @@ static void sd_response_r6_make(SDState *sd, uint8_t 
*response)
     response[3] = status & 0xff;
 }
 
-static void sd_response_r7_make(SDState *sd, uint8_t *response)
+static int sd_response_r7_make(SDState *sd, uint8_t *response)
 {
-    response[0] = (sd->vhs >> 24) & 0xff;
-    response[1] = (sd->vhs >> 16) & 0xff;
-    response[2] = (sd->vhs >>  8) & 0xff;
-    response[3] = (sd->vhs >>  0) & 0xff;
+    int len = 4;
+
+    if (sd->spi) {
+        len = 5;
+        *(response++) = sd_get_spi_status(sd, sd->card_status) >> 8;
+    }
+    *(response++) = (sd->vhs >> 24) & 0xff;
+    *(response++) = (sd->vhs >> 16) & 0xff;
+    *(response++) = (sd->vhs >>  8) & 0xff;
+    *(response++) = (sd->vhs >>  0) & 0xff;
+
+    return len;
 }
 
 static void sd_reset(SDState *sd, BlockDriverState *bdrv)
@@ -441,7 +519,7 @@ static const BlockDevOps sd_block_ops = {
 };
 
 /* We do not model the chip select pin, so allow the board to select
-   whether card should be in SSI or MMC/SD mode.  It is also up to the
+   whether card should be in SPI or MMC/SD mode.  It is also up to the
    board to ensure that ssi transfers only occur when the chip select
    is asserted.  */
 SDState *sd_init(BlockDriverState *bs, int is_spi)
@@ -843,7 +921,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
             if (sd->rca != rca)
                 return sd_r0;
 
-            return sd_r1;
+            return sd->spi ? sd_r1_long : sd_r1;
 
         default:
             break;
@@ -1339,8 +1417,11 @@ send_response:
     switch (rtype) {
     case sd_r1:
     case sd_r1b:
-        sd_response_r1_make(sd, response);
-        rsplen = 4;
+        rsplen = sd_response_r1_make(sd, response);
+        break;
+
+    case sd_r1_long:
+        rsplen = sd_response_r1_long_make(sd, response);
         break;
 
     case sd_r2_i:
@@ -1354,8 +1435,7 @@ send_response:
         break;
 
     case sd_r3:
-        sd_response_r3_make(sd, response);
-        rsplen = 4;
+        rsplen = sd_response_r3_make(sd, response);
         break;
 
     case sd_r6:
@@ -1364,8 +1444,7 @@ send_response:
         break;
 
     case sd_r7:
-        sd_response_r7_make(sd, response);
-        rsplen = 4;
+        rsplen = sd_response_r7_make(sd, response);
         break;
 
     case sd_r0:
diff --git a/hw/sd.h b/hw/sd.h
index ac4b7c4..9b95df9 100644
--- a/hw/sd.h
+++ b/hw/sd.h
@@ -51,6 +51,23 @@
 #define APP_CMD                        (1 << 5)
 #define AKE_SEQ_ERROR          (1 << 3)
 
+/* SPI State word bits.  */
+#define SPI_SDR_LOCKED          0x0001
+#define SPI_SDR_WP_ERASE        0x0002
+#define SPI_SDR_ERROR           0x0004
+#define SPI_SDR_CC_ERROR        0x0008
+#define SPI_SDR_ECC_FAILED      0x0010
+#define SPI_SDR_WP_VIOLATION    0x0020
+#define SPI_SDR_ERASE_PARAM     0x0040
+#define SPI_SDR_OUT_OF_RANGE    0x0080
+#define SPI_SDR_IDLE            0x0100
+#define SPI_SDR_ERASE_RESET     0x0200
+#define SPI_SDR_ILLEGAL_COMMAND 0x0400
+#define SPI_SDR_COM_CRC_ERROR   0x0800
+#define SPI_SDR_ERASE_SEQ_ERROR 0x1000
+#define SPI_SDR_ADDRESS_ERROR   0x2000
+#define SPI_SDR_PARAMETER_ERROR 0x4000
+
 typedef enum {
     sd_none = -1,
     sd_bc = 0, /* broadcast -- no response */
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
index b519bdb..08d1e78 100644
--- a/hw/ssi-sd.c
+++ b/hw/ssi-sd.c
@@ -40,30 +40,13 @@ typedef struct {
     ssi_sd_mode mode;
     int cmd;
     uint8_t cmdarg[4];
-    uint8_t response[5];
+    uint8_t response[16];
     int arglen;
     int response_pos;
     int stopping;
     SDState *sd;
 } ssi_sd_state;
 
-/* State word bits.  */
-#define SSI_SDR_LOCKED          0x0001
-#define SSI_SDR_WP_ERASE        0x0002
-#define SSI_SDR_ERROR           0x0004
-#define SSI_SDR_CC_ERROR        0x0008
-#define SSI_SDR_ECC_FAILED      0x0010
-#define SSI_SDR_WP_VIOLATION    0x0020
-#define SSI_SDR_ERASE_PARAM     0x0040
-#define SSI_SDR_OUT_OF_RANGE    0x0080
-#define SSI_SDR_IDLE            0x0100
-#define SSI_SDR_ERASE_RESET     0x0200
-#define SSI_SDR_ILLEGAL_COMMAND 0x0400
-#define SSI_SDR_COM_CRC_ERROR   0x0800
-#define SSI_SDR_ERASE_SEQ_ERROR 0x1000
-#define SSI_SDR_ADDRESS_ERROR   0x2000
-#define SSI_SDR_PARAMETER_ERROR 0x4000
-
 static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
 {
     ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
@@ -88,73 +71,17 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
     case SSI_SD_CMDARG:
         if (s->arglen == 4) {
             SDRequest request;
-            uint8_t longresp[16];
-            /* FIXME: Check CRC.  */
+            /* CRC check only needed when enabled by CMD59, which we don't
+               implement yet.  */
             request.cmd = s->cmd;
             request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16)
                            | (s->cmdarg[2] << 8) | s->cmdarg[3];
             DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg);
-            s->arglen = sd_do_command(s->sd, &request, longresp);
+            s->arglen = sd_do_command(s->sd, &request, s->response);
             if (s->arglen <= 0) {
                 s->arglen = 1;
-                s->response[0] = 4;
+                s->response[0] = SPI_SDR_PARAMETER_ERROR >> 8;
                 DPRINTF("SD command failed\n");
-            } else if (s->cmd == 58) {
-                /* CMD58 returns R3 response (OCR)  */
-                DPRINTF("Returned OCR\n");
-                s->arglen = 5;
-                s->response[0] = 1;
-                memcpy(&s->response[1], longresp, 4);
-            } else if (s->arglen != 4) {
-                BADF("Unexpected response to cmd %d\n", s->cmd);
-                /* Illegal command is about as near as we can get.  */
-                s->arglen = 1;
-                s->response[0] = 4;
-            } else {
-                /* All other commands return status.  */
-                uint32_t cardstatus;
-                uint16_t status;
-                /* CMD13 returns a 2-byte statuse work. Other commands
-                   only return the first byte.  */
-                s->arglen = (s->cmd == 13) ? 2 : 1;
-                cardstatus = (longresp[0] << 24) | (longresp[1] << 16)
-                             | (longresp[2] << 8) | longresp[3];
-                status = 0;
-                if (((cardstatus >> 9) & 0xf) < 4)
-                    status |= SSI_SDR_IDLE;
-                if (cardstatus & ERASE_RESET)
-                    status |= SSI_SDR_ERASE_RESET;
-                if (cardstatus & ILLEGAL_COMMAND)
-                    status |= SSI_SDR_ILLEGAL_COMMAND;
-                if (cardstatus & COM_CRC_ERROR)
-                    status |= SSI_SDR_COM_CRC_ERROR;
-                if (cardstatus & ERASE_SEQ_ERROR)
-                    status |= SSI_SDR_ERASE_SEQ_ERROR;
-                if (cardstatus & ADDRESS_ERROR)
-                    status |= SSI_SDR_ADDRESS_ERROR;
-                if (cardstatus & CARD_IS_LOCKED)
-                    status |= SSI_SDR_LOCKED;
-                if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP))
-                    status |= SSI_SDR_WP_ERASE;
-                if (cardstatus & SD_ERROR)
-                    status |= SSI_SDR_ERROR;
-                if (cardstatus & CC_ERROR)
-                    status |= SSI_SDR_CC_ERROR;
-                if (cardstatus & CARD_ECC_FAILED)
-                    status |= SSI_SDR_ECC_FAILED;
-                if (cardstatus & WP_VIOLATION)
-                    status |= SSI_SDR_WP_VIOLATION;
-                if (cardstatus & ERASE_PARAM)
-                    status |= SSI_SDR_ERASE_PARAM;
-                if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE))
-                    status |= SSI_SDR_OUT_OF_RANGE;
-                /* ??? Don't know what Parameter Error really means, so
-                   assume it's set if the second byte is nonzero.  */
-                if (status & 0xff)
-                    status |= SSI_SDR_PARAMETER_ERROR;
-                s->response[0] = status >> 8;
-                s->response[1] = status;
-                DPRINTF("Card status 0x%02x\n", status);
             }
             s->mode = SSI_SD_RESPONSE;
             s->response_pos = 0;
-- 
1.7.10




reply via email to

[Prev in Thread] Current Thread [Next in Thread]