SD/MMC patches
- Various improvements for SD cards in SPI mode (Bin Meng) - Add Bin Meng as SD/MMC cards co-maintainer -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmAwU0gACgkQ4+MsLN6t wN6gUg//TIPzwJSZsJ36S8E0TIflSRRbCBmrvhQF0KeXkaImFx2wOzaeb7eGFhWS l12z5tCo6kYgeDzfmcKQdMkKjweP7C3ct/tSytTRdPiApaDvae/nHcxsBKrtHqDU otVC6kQ5cB/u/6uf9vDIRrBCqB4AiXrBnL5l/NzDgrUqHkmYbXOjk+K2Xy+2MkYw Vfwzdh50gdFgYDQW2nM9GfD1VRq5XAtzCNXjxQhwBZkQLQ8G7KLMVE8MjzFjw3Vt PDXzfD8zWZoHM3awC34imWnJC+br0h0NNQpTkRj5DBcYpYYwo4FkBmr9pE2LuwYG dma0TalP+/gnlbmJr+Wq9wChGkmPmuyHfBanbgEtmA5cuNB/YRfWPaHqCvvCXSvk 4+UF0xgGQG3kuSUbeujVuRuak+/2a7f30fxK3EZ579L2TVbnUdloQoyVgoIr7xTd DWMsA0AjPNcvq4vA9myoFkj8/GWcT2jiYGGLQGIfnqcbMc2ii9E9wBibsAkuAnHb OvO6xdEWEui8LbTGJZ/PpMd68MWIcGQk65Su+53Ls5oPkOCoNNRBQ2eHMuObDDSm kNyQ5QgQed345U6W7gFp+krdOtBWbXM2X24eIo9lQ2U59kitdE1DPYgmTzO4SNSz 3uM4GiNd3i13fZshH+vUZJrJ87PliCVsQfazIOeeVBFoms/Cz7o= =LijF -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sdmmc-20210220' into staging SD/MMC patches - Various improvements for SD cards in SPI mode (Bin Meng) - Add Bin Meng as SD/MMC cards co-maintainer # gpg: Signature made Sat 20 Feb 2021 00:09:44 GMT # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * remotes/philmd-gitlab/tags/sdmmc-20210220: MAINTAINERS: Add Bin Meng as co-maintainer for SD/MMC cards hw/sd: sdhci: Simplify updating s->prnsts in sdhci_sdma_transfer_multi_blocks() hw/sd: sd: Bypass the RCA check for CMD13 in SPI mode hw/sd: sd: Skip write protect groups check in CMD24/25 for high capacity cards hw/sd: sd: Skip write protect groups check in sd_erase() for high capacity cards hw/sd: sd: Move the sd_block_{read, write} and macros ahead hw/sd: sd: Fix CMD30 response type hw/sd: sd: Only SDSC cards support CMD28/29/30 hw/sd: sd: Fix address check in sd_erase() hw/sd: ssi-sd: Handle the rest commands with R1b response type hw/sd: ssi-sd: Fix STOP_TRANSMISSION (CMD12) response hw/sd: ssi-sd: Fix SEND_IF_COND (CMD8) response hw/sd: ssi-sd: Support multiple block write hw/sd: ssi-sd: Support single block write hw/sd: Introduce receive_ready() callback hw/sd: sd: Allow single/multiple block write for SPI mode hw/sd: sd: Remove duplicated codes in single/multiple block read/write hw/sd: ssi-sd: Support multiple block read Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a528b8c4c6
@ -1754,6 +1754,7 @@ F: hw/ssi/xilinx_*
|
||||
|
||||
SD (Secure Card)
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
M: Bin Meng <bin.meng@windriver.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: include/hw/sd/sd*
|
||||
|
13
hw/sd/core.c
13
hw/sd/core.c
@ -160,6 +160,19 @@ void sdbus_read_data(SDBus *sdbus, void *buf, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
bool sdbus_receive_ready(SDBus *sdbus)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
|
||||
if (card) {
|
||||
SDCardClass *sc = SD_CARD_GET_CLASS(card);
|
||||
|
||||
return sc->receive_ready(card);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sdbus_data_ready(SDBus *sdbus)
|
||||
{
|
||||
SDState *card = get_card(sdbus);
|
||||
|
149
hw/sd/sd.c
149
hw/sd/sd.c
@ -739,11 +739,33 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
|
||||
qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0);
|
||||
}
|
||||
|
||||
static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
trace_sdcard_read_block(addr, len);
|
||||
if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) {
|
||||
fprintf(stderr, "sd_blk_read: read error on host side\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
trace_sdcard_write_block(addr, len);
|
||||
if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) {
|
||||
fprintf(stderr, "sd_blk_write: write error on host side\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
|
||||
#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len)
|
||||
#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
|
||||
#define APP_WRITE_BLOCK(a, len)
|
||||
|
||||
static void sd_erase(SDState *sd)
|
||||
{
|
||||
int i;
|
||||
uint64_t erase_start = sd->erase_start;
|
||||
uint64_t erase_end = sd->erase_end;
|
||||
bool sdsc = true;
|
||||
|
||||
trace_sdcard_erase(sd->erase_start, sd->erase_end);
|
||||
if (sd->erase_start == INVALID_ADDRESS
|
||||
@ -758,25 +780,30 @@ static void sd_erase(SDState *sd)
|
||||
/* High capacity memory card: erase units are 512 byte blocks */
|
||||
erase_start *= 512;
|
||||
erase_end *= 512;
|
||||
sdsc = false;
|
||||
}
|
||||
|
||||
if (sd->erase_start > sd->size || sd->erase_end > sd->size) {
|
||||
if (erase_start > sd->size || erase_end > sd->size) {
|
||||
sd->card_status |= OUT_OF_RANGE;
|
||||
sd->erase_start = INVALID_ADDRESS;
|
||||
sd->erase_end = INVALID_ADDRESS;
|
||||
return;
|
||||
}
|
||||
|
||||
erase_start = sd_addr_to_wpnum(erase_start);
|
||||
erase_end = sd_addr_to_wpnum(erase_end);
|
||||
sd->erase_start = INVALID_ADDRESS;
|
||||
sd->erase_end = INVALID_ADDRESS;
|
||||
sd->csd[14] |= 0x40;
|
||||
|
||||
for (i = erase_start; i <= erase_end; i++) {
|
||||
assert(i < sd->wpgrps_size);
|
||||
if (test_bit(i, sd->wp_groups)) {
|
||||
sd->card_status |= WP_ERASE_SKIP;
|
||||
/* Only SDSC cards support write protect groups */
|
||||
if (sdsc) {
|
||||
erase_start = sd_addr_to_wpnum(erase_start);
|
||||
erase_end = sd_addr_to_wpnum(erase_end);
|
||||
|
||||
for (i = erase_start; i <= erase_end; i++) {
|
||||
assert(i < sd->wpgrps_size);
|
||||
if (test_bit(i, sd->wp_groups)) {
|
||||
sd->card_status |= WP_ERASE_SKIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1136,8 +1163,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
case 13: /* CMD13: SEND_STATUS */
|
||||
switch (sd->mode) {
|
||||
case sd_data_transfer_mode:
|
||||
if (sd->rca != rca)
|
||||
if (!sd->spi && sd->rca != rca) {
|
||||
return sd_r0;
|
||||
}
|
||||
|
||||
return sd_r1;
|
||||
|
||||
@ -1181,24 +1209,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
break;
|
||||
|
||||
case 17: /* CMD17: READ_SINGLE_BLOCK */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
|
||||
if (addr + sd->blk_len > sd->size) {
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
return sd_r1;
|
||||
}
|
||||
|
||||
sd->state = sd_sendingdata_state;
|
||||
sd->data_start = addr;
|
||||
sd->data_offset = 0;
|
||||
return sd_r1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
@ -1245,41 +1255,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
|
||||
/* Block write commands (Class 4) */
|
||||
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
/* Writing in SPI mode not implemented. */
|
||||
if (sd->spi)
|
||||
break;
|
||||
|
||||
if (addr + sd->blk_len > sd->size) {
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
return sd_r1;
|
||||
}
|
||||
|
||||
sd->state = sd_receivingdata_state;
|
||||
sd->data_start = addr;
|
||||
sd->data_offset = 0;
|
||||
sd->blk_written = 0;
|
||||
|
||||
if (sd_wp_addr(sd, sd->data_start)) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
}
|
||||
if (sd->csd[14] & 0x30) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
}
|
||||
return sd_r1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
/* Writing in SPI mode not implemented. */
|
||||
if (sd->spi)
|
||||
break;
|
||||
|
||||
if (addr + sd->blk_len > sd->size) {
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
@ -1291,8 +1269,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
sd->data_offset = 0;
|
||||
sd->blk_written = 0;
|
||||
|
||||
if (sd_wp_addr(sd, sd->data_start)) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
if (sd->size <= SDSC_MAX_CAPACITY) {
|
||||
if (sd_wp_addr(sd, sd->data_start)) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
}
|
||||
}
|
||||
if (sd->csd[14] & 0x30) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
@ -1334,6 +1314,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
|
||||
/* Write protection (Class 6) */
|
||||
case 28: /* CMD28: SET_WRITE_PROT */
|
||||
if (sd->size > SDSC_MAX_CAPACITY) {
|
||||
return sd_illegal;
|
||||
}
|
||||
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
if (addr >= sd->size) {
|
||||
@ -1353,6 +1337,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
break;
|
||||
|
||||
case 29: /* CMD29: CLR_WRITE_PROT */
|
||||
if (sd->size > SDSC_MAX_CAPACITY) {
|
||||
return sd_illegal;
|
||||
}
|
||||
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
if (addr >= sd->size) {
|
||||
@ -1372,13 +1360,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
|
||||
break;
|
||||
|
||||
case 30: /* CMD30: SEND_WRITE_PROT */
|
||||
if (sd->size > SDSC_MAX_CAPACITY) {
|
||||
return sd_illegal;
|
||||
}
|
||||
|
||||
switch (sd->state) {
|
||||
case sd_transfer_state:
|
||||
sd->state = sd_sendingdata_state;
|
||||
*(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
|
||||
sd->data_start = addr;
|
||||
sd->data_offset = 0;
|
||||
return sd_r1b;
|
||||
return sd_r1;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -1792,27 +1784,6 @@ send_response:
|
||||
return rsplen;
|
||||
}
|
||||
|
||||
static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
trace_sdcard_read_block(addr, len);
|
||||
if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) {
|
||||
fprintf(stderr, "sd_blk_read: read error on host side\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
|
||||
{
|
||||
trace_sdcard_write_block(addr, len);
|
||||
if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) {
|
||||
fprintf(stderr, "sd_blk_write: write error on host side\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
|
||||
#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len)
|
||||
#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
|
||||
#define APP_WRITE_BLOCK(a, len)
|
||||
|
||||
void sd_write_byte(SDState *sd, uint8_t value)
|
||||
{
|
||||
int i;
|
||||
@ -1853,9 +1824,11 @@ void sd_write_byte(SDState *sd, uint8_t value)
|
||||
sd->card_status |= ADDRESS_ERROR;
|
||||
break;
|
||||
}
|
||||
if (sd_wp_addr(sd, sd->data_start)) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
break;
|
||||
if (sd->size <= SDSC_MAX_CAPACITY) {
|
||||
if (sd_wp_addr(sd, sd->data_start)) {
|
||||
sd->card_status |= WP_VIOLATION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sd->data[sd->data_offset++] = value;
|
||||
@ -2087,6 +2060,11 @@ uint8_t sd_read_byte(SDState *sd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool sd_receive_ready(SDState *sd)
|
||||
{
|
||||
return sd->state == sd_receivingdata_state;
|
||||
}
|
||||
|
||||
static bool sd_data_ready(SDState *sd)
|
||||
{
|
||||
return sd->state == sd_sendingdata_state;
|
||||
@ -2197,6 +2175,7 @@ static void sd_class_init(ObjectClass *klass, void *data)
|
||||
sc->do_command = sd_do_command;
|
||||
sc->write_byte = sd_write_byte;
|
||||
sc->read_byte = sd_read_byte;
|
||||
sc->receive_ready = sd_receive_ready;
|
||||
sc->data_ready = sd_data_ready;
|
||||
sc->enable = sd_enable;
|
||||
sc->get_inserted = sd_get_inserted;
|
||||
|
@ -596,9 +596,9 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||
page_aligned = true;
|
||||
}
|
||||
|
||||
s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
|
||||
if (s->trnmod & SDHC_TRNS_READ) {
|
||||
s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
|
||||
SDHC_DAT_LINE_ACTIVE;
|
||||
s->prnsts |= SDHC_DOING_READ;
|
||||
while (s->blkcnt) {
|
||||
if (s->data_count == 0) {
|
||||
sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
|
||||
@ -625,8 +625,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT |
|
||||
SDHC_DAT_LINE_ACTIVE;
|
||||
s->prnsts |= SDHC_DOING_WRITE;
|
||||
while (s->blkcnt) {
|
||||
begin = s->data_count;
|
||||
if (((boundary_count + begin) < block_size) && page_aligned) {
|
||||
|
136
hw/sd/ssi-sd.c
136
hw/sd/ssi-sd.c
@ -4,6 +4,11 @@
|
||||
* Copyright (c) 2007-2009 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* Copyright (c) 2021 Wind River Systems, Inc.
|
||||
* Improved by Bin Meng <bin.meng@windriver.com>
|
||||
*
|
||||
* Validated with U-Boot v2021.01 and Linux v5.10 mmc_spi driver
|
||||
*
|
||||
* This code is licensed under the GNU GPL v2.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
@ -43,6 +48,8 @@ typedef enum {
|
||||
SSI_SD_DATA_START,
|
||||
SSI_SD_DATA_READ,
|
||||
SSI_SD_DATA_CRC16,
|
||||
SSI_SD_DATA_WRITE,
|
||||
SSI_SD_SKIP_CRC16,
|
||||
} ssi_sd_mode;
|
||||
|
||||
struct ssi_sd_state {
|
||||
@ -52,6 +59,8 @@ struct ssi_sd_state {
|
||||
uint8_t cmdarg[4];
|
||||
uint8_t response[5];
|
||||
uint16_t crc16;
|
||||
int32_t read_bytes;
|
||||
int32_t write_bytes;
|
||||
int32_t arglen;
|
||||
int32_t response_pos;
|
||||
int32_t stopping;
|
||||
@ -78,37 +87,86 @@ OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD)
|
||||
#define SSI_SDR_ADDRESS_ERROR 0x2000
|
||||
#define SSI_SDR_PARAMETER_ERROR 0x4000
|
||||
|
||||
/* multiple block write */
|
||||
#define SSI_TOKEN_MULTI_WRITE 0xfc
|
||||
/* terminate multiple block write */
|
||||
#define SSI_TOKEN_STOP_TRAN 0xfd
|
||||
/* single block read/write, multiple block read */
|
||||
#define SSI_TOKEN_SINGLE 0xfe
|
||||
|
||||
/* dummy value - don't care */
|
||||
#define SSI_DUMMY 0xff
|
||||
|
||||
/* data accepted */
|
||||
#define DATA_RESPONSE_ACCEPTED 0x05
|
||||
|
||||
static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
{
|
||||
ssi_sd_state *s = SSI_SD(dev);
|
||||
SDRequest request;
|
||||
uint8_t longresp[16];
|
||||
|
||||
/* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */
|
||||
if (s->mode == SSI_SD_DATA_READ && val == 0x4c) {
|
||||
s->mode = SSI_SD_CMD;
|
||||
/* There must be at least one byte delay before the card responds. */
|
||||
s->stopping = 1;
|
||||
/*
|
||||
* Special case: allow CMD12 (STOP TRANSMISSION) while reading data.
|
||||
*
|
||||
* See "Physical Layer Specification Version 8.00" chapter 7.5.2.2,
|
||||
* to avoid conflict between CMD12 response and next data block,
|
||||
* timing of CMD12 should be controlled as follows:
|
||||
*
|
||||
* - CMD12 issued at the timing that end bit of CMD12 and end bit of
|
||||
* data block is overlapped
|
||||
* - CMD12 issued after one clock cycle after host receives a token
|
||||
* (either Start Block token or Data Error token)
|
||||
*
|
||||
* We need to catch CMD12 in all of the data read states.
|
||||
*/
|
||||
if (s->mode >= SSI_SD_PREP_DATA && s->mode <= SSI_SD_DATA_CRC16) {
|
||||
if (val == 0x4c) {
|
||||
s->mode = SSI_SD_CMD;
|
||||
/* There must be at least one byte delay before the card responds */
|
||||
s->stopping = 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (s->mode) {
|
||||
case SSI_SD_CMD:
|
||||
if (val == SSI_DUMMY) {
|
||||
switch (val) {
|
||||
case SSI_DUMMY:
|
||||
DPRINTF("NULL command\n");
|
||||
return SSI_DUMMY;
|
||||
break;
|
||||
case SSI_TOKEN_SINGLE:
|
||||
case SSI_TOKEN_MULTI_WRITE:
|
||||
DPRINTF("Start write block\n");
|
||||
s->mode = SSI_SD_DATA_WRITE;
|
||||
return SSI_DUMMY;
|
||||
case SSI_TOKEN_STOP_TRAN:
|
||||
DPRINTF("Stop multiple write\n");
|
||||
|
||||
/* manually issue cmd12 to stop the transfer */
|
||||
request.cmd = 12;
|
||||
request.arg = 0;
|
||||
s->arglen = sdbus_do_command(&s->sdbus, &request, longresp);
|
||||
if (s->arglen <= 0) {
|
||||
s->arglen = 1;
|
||||
/* a zero value indicates the card is busy */
|
||||
s->response[0] = 0;
|
||||
DPRINTF("SD card busy\n");
|
||||
} else {
|
||||
s->arglen = 1;
|
||||
/* a non-zero value indicates the card is ready */
|
||||
s->response[0] = SSI_DUMMY;
|
||||
}
|
||||
|
||||
return SSI_DUMMY;
|
||||
}
|
||||
|
||||
s->cmd = val & 0x3f;
|
||||
s->mode = SSI_SD_CMDARG;
|
||||
s->arglen = 0;
|
||||
return SSI_DUMMY;
|
||||
case SSI_SD_CMDARG:
|
||||
if (s->arglen == 4) {
|
||||
SDRequest request;
|
||||
uint8_t longresp[16];
|
||||
/* FIXME: Check CRC. */
|
||||
request.cmd = s->cmd;
|
||||
request.arg = ldl_be_p(s->cmdarg);
|
||||
@ -118,9 +176,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
s->arglen = 1;
|
||||
s->response[0] = 4;
|
||||
DPRINTF("SD command failed\n");
|
||||
} else if (s->cmd == 58) {
|
||||
/* CMD58 returns R3 response (OCR) */
|
||||
DPRINTF("Returned OCR\n");
|
||||
} else if (s->cmd == 8 || s->cmd == 58) {
|
||||
/* CMD8/CMD58 returns R3/R7 response */
|
||||
DPRINTF("Returned R3/R7\n");
|
||||
s->arglen = 5;
|
||||
s->response[0] = 1;
|
||||
memcpy(&s->response[1], longresp, 4);
|
||||
@ -136,6 +194,12 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
/* CMD13 returns a 2-byte statuse work. Other commands
|
||||
only return the first byte. */
|
||||
s->arglen = (s->cmd == 13) ? 2 : 1;
|
||||
|
||||
/* handle R1b */
|
||||
if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) {
|
||||
s->stopping = 1;
|
||||
}
|
||||
|
||||
cardstatus = ldl_be_p(longresp);
|
||||
status = 0;
|
||||
if (((cardstatus >> 9) & 0xf) < 4)
|
||||
@ -185,14 +249,15 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
s->mode = SSI_SD_RESPONSE;
|
||||
return SSI_DUMMY;
|
||||
case SSI_SD_RESPONSE:
|
||||
if (s->stopping) {
|
||||
s->stopping = 0;
|
||||
return SSI_DUMMY;
|
||||
}
|
||||
if (s->response_pos < s->arglen) {
|
||||
DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
|
||||
return s->response[s->response_pos++];
|
||||
}
|
||||
if (s->stopping) {
|
||||
s->stopping = 0;
|
||||
s->mode = SSI_SD_CMD;
|
||||
return SSI_DUMMY;
|
||||
}
|
||||
if (sdbus_data_ready(&s->sdbus)) {
|
||||
DPRINTF("Data read\n");
|
||||
s->mode = SSI_SD_DATA_START;
|
||||
@ -212,8 +277,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
return SSI_TOKEN_SINGLE;
|
||||
case SSI_SD_DATA_READ:
|
||||
val = sdbus_read_byte(&s->sdbus);
|
||||
s->read_bytes++;
|
||||
s->crc16 = crc_ccitt_false(s->crc16, (uint8_t *)&val, 1);
|
||||
if (!sdbus_data_ready(&s->sdbus)) {
|
||||
if (!sdbus_data_ready(&s->sdbus) || s->read_bytes == 512) {
|
||||
DPRINTF("Data read end\n");
|
||||
s->mode = SSI_SD_DATA_CRC16;
|
||||
}
|
||||
@ -224,10 +290,36 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
|
||||
s->response_pos++;
|
||||
if (s->response_pos == 2) {
|
||||
DPRINTF("CRC16 read end\n");
|
||||
s->mode = SSI_SD_CMD;
|
||||
if (s->read_bytes == 512 && s->cmd != 17) {
|
||||
s->mode = SSI_SD_PREP_DATA;
|
||||
} else {
|
||||
s->mode = SSI_SD_CMD;
|
||||
}
|
||||
s->read_bytes = 0;
|
||||
s->response_pos = 0;
|
||||
}
|
||||
return val;
|
||||
case SSI_SD_DATA_WRITE:
|
||||
sdbus_write_byte(&s->sdbus, val);
|
||||
s->write_bytes++;
|
||||
if (!sdbus_receive_ready(&s->sdbus) || s->write_bytes == 512) {
|
||||
DPRINTF("Data write end\n");
|
||||
s->mode = SSI_SD_SKIP_CRC16;
|
||||
s->response_pos = 0;
|
||||
}
|
||||
return val;
|
||||
case SSI_SD_SKIP_CRC16:
|
||||
/* we don't verify the crc16 */
|
||||
s->response_pos++;
|
||||
if (s->response_pos == 2) {
|
||||
DPRINTF("CRC16 receive end\n");
|
||||
s->mode = SSI_SD_RESPONSE;
|
||||
s->write_bytes = 0;
|
||||
s->arglen = 1;
|
||||
s->response[0] = DATA_RESPONSE_ACCEPTED;
|
||||
s->response_pos = 0;
|
||||
}
|
||||
return SSI_DUMMY;
|
||||
}
|
||||
/* Should never happen. */
|
||||
return SSI_DUMMY;
|
||||
@ -237,7 +329,7 @@ static int ssi_sd_post_load(void *opaque, int version_id)
|
||||
{
|
||||
ssi_sd_state *s = (ssi_sd_state *)opaque;
|
||||
|
||||
if (s->mode > SSI_SD_DATA_CRC16) {
|
||||
if (s->mode > SSI_SD_SKIP_CRC16) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (s->mode == SSI_SD_CMDARG &&
|
||||
@ -255,8 +347,8 @@ static int ssi_sd_post_load(void *opaque, int version_id)
|
||||
|
||||
static const VMStateDescription vmstate_ssi_sd = {
|
||||
.name = "ssi_sd",
|
||||
.version_id = 5,
|
||||
.minimum_version_id = 5,
|
||||
.version_id = 7,
|
||||
.minimum_version_id = 7,
|
||||
.post_load = ssi_sd_post_load,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT32(mode, ssi_sd_state),
|
||||
@ -264,6 +356,8 @@ static const VMStateDescription vmstate_ssi_sd = {
|
||||
VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4),
|
||||
VMSTATE_UINT8_ARRAY(response, ssi_sd_state, 5),
|
||||
VMSTATE_UINT16(crc16, ssi_sd_state),
|
||||
VMSTATE_INT32(read_bytes, ssi_sd_state),
|
||||
VMSTATE_INT32(write_bytes, ssi_sd_state),
|
||||
VMSTATE_INT32(arglen, ssi_sd_state),
|
||||
VMSTATE_INT32(response_pos, ssi_sd_state),
|
||||
VMSTATE_INT32(stopping, ssi_sd_state),
|
||||
@ -316,6 +410,8 @@ static void ssi_sd_reset(DeviceState *dev)
|
||||
memset(s->cmdarg, 0, sizeof(s->cmdarg));
|
||||
memset(s->response, 0, sizeof(s->response));
|
||||
s->crc16 = 0;
|
||||
s->read_bytes = 0;
|
||||
s->write_bytes = 0;
|
||||
s->arglen = 0;
|
||||
s->response_pos = 0;
|
||||
s->stopping = 0;
|
||||
|
@ -116,6 +116,7 @@ struct SDCardClass {
|
||||
* Return: byte value read
|
||||
*/
|
||||
uint8_t (*read_byte)(SDState *sd);
|
||||
bool (*receive_ready)(SDState *sd);
|
||||
bool (*data_ready)(SDState *sd);
|
||||
void (*set_voltage)(SDState *sd, uint16_t millivolts);
|
||||
uint8_t (*get_dat_lines)(SDState *sd);
|
||||
@ -187,6 +188,7 @@ void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length);
|
||||
* Read multiple bytes of data on the data lines of a SD bus.
|
||||
*/
|
||||
void sdbus_read_data(SDBus *sdbus, void *buf, size_t length);
|
||||
bool sdbus_receive_ready(SDBus *sd);
|
||||
bool sdbus_data_ready(SDBus *sd);
|
||||
bool sdbus_get_inserted(SDBus *sd);
|
||||
bool sdbus_get_readonly(SDBus *sd);
|
||||
|
Loading…
Reference in New Issue
Block a user