From: Juha Riihimäki <juha.riihim...@nokia.com> - reset support - add high capacity mmc support - sd: improve mmc emulation - make sd emulation work as mmc emulation as well - handle SD CMD5 without error messages - remove couple of unnecessary error messages - Fix block count for OMAP3 MMC emulation
Signed-Off-By: Riku Voipio <riku.voi...@nokia.com> Signed-Off-By: Juha Riihimäki <juha.riihim...@nokia.com> --- hw/sd.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 334 insertions(+), 56 deletions(-) diff --git a/hw/sd.c b/hw/sd.c index cc2839d..3a395e7 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -4,6 +4,7 @@ * * Copyright (c) 2006 Andrzej Zaborowski <bal...@zabor.org> * Copyright (c) 2007 CodeSourcery + * Copyright (c) 2009 Nokia Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -75,6 +76,7 @@ struct SDState { uint8_t scr[8]; uint8_t cid[16]; uint8_t csd[16]; + uint8_t ext_csd[512]; uint16_t rca; uint32_t card_status; uint8_t sd_status[64]; @@ -89,7 +91,7 @@ struct SDState { int pwd_len; int function_group[6]; - int spi; + int spi, mmc; int current_cmd; int blk_written; uint64_t data_start; @@ -101,6 +103,7 @@ struct SDState { uint8_t *buf; int enable; + int buswidth, highspeed; }; static void sd_set_status(SDState *sd) @@ -131,9 +134,9 @@ static void sd_set_status(SDState *sd) } static const sd_cmd_type_t sd_cmd_type[64] = { - sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, + sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_ac, sd_none, sd_ac, sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, - sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, + sd_ac, sd_adtc, sd_adtc, sd_none, sd_adtc, sd_none, sd_none, sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, @@ -200,8 +203,8 @@ static void sd_set_ocr(SDState *sd) static void sd_set_scr(SDState *sd) { - sd->scr[0] = 0x00; /* SCR Structure */ - sd->scr[1] = 0x2f; /* SD Security Support */ + sd->scr[0] = 0x00; /* SCR v1.0, SD spec v1.0/1.01 */ + sd->scr[1] = 0x25; /* erase=0, SD security v1.01, 1bit/4bit bus width */ sd->scr[2] = 0x00; sd->scr[3] = 0x00; sd->scr[4] = 0x00; @@ -212,7 +215,7 @@ static void sd_set_scr(SDState *sd) #define MID 0xaa #define OID "XY" -#define PNM "QEMU!" +#define PNM "QEMU!!" #define PRV 0x01 #define MDT_YR 2006 #define MDT_MON 2 @@ -227,14 +230,23 @@ static void sd_set_cid(SDState *sd) sd->cid[5] = PNM[2]; sd->cid[6] = PNM[3]; sd->cid[7] = PNM[4]; - sd->cid[8] = PRV; /* Fake product revision (PRV) */ + if (sd->mmc) { + sd->cid[8] = PNM[5]; + } else { + sd->cid[8] = PRV; /* Fake product revision (PRV) */ + } sd->cid[9] = 0xde; /* Fake serial number (PSN) */ sd->cid[10] = 0xad; sd->cid[11] = 0xbe; sd->cid[12] = 0xef; - sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ - ((MDT_YR - 2000) / 10); - sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; + if (sd->mmc) { + sd->cid[13] = 0x55; + sd->cid[14] = ((MDT_MON) << 4) | (MDT_YR - 1997); + } else { + sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ + ((MDT_YR - 2000) / 10); + sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; + } sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; } @@ -256,7 +268,12 @@ static void sd_set_csd(SDState *sd, uint64_t size) uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; if (size <= 0x40000000) { /* Standard Capacity SD */ - sd->csd[0] = 0x00; /* CSD structure */ + if (sd->mmc) { + sd->csd[0] = 0x80 | /* CSD structure: v1.2 */ + 0x0c; /* MMC v3.x */ + } else { + sd->csd[0] = 0x00; /* CSD structure: v0 */ + } sd->csd[1] = 0x26; /* Data read access-time-1 */ sd->csd[2] = 0x00; /* Data read access-time-2 */ sd->csd[3] = 0x5a; /* Max. data transfer rate */ @@ -282,25 +299,52 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[14] = 0x00; /* File format group */ sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; } else { /* SDHC */ - size /= 512 * 1024; - size -= 1; - sd->csd[0] = 0x40; + if (sd->mmc) { + sd->csd[0] = 0x90; /* CSD structure v1.2, MMC v4.0/4.1 */ + } else { + sd->csd[0] = 0x40; /* CSD structure v1 */ + } sd->csd[1] = 0x0e; sd->csd[2] = 0x00; sd->csd[3] = 0x32; sd->csd[4] = 0x5b; sd->csd[5] = 0x59; - sd->csd[6] = 0x00; - sd->csd[7] = (size >> 16) & 0xff; - sd->csd[8] = (size >> 8) & 0xff; - sd->csd[9] = (size & 0xff); - sd->csd[10] = 0x7f; + if (sd->mmc) { + sd->csd[6] = 0x03; + sd->csd[7] = 0xff; + sd->csd[8] = 0xff; + sd->csd[9] = 0xff; + sd->csd[10] = 0xff; + } else { + size /= 512 * 1024; + size -= 1; + sd->csd[6] = 0x00; + sd->csd[7] = (size >> 16) & 0xff; + sd->csd[8] = (size >> 8) & 0xff; + sd->csd[9] = (size & 0xff); + sd->csd[10] = 0x7f; + } sd->csd[11] = 0x80; sd->csd[12] = 0x0a; sd->csd[13] = 0x40; sd->csd[14] = 0x00; sd->csd[15] = 0x00; - sd->ocr |= 1 << 30; /* High Capacity SD Memort Card */ + sd->ocr |= 1 << 30; /* High Capacity SD Memory Card */ + if (sd->mmc) { + size /= 512; + sd->buswidth = 1; /* 4bit mode */ + sd->highspeed = 0; + memset(sd->ext_csd, 0, 512); + sd->ext_csd[183] = sd->buswidth; + sd->ext_csd[185] = sd->highspeed; + sd->ext_csd[192] = 0x03; /* EXT_CSD v3 */ + sd->ext_csd[196] = 0x03; /* supports 26MHz and 52MHz */ + sd->ext_csd[212] = (size & 0xff); + sd->ext_csd[213] = (size >> 8) & 0xff; + sd->ext_csd[214] = (size >> 16) & 0xff; + sd->ext_csd[215] = (size >> 24) & 0xff; + sd->ext_csd[217] = 0x00; /* sleep/awake timeout */ + } } } @@ -382,13 +426,13 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) response[3] = (sd->vhs >> 0) & 0xff; } -static void sd_reset(SDState *sd, BlockDriverState *bdrv) +void sd_reset(SDState *sd) { uint64_t size; uint64_t sect; - if (bdrv) { - bdrv_get_geometry(bdrv, §); + if (sd->bdrv) { + bdrv_get_geometry(sd->bdrv, §); } else { sect = 0; } @@ -397,7 +441,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) size = sect + 1; sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; - + sd->state = sd_idle_state; sd->rca = 0x0000; sd_set_ocr(sd); @@ -407,11 +451,9 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) sd_set_cardstatus(sd); sd_set_sdstatus(sd); - sd->bdrv = bdrv; - if (sd->wp_groups) qemu_free(sd->wp_groups); - sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : 0; + sd->wp_switch = sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0; sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect); memset(sd->function_group, 0, sizeof(int) * 6); sd->erase_start = 0; @@ -426,27 +468,121 @@ static void sd_cardchange(void *opaque) SDState *sd = opaque; qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv)); if (bdrv_is_inserted(sd->bdrv)) { - sd_reset(sd, sd->bdrv); + sd_reset(sd); qemu_set_irq(sd->readonly_cb, sd->wp_switch); } } +static void sd_save_state(QEMUFile *f, void *opaque) +{ + struct SDState *s = (struct SDState *)opaque; + int i; + uint32_t wpgc = (s->size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; + char *filename; + + filename = qemu_mallocz(1024); + bdrv_get_backing_filename(s->bdrv, filename, 1024); + qemu_put_buffer(f, (uint8_t *)filename, 1024); + free(filename); + + qemu_put_sbe32(f, s->mode); + qemu_put_sbe32(f, s->state); + qemu_put_be32(f, s->ocr); + qemu_put_buffer(f, s->scr, sizeof(s->scr)); + qemu_put_buffer(f, s->cid, sizeof(s->cid)); + qemu_put_buffer(f, s->csd, sizeof(s->csd)); + qemu_put_be16(f, s->rca); + qemu_put_be32(f, s->card_status); + qemu_put_buffer(f, s->sd_status, sizeof(s->sd_status)); + qemu_put_be32(f, s->vhs); + for (i = 0; i < wpgc; i++) + qemu_put_sbe32(f, s->wp_groups[i]); + qemu_put_sbe32(f, s->blk_len); + qemu_put_be32(f, s->erase_start); + qemu_put_be32(f, s->erase_end); + qemu_put_buffer(f, s->pwd, sizeof(s->pwd)); + qemu_put_sbe32(f, s->pwd_len); + for (i = 0; i < 6; i++) + qemu_put_sbe32(f, s->function_group[i]); + qemu_put_sbe32(f, s->current_cmd); + qemu_put_sbe32(f, s->blk_written); + qemu_put_be32(f, s->data_start); + qemu_put_be32(f, s->data_offset); + qemu_put_buffer(f, s->data, sizeof(s->data)); + qemu_put_buffer(f, s->buf, 512); + qemu_put_sbe32(f, s->enable); +} + +static int sd_load_state(QEMUFile *f, void *opaque, int version_id) +{ + struct SDState *s = (struct SDState *)opaque; + int i; + uint32_t wpgc = (s->size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; + char *filename1, *filename2; + int result = 0; + + if (version_id) + return -EINVAL; + + filename1 = qemu_mallocz(1024); + filename2 = qemu_mallocz(1024); + bdrv_get_backing_filename(s->bdrv, filename1, 1024); + qemu_get_buffer(f, (uint8_t *)filename2, 1024); + if (!strcmp(filename1, filename2)) { + s->mode = qemu_get_sbe32(f); + s->state = qemu_get_sbe32(f); + s->ocr = qemu_get_be32(f); + qemu_get_buffer(f, s->scr, sizeof(s->scr)); + qemu_get_buffer(f, s->cid, sizeof(s->cid)); + qemu_get_buffer(f, s->csd, sizeof(s->csd)); + s->rca = qemu_get_be16(f); + s->card_status = qemu_get_be32(f); + qemu_get_buffer(f, s->sd_status, sizeof(s->sd_status)); + s->vhs = qemu_get_be32(f); + for (i = 0; i < wpgc; i++) + s->wp_groups[i] = qemu_get_sbe32(f); + s->blk_len = qemu_get_sbe32(f); + s->erase_start = qemu_get_be32(f); + s->erase_end = qemu_get_be32(f); + qemu_get_buffer(f, s->pwd, sizeof(s->pwd)); + s->pwd_len = qemu_get_sbe32(f); + for (i = 0; i < 6; i++) + s->function_group[i] = qemu_get_sbe32(f); + s->current_cmd = qemu_get_sbe32(f); + s->blk_written = qemu_get_sbe32(f); + s->data_start = qemu_get_be32(f); + s->data_offset = qemu_get_be32(f); + qemu_get_buffer(f, s->data, sizeof(s->data)); + qemu_get_buffer(f, s->buf, 512); + s->enable = qemu_get_sbe32(f); + } else + result = -EINVAL; + free(filename2); + free(filename1); + return result; +} + /* 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 board to ensure that ssi transfers only occur when the chip select is asserted. */ -SDState *sd_init(BlockDriverState *bs, int is_spi) +SDState *sd_init(BlockDriverState *bs, int is_spi, int is_mmc) { SDState *sd; + static int instance_number = 1; sd = (SDState *) qemu_mallocz(sizeof(SDState)); sd->buf = qemu_memalign(512, 512); sd->spi = is_spi; + sd->mmc = is_mmc; sd->enable = 1; - sd_reset(sd, bs); + sd->bdrv = bs; + sd_reset(sd); if (sd->bdrv) { bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd); } + register_savevm("sd", instance_number++, 0, + sd_save_state, sd_load_state, sd); return sd; } @@ -524,6 +660,46 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[66] = crc & 0xff; } +static void mmc_function_switch(SDState *sd, uint32_t arg) +{ + enum { + cmd_set = 0, + set_bits, + clear_bits, + write_byte, + + unknown + } mode = (arg >> 24); + if (mode >= unknown) { + fprintf(stderr, "%s: unknown mode 0x%02x\n", __FUNCTION__, mode); + } else { + if (mode == cmd_set) { + fprintf(stderr, "%s: command set change not implemented!\n", + __FUNCTION__); + } else { + uint8_t index = (arg >> 16) & 0xff; + /* ignore writes to read-only fields */ + if (index != 192 && index != 196 && + (index < 212 || index > 215)) { + uint8_t value = (arg >> 8) & 0xff; + switch (mode) { + case set_bits: + sd->ext_csd[index] |= value; + break; + case clear_bits: + sd->ext_csd[index] &= ~value; + break; + case write_byte: + sd->ext_csd[index] = value; + break; + default: + break; + } + } + } + } +} + static inline int sd_wp_addr(SDState *sd, uint32_t addr) { return sd->wp_groups[addr >> @@ -617,15 +793,21 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, default: sd->state = sd_idle_state; - sd_reset(sd, sd->bdrv); + sd_reset(sd); return sd->spi ? sd_r1 : sd_r0; } break; case 1: /* CMD1: SEND_OP_CMD */ + if (sd->mmc) { + if (sd->state == sd_idle_state) { + sd->state = sd_ready_state; + return sd_r3; + } + break; + } if (!sd->spi) goto bad_cmd; - sd->state = sd_transfer_state; return sd_r1; @@ -647,8 +829,12 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, goto bad_cmd; switch (sd->state) { case sd_identification_state: - case sd_standby_state: sd->state = sd_standby_state; + case sd_standby_state: + if (sd->mmc) { + sd->rca = req.arg >> 16; + return sd_r1; + } sd_set_rca(sd); return sd_r6; @@ -662,14 +848,25 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, goto bad_cmd; switch (sd->state) { case sd_standby_state: - break; + return sd_r0; default: break; } break; - case 5: /* CMD5: reserved for SDIO cards */ + case 5: /* CMD5: reserved for SDIO cards / SLEEP_AWAKE (MMC) */ + if (sd->mmc) { + if (sd->rca != rca) { + return sd_r0; + } + if (req.arg & (1 << 15)) { + sd->state = sd_transfer_state; + } else { + sd->state = sd_standby_state; + } + return sd_r1b; + } sd->card_status |= ILLEGAL_COMMAND; return sd_r0; @@ -678,11 +875,16 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, goto bad_cmd; switch (sd->mode) { case sd_data_transfer_mode: - sd_function_switch(sd, req.arg); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; + if (sd->mmc) { + mmc_function_switch(sd, req.arg); + return sd_r1b; + } else { + sd_function_switch(sd, req.arg); + sd->state = sd_sendingdata_state; + sd->data_start = 0; + sd->data_offset = 0; + return sd_r1; + } default: break; @@ -727,22 +929,30 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; - case 8: /* CMD8: SEND_IF_COND */ - /* Physical Layer Specification Version 2.00 command */ - switch (sd->state) { - case sd_idle_state: - sd->vhs = 0; + case 8: /* CMD8: SEND_IF_COND / SEND_EXT_CSD (MMC) */ + if (sd->mmc) { + sd->state = sd_sendingdata_state; + memcpy(sd->data, sd->ext_csd, 512); + sd->data_start = addr; + sd->data_offset = 0; + return sd_r1; + } else { + /* Physical Layer Specification Version 2.00 command */ + switch (sd->state) { + case sd_idle_state: + sd->vhs = 0; - /* No response if not exactly one VHS bit is set. */ - if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff))) - return sd->spi ? sd_r7 : sd_r0; + /* No response if not exactly one VHS bit is set. */ + if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff))) + return sd->spi ? sd_r7 : sd_r0; - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; + /* Accept. */ + sd->vhs = req.arg; + return sd_r7; - default: - break; + default: + break; + } } break; @@ -901,8 +1111,33 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; } break; + + /* Block write commands (Class 3) */ + case 20: /* CMD20: WRITE_DAT_UNTIL_STOP */ + if (sd->mmc) { + if (sd->state == sd_transfer_state) { + sd->state = sd_sendingdata_state; + sd->data_start = req.arg; + sd->data_offset = 0; + + if (sd->data_start + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + } + return sd_r0; + } + break; + } + goto bad_cmd; /* Block write commands (Class 4) */ + case 23: /* CMD23: SET_BLOCK_COUNT */ + if (sd->mmc) { + sd->card_status |= ILLEGAL_COMMAND; + fprintf(stderr, "%s: CMD23 not implemented\n", __FUNCTION__); + return sd_r0; + } + goto bad_cmd; + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ if (sd->spi) goto unimplemented_cmd; @@ -1042,6 +1277,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Erase commands (Class 5) */ case 32: /* CMD32: ERASE_WR_BLK_START */ + case 35: /* CMD35: ERASE_GROUP_START */ + if ((req.cmd == 35 && !sd->mmc) || + (req.cmd == 32 && sd->mmc)) { + goto bad_cmd; + } switch (sd->state) { case sd_transfer_state: sd->erase_start = req.arg; @@ -1053,6 +1293,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 33: /* CMD33: ERASE_WR_BLK_END */ + case 36: /* CMD36: ERASE_GROUP_END */ + if ((req.cmd == 36 && !sd->mmc) || + (req.cmd == 33 && sd->mmc)) { + goto bad_cmd; + } switch (sd->state) { case sd_transfer_state: sd->erase_end = req.arg; @@ -1081,6 +1326,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; } break; + + /* Class 9 */ + case 39: /* CMD39: FAST_IO */ + case 40: /* CMD40: GO_IRQ_STATE */ + if (sd->mmc) { + sd->card_status |= ILLEGAL_COMMAND; + fprintf(stderr, "%s: CMD%d not implemented\n", + __FUNCTION__, req.cmd); + return sd_r0; + } + goto bad_cmd; /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ @@ -1336,8 +1592,8 @@ int sd_do_command(SDState *sd, SDRequest *req, int i; DPRINTF("Response:"); for (i = 0; i < rsplen; i++) - printf(" %02x", response[i]); - printf(" state %d\n", sd->state); + fprintf(stderr, " %02x", response[i]); + fprintf(stderr, " state %d\n", sd->state); } else { DPRINTF("No response %d\n", sd->state); } @@ -1434,6 +1690,11 @@ void sd_write_data(SDState *sd, uint8_t value) } break; + case 20: /* CMD20: WRITE_DAT_UNTIL_STOP */ + if (!sd->mmc) { + goto unknown_command; + } + /* fall through */ case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ sd->data[sd->data_offset ++] = value; if (sd->data_offset >= sd->blk_len) { @@ -1521,6 +1782,7 @@ void sd_write_data(SDState *sd, uint8_t value) break; default: + unknown_command: fprintf(stderr, "sd_write_data: unknown command\n"); break; } @@ -1536,7 +1798,7 @@ uint8_t sd_read_data(SDState *sd) return 0x00; if (sd->state != sd_sendingdata_state) { - fprintf(stderr, "sd_read_data: not in Sending-Data state\n"); + fprintf(stderr, "sd_read_data: not in Sending-Data state (state=%d)\n", sd->state); return 0x00; } @@ -1553,6 +1815,16 @@ uint8_t sd_read_data(SDState *sd) sd->state = sd_transfer_state; break; + case 8: /* CMD8: SEND_EXT_CSD (MMC only) */ + if (sd->mmc) { + ret = sd->data[sd->data_offset++]; + if (sd->data_offset >= 512) { + sd->state = sd_transfer_state; + } + } else { + goto unknown_command; + } + break; case 9: /* CMD9: SEND_CSD */ case 10: /* CMD10: SEND_CID */ ret = sd->data[sd->data_offset ++]; @@ -1638,6 +1910,7 @@ uint8_t sd_read_data(SDState *sd) break; default: + unknown_command: fprintf(stderr, "sd_read_data: unknown command\n"); return 0x00; } @@ -1654,3 +1927,8 @@ void sd_enable(SDState *sd, int enable) { sd->enable = enable; } + +int sd_is_mmc(SDState *sd) +{ + return sd->mmc; +} -- 1.6.5