2007-09-16 23:08:06 +02:00
|
|
|
/*
|
2007-04-06 18:49:48 +02:00
|
|
|
* Arm PrimeCell PL181 MultiMedia Card Interface
|
|
|
|
*
|
|
|
|
* Copyright (c) 2007 CodeSourcery.
|
|
|
|
* Written by Paul Brook
|
|
|
|
*
|
2011-06-26 04:21:35 +02:00
|
|
|
* This code is licensed under the GPL.
|
2007-04-06 18:49:48 +02:00
|
|
|
*/
|
|
|
|
|
2012-12-17 18:20:04 +01:00
|
|
|
#include "sysemu/blockdev.h"
|
2013-02-04 15:40:22 +01:00
|
|
|
#include "hw/sysbus.h"
|
|
|
|
#include "hw/sd.h"
|
2007-04-06 18:49:48 +02:00
|
|
|
|
|
|
|
//#define DEBUG_PL181 1
|
|
|
|
|
|
|
|
#ifdef DEBUG_PL181
|
2009-05-13 19:53:17 +02:00
|
|
|
#define DPRINTF(fmt, ...) \
|
|
|
|
do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
|
2007-04-06 18:49:48 +02:00
|
|
|
#else
|
2009-05-13 19:53:17 +02:00
|
|
|
#define DPRINTF(fmt, ...) do {} while(0)
|
2007-04-06 18:49:48 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define PL181_FIFO_LEN 16
|
|
|
|
|
2013-07-27 13:23:41 +02:00
|
|
|
#define TYPE_PL181 "pl181"
|
|
|
|
#define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
|
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
typedef struct PL181State {
|
2013-07-27 13:23:41 +02:00
|
|
|
SysBusDevice parent_obj;
|
|
|
|
|
2011-10-11 13:54:48 +02:00
|
|
|
MemoryRegion iomem;
|
2007-04-06 18:54:11 +02:00
|
|
|
SDState *card;
|
2007-04-06 18:49:48 +02:00
|
|
|
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];
|
2011-12-19 13:01:44 +01:00
|
|
|
int32_t fifo_pos;
|
|
|
|
int32_t fifo_len;
|
2007-06-09 16:07:54 +02:00
|
|
|
/* 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
|
|
|
|
*/
|
2011-12-19 13:01:44 +01:00
|
|
|
int32_t linux_hack;
|
2007-04-06 18:49:48 +02:00
|
|
|
uint32_t fifo[PL181_FIFO_LEN];
|
2007-04-07 20:14:41 +02:00
|
|
|
qemu_irq irq[2];
|
2011-02-21 21:57:49 +01:00
|
|
|
/* GPIO outputs for 'card is readonly' and 'card inserted' */
|
|
|
|
qemu_irq cardstatus[2];
|
2013-07-27 13:20:09 +02:00
|
|
|
} PL181State;
|
2007-04-06 18:49:48 +02:00
|
|
|
|
2011-12-19 13:01:44 +01:00
|
|
|
static const VMStateDescription vmstate_pl181 = {
|
|
|
|
.name = "pl181",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.fields = (VMStateField[]) {
|
2013-07-27 13:20:09 +02:00
|
|
|
VMSTATE_UINT32(clock, PL181State),
|
|
|
|
VMSTATE_UINT32(power, PL181State),
|
|
|
|
VMSTATE_UINT32(cmdarg, PL181State),
|
|
|
|
VMSTATE_UINT32(cmd, PL181State),
|
|
|
|
VMSTATE_UINT32(datatimer, PL181State),
|
|
|
|
VMSTATE_UINT32(datalength, PL181State),
|
|
|
|
VMSTATE_UINT32(respcmd, PL181State),
|
|
|
|
VMSTATE_UINT32_ARRAY(response, PL181State, 4),
|
|
|
|
VMSTATE_UINT32(datactrl, PL181State),
|
|
|
|
VMSTATE_UINT32(datacnt, PL181State),
|
|
|
|
VMSTATE_UINT32(status, PL181State),
|
|
|
|
VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
|
|
|
|
VMSTATE_INT32(fifo_pos, PL181State),
|
|
|
|
VMSTATE_INT32(fifo_len, PL181State),
|
|
|
|
VMSTATE_INT32(linux_hack, PL181State),
|
|
|
|
VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
|
2011-12-19 13:01:44 +01:00
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2007-04-06 18:49:48 +02:00
|
|
|
#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 };
|
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
static void pl181_update(PL181State *s)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++) {
|
2007-04-07 20:14:41 +02:00
|
|
|
qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
|
2007-04-06 18:49:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
static void pl181_fifo_push(PL181State *s, uint32_t value)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
static uint32_t pl181_fifo_pop(PL181State *s)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
static void pl181_send_command(PL181State *s)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
2009-05-10 02:44:56 +02:00
|
|
|
SDRequest request;
|
2007-04-06 18:49:48 +02:00
|
|
|
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) {
|
2013-06-27 16:03:51 +02:00
|
|
|
#define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \
|
2007-04-06 18:49:48 +02:00
|
|
|
| (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;
|
|
|
|
}
|
2007-07-12 00:48:58 +02:00
|
|
|
DPRINTF("Response received\n");
|
2007-04-06 18:49:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-07-12 00:48:58 +02:00
|
|
|
/* Transfer data between the card and the FIFO. This is complicated by
|
2007-04-06 18:49:48 +02:00
|
|
|
the FIFO holding 32-bit words and the card taking data in single byte
|
|
|
|
chunks. FIFO bytes are transferred in little-endian order. */
|
2007-09-17 10:09:54 +02:00
|
|
|
|
2013-07-27 13:20:09 +02:00
|
|
|
static void pl181_fifo_run(PL181State *s)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
|
|
|
uint32_t bits;
|
2010-02-22 21:42:51 +01:00
|
|
|
uint32_t value = 0;
|
2007-04-06 18:49:48 +02:00
|
|
|
int n;
|
|
|
|
int is_read;
|
|
|
|
|
|
|
|
is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
|
2007-06-09 16:07:54 +02:00
|
|
|
if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
|
|
|
|
&& !s->linux_hack) {
|
2010-02-22 16:33:59 +01:00
|
|
|
if (is_read) {
|
|
|
|
n = 0;
|
|
|
|
while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
|
2007-04-06 18:49:48 +02:00
|
|
|
value |= (uint32_t)sd_read_data(s->card) << (n * 8);
|
2010-02-22 16:33:59 +01:00
|
|
|
s->datacnt--;
|
2007-04-06 18:49:48 +02:00
|
|
|
n++;
|
|
|
|
if (n == 4) {
|
|
|
|
pl181_fifo_push(s, value);
|
|
|
|
n = 0;
|
2010-02-22 16:33:59 +01:00
|
|
|
value = 0;
|
2007-04-06 18:49:48 +02:00
|
|
|
}
|
2010-02-22 16:33:59 +01:00
|
|
|
}
|
|
|
|
if (n != 0) {
|
|
|
|
pl181_fifo_push(s, value);
|
|
|
|
}
|
|
|
|
} else { /* write */
|
|
|
|
n = 0;
|
|
|
|
while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
|
2007-04-06 18:49:48 +02:00
|
|
|
if (n == 0) {
|
|
|
|
value = pl181_fifo_pop(s);
|
|
|
|
n = 4;
|
|
|
|
}
|
2010-02-22 16:33:59 +01:00
|
|
|
n--;
|
|
|
|
s->datacnt--;
|
2007-04-06 18:49:48 +02:00
|
|
|
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");
|
|
|
|
}
|
2007-06-09 16:07:54 +02:00
|
|
|
if (s->datacnt == 0 && s->fifo_len == 0) {
|
2007-04-06 18:49:48 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 12:30:10 +02:00
|
|
|
static uint64_t pl181_read(void *opaque, hwaddr offset,
|
2011-10-11 13:54:48 +02:00
|
|
|
unsigned size)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
2013-07-27 13:20:09 +02:00
|
|
|
PL181State *s = (PL181State *)opaque;
|
2007-06-09 16:07:54 +02:00
|
|
|
uint32_t tmp;
|
2007-04-06 18:49:48 +02:00
|
|
|
|
|
|
|
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 */
|
2007-06-09 16:07:54 +02:00
|
|
|
tmp = s->status;
|
|
|
|
if (s->linux_hack) {
|
|
|
|
s->linux_hack = 0;
|
|
|
|
pl181_fifo_run(s);
|
|
|
|
pl181_update(s);
|
|
|
|
}
|
|
|
|
return tmp;
|
2007-04-06 18:49:48 +02:00
|
|
|
case 0x3c: /* Mask0 */
|
|
|
|
return s->mask[0];
|
|
|
|
case 0x40: /* Mask1 */
|
|
|
|
return s->mask[1];
|
|
|
|
case 0x48: /* FifoCnt */
|
2007-06-09 16:07:54 +02:00
|
|
|
/* The documentation is somewhat vague about exactly what FifoCnt
|
|
|
|
does. On real hardware it appears to be when decrememnted
|
2011-11-29 09:52:39 +01:00
|
|
|
when a word is transferred between the FIFO and the serial
|
2007-06-09 16:07:54 +02:00
|
|
|
data engine. DataCnt is decremented after each byte is
|
2011-11-29 09:52:39 +01:00
|
|
|
transferred between the serial engine and the card.
|
2007-06-09 16:07:54 +02:00
|
|
|
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;
|
2007-04-06 18:49:48 +02:00
|
|
|
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:
|
2007-06-09 16:07:54 +02:00
|
|
|
if (s->fifo_len == 0) {
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
|
2007-04-06 18:49:48 +02:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
uint32_t value;
|
|
|
|
value = pl181_fifo_pop(s);
|
2007-06-09 16:07:54 +02:00
|
|
|
s->linux_hack = 1;
|
2007-04-06 18:49:48 +02:00
|
|
|
pl181_fifo_run(s);
|
|
|
|
pl181_update(s);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
default:
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"pl181_read: Bad offset %x\n", (int)offset);
|
2007-04-06 18:49:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-23 12:30:10 +02:00
|
|
|
static void pl181_write(void *opaque, hwaddr offset,
|
2011-10-11 13:54:48 +02:00
|
|
|
uint64_t value, unsigned size)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
2013-07-27 13:20:09 +02:00
|
|
|
PL181State *s = (PL181State *)opaque;
|
2007-04-06 18:49:48 +02:00
|
|
|
|
|
|
|
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) {
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_UNIMP,
|
|
|
|
"pl181: Interrupt mode not implemented\n");
|
2007-04-06 18:49:48 +02:00
|
|
|
} if (s->cmd & PL181_CMD_PENDING) {
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_UNIMP,
|
|
|
|
"pl181: Pending commands not implemented\n");
|
2007-04-06 18:49:48 +02:00
|
|
|
} 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:
|
2007-06-09 16:07:54 +02:00
|
|
|
if (s->datacnt == 0) {
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
|
2007-04-06 18:49:48 +02:00
|
|
|
} else {
|
|
|
|
pl181_fifo_push(s, value);
|
|
|
|
pl181_fifo_run(s);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-10-18 15:11:37 +02:00
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"pl181_write: Bad offset %x\n", (int)offset);
|
2007-04-06 18:49:48 +02:00
|
|
|
}
|
|
|
|
pl181_update(s);
|
|
|
|
}
|
|
|
|
|
2011-10-11 13:54:48 +02:00
|
|
|
static const MemoryRegionOps pl181_ops = {
|
|
|
|
.read = pl181_read,
|
|
|
|
.write = pl181_write,
|
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
2007-04-06 18:49:48 +02:00
|
|
|
};
|
|
|
|
|
2011-12-19 13:01:44 +01:00
|
|
|
static void pl181_reset(DeviceState *d)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
2013-07-27 13:23:41 +02:00
|
|
|
PL181State *s = PL181(d);
|
2007-04-06 18:49:48 +02:00
|
|
|
|
|
|
|
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;
|
2007-06-09 16:07:54 +02:00
|
|
|
s->linux_hack = 0;
|
2007-04-06 18:49:48 +02:00
|
|
|
s->mask[0] = 0;
|
|
|
|
s->mask[1] = 0;
|
2011-02-21 21:57:49 +01:00
|
|
|
|
|
|
|
/* We can assume our GPIO outputs have been wired up now */
|
|
|
|
sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
|
2007-04-06 18:49:48 +02:00
|
|
|
}
|
|
|
|
|
2013-07-27 13:23:41 +02:00
|
|
|
static int pl181_init(SysBusDevice *sbd)
|
2007-04-06 18:49:48 +02:00
|
|
|
{
|
2013-07-27 13:23:41 +02:00
|
|
|
DeviceState *dev = DEVICE(sbd);
|
|
|
|
PL181State *s = PL181(dev);
|
2011-01-28 11:21:37 +01:00
|
|
|
DriveInfo *dinfo;
|
2007-04-06 18:49:48 +02:00
|
|
|
|
2013-06-07 03:25:08 +02:00
|
|
|
memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000);
|
2013-07-27 13:23:41 +02:00
|
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
|
|
sysbus_init_irq(sbd, &s->irq[0]);
|
|
|
|
sysbus_init_irq(sbd, &s->irq[1]);
|
|
|
|
qdev_init_gpio_out(dev, s->cardstatus, 2);
|
2011-01-28 11:21:37 +01:00
|
|
|
dinfo = drive_get_next(IF_SD);
|
2013-06-18 05:45:36 +02:00
|
|
|
s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
|
2013-09-13 15:51:47 +02:00
|
|
|
if (s->card == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-08-14 10:36:05 +02:00
|
|
|
return 0;
|
2007-04-06 18:49:48 +02:00
|
|
|
}
|
2009-05-14 23:35:07 +02:00
|
|
|
|
2012-01-24 20:12:29 +01:00
|
|
|
static void pl181_class_init(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
2011-12-08 04:34:16 +01:00
|
|
|
DeviceClass *k = DEVICE_CLASS(klass);
|
2012-01-24 20:12:29 +01:00
|
|
|
|
|
|
|
sdc->init = pl181_init;
|
2011-12-08 04:34:16 +01:00
|
|
|
k->vmsd = &vmstate_pl181;
|
|
|
|
k->reset = pl181_reset;
|
qdev: Replace no_user by cannot_instantiate_with_device_add_yet
In an ideal world, machines can be built by wiring devices together
with configuration, not code. Unfortunately, that's not the world we
live in right now. We still have quite a few devices that need to be
wired up by code. If you try to device_add such a device, it'll fail
in sometimes mysterious ways. If you're lucky, you get an
unmysterious immediate crash.
To protect users from such badness, DeviceClass member no_user used to
make device models unavailable with -device / device_add, but that
regressed in commit 18b6dad. The device model is still omitted from
help, but is available anyway.
Attempts to fix the regression have been rejected with the argument
that the purpose of no_user isn't clear, and it's prone to misuse.
This commit clarifies no_user's purpose. Anthony suggested to rename
it cannot_instantiate_with_device_add_yet_due_to_internal_bugs, which
I shorten somewhat to keep checkpatch happy. While there, make it
bool.
Every use of cannot_instantiate_with_device_add_yet gets a FIXME
comment asking for rationale. The next few commits will clean them
all up, either by providing a rationale, or by getting rid of the use.
With that done, the regression fix is hopefully acceptable.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marcel Apfelbaum <marcel.a@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2013-11-28 17:26:54 +01:00
|
|
|
k->cannot_instantiate_with_device_add_yet = true; /* FIXME explain why */
|
2012-01-24 20:12:29 +01:00
|
|
|
}
|
|
|
|
|
2013-01-10 16:19:07 +01:00
|
|
|
static const TypeInfo pl181_info = {
|
2013-07-27 13:23:41 +02:00
|
|
|
.name = TYPE_PL181,
|
2011-12-08 04:34:16 +01:00
|
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
2013-07-27 13:20:09 +02:00
|
|
|
.instance_size = sizeof(PL181State),
|
2011-12-08 04:34:16 +01:00
|
|
|
.class_init = pl181_class_init,
|
2011-12-19 13:01:44 +01:00
|
|
|
};
|
|
|
|
|
2012-02-09 15:20:55 +01:00
|
|
|
static void pl181_register_types(void)
|
2009-05-14 23:35:07 +02:00
|
|
|
{
|
2011-12-08 04:34:16 +01:00
|
|
|
type_register_static(&pl181_info);
|
2009-05-14 23:35:07 +02:00
|
|
|
}
|
|
|
|
|
2012-02-09 15:20:55 +01:00
|
|
|
type_init(pl181_register_types)
|