xref: /openbmc/qemu/hw/sd/pl181.c (revision 1d998d93acbbefc3804200ab4387cb0286b07d7b)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Arm PrimeCell PL181 MultiMedia Card Interface
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2007 CodeSourcery.
549ab747fSPaolo Bonzini  * Written by Paul Brook
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GPL.
849ab747fSPaolo Bonzini  */
949ab747fSPaolo Bonzini 
1049ab747fSPaolo Bonzini #include "sysemu/blockdev.h"
1149ab747fSPaolo Bonzini #include "hw/sysbus.h"
1249ab747fSPaolo Bonzini #include "hw/sd.h"
1349ab747fSPaolo Bonzini 
1449ab747fSPaolo Bonzini //#define DEBUG_PL181 1
1549ab747fSPaolo Bonzini 
1649ab747fSPaolo Bonzini #ifdef DEBUG_PL181
1749ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
1849ab747fSPaolo Bonzini do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
1949ab747fSPaolo Bonzini #else
2049ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
2149ab747fSPaolo Bonzini #endif
2249ab747fSPaolo Bonzini 
2349ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16
2449ab747fSPaolo Bonzini 
25*1d998d93SAndreas Färber typedef struct PL181State {
2649ab747fSPaolo Bonzini     SysBusDevice busdev;
2749ab747fSPaolo Bonzini     MemoryRegion iomem;
2849ab747fSPaolo Bonzini     SDState *card;
2949ab747fSPaolo Bonzini     uint32_t clock;
3049ab747fSPaolo Bonzini     uint32_t power;
3149ab747fSPaolo Bonzini     uint32_t cmdarg;
3249ab747fSPaolo Bonzini     uint32_t cmd;
3349ab747fSPaolo Bonzini     uint32_t datatimer;
3449ab747fSPaolo Bonzini     uint32_t datalength;
3549ab747fSPaolo Bonzini     uint32_t respcmd;
3649ab747fSPaolo Bonzini     uint32_t response[4];
3749ab747fSPaolo Bonzini     uint32_t datactrl;
3849ab747fSPaolo Bonzini     uint32_t datacnt;
3949ab747fSPaolo Bonzini     uint32_t status;
4049ab747fSPaolo Bonzini     uint32_t mask[2];
4149ab747fSPaolo Bonzini     int32_t fifo_pos;
4249ab747fSPaolo Bonzini     int32_t fifo_len;
4349ab747fSPaolo Bonzini     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
4449ab747fSPaolo Bonzini        while it is reading the FIFO.  We hack around this be defering
4549ab747fSPaolo Bonzini        subsequent transfers until after the driver polls the status word.
4649ab747fSPaolo Bonzini        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
4749ab747fSPaolo Bonzini      */
4849ab747fSPaolo Bonzini     int32_t linux_hack;
4949ab747fSPaolo Bonzini     uint32_t fifo[PL181_FIFO_LEN];
5049ab747fSPaolo Bonzini     qemu_irq irq[2];
5149ab747fSPaolo Bonzini     /* GPIO outputs for 'card is readonly' and 'card inserted' */
5249ab747fSPaolo Bonzini     qemu_irq cardstatus[2];
53*1d998d93SAndreas Färber } PL181State;
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = {
5649ab747fSPaolo Bonzini     .name = "pl181",
5749ab747fSPaolo Bonzini     .version_id = 1,
5849ab747fSPaolo Bonzini     .minimum_version_id = 1,
5949ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
60*1d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
61*1d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
62*1d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
63*1d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
64*1d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
65*1d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
66*1d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
67*1d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
68*1d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
69*1d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
70*1d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
71*1d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
72*1d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
73*1d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
74*1d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
75*1d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
7649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
7749ab747fSPaolo Bonzini     }
7849ab747fSPaolo Bonzini };
7949ab747fSPaolo Bonzini 
8049ab747fSPaolo Bonzini #define PL181_CMD_INDEX     0x3f
8149ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE  (1 << 6)
8249ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP  (1 << 7)
8349ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8)
8449ab747fSPaolo Bonzini #define PL181_CMD_PENDING   (1 << 9)
8549ab747fSPaolo Bonzini #define PL181_CMD_ENABLE    (1 << 10)
8649ab747fSPaolo Bonzini 
8749ab747fSPaolo Bonzini #define PL181_DATA_ENABLE             (1 << 0)
8849ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION          (1 << 1)
8949ab747fSPaolo Bonzini #define PL181_DATA_MODE               (1 << 2)
9049ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE          (1 << 3)
9149ab747fSPaolo Bonzini 
9249ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
9349ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL      (1 << 1)
9449ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
9549ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT      (1 << 3)
9649ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN       (1 << 4)
9749ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN        (1 << 5)
9849ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND       (1 << 6)
9949ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT          (1 << 7)
10049ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND          (1 << 8)
10149ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND     (1 << 10)
10249ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE        (1 << 11)
10349ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE         (1 << 12)
10449ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE         (1 << 13)
10549ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
10649ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
10749ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL       (1 << 16)
10849ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL       (1 << 17)
10949ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
11049ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
11149ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
11249ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
11549ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOHALFEMPTY \
11649ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOFULL \
11749ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOEMPTY \
11849ab747fSPaolo Bonzini                              |PL181_STATUS_TXDATAAVLBL)
11949ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
12049ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOHALFFULL \
12149ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOFULL \
12249ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOEMPTY \
12349ab747fSPaolo Bonzini                              |PL181_STATUS_RXDATAAVLBL)
12449ab747fSPaolo Bonzini 
12549ab747fSPaolo Bonzini static const unsigned char pl181_id[] =
12649ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
12749ab747fSPaolo Bonzini 
128*1d998d93SAndreas Färber static void pl181_update(PL181State *s)
12949ab747fSPaolo Bonzini {
13049ab747fSPaolo Bonzini     int i;
13149ab747fSPaolo Bonzini     for (i = 0; i < 2; i++) {
13249ab747fSPaolo Bonzini         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
13349ab747fSPaolo Bonzini     }
13449ab747fSPaolo Bonzini }
13549ab747fSPaolo Bonzini 
136*1d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
13749ab747fSPaolo Bonzini {
13849ab747fSPaolo Bonzini     int n;
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini     if (s->fifo_len == PL181_FIFO_LEN) {
14149ab747fSPaolo Bonzini         fprintf(stderr, "pl181: FIFO overflow\n");
14249ab747fSPaolo Bonzini         return;
14349ab747fSPaolo Bonzini     }
14449ab747fSPaolo Bonzini     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
14549ab747fSPaolo Bonzini     s->fifo_len++;
14649ab747fSPaolo Bonzini     s->fifo[n] = value;
14749ab747fSPaolo Bonzini     DPRINTF("FIFO push %08x\n", (int)value);
14849ab747fSPaolo Bonzini }
14949ab747fSPaolo Bonzini 
150*1d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
15149ab747fSPaolo Bonzini {
15249ab747fSPaolo Bonzini     uint32_t value;
15349ab747fSPaolo Bonzini 
15449ab747fSPaolo Bonzini     if (s->fifo_len == 0) {
15549ab747fSPaolo Bonzini         fprintf(stderr, "pl181: FIFO underflow\n");
15649ab747fSPaolo Bonzini         return 0;
15749ab747fSPaolo Bonzini     }
15849ab747fSPaolo Bonzini     value = s->fifo[s->fifo_pos];
15949ab747fSPaolo Bonzini     s->fifo_len--;
16049ab747fSPaolo Bonzini     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
16149ab747fSPaolo Bonzini     DPRINTF("FIFO pop %08x\n", (int)value);
16249ab747fSPaolo Bonzini     return value;
16349ab747fSPaolo Bonzini }
16449ab747fSPaolo Bonzini 
165*1d998d93SAndreas Färber static void pl181_send_command(PL181State *s)
16649ab747fSPaolo Bonzini {
16749ab747fSPaolo Bonzini     SDRequest request;
16849ab747fSPaolo Bonzini     uint8_t response[16];
16949ab747fSPaolo Bonzini     int rlen;
17049ab747fSPaolo Bonzini 
17149ab747fSPaolo Bonzini     request.cmd = s->cmd & PL181_CMD_INDEX;
17249ab747fSPaolo Bonzini     request.arg = s->cmdarg;
17349ab747fSPaolo Bonzini     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
17449ab747fSPaolo Bonzini     rlen = sd_do_command(s->card, &request, response);
17549ab747fSPaolo Bonzini     if (rlen < 0)
17649ab747fSPaolo Bonzini         goto error;
17749ab747fSPaolo Bonzini     if (s->cmd & PL181_CMD_RESPONSE) {
1788827b0fbSPeter Maydell #define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \
17949ab747fSPaolo Bonzini                   | (response[n + 2] << 8) | response[n + 3])
18049ab747fSPaolo Bonzini         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
18149ab747fSPaolo Bonzini             goto error;
18249ab747fSPaolo Bonzini         if (rlen != 4 && rlen != 16)
18349ab747fSPaolo Bonzini             goto error;
18449ab747fSPaolo Bonzini         s->response[0] = RWORD(0);
18549ab747fSPaolo Bonzini         if (rlen == 4) {
18649ab747fSPaolo Bonzini             s->response[1] = s->response[2] = s->response[3] = 0;
18749ab747fSPaolo Bonzini         } else {
18849ab747fSPaolo Bonzini             s->response[1] = RWORD(4);
18949ab747fSPaolo Bonzini             s->response[2] = RWORD(8);
19049ab747fSPaolo Bonzini             s->response[3] = RWORD(12) & ~1;
19149ab747fSPaolo Bonzini         }
19249ab747fSPaolo Bonzini         DPRINTF("Response received\n");
19349ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDRESPEND;
19449ab747fSPaolo Bonzini #undef RWORD
19549ab747fSPaolo Bonzini     } else {
19649ab747fSPaolo Bonzini         DPRINTF("Command sent\n");
19749ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDSENT;
19849ab747fSPaolo Bonzini     }
19949ab747fSPaolo Bonzini     return;
20049ab747fSPaolo Bonzini 
20149ab747fSPaolo Bonzini error:
20249ab747fSPaolo Bonzini     DPRINTF("Timeout\n");
20349ab747fSPaolo Bonzini     s->status |= PL181_STATUS_CMDTIMEOUT;
20449ab747fSPaolo Bonzini }
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO.  This is complicated by
20749ab747fSPaolo Bonzini    the FIFO holding 32-bit words and the card taking data in single byte
20849ab747fSPaolo Bonzini    chunks.  FIFO bytes are transferred in little-endian order.  */
20949ab747fSPaolo Bonzini 
210*1d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
21149ab747fSPaolo Bonzini {
21249ab747fSPaolo Bonzini     uint32_t bits;
21349ab747fSPaolo Bonzini     uint32_t value = 0;
21449ab747fSPaolo Bonzini     int n;
21549ab747fSPaolo Bonzini     int is_read;
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
21849ab747fSPaolo Bonzini     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
21949ab747fSPaolo Bonzini             && !s->linux_hack) {
22049ab747fSPaolo Bonzini         if (is_read) {
22149ab747fSPaolo Bonzini             n = 0;
22249ab747fSPaolo Bonzini             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
22349ab747fSPaolo Bonzini                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
22449ab747fSPaolo Bonzini                 s->datacnt--;
22549ab747fSPaolo Bonzini                 n++;
22649ab747fSPaolo Bonzini                 if (n == 4) {
22749ab747fSPaolo Bonzini                     pl181_fifo_push(s, value);
22849ab747fSPaolo Bonzini                     n = 0;
22949ab747fSPaolo Bonzini                     value = 0;
23049ab747fSPaolo Bonzini                 }
23149ab747fSPaolo Bonzini             }
23249ab747fSPaolo Bonzini             if (n != 0) {
23349ab747fSPaolo Bonzini                 pl181_fifo_push(s, value);
23449ab747fSPaolo Bonzini             }
23549ab747fSPaolo Bonzini         } else { /* write */
23649ab747fSPaolo Bonzini             n = 0;
23749ab747fSPaolo Bonzini             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
23849ab747fSPaolo Bonzini                 if (n == 0) {
23949ab747fSPaolo Bonzini                     value = pl181_fifo_pop(s);
24049ab747fSPaolo Bonzini                     n = 4;
24149ab747fSPaolo Bonzini                 }
24249ab747fSPaolo Bonzini                 n--;
24349ab747fSPaolo Bonzini                 s->datacnt--;
24449ab747fSPaolo Bonzini                 sd_write_data(s->card, value & 0xff);
24549ab747fSPaolo Bonzini                 value >>= 8;
24649ab747fSPaolo Bonzini             }
24749ab747fSPaolo Bonzini         }
24849ab747fSPaolo Bonzini     }
24949ab747fSPaolo Bonzini     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
25049ab747fSPaolo Bonzini     if (s->datacnt == 0) {
25149ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATAEND;
25249ab747fSPaolo Bonzini         /* HACK: */
25349ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATABLOCKEND;
25449ab747fSPaolo Bonzini         DPRINTF("Transfer Complete\n");
25549ab747fSPaolo Bonzini     }
25649ab747fSPaolo Bonzini     if (s->datacnt == 0 && s->fifo_len == 0) {
25749ab747fSPaolo Bonzini         s->datactrl &= ~PL181_DATA_ENABLE;
25849ab747fSPaolo Bonzini         DPRINTF("Data engine idle\n");
25949ab747fSPaolo Bonzini     } else {
26049ab747fSPaolo Bonzini         /* Update FIFO bits.  */
26149ab747fSPaolo Bonzini         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
26249ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
26349ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOEMPTY;
26449ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOEMPTY;
26549ab747fSPaolo Bonzini         } else {
26649ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXDATAAVLBL;
26749ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXDATAAVLBL;
26849ab747fSPaolo Bonzini         }
26949ab747fSPaolo Bonzini         if (s->fifo_len == 16) {
27049ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOFULL;
27149ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOFULL;
27249ab747fSPaolo Bonzini         }
27349ab747fSPaolo Bonzini         if (s->fifo_len <= 8) {
27449ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
27549ab747fSPaolo Bonzini         }
27649ab747fSPaolo Bonzini         if (s->fifo_len >= 8) {
27749ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOHALFFULL;
27849ab747fSPaolo Bonzini         }
27949ab747fSPaolo Bonzini         if (s->datactrl & PL181_DATA_DIRECTION) {
28049ab747fSPaolo Bonzini             bits &= PL181_STATUS_RX_FIFO;
28149ab747fSPaolo Bonzini         } else {
28249ab747fSPaolo Bonzini             bits &= PL181_STATUS_TX_FIFO;
28349ab747fSPaolo Bonzini         }
28449ab747fSPaolo Bonzini         s->status |= bits;
28549ab747fSPaolo Bonzini     }
28649ab747fSPaolo Bonzini }
28749ab747fSPaolo Bonzini 
28849ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset,
28949ab747fSPaolo Bonzini                            unsigned size)
29049ab747fSPaolo Bonzini {
291*1d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
29249ab747fSPaolo Bonzini     uint32_t tmp;
29349ab747fSPaolo Bonzini 
29449ab747fSPaolo Bonzini     if (offset >= 0xfe0 && offset < 0x1000) {
29549ab747fSPaolo Bonzini         return pl181_id[(offset - 0xfe0) >> 2];
29649ab747fSPaolo Bonzini     }
29749ab747fSPaolo Bonzini     switch (offset) {
29849ab747fSPaolo Bonzini     case 0x00: /* Power */
29949ab747fSPaolo Bonzini         return s->power;
30049ab747fSPaolo Bonzini     case 0x04: /* Clock */
30149ab747fSPaolo Bonzini         return s->clock;
30249ab747fSPaolo Bonzini     case 0x08: /* Argument */
30349ab747fSPaolo Bonzini         return s->cmdarg;
30449ab747fSPaolo Bonzini     case 0x0c: /* Command */
30549ab747fSPaolo Bonzini         return s->cmd;
30649ab747fSPaolo Bonzini     case 0x10: /* RespCmd */
30749ab747fSPaolo Bonzini         return s->respcmd;
30849ab747fSPaolo Bonzini     case 0x14: /* Response0 */
30949ab747fSPaolo Bonzini         return s->response[0];
31049ab747fSPaolo Bonzini     case 0x18: /* Response1 */
31149ab747fSPaolo Bonzini         return s->response[1];
31249ab747fSPaolo Bonzini     case 0x1c: /* Response2 */
31349ab747fSPaolo Bonzini         return s->response[2];
31449ab747fSPaolo Bonzini     case 0x20: /* Response3 */
31549ab747fSPaolo Bonzini         return s->response[3];
31649ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
31749ab747fSPaolo Bonzini         return s->datatimer;
31849ab747fSPaolo Bonzini     case 0x28: /* DataLength */
31949ab747fSPaolo Bonzini         return s->datalength;
32049ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
32149ab747fSPaolo Bonzini         return s->datactrl;
32249ab747fSPaolo Bonzini     case 0x30: /* DataCnt */
32349ab747fSPaolo Bonzini         return s->datacnt;
32449ab747fSPaolo Bonzini     case 0x34: /* Status */
32549ab747fSPaolo Bonzini         tmp = s->status;
32649ab747fSPaolo Bonzini         if (s->linux_hack) {
32749ab747fSPaolo Bonzini             s->linux_hack = 0;
32849ab747fSPaolo Bonzini             pl181_fifo_run(s);
32949ab747fSPaolo Bonzini             pl181_update(s);
33049ab747fSPaolo Bonzini         }
33149ab747fSPaolo Bonzini         return tmp;
33249ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
33349ab747fSPaolo Bonzini         return s->mask[0];
33449ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
33549ab747fSPaolo Bonzini         return s->mask[1];
33649ab747fSPaolo Bonzini     case 0x48: /* FifoCnt */
33749ab747fSPaolo Bonzini         /* The documentation is somewhat vague about exactly what FifoCnt
33849ab747fSPaolo Bonzini            does.  On real hardware it appears to be when decrememnted
33949ab747fSPaolo Bonzini            when a word is transferred between the FIFO and the serial
34049ab747fSPaolo Bonzini            data engine.  DataCnt is decremented after each byte is
34149ab747fSPaolo Bonzini            transferred between the serial engine and the card.
34249ab747fSPaolo Bonzini            We don't emulate this level of detail, so both can be the same.  */
34349ab747fSPaolo Bonzini         tmp = (s->datacnt + 3) >> 2;
34449ab747fSPaolo Bonzini         if (s->linux_hack) {
34549ab747fSPaolo Bonzini             s->linux_hack = 0;
34649ab747fSPaolo Bonzini             pl181_fifo_run(s);
34749ab747fSPaolo Bonzini             pl181_update(s);
34849ab747fSPaolo Bonzini         }
34949ab747fSPaolo Bonzini         return tmp;
35049ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
35149ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
35249ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
35349ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
35449ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
35549ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
35649ab747fSPaolo Bonzini             return 0;
35749ab747fSPaolo Bonzini         } else {
35849ab747fSPaolo Bonzini             uint32_t value;
35949ab747fSPaolo Bonzini             value = pl181_fifo_pop(s);
36049ab747fSPaolo Bonzini             s->linux_hack = 1;
36149ab747fSPaolo Bonzini             pl181_fifo_run(s);
36249ab747fSPaolo Bonzini             pl181_update(s);
36349ab747fSPaolo Bonzini             return value;
36449ab747fSPaolo Bonzini         }
36549ab747fSPaolo Bonzini     default:
36649ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
36749ab747fSPaolo Bonzini                       "pl181_read: Bad offset %x\n", (int)offset);
36849ab747fSPaolo Bonzini         return 0;
36949ab747fSPaolo Bonzini     }
37049ab747fSPaolo Bonzini }
37149ab747fSPaolo Bonzini 
37249ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset,
37349ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
37449ab747fSPaolo Bonzini {
375*1d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini     switch (offset) {
37849ab747fSPaolo Bonzini     case 0x00: /* Power */
37949ab747fSPaolo Bonzini         s->power = value & 0xff;
38049ab747fSPaolo Bonzini         break;
38149ab747fSPaolo Bonzini     case 0x04: /* Clock */
38249ab747fSPaolo Bonzini         s->clock = value & 0xff;
38349ab747fSPaolo Bonzini         break;
38449ab747fSPaolo Bonzini     case 0x08: /* Argument */
38549ab747fSPaolo Bonzini         s->cmdarg = value;
38649ab747fSPaolo Bonzini         break;
38749ab747fSPaolo Bonzini     case 0x0c: /* Command */
38849ab747fSPaolo Bonzini         s->cmd = value;
38949ab747fSPaolo Bonzini         if (s->cmd & PL181_CMD_ENABLE) {
39049ab747fSPaolo Bonzini             if (s->cmd & PL181_CMD_INTERRUPT) {
39149ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
39249ab747fSPaolo Bonzini                               "pl181: Interrupt mode not implemented\n");
39349ab747fSPaolo Bonzini             } if (s->cmd & PL181_CMD_PENDING) {
39449ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
39549ab747fSPaolo Bonzini                               "pl181: Pending commands not implemented\n");
39649ab747fSPaolo Bonzini             } else {
39749ab747fSPaolo Bonzini                 pl181_send_command(s);
39849ab747fSPaolo Bonzini                 pl181_fifo_run(s);
39949ab747fSPaolo Bonzini             }
40049ab747fSPaolo Bonzini             /* The command has completed one way or the other.  */
40149ab747fSPaolo Bonzini             s->cmd &= ~PL181_CMD_ENABLE;
40249ab747fSPaolo Bonzini         }
40349ab747fSPaolo Bonzini         break;
40449ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
40549ab747fSPaolo Bonzini         s->datatimer = value;
40649ab747fSPaolo Bonzini         break;
40749ab747fSPaolo Bonzini     case 0x28: /* DataLength */
40849ab747fSPaolo Bonzini         s->datalength = value & 0xffff;
40949ab747fSPaolo Bonzini         break;
41049ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
41149ab747fSPaolo Bonzini         s->datactrl = value & 0xff;
41249ab747fSPaolo Bonzini         if (value & PL181_DATA_ENABLE) {
41349ab747fSPaolo Bonzini             s->datacnt = s->datalength;
41449ab747fSPaolo Bonzini             pl181_fifo_run(s);
41549ab747fSPaolo Bonzini         }
41649ab747fSPaolo Bonzini         break;
41749ab747fSPaolo Bonzini     case 0x38: /* Clear */
41849ab747fSPaolo Bonzini         s->status &= ~(value & 0x7ff);
41949ab747fSPaolo Bonzini         break;
42049ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
42149ab747fSPaolo Bonzini         s->mask[0] = value;
42249ab747fSPaolo Bonzini         break;
42349ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
42449ab747fSPaolo Bonzini         s->mask[1] = value;
42549ab747fSPaolo Bonzini         break;
42649ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
42749ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
42849ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
42949ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
43049ab747fSPaolo Bonzini         if (s->datacnt == 0) {
43149ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
43249ab747fSPaolo Bonzini         } else {
43349ab747fSPaolo Bonzini             pl181_fifo_push(s, value);
43449ab747fSPaolo Bonzini             pl181_fifo_run(s);
43549ab747fSPaolo Bonzini         }
43649ab747fSPaolo Bonzini         break;
43749ab747fSPaolo Bonzini     default:
43849ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
43949ab747fSPaolo Bonzini                       "pl181_write: Bad offset %x\n", (int)offset);
44049ab747fSPaolo Bonzini     }
44149ab747fSPaolo Bonzini     pl181_update(s);
44249ab747fSPaolo Bonzini }
44349ab747fSPaolo Bonzini 
44449ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = {
44549ab747fSPaolo Bonzini     .read = pl181_read,
44649ab747fSPaolo Bonzini     .write = pl181_write,
44749ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
44849ab747fSPaolo Bonzini };
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d)
45149ab747fSPaolo Bonzini {
452*1d998d93SAndreas Färber     PL181State *s = DO_UPCAST(PL181State, busdev.qdev, d);
45349ab747fSPaolo Bonzini 
45449ab747fSPaolo Bonzini     s->power = 0;
45549ab747fSPaolo Bonzini     s->cmdarg = 0;
45649ab747fSPaolo Bonzini     s->cmd = 0;
45749ab747fSPaolo Bonzini     s->datatimer = 0;
45849ab747fSPaolo Bonzini     s->datalength = 0;
45949ab747fSPaolo Bonzini     s->respcmd = 0;
46049ab747fSPaolo Bonzini     s->response[0] = 0;
46149ab747fSPaolo Bonzini     s->response[1] = 0;
46249ab747fSPaolo Bonzini     s->response[2] = 0;
46349ab747fSPaolo Bonzini     s->response[3] = 0;
46449ab747fSPaolo Bonzini     s->datatimer = 0;
46549ab747fSPaolo Bonzini     s->datalength = 0;
46649ab747fSPaolo Bonzini     s->datactrl = 0;
46749ab747fSPaolo Bonzini     s->datacnt = 0;
46849ab747fSPaolo Bonzini     s->status = 0;
46949ab747fSPaolo Bonzini     s->linux_hack = 0;
47049ab747fSPaolo Bonzini     s->mask[0] = 0;
47149ab747fSPaolo Bonzini     s->mask[1] = 0;
47249ab747fSPaolo Bonzini 
47349ab747fSPaolo Bonzini     /* We can assume our GPIO outputs have been wired up now */
47449ab747fSPaolo Bonzini     sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
47549ab747fSPaolo Bonzini }
47649ab747fSPaolo Bonzini 
47749ab747fSPaolo Bonzini static int pl181_init(SysBusDevice *dev)
47849ab747fSPaolo Bonzini {
479*1d998d93SAndreas Färber     PL181State *s = FROM_SYSBUS(PL181State, dev);
48049ab747fSPaolo Bonzini     DriveInfo *dinfo;
48149ab747fSPaolo Bonzini 
48229776739SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000);
48349ab747fSPaolo Bonzini     sysbus_init_mmio(dev, &s->iomem);
48449ab747fSPaolo Bonzini     sysbus_init_irq(dev, &s->irq[0]);
48549ab747fSPaolo Bonzini     sysbus_init_irq(dev, &s->irq[1]);
48649ab747fSPaolo Bonzini     qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2);
48749ab747fSPaolo Bonzini     dinfo = drive_get_next(IF_SD);
4886790f59dSliguang     s->card = sd_init(dinfo ? dinfo->bdrv : NULL, false);
48949ab747fSPaolo Bonzini     return 0;
49049ab747fSPaolo Bonzini }
49149ab747fSPaolo Bonzini 
49249ab747fSPaolo Bonzini static void pl181_class_init(ObjectClass *klass, void *data)
49349ab747fSPaolo Bonzini {
49449ab747fSPaolo Bonzini     SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
49549ab747fSPaolo Bonzini     DeviceClass *k = DEVICE_CLASS(klass);
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini     sdc->init = pl181_init;
49849ab747fSPaolo Bonzini     k->vmsd = &vmstate_pl181;
49949ab747fSPaolo Bonzini     k->reset = pl181_reset;
50049ab747fSPaolo Bonzini     k->no_user = 1;
50149ab747fSPaolo Bonzini }
50249ab747fSPaolo Bonzini 
50349ab747fSPaolo Bonzini static const TypeInfo pl181_info = {
50449ab747fSPaolo Bonzini     .name          = "pl181",
50549ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
506*1d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
50749ab747fSPaolo Bonzini     .class_init    = pl181_class_init,
50849ab747fSPaolo Bonzini };
50949ab747fSPaolo Bonzini 
51049ab747fSPaolo Bonzini static void pl181_register_types(void)
51149ab747fSPaolo Bonzini {
51249ab747fSPaolo Bonzini     type_register_static(&pl181_info);
51349ab747fSPaolo Bonzini }
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini type_init(pl181_register_types)
516