Commit 775616c3 authored by pbrook's avatar pbrook
Browse files

Partial SD card SPI mode support.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3731 c046a42c-6fe2-441c-8c8c-71466251a162
parent f3b9f954
......@@ -57,6 +57,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
OBJS+=sd.o ssi-sd.o
ifdef CONFIG_WIN32
OBJS+=tap-win32.o
......
......@@ -488,7 +488,7 @@ endif
ifeq ($(TARGET_BASE_ARCH), arm)
VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
VL_OBJS+= versatile_pci.o sd.o ptimer.o
VL_OBJS+= versatile_pci.o ptimer.o
VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
VL_OBJS+= pl061.o
......
......@@ -525,7 +525,7 @@ struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
cpu_register_physical_memory(s->base, 0x800, iomemtype);
/* Instantiate the storage */
s->card = sd_init(bd);
s->card = sd_init(bd, 0);
return s;
}
......
......@@ -48,6 +48,7 @@ typedef struct {
uint8_t slr;
uint8_t den;
uint8_t cr;
uint8_t float_high;
qemu_irq irq;
qemu_irq out[8];
} pl061_state;
......@@ -56,18 +57,22 @@ static void pl061_update(pl061_state *s)
{
uint8_t changed;
uint8_t mask;
uint8_t out;
int i;
changed = s->old_data ^ s->data;
/* Outputs float high. */
/* FIXME: This is board dependent. */
out = (s->data & s->dir) | ~s->dir;
changed = s->old_data ^ out;
if (!changed)
return;
s->old_data = s->data;
s->old_data = out;
for (i = 0; i < 8; i++) {
mask = 1 << i;
if ((changed & mask & s->dir) && s->out) {
DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0);
qemu_set_irq(s->out[i], (s->data & mask) != 0);
if ((changed & mask) && s->out) {
DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
qemu_set_irq(s->out[i], (out & mask) != 0);
}
}
......
......@@ -458,7 +458,7 @@ void pl181_init(uint32_t base, BlockDriverState *bd,
pl181_writefn, s);
cpu_register_physical_memory(base, 0x00001000, iomemtype);
s->base = base;
s->card = sd_init(bd);
s->card = sd_init(bd, 0);
s->irq[0] = irq0;
s->irq[1] = irq1;
qemu_register_reset(pl181_reset, s);
......
......@@ -21,13 +21,15 @@ void pl011_init(uint32_t base, qemu_irq irq, CharDriverState *chr,
enum pl011_type type);
/* pl022.c */
void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
typedef int (*ssi_xfer_cb)(void *, int);
void pl022_init(uint32_t base, qemu_irq irq, ssi_xfer_cb xfer_cb,
void *opaque);
/* pl050.c */
void pl050_init(uint32_t base, qemu_irq irq, int is_mouse);
/* pl061.c */
void pl061_float_high(void *opaque, uint8_t mask);
qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out);
/* pl080.c */
......
......@@ -538,7 +538,7 @@ struct pxa2xx_mmci_s *pxa2xx_mmci_init(target_phys_addr_t base,
cpu_register_physical_memory(base, 0x00100000, iomemtype);
/* Instantiate the actual storage */
s->card = sd_init(bd);
s->card = sd_init(bd, 1);
register_savevm("pxa2xx_mmci", 0, 0,
pxa2xx_mmci_save, pxa2xx_mmci_load, s);
......
......@@ -87,6 +87,7 @@ struct SDState {
int pwd_len;
int function_group[6];
int spi;
int current_cmd;
int blk_written;
uint32_t data_start;
......@@ -395,11 +396,16 @@ static void sd_cardchange(void *opaque)
}
}
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 ot 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;
......@@ -567,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;
......@@ -588,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:
......@@ -601,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;
......@@ -611,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);
......@@ -625,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)
......@@ -668,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;
}
......@@ -681,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;
......@@ -733,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)
......@@ -796,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;
......@@ -817,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;
......@@ -838,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;
......@@ -851,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;
......@@ -962,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;
......@@ -1000,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;
......@@ -1069,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. */
......@@ -1414,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);
......
......@@ -67,7 +67,7 @@ struct sd_request_s {
typedef struct SDState SDState;
SDState *sd_init(BlockDriverState *bs);
SDState *sd_init(BlockDriverState *bs, int is_spi);
int sd_do_command(SDState *sd, struct sd_request_s *req,
uint8_t *response);
void sd_write_data(SDState *sd, uint8_t value);
......@@ -75,4 +75,8 @@ uint8_t sd_read_data(SDState *sd);
void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert);
int sd_data_ready(SDState *sd);
/* ssi-sd.c */
int ssi_sd_xfer(void *opaque, int val);
void *ssi_sd_init(BlockDriverState *bs);
#endif /* __hw_sd_h */
......@@ -157,6 +157,9 @@ int ssd0323_xfer_ssi(void *opaque, int data)
case 0xe3: /* NOP. */
DATA(0);
break;
case 0xff: /* Nasty hack because we don't handle chip selects
properly. */
break;
default:
BADF("Unknown command: 0x%x\n", data);
}
......
/*
* SSI to SD card adapter.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
#include "hw.h"
#include "sd.h"
//#define DEBUG_SSI_SD 1
#ifdef DEBUG_SSI_SD
#define DPRINTF(fmt, args...) \
do { printf("ssi_sd: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssi_sd: error: " fmt , ##args); exit(1);} while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "ssi_sd: error: " fmt , ##args);} while (0)
#endif
typedef enum {
SSI_SD_CMD,
SSI_SD_CMDARG,
SSI_SD_RESPONSE,
SSI_SD_DATA_START,
SSI_SD_DATA_READ,
} ssi_sd_mode;
typedef struct {
ssi_sd_mode mode;
int cmd;
uint8_t cmdarg[4];
uint8_t response[5];
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
int ssi_sd_xfer(void *opaque, int val)
{
ssi_sd_state *s = (ssi_sd_state *)opaque;
/* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */
if (s->mode == SSI_SD_DATA_READ && val == 0x4d) {
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 == 0xff) {
DPRINTF("NULL command\n");
return 0xff;
}
s->cmd = val & 0x3f;
s->mode = SSI_SD_CMDARG;
s->arglen = 0;
return 0xff;
case SSI_SD_CMDARG:
if (s->arglen == 4) {
struct sd_request_s request;
uint8_t longresp[16];
/* FIXME: Check CRC. */
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);
if (s->arglen <= 0) {
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");
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;
} else {
s->cmdarg[s->arglen++] = val;
}
return 0xff;
case SSI_SD_RESPONSE:
if (s->stopping) {
s->stopping = 0;
return 0xff;
}
if (s->response_pos < s->arglen) {
DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
return s->response[s->response_pos++];
}
if (sd_data_ready(s->sd)) {
DPRINTF("Data read\n");
s->mode = SSI_SD_DATA_START;
} else {
DPRINTF("End of command\n");
s->mode = SSI_SD_CMD;
}
return 0xff;
case SSI_SD_DATA_START:
DPRINTF("Start read block\n");
s->mode = SSI_SD_DATA_READ;
return 0xfe;
case SSI_SD_DATA_READ:
val = sd_read_data(s->sd);
if (!sd_data_ready(s->sd)) {
DPRINTF("Data read end\n");
s->mode = SSI_SD_CMD;
}
return val;
}
/* Should never happen. */
return 0xff;
}
void *ssi_sd_init(BlockDriverState *bs)
{
ssi_sd_state *s;
s = (ssi_sd_state *)qemu_mallocz(sizeof(ssi_sd_state));
s->mode = SSI_SD_CMD;
s->sd = sd_init(bs, 1);
return s;
}
......@@ -14,6 +14,7 @@
#include "qemu-timer.h"
#include "i2c.h"
#include "net.h"
#include "sd.h"
#include "sysemu.h"
#include "boards.h"
......@@ -1000,6 +1001,51 @@ static qemu_irq stellaris_adc_init(uint32_t base, qemu_irq irq)
return qi[0];
}
/* Some boards have both an OLED controller and SD card connected to
the same SSI port, with the SD card chip select connected to a
GPIO pin. Technically the OLED chip select is connected to the SSI
Fss pin. We do not bother emulating that as both devices should
never be selected simultaneously, and our OLED controller ignores stray
0xff commands that occur when deselecting the SD card. */
typedef struct {
ssi_xfer_cb xfer_cb[2];
void *opaque[2];
qemu_irq irq;
int current_dev;
} stellaris_ssi_bus_state;
static void stellaris_ssi_bus_select(void *opaque, int irq, int level)
{
stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
s->current_dev = level;
}
static int stellaris_ssi_bus_xfer(void *opaque, int val)
{
stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
return s->xfer_cb[s->current_dev](s->opaque[s->current_dev], val);
}
static void *stellaris_ssi_bus_init(qemu_irq *irqp,
ssi_xfer_cb cb0, void *opaque0,
ssi_xfer_cb cb1, void *opaque1)
{
qemu_irq *qi;
stellaris_ssi_bus_state *s;
s = (stellaris_ssi_bus_state *)qemu_mallocz(sizeof(stellaris_ssi_bus_state));
s->xfer_cb[0] = cb0;
s->opaque[0] = opaque0;
s->xfer_cb[1] = cb1;
s->opaque[1] = opaque1;
qi = qemu_allocate_irqs(stellaris_ssi_bus_select, s, 1);
*irqp = *qi;
return s;
}
/* Board init. */
static stellaris_board_info stellaris_boards[] = {
{ "LM3S811EVB",
......@@ -1085,9 +1131,19 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
if (board->dc2 & (1 << 4)) {