diff options
Diffstat (limited to 'hw/sd.c')
-rw-r--r-- | hw/sd.c | 130 |
1 files changed, 103 insertions, 27 deletions
@@ -29,6 +29,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "hw.h" +#include "block.h" #include "sd.h" //#define DEBUG_SD 1 @@ -85,14 +87,14 @@ struct SDState { int pwd_len; int function_group[6]; + int spi; int current_cmd; int blk_written; uint32_t data_start; uint32_t data_offset; uint8_t data[512]; - void (*readonly_cb)(void *, int); - void (*inserted_cb)(void *, int); - void *opaque; + qemu_irq readonly_cb; + qemu_irq inserted_cb; BlockDriverState *bdrv; }; @@ -187,7 +189,7 @@ static uint16_t sd_crc16(void *message, size_t width) static void sd_set_ocr(SDState *sd) { - sd->ocr = 0x80fffff0; + sd->ocr = 0x80ffff80; } static void sd_set_scr(SDState *sd) @@ -307,8 +309,8 @@ static int sd_req_crc_validate(struct sd_request_s *req) return sd_crc7(buffer, 5) != req->crc; /* TODO */ } -void sd_response_r1_make(SDState *sd, - uint8_t *response, uint32_t last_status) +static void sd_response_r1_make(SDState *sd, + uint8_t *response, uint32_t last_status) { uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND; uint32_t status; @@ -322,7 +324,7 @@ void sd_response_r1_make(SDState *sd, response[3] = (status >> 0) & 0xff; } -void sd_response_r3_make(SDState *sd, uint8_t *response) +static void sd_response_r3_make(SDState *sd, uint8_t *response) { response[0] = (sd->ocr >> 24) & 0xff; response[1] = (sd->ocr >> 16) & 0xff; @@ -330,7 +332,7 @@ void sd_response_r3_make(SDState *sd, uint8_t *response) response[3] = (sd->ocr >> 0) & 0xff; } -void sd_response_r6_make(SDState *sd, uint8_t *response) +static void sd_response_r6_make(SDState *sd, uint8_t *response) { uint16_t arg; uint16_t status; @@ -372,9 +374,10 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) sd->bdrv = bdrv; + if (sd->wp_groups) + qemu_free(sd->wp_groups); sd->wp_switch = bdrv_is_read_only(bdrv); sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect); - memset(sd->wp_groups, 0, sizeof(int) * sect); memset(sd->function_group, 0, sizeof(int) * 6); sd->erase_start = 0; sd->erase_end = 0; @@ -386,36 +389,34 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) static void sd_cardchange(void *opaque) { SDState *sd = opaque; - if (sd->inserted_cb) - sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv)); + qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv)); if (bdrv_is_inserted(sd->bdrv)) { sd_reset(sd, sd->bdrv); - if (sd->readonly_cb) - sd->readonly_cb(sd->opaque, sd->wp_switch); + qemu_set_irq(sd->readonly_cb, sd->wp_switch); } } -SDState *sd_init(BlockDriverState *bs) +/* 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; sd = (SDState *) qemu_mallocz(sizeof(SDState)); + sd->spi = is_spi; sd_reset(sd, bs); + bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd); return sd; } -void sd_set_cb(SDState *sd, void *opaque, - void (*readonly_cb)(void *, int), - void (*inserted_cb)(void *, int)) +void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) { - sd->opaque = opaque; - sd->readonly_cb = readonly_cb; - sd->inserted_cb = inserted_cb; - if (sd->readonly_cb) - sd->readonly_cb(sd->opaque, bdrv_is_read_only(sd->bdrv)); - if (sd->inserted_cb) - sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv)); - bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd); + sd->readonly_cb = readonly; + sd->inserted_cb = insert; + qemu_set_irq(readonly, bdrv_is_read_only(sd->bdrv)); + qemu_set_irq(insert, bdrv_is_inserted(sd->bdrv)); } static void sd_erase(SDState *sd) @@ -572,16 +573,25 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, case 0: /* CMD0: GO_IDLE_STATE */ switch (sd->state) { case sd_inactive_state: - return sd_r0; + return sd->spi ? sd_r1 : sd_r0; default: sd->state = sd_idle_state; sd_reset(sd, sd->bdrv); - return sd_r0; + return sd->spi ? sd_r1 : sd_r0; } break; + case 1: /* CMD1: SEND_OP_CMD */ + if (!sd->spi) + goto bad_cmd; + + sd->state = sd_transfer_state; + return sd_r1; + case 2: /* CMD2: ALL_SEND_CID */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_ready_state: sd->state = sd_identification_state; @@ -593,6 +603,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 3: /* CMD3: SEND_RELATIVE_ADDR */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_identification_state: case sd_standby_state: @@ -606,6 +618,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 4: /* CMD4: SEND_DSR */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_standby_state: break; @@ -616,6 +630,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 6: /* CMD6: SWITCH_FUNCTION */ + if (sd->spi) + goto bad_cmd; switch (sd->mode) { case sd_data_transfer_mode: sd_function_switch(sd, req.arg); @@ -630,6 +646,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 7: /* CMD7: SELECT/DESELECT_CARD */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_standby_state: if (sd->rca != rca) @@ -673,6 +691,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, return sd_r2_s; + case sd_transfer_state: + if (!sd->spi) + break; + sd->state = sd_sendingdata_state; + memcpy(sd->data, sd->csd, 16); + sd->data_start = req.arg; + sd->data_offset = 0; + return sd_r1; + default: break; } @@ -686,12 +713,23 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, return sd_r2_i; + case sd_transfer_state: + if (!sd->spi) + break; + sd->state = sd_sendingdata_state; + memcpy(sd->data, sd->cid, 16); + sd->data_start = req.arg; + sd->data_offset = 0; + return sd_r1; + default: break; } break; case 11: /* CMD11: READ_DAT_UNTIL_STOP */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_transfer_state: sd->state = sd_sendingdata_state; @@ -738,6 +776,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 15: /* CMD15: GO_INACTIVE_STATE */ + if (sd->spi) + goto bad_cmd; switch (sd->mode) { case sd_data_transfer_mode: if (sd->rca != rca) @@ -801,8 +841,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + if (sd->spi) + goto unimplemented_cmd; switch (sd->state) { case sd_transfer_state: + /* Writing in SPI mode not implemented. */ + if (sd->spi) + break; sd->state = sd_receivingdata_state; sd->data_start = req.arg; sd->data_offset = 0; @@ -822,8 +867,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + if (sd->spi) + goto unimplemented_cmd; switch (sd->state) { case sd_transfer_state: + /* Writing in SPI mode not implemented. */ + if (sd->spi) + break; sd->state = sd_receivingdata_state; sd->data_start = req.arg; sd->data_offset = 0; @@ -843,6 +893,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 26: /* CMD26: PROGRAM_CID */ + if (sd->spi) + goto bad_cmd; switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -856,6 +908,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 27: /* CMD27: PROGRAM_CSD */ + if (sd->spi) + goto unimplemented_cmd; switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -967,6 +1021,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ + if (sd->spi) + goto unimplemented_cmd; switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1005,10 +1061,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; default: + bad_cmd: sd->card_status |= ILLEGAL_COMMAND; printf("SD: Unknown CMD%i\n", req.cmd); return sd_r0; + + unimplemented_cmd: + /* Commands that are recognised but not yet implemented in SPI mode. */ + sd->card_status |= ILLEGAL_COMMAND; + printf ("SD: CMD%i not implemented in SPI mode\n", req.cmd); + return sd_r0; } sd->card_status |= ILLEGAL_COMMAND; @@ -1074,6 +1137,11 @@ static sd_rsp_type_t sd_app_command(SDState *sd, break; case 41: /* ACMD41: SD_APP_OP_COND */ + if (sd->spi) { + /* SEND_OP_CMD */ + sd->state = sd_transfer_state; + return sd_r1; + } switch (sd->state) { case sd_idle_state: /* We accept any voltage. 10000 V is nothing. */ @@ -1419,6 +1487,14 @@ uint8_t sd_read_data(SDState *sd) sd->state = sd_transfer_state; break; + case 9: /* CMD9: SEND_CSD */ + case 10: /* CMD10: SEND_CID */ + ret = sd->data[sd->data_offset ++]; + + if (sd->data_offset >= 16) + sd->state = sd_transfer_state; + break; + case 11: /* CMD11: READ_DAT_UNTIL_STOP */ if (sd->data_offset == 0) BLK_READ_BLOCK(sd->data_start, sd->blk_len); |