mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 09:43:56 -06:00
hw: move target-independent files to subdirectories
This patch tackles all files that are compiled once, moving them to subdirectories of hw/. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
ce3b494cb5
commit
49ab747f66
227 changed files with 205 additions and 208 deletions
|
@ -0,0 +1,4 @@
|
|||
common-obj-$(CONFIG_PL181) += pl181.o
|
||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||
common-obj-$(CONFIG_SD) += sd.o
|
||||
common-obj-$(CONFIG_SDHCI) += sdhci.o
|
515
hw/sd/pl181.c
Normal file
515
hw/sd/pl181.c
Normal file
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* Arm PrimeCell PL181 MultiMedia Card Interface
|
||||
*
|
||||
* Copyright (c) 2007 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/sd.h"
|
||||
|
||||
//#define DEBUG_PL181 1
|
||||
|
||||
#ifdef DEBUG_PL181
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define PL181_FIFO_LEN 16
|
||||
|
||||
typedef struct {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
SDState *card;
|
||||
uint32_t clock;
|
||||
uint32_t power;
|
||||
uint32_t cmdarg;
|
||||
uint32_t cmd;
|
||||
uint32_t datatimer;
|
||||
uint32_t datalength;
|
||||
uint32_t respcmd;
|
||||
uint32_t response[4];
|
||||
uint32_t datactrl;
|
||||
uint32_t datacnt;
|
||||
uint32_t status;
|
||||
uint32_t mask[2];
|
||||
int32_t fifo_pos;
|
||||
int32_t fifo_len;
|
||||
/* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
|
||||
while it is reading the FIFO. We hack around this be defering
|
||||
subsequent transfers until after the driver polls the status word.
|
||||
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
|
||||
*/
|
||||
int32_t linux_hack;
|
||||
uint32_t fifo[PL181_FIFO_LEN];
|
||||
qemu_irq irq[2];
|
||||
/* GPIO outputs for 'card is readonly' and 'card inserted' */
|
||||
qemu_irq cardstatus[2];
|
||||
} pl181_state;
|
||||
|
||||
static const VMStateDescription vmstate_pl181 = {
|
||||
.name = "pl181",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(clock, pl181_state),
|
||||
VMSTATE_UINT32(power, pl181_state),
|
||||
VMSTATE_UINT32(cmdarg, pl181_state),
|
||||
VMSTATE_UINT32(cmd, pl181_state),
|
||||
VMSTATE_UINT32(datatimer, pl181_state),
|
||||
VMSTATE_UINT32(datalength, pl181_state),
|
||||
VMSTATE_UINT32(respcmd, pl181_state),
|
||||
VMSTATE_UINT32_ARRAY(response, pl181_state, 4),
|
||||
VMSTATE_UINT32(datactrl, pl181_state),
|
||||
VMSTATE_UINT32(datacnt, pl181_state),
|
||||
VMSTATE_UINT32(status, pl181_state),
|
||||
VMSTATE_UINT32_ARRAY(mask, pl181_state, 2),
|
||||
VMSTATE_INT32(fifo_pos, pl181_state),
|
||||
VMSTATE_INT32(fifo_len, pl181_state),
|
||||
VMSTATE_INT32(linux_hack, pl181_state),
|
||||
VMSTATE_UINT32_ARRAY(fifo, pl181_state, PL181_FIFO_LEN),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
#define PL181_CMD_INDEX 0x3f
|
||||
#define PL181_CMD_RESPONSE (1 << 6)
|
||||
#define PL181_CMD_LONGRESP (1 << 7)
|
||||
#define PL181_CMD_INTERRUPT (1 << 8)
|
||||
#define PL181_CMD_PENDING (1 << 9)
|
||||
#define PL181_CMD_ENABLE (1 << 10)
|
||||
|
||||
#define PL181_DATA_ENABLE (1 << 0)
|
||||
#define PL181_DATA_DIRECTION (1 << 1)
|
||||
#define PL181_DATA_MODE (1 << 2)
|
||||
#define PL181_DATA_DMAENABLE (1 << 3)
|
||||
|
||||
#define PL181_STATUS_CMDCRCFAIL (1 << 0)
|
||||
#define PL181_STATUS_DATACRCFAIL (1 << 1)
|
||||
#define PL181_STATUS_CMDTIMEOUT (1 << 2)
|
||||
#define PL181_STATUS_DATATIMEOUT (1 << 3)
|
||||
#define PL181_STATUS_TXUNDERRUN (1 << 4)
|
||||
#define PL181_STATUS_RXOVERRUN (1 << 5)
|
||||
#define PL181_STATUS_CMDRESPEND (1 << 6)
|
||||
#define PL181_STATUS_CMDSENT (1 << 7)
|
||||
#define PL181_STATUS_DATAEND (1 << 8)
|
||||
#define PL181_STATUS_DATABLOCKEND (1 << 10)
|
||||
#define PL181_STATUS_CMDACTIVE (1 << 11)
|
||||
#define PL181_STATUS_TXACTIVE (1 << 12)
|
||||
#define PL181_STATUS_RXACTIVE (1 << 13)
|
||||
#define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14)
|
||||
#define PL181_STATUS_RXFIFOHALFFULL (1 << 15)
|
||||
#define PL181_STATUS_TXFIFOFULL (1 << 16)
|
||||
#define PL181_STATUS_RXFIFOFULL (1 << 17)
|
||||
#define PL181_STATUS_TXFIFOEMPTY (1 << 18)
|
||||
#define PL181_STATUS_RXFIFOEMPTY (1 << 19)
|
||||
#define PL181_STATUS_TXDATAAVLBL (1 << 20)
|
||||
#define PL181_STATUS_RXDATAAVLBL (1 << 21)
|
||||
|
||||
#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
|
||||
|PL181_STATUS_TXFIFOHALFEMPTY \
|
||||
|PL181_STATUS_TXFIFOFULL \
|
||||
|PL181_STATUS_TXFIFOEMPTY \
|
||||
|PL181_STATUS_TXDATAAVLBL)
|
||||
#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
|
||||
|PL181_STATUS_RXFIFOHALFFULL \
|
||||
|PL181_STATUS_RXFIFOFULL \
|
||||
|PL181_STATUS_RXFIFOEMPTY \
|
||||
|PL181_STATUS_RXDATAAVLBL)
|
||||
|
||||
static const unsigned char pl181_id[] =
|
||||
{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
|
||||
|
||||
static void pl181_update(pl181_state *s)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pl181_fifo_push(pl181_state *s, uint32_t value)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (s->fifo_len == PL181_FIFO_LEN) {
|
||||
fprintf(stderr, "pl181: FIFO overflow\n");
|
||||
return;
|
||||
}
|
||||
n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
|
||||
s->fifo_len++;
|
||||
s->fifo[n] = value;
|
||||
DPRINTF("FIFO push %08x\n", (int)value);
|
||||
}
|
||||
|
||||
static uint32_t pl181_fifo_pop(pl181_state *s)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
if (s->fifo_len == 0) {
|
||||
fprintf(stderr, "pl181: FIFO underflow\n");
|
||||
return 0;
|
||||
}
|
||||
value = s->fifo[s->fifo_pos];
|
||||
s->fifo_len--;
|
||||
s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
|
||||
DPRINTF("FIFO pop %08x\n", (int)value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void pl181_send_command(pl181_state *s)
|
||||
{
|
||||
SDRequest request;
|
||||
uint8_t response[16];
|
||||
int rlen;
|
||||
|
||||
request.cmd = s->cmd & PL181_CMD_INDEX;
|
||||
request.arg = s->cmdarg;
|
||||
DPRINTF("Command %d %08x\n", request.cmd, request.arg);
|
||||
rlen = sd_do_command(s->card, &request, response);
|
||||
if (rlen < 0)
|
||||
goto error;
|
||||
if (s->cmd & PL181_CMD_RESPONSE) {
|
||||
#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
|
||||
| (response[n + 2] << 8) | response[n + 3])
|
||||
if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
|
||||
goto error;
|
||||
if (rlen != 4 && rlen != 16)
|
||||
goto error;
|
||||
s->response[0] = RWORD(0);
|
||||
if (rlen == 4) {
|
||||
s->response[1] = s->response[2] = s->response[3] = 0;
|
||||
} else {
|
||||
s->response[1] = RWORD(4);
|
||||
s->response[2] = RWORD(8);
|
||||
s->response[3] = RWORD(12) & ~1;
|
||||
}
|
||||
DPRINTF("Response received\n");
|
||||
s->status |= PL181_STATUS_CMDRESPEND;
|
||||
#undef RWORD
|
||||
} else {
|
||||
DPRINTF("Command sent\n");
|
||||
s->status |= PL181_STATUS_CMDSENT;
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
DPRINTF("Timeout\n");
|
||||
s->status |= PL181_STATUS_CMDTIMEOUT;
|
||||
}
|
||||
|
||||
/* Transfer data between the card and the FIFO. This is complicated by
|
||||
the FIFO holding 32-bit words and the card taking data in single byte
|
||||
chunks. FIFO bytes are transferred in little-endian order. */
|
||||
|
||||
static void pl181_fifo_run(pl181_state *s)
|
||||
{
|
||||
uint32_t bits;
|
||||
uint32_t value = 0;
|
||||
int n;
|
||||
int is_read;
|
||||
|
||||
is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
|
||||
if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
|
||||
&& !s->linux_hack) {
|
||||
if (is_read) {
|
||||
n = 0;
|
||||
while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
|
||||
value |= (uint32_t)sd_read_data(s->card) << (n * 8);
|
||||
s->datacnt--;
|
||||
n++;
|
||||
if (n == 4) {
|
||||
pl181_fifo_push(s, value);
|
||||
n = 0;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
if (n != 0) {
|
||||
pl181_fifo_push(s, value);
|
||||
}
|
||||
} else { /* write */
|
||||
n = 0;
|
||||
while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
|
||||
if (n == 0) {
|
||||
value = pl181_fifo_pop(s);
|
||||
n = 4;
|
||||
}
|
||||
n--;
|
||||
s->datacnt--;
|
||||
sd_write_data(s->card, value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
|
||||
if (s->datacnt == 0) {
|
||||
s->status |= PL181_STATUS_DATAEND;
|
||||
/* HACK: */
|
||||
s->status |= PL181_STATUS_DATABLOCKEND;
|
||||
DPRINTF("Transfer Complete\n");
|
||||
}
|
||||
if (s->datacnt == 0 && s->fifo_len == 0) {
|
||||
s->datactrl &= ~PL181_DATA_ENABLE;
|
||||
DPRINTF("Data engine idle\n");
|
||||
} else {
|
||||
/* Update FIFO bits. */
|
||||
bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
|
||||
if (s->fifo_len == 0) {
|
||||
bits |= PL181_STATUS_TXFIFOEMPTY;
|
||||
bits |= PL181_STATUS_RXFIFOEMPTY;
|
||||
} else {
|
||||
bits |= PL181_STATUS_TXDATAAVLBL;
|
||||
bits |= PL181_STATUS_RXDATAAVLBL;
|
||||
}
|
||||
if (s->fifo_len == 16) {
|
||||
bits |= PL181_STATUS_TXFIFOFULL;
|
||||
bits |= PL181_STATUS_RXFIFOFULL;
|
||||
}
|
||||
if (s->fifo_len <= 8) {
|
||||
bits |= PL181_STATUS_TXFIFOHALFEMPTY;
|
||||
}
|
||||
if (s->fifo_len >= 8) {
|
||||
bits |= PL181_STATUS_RXFIFOHALFFULL;
|
||||
}
|
||||
if (s->datactrl & PL181_DATA_DIRECTION) {
|
||||
bits &= PL181_STATUS_RX_FIFO;
|
||||
} else {
|
||||
bits &= PL181_STATUS_TX_FIFO;
|
||||
}
|
||||
s->status |= bits;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pl181_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
pl181_state *s = (pl181_state *)opaque;
|
||||
uint32_t tmp;
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
return pl181_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
switch (offset) {
|
||||
case 0x00: /* Power */
|
||||
return s->power;
|
||||
case 0x04: /* Clock */
|
||||
return s->clock;
|
||||
case 0x08: /* Argument */
|
||||
return s->cmdarg;
|
||||
case 0x0c: /* Command */
|
||||
return s->cmd;
|
||||
case 0x10: /* RespCmd */
|
||||
return s->respcmd;
|
||||
case 0x14: /* Response0 */
|
||||
return s->response[0];
|
||||
case 0x18: /* Response1 */
|
||||
return s->response[1];
|
||||
case 0x1c: /* Response2 */
|
||||
return s->response[2];
|
||||
case 0x20: /* Response3 */
|
||||
return s->response[3];
|
||||
case 0x24: /* DataTimer */
|
||||
return s->datatimer;
|
||||
case 0x28: /* DataLength */
|
||||
return s->datalength;
|
||||
case 0x2c: /* DataCtrl */
|
||||
return s->datactrl;
|
||||
case 0x30: /* DataCnt */
|
||||
return s->datacnt;
|
||||
case 0x34: /* Status */
|
||||
tmp = s->status;
|
||||
if (s->linux_hack) {
|
||||
s->linux_hack = 0;
|
||||
pl181_fifo_run(s);
|
||||
pl181_update(s);
|
||||
}
|
||||
return tmp;
|
||||
case 0x3c: /* Mask0 */
|
||||
return s->mask[0];
|
||||
case 0x40: /* Mask1 */
|
||||
return s->mask[1];
|
||||
case 0x48: /* FifoCnt */
|
||||
/* The documentation is somewhat vague about exactly what FifoCnt
|
||||
does. On real hardware it appears to be when decrememnted
|
||||
when a word is transferred between the FIFO and the serial
|
||||
data engine. DataCnt is decremented after each byte is
|
||||
transferred between the serial engine and the card.
|
||||
We don't emulate this level of detail, so both can be the same. */
|
||||
tmp = (s->datacnt + 3) >> 2;
|
||||
if (s->linux_hack) {
|
||||
s->linux_hack = 0;
|
||||
pl181_fifo_run(s);
|
||||
pl181_update(s);
|
||||
}
|
||||
return tmp;
|
||||
case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
|
||||
case 0x90: case 0x94: case 0x98: case 0x9c:
|
||||
case 0xa0: case 0xa4: case 0xa8: case 0xac:
|
||||
case 0xb0: case 0xb4: case 0xb8: case 0xbc:
|
||||
if (s->fifo_len == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
|
||||
return 0;
|
||||
} else {
|
||||
uint32_t value;
|
||||
value = pl181_fifo_pop(s);
|
||||
s->linux_hack = 1;
|
||||
pl181_fifo_run(s);
|
||||
pl181_update(s);
|
||||
return value;
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"pl181_read: Bad offset %x\n", (int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl181_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
pl181_state *s = (pl181_state *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case 0x00: /* Power */
|
||||
s->power = value & 0xff;
|
||||
break;
|
||||
case 0x04: /* Clock */
|
||||
s->clock = value & 0xff;
|
||||
break;
|
||||
case 0x08: /* Argument */
|
||||
s->cmdarg = value;
|
||||
break;
|
||||
case 0x0c: /* Command */
|
||||
s->cmd = value;
|
||||
if (s->cmd & PL181_CMD_ENABLE) {
|
||||
if (s->cmd & PL181_CMD_INTERRUPT) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"pl181: Interrupt mode not implemented\n");
|
||||
} if (s->cmd & PL181_CMD_PENDING) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"pl181: Pending commands not implemented\n");
|
||||
} else {
|
||||
pl181_send_command(s);
|
||||
pl181_fifo_run(s);
|
||||
}
|
||||
/* The command has completed one way or the other. */
|
||||
s->cmd &= ~PL181_CMD_ENABLE;
|
||||
}
|
||||
break;
|
||||
case 0x24: /* DataTimer */
|
||||
s->datatimer = value;
|
||||
break;
|
||||
case 0x28: /* DataLength */
|
||||
s->datalength = value & 0xffff;
|
||||
break;
|
||||
case 0x2c: /* DataCtrl */
|
||||
s->datactrl = value & 0xff;
|
||||
if (value & PL181_DATA_ENABLE) {
|
||||
s->datacnt = s->datalength;
|
||||
pl181_fifo_run(s);
|
||||
}
|
||||
break;
|
||||
case 0x38: /* Clear */
|
||||
s->status &= ~(value & 0x7ff);
|
||||
break;
|
||||
case 0x3c: /* Mask0 */
|
||||
s->mask[0] = value;
|
||||
break;
|
||||
case 0x40: /* Mask1 */
|
||||
s->mask[1] = value;
|
||||
break;
|
||||
case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
|
||||
case 0x90: case 0x94: case 0x98: case 0x9c:
|
||||
case 0xa0: case 0xa4: case 0xa8: case 0xac:
|
||||
case 0xb0: case 0xb4: case 0xb8: case 0xbc:
|
||||
if (s->datacnt == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
|
||||
} else {
|
||||
pl181_fifo_push(s, value);
|
||||
pl181_fifo_run(s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"pl181_write: Bad offset %x\n", (int)offset);
|
||||
}
|
||||
pl181_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pl181_ops = {
|
||||
.read = pl181_read,
|
||||
.write = pl181_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void pl181_reset(DeviceState *d)
|
||||
{
|
||||
pl181_state *s = DO_UPCAST(pl181_state, busdev.qdev, d);
|
||||
|
||||
s->power = 0;
|
||||
s->cmdarg = 0;
|
||||
s->cmd = 0;
|
||||
s->datatimer = 0;
|
||||
s->datalength = 0;
|
||||
s->respcmd = 0;
|
||||
s->response[0] = 0;
|
||||
s->response[1] = 0;
|
||||
s->response[2] = 0;
|
||||
s->response[3] = 0;
|
||||
s->datatimer = 0;
|
||||
s->datalength = 0;
|
||||
s->datactrl = 0;
|
||||
s->datacnt = 0;
|
||||
s->status = 0;
|
||||
s->linux_hack = 0;
|
||||
s->mask[0] = 0;
|
||||
s->mask[1] = 0;
|
||||
|
||||
/* We can assume our GPIO outputs have been wired up now */
|
||||
sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
|
||||
}
|
||||
|
||||
static int pl181_init(SysBusDevice *dev)
|
||||
{
|
||||
pl181_state *s = FROM_SYSBUS(pl181_state, dev);
|
||||
DriveInfo *dinfo;
|
||||
|
||||
memory_region_init_io(&s->iomem, &pl181_ops, s, "pl181", 0x1000);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
sysbus_init_irq(dev, &s->irq[0]);
|
||||
sysbus_init_irq(dev, &s->irq[1]);
|
||||
qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2);
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl181_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
||||
DeviceClass *k = DEVICE_CLASS(klass);
|
||||
|
||||
sdc->init = pl181_init;
|
||||
k->vmsd = &vmstate_pl181;
|
||||
k->reset = pl181_reset;
|
||||
k->no_user = 1;
|
||||
}
|
||||
|
||||
static const TypeInfo pl181_info = {
|
||||
.name = "pl181",
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(pl181_state),
|
||||
.class_init = pl181_class_init,
|
||||
};
|
||||
|
||||
static void pl181_register_types(void)
|
||||
{
|
||||
type_register_static(&pl181_info);
|
||||
}
|
||||
|
||||
type_init(pl181_register_types)
|
1764
hw/sd/sd.c
Normal file
1764
hw/sd/sd.c
Normal file
File diff suppressed because it is too large
Load diff
1300
hw/sd/sdhci.c
Normal file
1300
hw/sd/sdhci.c
Normal file
File diff suppressed because it is too large
Load diff
274
hw/sd/ssi-sd.c
Normal file
274
hw/sd/ssi-sd.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* SSI to SD card adapter.
|
||||
*
|
||||
* Copyright (c) 2007-2009 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GNU GPL v2.
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/ssi.h"
|
||||
#include "hw/sd.h"
|
||||
|
||||
//#define DEBUG_SSI_SD 1
|
||||
|
||||
#ifdef DEBUG_SSI_SD
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0)
|
||||
#define BADF(fmt, ...) \
|
||||
do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) do {} while(0)
|
||||
#define BADF(fmt, ...) \
|
||||
do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_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 {
|
||||
SSISlave ssidev;
|
||||
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
|
||||
|
||||
static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
|
||||
{
|
||||
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
|
||||
|
||||
/* 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) {
|
||||
SDRequest 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;
|
||||
}
|
||||
|
||||
static void ssi_sd_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
SSISlave *ss = SSI_SLAVE(opaque);
|
||||
ssi_sd_state *s = (ssi_sd_state *)opaque;
|
||||
int i;
|
||||
|
||||
qemu_put_be32(f, s->mode);
|
||||
qemu_put_be32(f, s->cmd);
|
||||
for (i = 0; i < 4; i++)
|
||||
qemu_put_be32(f, s->cmdarg[i]);
|
||||
for (i = 0; i < 5; i++)
|
||||
qemu_put_be32(f, s->response[i]);
|
||||
qemu_put_be32(f, s->arglen);
|
||||
qemu_put_be32(f, s->response_pos);
|
||||
qemu_put_be32(f, s->stopping);
|
||||
|
||||
qemu_put_be32(f, ss->cs);
|
||||
}
|
||||
|
||||
static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
SSISlave *ss = SSI_SLAVE(opaque);
|
||||
ssi_sd_state *s = (ssi_sd_state *)opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
s->mode = qemu_get_be32(f);
|
||||
s->cmd = qemu_get_be32(f);
|
||||
for (i = 0; i < 4; i++)
|
||||
s->cmdarg[i] = qemu_get_be32(f);
|
||||
for (i = 0; i < 5; i++)
|
||||
s->response[i] = qemu_get_be32(f);
|
||||
s->arglen = qemu_get_be32(f);
|
||||
s->response_pos = qemu_get_be32(f);
|
||||
s->stopping = qemu_get_be32(f);
|
||||
|
||||
ss->cs = qemu_get_be32(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssi_sd_init(SSISlave *dev)
|
||||
{
|
||||
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
|
||||
DriveInfo *dinfo;
|
||||
|
||||
s->mode = SSI_SD_CMD;
|
||||
dinfo = drive_get_next(IF_SD);
|
||||
s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1);
|
||||
register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssi_sd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
|
||||
|
||||
k->init = ssi_sd_init;
|
||||
k->transfer = ssi_sd_transfer;
|
||||
k->cs_polarity = SSI_CS_LOW;
|
||||
}
|
||||
|
||||
static const TypeInfo ssi_sd_info = {
|
||||
.name = "ssi-sd",
|
||||
.parent = TYPE_SSI_SLAVE,
|
||||
.instance_size = sizeof(ssi_sd_state),
|
||||
.class_init = ssi_sd_class_init,
|
||||
};
|
||||
|
||||
static void ssi_sd_register_types(void)
|
||||
{
|
||||
type_register_static(&ssi_sd_info);
|
||||
}
|
||||
|
||||
type_init(ssi_sd_register_types)
|
Loading…
Add table
Add a link
Reference in a new issue