From patchwork Mon Apr 30 12:01:29 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Brook X-Patchwork-Id: 155845 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 179D4B6FA5 for ; Mon, 30 Apr 2012 22:02:30 +1000 (EST) Received: from localhost ([::1]:50978 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SOpJE-0006tg-04 for incoming@patchwork.ozlabs.org; Mon, 30 Apr 2012 08:02:28 -0400 Received: from eggs.gnu.org ([208.118.235.92]:52897) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SOpId-0005IJ-VN for qemu-devel@nongnu.org; Mon, 30 Apr 2012 08:01:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SOpIX-0007h3-0G for qemu-devel@nongnu.org; Mon, 30 Apr 2012 08:01:51 -0400 Received: from relay1.mentorg.com ([192.94.38.131]:45999) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SOpIW-0007gi-O4 for qemu-devel@nongnu.org; Mon, 30 Apr 2012 08:01:44 -0400 Received: from nat-ies.mentorg.com ([192.94.31.2] helo=EU1-MAIL.mgc.mentorg.com) by relay1.mentorg.com with esmtp id 1SOpIU-0005cz-BF from Paul_Brook@mentor.com for qemu-devel@nongnu.org; Mon, 30 Apr 2012 05:01:42 -0700 Received: from nowt.org ([172.30.64.79]) by EU1-MAIL.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 30 Apr 2012 13:01:41 +0100 Received: from wren.home (wren.home [192.168.93.7]) by nowt.org (Postfix) with ESMTP id C02916F58A; Mon, 30 Apr 2012 13:01:40 +0100 (BST) From: Paul Brook To: qemu-devel@nongnu.org Date: Mon, 30 Apr 2012 13:01:29 +0100 Message-Id: <1335787290-16988-2-git-send-email-paul@codesourcery.com> X-Mailer: git-send-email 1.7.10 In-Reply-To: <1335787290-16988-1-git-send-email-paul@codesourcery.com> References: <1335787290-16988-1-git-send-email-paul@codesourcery.com> X-OriginalArrivalTime: 30 Apr 2012 12:01:41.0257 (UTC) FILETIME=[011CF790:01CD26C9] X-detected-operating-system: by eggs.gnu.org: Solaris 10 (beta) X-Received-From: 192.94.38.131 Cc: Paul Brook Subject: [Qemu-devel] [PATCH 1/2] Fix SPI SD card command responses X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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;