aboutsummaryrefslogtreecommitdiff
path: root/hw/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/sd.c')
-rw-r--r--hw/sd.c130
1 files changed, 103 insertions, 27 deletions
diff --git a/hw/sd.c b/hw/sd.c
index d59c4bf56..130072fd7 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -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);