xref: /openbmc/qemu/hw/sd/pl181.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
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 
108ef94f0bSPeter Maydell #include "qemu/osdep.h"
1149ab747fSPaolo Bonzini #include "sysemu/blockdev.h"
1249ab747fSPaolo Bonzini #include "hw/sysbus.h"
13e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h"
1403dd024fSPaolo Bonzini #include "qemu/log.h"
15*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
160d554cb0Sxiaoqiang zhao #include "qapi/error.h"
1749ab747fSPaolo Bonzini 
1849ab747fSPaolo Bonzini //#define DEBUG_PL181 1
1949ab747fSPaolo Bonzini 
2049ab747fSPaolo Bonzini #ifdef DEBUG_PL181
2149ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2249ab747fSPaolo Bonzini do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
2349ab747fSPaolo Bonzini #else
2449ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
2549ab747fSPaolo Bonzini #endif
2649ab747fSPaolo Bonzini 
2749ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16
2849ab747fSPaolo Bonzini 
29630f4442SAndreas Färber #define TYPE_PL181 "pl181"
30630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
31630f4442SAndreas Färber 
321d998d93SAndreas Färber typedef struct PL181State {
33630f4442SAndreas Färber     SysBusDevice parent_obj;
34630f4442SAndreas Färber 
3549ab747fSPaolo Bonzini     MemoryRegion iomem;
3649ab747fSPaolo Bonzini     SDState *card;
3749ab747fSPaolo Bonzini     uint32_t clock;
3849ab747fSPaolo Bonzini     uint32_t power;
3949ab747fSPaolo Bonzini     uint32_t cmdarg;
4049ab747fSPaolo Bonzini     uint32_t cmd;
4149ab747fSPaolo Bonzini     uint32_t datatimer;
4249ab747fSPaolo Bonzini     uint32_t datalength;
4349ab747fSPaolo Bonzini     uint32_t respcmd;
4449ab747fSPaolo Bonzini     uint32_t response[4];
4549ab747fSPaolo Bonzini     uint32_t datactrl;
4649ab747fSPaolo Bonzini     uint32_t datacnt;
4749ab747fSPaolo Bonzini     uint32_t status;
4849ab747fSPaolo Bonzini     uint32_t mask[2];
4949ab747fSPaolo Bonzini     int32_t fifo_pos;
5049ab747fSPaolo Bonzini     int32_t fifo_len;
5149ab747fSPaolo Bonzini     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5267cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
5349ab747fSPaolo Bonzini        subsequent transfers until after the driver polls the status word.
5449ab747fSPaolo Bonzini        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
5549ab747fSPaolo Bonzini      */
5649ab747fSPaolo Bonzini     int32_t linux_hack;
5749ab747fSPaolo Bonzini     uint32_t fifo[PL181_FIFO_LEN];
5849ab747fSPaolo Bonzini     qemu_irq irq[2];
5949ab747fSPaolo Bonzini     /* GPIO outputs for 'card is readonly' and 'card inserted' */
6049ab747fSPaolo Bonzini     qemu_irq cardstatus[2];
611d998d93SAndreas Färber } PL181State;
6249ab747fSPaolo Bonzini 
6349ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = {
6449ab747fSPaolo Bonzini     .name = "pl181",
6549ab747fSPaolo Bonzini     .version_id = 1,
6649ab747fSPaolo Bonzini     .minimum_version_id = 1,
6749ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
681d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
691d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
701d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
751d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
761d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
781d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
791d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
801d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
811d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
821d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
831d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
8449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
8549ab747fSPaolo Bonzini     }
8649ab747fSPaolo Bonzini };
8749ab747fSPaolo Bonzini 
8849ab747fSPaolo Bonzini #define PL181_CMD_INDEX     0x3f
8949ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE  (1 << 6)
9049ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP  (1 << 7)
9149ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8)
9249ab747fSPaolo Bonzini #define PL181_CMD_PENDING   (1 << 9)
9349ab747fSPaolo Bonzini #define PL181_CMD_ENABLE    (1 << 10)
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini #define PL181_DATA_ENABLE             (1 << 0)
9649ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION          (1 << 1)
9749ab747fSPaolo Bonzini #define PL181_DATA_MODE               (1 << 2)
9849ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE          (1 << 3)
9949ab747fSPaolo Bonzini 
10049ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
10149ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL      (1 << 1)
10249ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
10349ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT      (1 << 3)
10449ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN       (1 << 4)
10549ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN        (1 << 5)
10649ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND       (1 << 6)
10749ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT          (1 << 7)
10849ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND          (1 << 8)
10949ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND     (1 << 10)
11049ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE        (1 << 11)
11149ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE         (1 << 12)
11249ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE         (1 << 13)
11349ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
11449ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
11549ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL       (1 << 16)
11649ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL       (1 << 17)
11749ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
11849ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
11949ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
12049ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
12149ab747fSPaolo Bonzini 
12249ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
12349ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOHALFEMPTY \
12449ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOFULL \
12549ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOEMPTY \
12649ab747fSPaolo Bonzini                              |PL181_STATUS_TXDATAAVLBL)
12749ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
12849ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOHALFFULL \
12949ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOFULL \
13049ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOEMPTY \
13149ab747fSPaolo Bonzini                              |PL181_STATUS_RXDATAAVLBL)
13249ab747fSPaolo Bonzini 
13349ab747fSPaolo Bonzini static const unsigned char pl181_id[] =
13449ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
13549ab747fSPaolo Bonzini 
1361d998d93SAndreas Färber static void pl181_update(PL181State *s)
13749ab747fSPaolo Bonzini {
13849ab747fSPaolo Bonzini     int i;
13949ab747fSPaolo Bonzini     for (i = 0; i < 2; i++) {
14049ab747fSPaolo Bonzini         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
14149ab747fSPaolo Bonzini     }
14249ab747fSPaolo Bonzini }
14349ab747fSPaolo Bonzini 
1441d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
14549ab747fSPaolo Bonzini {
14649ab747fSPaolo Bonzini     int n;
14749ab747fSPaolo Bonzini 
14849ab747fSPaolo Bonzini     if (s->fifo_len == PL181_FIFO_LEN) {
14949ab747fSPaolo Bonzini         fprintf(stderr, "pl181: FIFO overflow\n");
15049ab747fSPaolo Bonzini         return;
15149ab747fSPaolo Bonzini     }
15249ab747fSPaolo Bonzini     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
15349ab747fSPaolo Bonzini     s->fifo_len++;
15449ab747fSPaolo Bonzini     s->fifo[n] = value;
15549ab747fSPaolo Bonzini     DPRINTF("FIFO push %08x\n", (int)value);
15649ab747fSPaolo Bonzini }
15749ab747fSPaolo Bonzini 
1581d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
15949ab747fSPaolo Bonzini {
16049ab747fSPaolo Bonzini     uint32_t value;
16149ab747fSPaolo Bonzini 
16249ab747fSPaolo Bonzini     if (s->fifo_len == 0) {
16349ab747fSPaolo Bonzini         fprintf(stderr, "pl181: FIFO underflow\n");
16449ab747fSPaolo Bonzini         return 0;
16549ab747fSPaolo Bonzini     }
16649ab747fSPaolo Bonzini     value = s->fifo[s->fifo_pos];
16749ab747fSPaolo Bonzini     s->fifo_len--;
16849ab747fSPaolo Bonzini     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
16949ab747fSPaolo Bonzini     DPRINTF("FIFO pop %08x\n", (int)value);
17049ab747fSPaolo Bonzini     return value;
17149ab747fSPaolo Bonzini }
17249ab747fSPaolo Bonzini 
1731d998d93SAndreas Färber static void pl181_send_command(PL181State *s)
17449ab747fSPaolo Bonzini {
17549ab747fSPaolo Bonzini     SDRequest request;
17649ab747fSPaolo Bonzini     uint8_t response[16];
17749ab747fSPaolo Bonzini     int rlen;
17849ab747fSPaolo Bonzini 
17949ab747fSPaolo Bonzini     request.cmd = s->cmd & PL181_CMD_INDEX;
18049ab747fSPaolo Bonzini     request.arg = s->cmdarg;
18149ab747fSPaolo Bonzini     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
18249ab747fSPaolo Bonzini     rlen = sd_do_command(s->card, &request, response);
18349ab747fSPaolo Bonzini     if (rlen < 0)
18449ab747fSPaolo Bonzini         goto error;
18549ab747fSPaolo Bonzini     if (s->cmd & PL181_CMD_RESPONSE) {
18649ab747fSPaolo Bonzini         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
18749ab747fSPaolo Bonzini             goto error;
18849ab747fSPaolo Bonzini         if (rlen != 4 && rlen != 16)
18949ab747fSPaolo Bonzini             goto error;
190b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
19149ab747fSPaolo Bonzini         if (rlen == 4) {
19249ab747fSPaolo Bonzini             s->response[1] = s->response[2] = s->response[3] = 0;
19349ab747fSPaolo Bonzini         } else {
194b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
195b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
196b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
19749ab747fSPaolo Bonzini         }
19849ab747fSPaolo Bonzini         DPRINTF("Response received\n");
19949ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDRESPEND;
20049ab747fSPaolo Bonzini     } else {
20149ab747fSPaolo Bonzini         DPRINTF("Command sent\n");
20249ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDSENT;
20349ab747fSPaolo Bonzini     }
20449ab747fSPaolo Bonzini     return;
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini error:
20749ab747fSPaolo Bonzini     DPRINTF("Timeout\n");
20849ab747fSPaolo Bonzini     s->status |= PL181_STATUS_CMDTIMEOUT;
20949ab747fSPaolo Bonzini }
21049ab747fSPaolo Bonzini 
21149ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO.  This is complicated by
21249ab747fSPaolo Bonzini    the FIFO holding 32-bit words and the card taking data in single byte
21349ab747fSPaolo Bonzini    chunks.  FIFO bytes are transferred in little-endian order.  */
21449ab747fSPaolo Bonzini 
2151d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
21649ab747fSPaolo Bonzini {
21749ab747fSPaolo Bonzini     uint32_t bits;
21849ab747fSPaolo Bonzini     uint32_t value = 0;
21949ab747fSPaolo Bonzini     int n;
22049ab747fSPaolo Bonzini     int is_read;
22149ab747fSPaolo Bonzini 
22249ab747fSPaolo Bonzini     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
22349ab747fSPaolo Bonzini     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
22449ab747fSPaolo Bonzini             && !s->linux_hack) {
22549ab747fSPaolo Bonzini         if (is_read) {
22649ab747fSPaolo Bonzini             n = 0;
22749ab747fSPaolo Bonzini             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
22849ab747fSPaolo Bonzini                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
22949ab747fSPaolo Bonzini                 s->datacnt--;
23049ab747fSPaolo Bonzini                 n++;
23149ab747fSPaolo Bonzini                 if (n == 4) {
23249ab747fSPaolo Bonzini                     pl181_fifo_push(s, value);
23349ab747fSPaolo Bonzini                     n = 0;
23449ab747fSPaolo Bonzini                     value = 0;
23549ab747fSPaolo Bonzini                 }
23649ab747fSPaolo Bonzini             }
23749ab747fSPaolo Bonzini             if (n != 0) {
23849ab747fSPaolo Bonzini                 pl181_fifo_push(s, value);
23949ab747fSPaolo Bonzini             }
24049ab747fSPaolo Bonzini         } else { /* write */
24149ab747fSPaolo Bonzini             n = 0;
24249ab747fSPaolo Bonzini             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
24349ab747fSPaolo Bonzini                 if (n == 0) {
24449ab747fSPaolo Bonzini                     value = pl181_fifo_pop(s);
24549ab747fSPaolo Bonzini                     n = 4;
24649ab747fSPaolo Bonzini                 }
24749ab747fSPaolo Bonzini                 n--;
24849ab747fSPaolo Bonzini                 s->datacnt--;
24949ab747fSPaolo Bonzini                 sd_write_data(s->card, value & 0xff);
25049ab747fSPaolo Bonzini                 value >>= 8;
25149ab747fSPaolo Bonzini             }
25249ab747fSPaolo Bonzini         }
25349ab747fSPaolo Bonzini     }
25449ab747fSPaolo Bonzini     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
25549ab747fSPaolo Bonzini     if (s->datacnt == 0) {
25649ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATAEND;
25749ab747fSPaolo Bonzini         /* HACK: */
25849ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATABLOCKEND;
25949ab747fSPaolo Bonzini         DPRINTF("Transfer Complete\n");
26049ab747fSPaolo Bonzini     }
26149ab747fSPaolo Bonzini     if (s->datacnt == 0 && s->fifo_len == 0) {
26249ab747fSPaolo Bonzini         s->datactrl &= ~PL181_DATA_ENABLE;
26349ab747fSPaolo Bonzini         DPRINTF("Data engine idle\n");
26449ab747fSPaolo Bonzini     } else {
26549ab747fSPaolo Bonzini         /* Update FIFO bits.  */
26649ab747fSPaolo Bonzini         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
26749ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
26849ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOEMPTY;
26949ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOEMPTY;
27049ab747fSPaolo Bonzini         } else {
27149ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXDATAAVLBL;
27249ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXDATAAVLBL;
27349ab747fSPaolo Bonzini         }
27449ab747fSPaolo Bonzini         if (s->fifo_len == 16) {
27549ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOFULL;
27649ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOFULL;
27749ab747fSPaolo Bonzini         }
27849ab747fSPaolo Bonzini         if (s->fifo_len <= 8) {
27949ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
28049ab747fSPaolo Bonzini         }
28149ab747fSPaolo Bonzini         if (s->fifo_len >= 8) {
28249ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOHALFFULL;
28349ab747fSPaolo Bonzini         }
28449ab747fSPaolo Bonzini         if (s->datactrl & PL181_DATA_DIRECTION) {
28549ab747fSPaolo Bonzini             bits &= PL181_STATUS_RX_FIFO;
28649ab747fSPaolo Bonzini         } else {
28749ab747fSPaolo Bonzini             bits &= PL181_STATUS_TX_FIFO;
28849ab747fSPaolo Bonzini         }
28949ab747fSPaolo Bonzini         s->status |= bits;
29049ab747fSPaolo Bonzini     }
29149ab747fSPaolo Bonzini }
29249ab747fSPaolo Bonzini 
29349ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset,
29449ab747fSPaolo Bonzini                            unsigned size)
29549ab747fSPaolo Bonzini {
2961d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
29749ab747fSPaolo Bonzini     uint32_t tmp;
29849ab747fSPaolo Bonzini 
29949ab747fSPaolo Bonzini     if (offset >= 0xfe0 && offset < 0x1000) {
30049ab747fSPaolo Bonzini         return pl181_id[(offset - 0xfe0) >> 2];
30149ab747fSPaolo Bonzini     }
30249ab747fSPaolo Bonzini     switch (offset) {
30349ab747fSPaolo Bonzini     case 0x00: /* Power */
30449ab747fSPaolo Bonzini         return s->power;
30549ab747fSPaolo Bonzini     case 0x04: /* Clock */
30649ab747fSPaolo Bonzini         return s->clock;
30749ab747fSPaolo Bonzini     case 0x08: /* Argument */
30849ab747fSPaolo Bonzini         return s->cmdarg;
30949ab747fSPaolo Bonzini     case 0x0c: /* Command */
31049ab747fSPaolo Bonzini         return s->cmd;
31149ab747fSPaolo Bonzini     case 0x10: /* RespCmd */
31249ab747fSPaolo Bonzini         return s->respcmd;
31349ab747fSPaolo Bonzini     case 0x14: /* Response0 */
31449ab747fSPaolo Bonzini         return s->response[0];
31549ab747fSPaolo Bonzini     case 0x18: /* Response1 */
31649ab747fSPaolo Bonzini         return s->response[1];
31749ab747fSPaolo Bonzini     case 0x1c: /* Response2 */
31849ab747fSPaolo Bonzini         return s->response[2];
31949ab747fSPaolo Bonzini     case 0x20: /* Response3 */
32049ab747fSPaolo Bonzini         return s->response[3];
32149ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
32249ab747fSPaolo Bonzini         return s->datatimer;
32349ab747fSPaolo Bonzini     case 0x28: /* DataLength */
32449ab747fSPaolo Bonzini         return s->datalength;
32549ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
32649ab747fSPaolo Bonzini         return s->datactrl;
32749ab747fSPaolo Bonzini     case 0x30: /* DataCnt */
32849ab747fSPaolo Bonzini         return s->datacnt;
32949ab747fSPaolo Bonzini     case 0x34: /* Status */
33049ab747fSPaolo Bonzini         tmp = s->status;
33149ab747fSPaolo Bonzini         if (s->linux_hack) {
33249ab747fSPaolo Bonzini             s->linux_hack = 0;
33349ab747fSPaolo Bonzini             pl181_fifo_run(s);
33449ab747fSPaolo Bonzini             pl181_update(s);
33549ab747fSPaolo Bonzini         }
33649ab747fSPaolo Bonzini         return tmp;
33749ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
33849ab747fSPaolo Bonzini         return s->mask[0];
33949ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
34049ab747fSPaolo Bonzini         return s->mask[1];
34149ab747fSPaolo Bonzini     case 0x48: /* FifoCnt */
34249ab747fSPaolo Bonzini         /* The documentation is somewhat vague about exactly what FifoCnt
34349ab747fSPaolo Bonzini            does.  On real hardware it appears to be when decrememnted
34449ab747fSPaolo Bonzini            when a word is transferred between the FIFO and the serial
34549ab747fSPaolo Bonzini            data engine.  DataCnt is decremented after each byte is
34649ab747fSPaolo Bonzini            transferred between the serial engine and the card.
34749ab747fSPaolo Bonzini            We don't emulate this level of detail, so both can be the same.  */
34849ab747fSPaolo Bonzini         tmp = (s->datacnt + 3) >> 2;
34949ab747fSPaolo Bonzini         if (s->linux_hack) {
35049ab747fSPaolo Bonzini             s->linux_hack = 0;
35149ab747fSPaolo Bonzini             pl181_fifo_run(s);
35249ab747fSPaolo Bonzini             pl181_update(s);
35349ab747fSPaolo Bonzini         }
35449ab747fSPaolo Bonzini         return tmp;
35549ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
35649ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
35749ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
35849ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
35949ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
36049ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
36149ab747fSPaolo Bonzini             return 0;
36249ab747fSPaolo Bonzini         } else {
36349ab747fSPaolo Bonzini             uint32_t value;
36449ab747fSPaolo Bonzini             value = pl181_fifo_pop(s);
36549ab747fSPaolo Bonzini             s->linux_hack = 1;
36649ab747fSPaolo Bonzini             pl181_fifo_run(s);
36749ab747fSPaolo Bonzini             pl181_update(s);
36849ab747fSPaolo Bonzini             return value;
36949ab747fSPaolo Bonzini         }
37049ab747fSPaolo Bonzini     default:
37149ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
37249ab747fSPaolo Bonzini                       "pl181_read: Bad offset %x\n", (int)offset);
37349ab747fSPaolo Bonzini         return 0;
37449ab747fSPaolo Bonzini     }
37549ab747fSPaolo Bonzini }
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset,
37849ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
37949ab747fSPaolo Bonzini {
3801d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
38149ab747fSPaolo Bonzini 
38249ab747fSPaolo Bonzini     switch (offset) {
38349ab747fSPaolo Bonzini     case 0x00: /* Power */
38449ab747fSPaolo Bonzini         s->power = value & 0xff;
38549ab747fSPaolo Bonzini         break;
38649ab747fSPaolo Bonzini     case 0x04: /* Clock */
38749ab747fSPaolo Bonzini         s->clock = value & 0xff;
38849ab747fSPaolo Bonzini         break;
38949ab747fSPaolo Bonzini     case 0x08: /* Argument */
39049ab747fSPaolo Bonzini         s->cmdarg = value;
39149ab747fSPaolo Bonzini         break;
39249ab747fSPaolo Bonzini     case 0x0c: /* Command */
39349ab747fSPaolo Bonzini         s->cmd = value;
39449ab747fSPaolo Bonzini         if (s->cmd & PL181_CMD_ENABLE) {
39549ab747fSPaolo Bonzini             if (s->cmd & PL181_CMD_INTERRUPT) {
39649ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
39749ab747fSPaolo Bonzini                               "pl181: Interrupt mode not implemented\n");
39849ab747fSPaolo Bonzini             } if (s->cmd & PL181_CMD_PENDING) {
39949ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
40049ab747fSPaolo Bonzini                               "pl181: Pending commands not implemented\n");
40149ab747fSPaolo Bonzini             } else {
40249ab747fSPaolo Bonzini                 pl181_send_command(s);
40349ab747fSPaolo Bonzini                 pl181_fifo_run(s);
40449ab747fSPaolo Bonzini             }
40549ab747fSPaolo Bonzini             /* The command has completed one way or the other.  */
40649ab747fSPaolo Bonzini             s->cmd &= ~PL181_CMD_ENABLE;
40749ab747fSPaolo Bonzini         }
40849ab747fSPaolo Bonzini         break;
40949ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
41049ab747fSPaolo Bonzini         s->datatimer = value;
41149ab747fSPaolo Bonzini         break;
41249ab747fSPaolo Bonzini     case 0x28: /* DataLength */
41349ab747fSPaolo Bonzini         s->datalength = value & 0xffff;
41449ab747fSPaolo Bonzini         break;
41549ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
41649ab747fSPaolo Bonzini         s->datactrl = value & 0xff;
41749ab747fSPaolo Bonzini         if (value & PL181_DATA_ENABLE) {
41849ab747fSPaolo Bonzini             s->datacnt = s->datalength;
41949ab747fSPaolo Bonzini             pl181_fifo_run(s);
42049ab747fSPaolo Bonzini         }
42149ab747fSPaolo Bonzini         break;
42249ab747fSPaolo Bonzini     case 0x38: /* Clear */
42349ab747fSPaolo Bonzini         s->status &= ~(value & 0x7ff);
42449ab747fSPaolo Bonzini         break;
42549ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
42649ab747fSPaolo Bonzini         s->mask[0] = value;
42749ab747fSPaolo Bonzini         break;
42849ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
42949ab747fSPaolo Bonzini         s->mask[1] = value;
43049ab747fSPaolo Bonzini         break;
43149ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
43249ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
43349ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
43449ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
43549ab747fSPaolo Bonzini         if (s->datacnt == 0) {
43649ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
43749ab747fSPaolo Bonzini         } else {
43849ab747fSPaolo Bonzini             pl181_fifo_push(s, value);
43949ab747fSPaolo Bonzini             pl181_fifo_run(s);
44049ab747fSPaolo Bonzini         }
44149ab747fSPaolo Bonzini         break;
44249ab747fSPaolo Bonzini     default:
44349ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
44449ab747fSPaolo Bonzini                       "pl181_write: Bad offset %x\n", (int)offset);
44549ab747fSPaolo Bonzini     }
44649ab747fSPaolo Bonzini     pl181_update(s);
44749ab747fSPaolo Bonzini }
44849ab747fSPaolo Bonzini 
44949ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = {
45049ab747fSPaolo Bonzini     .read = pl181_read,
45149ab747fSPaolo Bonzini     .write = pl181_write,
45249ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
45349ab747fSPaolo Bonzini };
45449ab747fSPaolo Bonzini 
45549ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d)
45649ab747fSPaolo Bonzini {
457630f4442SAndreas Färber     PL181State *s = PL181(d);
45849ab747fSPaolo Bonzini 
45949ab747fSPaolo Bonzini     s->power = 0;
46049ab747fSPaolo Bonzini     s->cmdarg = 0;
46149ab747fSPaolo Bonzini     s->cmd = 0;
46249ab747fSPaolo Bonzini     s->datatimer = 0;
46349ab747fSPaolo Bonzini     s->datalength = 0;
46449ab747fSPaolo Bonzini     s->respcmd = 0;
46549ab747fSPaolo Bonzini     s->response[0] = 0;
46649ab747fSPaolo Bonzini     s->response[1] = 0;
46749ab747fSPaolo Bonzini     s->response[2] = 0;
46849ab747fSPaolo Bonzini     s->response[3] = 0;
46949ab747fSPaolo Bonzini     s->datatimer = 0;
47049ab747fSPaolo Bonzini     s->datalength = 0;
47149ab747fSPaolo Bonzini     s->datactrl = 0;
47249ab747fSPaolo Bonzini     s->datacnt = 0;
47349ab747fSPaolo Bonzini     s->status = 0;
47449ab747fSPaolo Bonzini     s->linux_hack = 0;
47549ab747fSPaolo Bonzini     s->mask[0] = 0;
47649ab747fSPaolo Bonzini     s->mask[1] = 0;
47749ab747fSPaolo Bonzini 
47849ab747fSPaolo Bonzini     /* We can assume our GPIO outputs have been wired up now */
47949ab747fSPaolo Bonzini     sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
4800cb57cc7SPeter Maydell     /* Since we're still using the legacy SD API the card is not plugged
4810cb57cc7SPeter Maydell      * into any bus, and we must reset it manually.
4820cb57cc7SPeter Maydell      */
4830cb57cc7SPeter Maydell     device_reset(DEVICE(s->card));
48449ab747fSPaolo Bonzini }
48549ab747fSPaolo Bonzini 
4860d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
48749ab747fSPaolo Bonzini {
4880d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4890d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
4900d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
49149ab747fSPaolo Bonzini 
4920d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
493630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
494630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
495630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
496630f4442SAndreas Färber     qdev_init_gpio_out(dev, s->cardstatus, 2);
4970d554cb0Sxiaoqiang zhao }
4980d554cb0Sxiaoqiang zhao 
4990d554cb0Sxiaoqiang zhao static void pl181_realize(DeviceState *dev, Error **errp)
5000d554cb0Sxiaoqiang zhao {
5010d554cb0Sxiaoqiang zhao     PL181State *s = PL181(dev);
5020d554cb0Sxiaoqiang zhao     DriveInfo *dinfo;
5030d554cb0Sxiaoqiang zhao 
504af9e40aaSMarkus Armbruster     /* FIXME use a qdev drive property instead of drive_get_next() */
50549ab747fSPaolo Bonzini     dinfo = drive_get_next(IF_SD);
5064be74634SMarkus Armbruster     s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
5074f8a066bSKevin Wolf     if (s->card == NULL) {
5080d554cb0Sxiaoqiang zhao         error_setg(errp, "sd_init failed");
5094f8a066bSKevin Wolf     }
51049ab747fSPaolo Bonzini }
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini static void pl181_class_init(ObjectClass *klass, void *data)
51349ab747fSPaolo Bonzini {
51449ab747fSPaolo Bonzini     DeviceClass *k = DEVICE_CLASS(klass);
51549ab747fSPaolo Bonzini 
51649ab747fSPaolo Bonzini     k->vmsd = &vmstate_pl181;
51749ab747fSPaolo Bonzini     k->reset = pl181_reset;
5189f9bdf43SMarkus Armbruster     /* Reason: init() method uses drive_get_next() */
519e90f2a8cSEduardo Habkost     k->user_creatable = false;
5200d554cb0Sxiaoqiang zhao     k->realize = pl181_realize;
52149ab747fSPaolo Bonzini }
52249ab747fSPaolo Bonzini 
52349ab747fSPaolo Bonzini static const TypeInfo pl181_info = {
524630f4442SAndreas Färber     .name          = TYPE_PL181,
52549ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
5261d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5270d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
52849ab747fSPaolo Bonzini     .class_init    = pl181_class_init,
52949ab747fSPaolo Bonzini };
53049ab747fSPaolo Bonzini 
53149ab747fSPaolo Bonzini static void pl181_register_types(void)
53249ab747fSPaolo Bonzini {
53349ab747fSPaolo Bonzini     type_register_static(&pl181_info);
53449ab747fSPaolo Bonzini }
53549ab747fSPaolo Bonzini 
53649ab747fSPaolo Bonzini type_init(pl181_register_types)
537