xref: /openbmc/qemu/hw/sd/pl181.c (revision 2762eed1f5534074fcce703bdda8702905dc4c61)
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"
13d6454270SMarkus Armbruster #include "migration/vmstate.h"
1464552b6bSMarkus Armbruster #include "hw/irq.h"
15e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h"
1603dd024fSPaolo Bonzini #include "qemu/log.h"
170b8fa32fSMarkus Armbruster #include "qemu/module.h"
184858e256SAlistair Francis #include "qemu/error-report.h"
190d554cb0Sxiaoqiang zhao #include "qapi/error.h"
20*2762eed1SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
2149ab747fSPaolo Bonzini 
2249ab747fSPaolo Bonzini //#define DEBUG_PL181 1
2349ab747fSPaolo Bonzini 
2449ab747fSPaolo Bonzini #ifdef DEBUG_PL181
2549ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2649ab747fSPaolo Bonzini do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
2749ab747fSPaolo Bonzini #else
2849ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
2949ab747fSPaolo Bonzini #endif
3049ab747fSPaolo Bonzini 
3149ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16
3249ab747fSPaolo Bonzini 
33630f4442SAndreas Färber #define TYPE_PL181 "pl181"
34630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
35630f4442SAndreas Färber 
36*2762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus"
37*2762eed1SPhilippe Mathieu-Daudé 
381d998d93SAndreas Färber typedef struct PL181State {
39630f4442SAndreas Färber     SysBusDevice parent_obj;
40630f4442SAndreas Färber 
4149ab747fSPaolo Bonzini     MemoryRegion iomem;
42*2762eed1SPhilippe Mathieu-Daudé     SDBus sdbus;
4349ab747fSPaolo Bonzini     uint32_t clock;
4449ab747fSPaolo Bonzini     uint32_t power;
4549ab747fSPaolo Bonzini     uint32_t cmdarg;
4649ab747fSPaolo Bonzini     uint32_t cmd;
4749ab747fSPaolo Bonzini     uint32_t datatimer;
4849ab747fSPaolo Bonzini     uint32_t datalength;
4949ab747fSPaolo Bonzini     uint32_t respcmd;
5049ab747fSPaolo Bonzini     uint32_t response[4];
5149ab747fSPaolo Bonzini     uint32_t datactrl;
5249ab747fSPaolo Bonzini     uint32_t datacnt;
5349ab747fSPaolo Bonzini     uint32_t status;
5449ab747fSPaolo Bonzini     uint32_t mask[2];
5549ab747fSPaolo Bonzini     int32_t fifo_pos;
5649ab747fSPaolo Bonzini     int32_t fifo_len;
5749ab747fSPaolo Bonzini     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5867cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
5949ab747fSPaolo Bonzini        subsequent transfers until after the driver polls the status word.
6049ab747fSPaolo Bonzini        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
6149ab747fSPaolo Bonzini      */
6249ab747fSPaolo Bonzini     int32_t linux_hack;
630e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
6449ab747fSPaolo Bonzini     qemu_irq irq[2];
6549ab747fSPaolo Bonzini     /* GPIO outputs for 'card is readonly' and 'card inserted' */
6626c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_readonly;
6726c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_inserted;
681d998d93SAndreas Färber } PL181State;
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = {
7149ab747fSPaolo Bonzini     .name = "pl181",
7249ab747fSPaolo Bonzini     .version_id = 1,
7349ab747fSPaolo Bonzini     .minimum_version_id = 1,
7449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
751d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
781d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
791d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
801d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
811d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
821d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
831d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
841d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
851d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
861d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
871d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
881d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
891d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
901d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
9149ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
9249ab747fSPaolo Bonzini     }
9349ab747fSPaolo Bonzini };
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini #define PL181_CMD_INDEX     0x3f
9649ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE  (1 << 6)
9749ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP  (1 << 7)
9849ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8)
9949ab747fSPaolo Bonzini #define PL181_CMD_PENDING   (1 << 9)
10049ab747fSPaolo Bonzini #define PL181_CMD_ENABLE    (1 << 10)
10149ab747fSPaolo Bonzini 
10249ab747fSPaolo Bonzini #define PL181_DATA_ENABLE             (1 << 0)
10349ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION          (1 << 1)
10449ab747fSPaolo Bonzini #define PL181_DATA_MODE               (1 << 2)
10549ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE          (1 << 3)
10649ab747fSPaolo Bonzini 
10749ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
10849ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL      (1 << 1)
10949ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
11049ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT      (1 << 3)
11149ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN       (1 << 4)
11249ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN        (1 << 5)
11349ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND       (1 << 6)
11449ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT          (1 << 7)
11549ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND          (1 << 8)
11649ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND     (1 << 10)
11749ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE        (1 << 11)
11849ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE         (1 << 12)
11949ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE         (1 << 13)
12049ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
12149ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
12249ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL       (1 << 16)
12349ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL       (1 << 17)
12449ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
12549ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
12649ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
12749ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
12849ab747fSPaolo Bonzini 
12949ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
13049ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOHALFEMPTY \
13149ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOFULL \
13249ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOEMPTY \
13349ab747fSPaolo Bonzini                              |PL181_STATUS_TXDATAAVLBL)
13449ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
13549ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOHALFFULL \
13649ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOFULL \
13749ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOEMPTY \
13849ab747fSPaolo Bonzini                              |PL181_STATUS_RXDATAAVLBL)
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini static const unsigned char pl181_id[] =
14149ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
14249ab747fSPaolo Bonzini 
1431d998d93SAndreas Färber static void pl181_update(PL181State *s)
14449ab747fSPaolo Bonzini {
14549ab747fSPaolo Bonzini     int i;
14649ab747fSPaolo Bonzini     for (i = 0; i < 2; i++) {
14749ab747fSPaolo Bonzini         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
14849ab747fSPaolo Bonzini     }
14949ab747fSPaolo Bonzini }
15049ab747fSPaolo Bonzini 
1511d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
15249ab747fSPaolo Bonzini {
15349ab747fSPaolo Bonzini     int n;
15449ab747fSPaolo Bonzini 
15549ab747fSPaolo Bonzini     if (s->fifo_len == PL181_FIFO_LEN) {
1564858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
15749ab747fSPaolo Bonzini         return;
15849ab747fSPaolo Bonzini     }
15949ab747fSPaolo Bonzini     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
16049ab747fSPaolo Bonzini     s->fifo_len++;
16149ab747fSPaolo Bonzini     s->fifo[n] = value;
16249ab747fSPaolo Bonzini     DPRINTF("FIFO push %08x\n", (int)value);
16349ab747fSPaolo Bonzini }
16449ab747fSPaolo Bonzini 
1651d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
16649ab747fSPaolo Bonzini {
16749ab747fSPaolo Bonzini     uint32_t value;
16849ab747fSPaolo Bonzini 
16949ab747fSPaolo Bonzini     if (s->fifo_len == 0) {
1704858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
17149ab747fSPaolo Bonzini         return 0;
17249ab747fSPaolo Bonzini     }
17349ab747fSPaolo Bonzini     value = s->fifo[s->fifo_pos];
17449ab747fSPaolo Bonzini     s->fifo_len--;
17549ab747fSPaolo Bonzini     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
17649ab747fSPaolo Bonzini     DPRINTF("FIFO pop %08x\n", (int)value);
17749ab747fSPaolo Bonzini     return value;
17849ab747fSPaolo Bonzini }
17949ab747fSPaolo Bonzini 
180b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
18149ab747fSPaolo Bonzini {
18249ab747fSPaolo Bonzini     SDRequest request;
18349ab747fSPaolo Bonzini     uint8_t response[16];
18449ab747fSPaolo Bonzini     int rlen;
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini     request.cmd = s->cmd & PL181_CMD_INDEX;
18749ab747fSPaolo Bonzini     request.arg = s->cmdarg;
18849ab747fSPaolo Bonzini     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
189*2762eed1SPhilippe Mathieu-Daudé     rlen = sdbus_do_command(&s->sdbus, &request, response);
19049ab747fSPaolo Bonzini     if (rlen < 0)
19149ab747fSPaolo Bonzini         goto error;
19249ab747fSPaolo Bonzini     if (s->cmd & PL181_CMD_RESPONSE) {
19349ab747fSPaolo Bonzini         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
19449ab747fSPaolo Bonzini             goto error;
19549ab747fSPaolo Bonzini         if (rlen != 4 && rlen != 16)
19649ab747fSPaolo Bonzini             goto error;
197b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
19849ab747fSPaolo Bonzini         if (rlen == 4) {
19949ab747fSPaolo Bonzini             s->response[1] = s->response[2] = s->response[3] = 0;
20049ab747fSPaolo Bonzini         } else {
201b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
202b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
203b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
20449ab747fSPaolo Bonzini         }
20549ab747fSPaolo Bonzini         DPRINTF("Response received\n");
20649ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDRESPEND;
20749ab747fSPaolo Bonzini     } else {
20849ab747fSPaolo Bonzini         DPRINTF("Command sent\n");
20949ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDSENT;
21049ab747fSPaolo Bonzini     }
21149ab747fSPaolo Bonzini     return;
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini error:
21449ab747fSPaolo Bonzini     DPRINTF("Timeout\n");
21549ab747fSPaolo Bonzini     s->status |= PL181_STATUS_CMDTIMEOUT;
21649ab747fSPaolo Bonzini }
21749ab747fSPaolo Bonzini 
21849ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO.  This is complicated by
21949ab747fSPaolo Bonzini    the FIFO holding 32-bit words and the card taking data in single byte
22049ab747fSPaolo Bonzini    chunks.  FIFO bytes are transferred in little-endian order.  */
22149ab747fSPaolo Bonzini 
2221d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
22349ab747fSPaolo Bonzini {
22449ab747fSPaolo Bonzini     uint32_t bits;
22549ab747fSPaolo Bonzini     uint32_t value = 0;
22649ab747fSPaolo Bonzini     int n;
22749ab747fSPaolo Bonzini     int is_read;
22849ab747fSPaolo Bonzini 
22949ab747fSPaolo Bonzini     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
230*2762eed1SPhilippe Mathieu-Daudé     if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
23149ab747fSPaolo Bonzini             && !s->linux_hack) {
23249ab747fSPaolo Bonzini         if (is_read) {
23349ab747fSPaolo Bonzini             n = 0;
23449ab747fSPaolo Bonzini             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
235*2762eed1SPhilippe Mathieu-Daudé                 value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8);
23649ab747fSPaolo Bonzini                 s->datacnt--;
23749ab747fSPaolo Bonzini                 n++;
23849ab747fSPaolo Bonzini                 if (n == 4) {
23949ab747fSPaolo Bonzini                     pl181_fifo_push(s, value);
24049ab747fSPaolo Bonzini                     n = 0;
24149ab747fSPaolo Bonzini                     value = 0;
24249ab747fSPaolo Bonzini                 }
24349ab747fSPaolo Bonzini             }
24449ab747fSPaolo Bonzini             if (n != 0) {
24549ab747fSPaolo Bonzini                 pl181_fifo_push(s, value);
24649ab747fSPaolo Bonzini             }
24749ab747fSPaolo Bonzini         } else { /* write */
24849ab747fSPaolo Bonzini             n = 0;
24949ab747fSPaolo Bonzini             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
25049ab747fSPaolo Bonzini                 if (n == 0) {
25149ab747fSPaolo Bonzini                     value = pl181_fifo_pop(s);
25249ab747fSPaolo Bonzini                     n = 4;
25349ab747fSPaolo Bonzini                 }
25449ab747fSPaolo Bonzini                 n--;
25549ab747fSPaolo Bonzini                 s->datacnt--;
256*2762eed1SPhilippe Mathieu-Daudé                 sdbus_write_data(&s->sdbus, value & 0xff);
25749ab747fSPaolo Bonzini                 value >>= 8;
25849ab747fSPaolo Bonzini             }
25949ab747fSPaolo Bonzini         }
26049ab747fSPaolo Bonzini     }
26149ab747fSPaolo Bonzini     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
26249ab747fSPaolo Bonzini     if (s->datacnt == 0) {
26349ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATAEND;
26449ab747fSPaolo Bonzini         /* HACK: */
26549ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATABLOCKEND;
26649ab747fSPaolo Bonzini         DPRINTF("Transfer Complete\n");
26749ab747fSPaolo Bonzini     }
26849ab747fSPaolo Bonzini     if (s->datacnt == 0 && s->fifo_len == 0) {
26949ab747fSPaolo Bonzini         s->datactrl &= ~PL181_DATA_ENABLE;
27049ab747fSPaolo Bonzini         DPRINTF("Data engine idle\n");
27149ab747fSPaolo Bonzini     } else {
27249ab747fSPaolo Bonzini         /* Update FIFO bits.  */
27349ab747fSPaolo Bonzini         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
27449ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
27549ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOEMPTY;
27649ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOEMPTY;
27749ab747fSPaolo Bonzini         } else {
27849ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXDATAAVLBL;
27949ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXDATAAVLBL;
28049ab747fSPaolo Bonzini         }
28149ab747fSPaolo Bonzini         if (s->fifo_len == 16) {
28249ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOFULL;
28349ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOFULL;
28449ab747fSPaolo Bonzini         }
28549ab747fSPaolo Bonzini         if (s->fifo_len <= 8) {
28649ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
28749ab747fSPaolo Bonzini         }
28849ab747fSPaolo Bonzini         if (s->fifo_len >= 8) {
28949ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOHALFFULL;
29049ab747fSPaolo Bonzini         }
29149ab747fSPaolo Bonzini         if (s->datactrl & PL181_DATA_DIRECTION) {
29249ab747fSPaolo Bonzini             bits &= PL181_STATUS_RX_FIFO;
29349ab747fSPaolo Bonzini         } else {
29449ab747fSPaolo Bonzini             bits &= PL181_STATUS_TX_FIFO;
29549ab747fSPaolo Bonzini         }
29649ab747fSPaolo Bonzini         s->status |= bits;
29749ab747fSPaolo Bonzini     }
29849ab747fSPaolo Bonzini }
29949ab747fSPaolo Bonzini 
30049ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset,
30149ab747fSPaolo Bonzini                            unsigned size)
30249ab747fSPaolo Bonzini {
3031d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
30449ab747fSPaolo Bonzini     uint32_t tmp;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini     if (offset >= 0xfe0 && offset < 0x1000) {
30749ab747fSPaolo Bonzini         return pl181_id[(offset - 0xfe0) >> 2];
30849ab747fSPaolo Bonzini     }
30949ab747fSPaolo Bonzini     switch (offset) {
31049ab747fSPaolo Bonzini     case 0x00: /* Power */
31149ab747fSPaolo Bonzini         return s->power;
31249ab747fSPaolo Bonzini     case 0x04: /* Clock */
31349ab747fSPaolo Bonzini         return s->clock;
31449ab747fSPaolo Bonzini     case 0x08: /* Argument */
31549ab747fSPaolo Bonzini         return s->cmdarg;
31649ab747fSPaolo Bonzini     case 0x0c: /* Command */
31749ab747fSPaolo Bonzini         return s->cmd;
31849ab747fSPaolo Bonzini     case 0x10: /* RespCmd */
31949ab747fSPaolo Bonzini         return s->respcmd;
32049ab747fSPaolo Bonzini     case 0x14: /* Response0 */
32149ab747fSPaolo Bonzini         return s->response[0];
32249ab747fSPaolo Bonzini     case 0x18: /* Response1 */
32349ab747fSPaolo Bonzini         return s->response[1];
32449ab747fSPaolo Bonzini     case 0x1c: /* Response2 */
32549ab747fSPaolo Bonzini         return s->response[2];
32649ab747fSPaolo Bonzini     case 0x20: /* Response3 */
32749ab747fSPaolo Bonzini         return s->response[3];
32849ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
32949ab747fSPaolo Bonzini         return s->datatimer;
33049ab747fSPaolo Bonzini     case 0x28: /* DataLength */
33149ab747fSPaolo Bonzini         return s->datalength;
33249ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
33349ab747fSPaolo Bonzini         return s->datactrl;
33449ab747fSPaolo Bonzini     case 0x30: /* DataCnt */
33549ab747fSPaolo Bonzini         return s->datacnt;
33649ab747fSPaolo Bonzini     case 0x34: /* Status */
33749ab747fSPaolo Bonzini         tmp = s->status;
33849ab747fSPaolo Bonzini         if (s->linux_hack) {
33949ab747fSPaolo Bonzini             s->linux_hack = 0;
34049ab747fSPaolo Bonzini             pl181_fifo_run(s);
34149ab747fSPaolo Bonzini             pl181_update(s);
34249ab747fSPaolo Bonzini         }
34349ab747fSPaolo Bonzini         return tmp;
34449ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
34549ab747fSPaolo Bonzini         return s->mask[0];
34649ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
34749ab747fSPaolo Bonzini         return s->mask[1];
34849ab747fSPaolo Bonzini     case 0x48: /* FifoCnt */
34949ab747fSPaolo Bonzini         /* The documentation is somewhat vague about exactly what FifoCnt
35049ab747fSPaolo Bonzini            does.  On real hardware it appears to be when decrememnted
35149ab747fSPaolo Bonzini            when a word is transferred between the FIFO and the serial
35249ab747fSPaolo Bonzini            data engine.  DataCnt is decremented after each byte is
35349ab747fSPaolo Bonzini            transferred between the serial engine and the card.
35449ab747fSPaolo Bonzini            We don't emulate this level of detail, so both can be the same.  */
35549ab747fSPaolo Bonzini         tmp = (s->datacnt + 3) >> 2;
35649ab747fSPaolo Bonzini         if (s->linux_hack) {
35749ab747fSPaolo Bonzini             s->linux_hack = 0;
35849ab747fSPaolo Bonzini             pl181_fifo_run(s);
35949ab747fSPaolo Bonzini             pl181_update(s);
36049ab747fSPaolo Bonzini         }
36149ab747fSPaolo Bonzini         return tmp;
36249ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
36349ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
36449ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
36549ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
36649ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
36749ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
36849ab747fSPaolo Bonzini             return 0;
36949ab747fSPaolo Bonzini         } else {
37049ab747fSPaolo Bonzini             uint32_t value;
37149ab747fSPaolo Bonzini             value = pl181_fifo_pop(s);
37249ab747fSPaolo Bonzini             s->linux_hack = 1;
37349ab747fSPaolo Bonzini             pl181_fifo_run(s);
37449ab747fSPaolo Bonzini             pl181_update(s);
37549ab747fSPaolo Bonzini             return value;
37649ab747fSPaolo Bonzini         }
37749ab747fSPaolo Bonzini     default:
37849ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
37949ab747fSPaolo Bonzini                       "pl181_read: Bad offset %x\n", (int)offset);
38049ab747fSPaolo Bonzini         return 0;
38149ab747fSPaolo Bonzini     }
38249ab747fSPaolo Bonzini }
38349ab747fSPaolo Bonzini 
38449ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset,
38549ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
38649ab747fSPaolo Bonzini {
3871d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
38849ab747fSPaolo Bonzini 
38949ab747fSPaolo Bonzini     switch (offset) {
39049ab747fSPaolo Bonzini     case 0x00: /* Power */
39149ab747fSPaolo Bonzini         s->power = value & 0xff;
39249ab747fSPaolo Bonzini         break;
39349ab747fSPaolo Bonzini     case 0x04: /* Clock */
39449ab747fSPaolo Bonzini         s->clock = value & 0xff;
39549ab747fSPaolo Bonzini         break;
39649ab747fSPaolo Bonzini     case 0x08: /* Argument */
39749ab747fSPaolo Bonzini         s->cmdarg = value;
39849ab747fSPaolo Bonzini         break;
39949ab747fSPaolo Bonzini     case 0x0c: /* Command */
40049ab747fSPaolo Bonzini         s->cmd = value;
40149ab747fSPaolo Bonzini         if (s->cmd & PL181_CMD_ENABLE) {
40249ab747fSPaolo Bonzini             if (s->cmd & PL181_CMD_INTERRUPT) {
40349ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
40449ab747fSPaolo Bonzini                               "pl181: Interrupt mode not implemented\n");
40549ab747fSPaolo Bonzini             } if (s->cmd & PL181_CMD_PENDING) {
40649ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
40749ab747fSPaolo Bonzini                               "pl181: Pending commands not implemented\n");
40849ab747fSPaolo Bonzini             } else {
409b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
41049ab747fSPaolo Bonzini                 pl181_fifo_run(s);
41149ab747fSPaolo Bonzini             }
41249ab747fSPaolo Bonzini             /* The command has completed one way or the other.  */
41349ab747fSPaolo Bonzini             s->cmd &= ~PL181_CMD_ENABLE;
41449ab747fSPaolo Bonzini         }
41549ab747fSPaolo Bonzini         break;
41649ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
41749ab747fSPaolo Bonzini         s->datatimer = value;
41849ab747fSPaolo Bonzini         break;
41949ab747fSPaolo Bonzini     case 0x28: /* DataLength */
42049ab747fSPaolo Bonzini         s->datalength = value & 0xffff;
42149ab747fSPaolo Bonzini         break;
42249ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
42349ab747fSPaolo Bonzini         s->datactrl = value & 0xff;
42449ab747fSPaolo Bonzini         if (value & PL181_DATA_ENABLE) {
42549ab747fSPaolo Bonzini             s->datacnt = s->datalength;
42649ab747fSPaolo Bonzini             pl181_fifo_run(s);
42749ab747fSPaolo Bonzini         }
42849ab747fSPaolo Bonzini         break;
42949ab747fSPaolo Bonzini     case 0x38: /* Clear */
43049ab747fSPaolo Bonzini         s->status &= ~(value & 0x7ff);
43149ab747fSPaolo Bonzini         break;
43249ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
43349ab747fSPaolo Bonzini         s->mask[0] = value;
43449ab747fSPaolo Bonzini         break;
43549ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
43649ab747fSPaolo Bonzini         s->mask[1] = value;
43749ab747fSPaolo Bonzini         break;
43849ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
43949ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
44049ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
44149ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
44249ab747fSPaolo Bonzini         if (s->datacnt == 0) {
44349ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
44449ab747fSPaolo Bonzini         } else {
44549ab747fSPaolo Bonzini             pl181_fifo_push(s, value);
44649ab747fSPaolo Bonzini             pl181_fifo_run(s);
44749ab747fSPaolo Bonzini         }
44849ab747fSPaolo Bonzini         break;
44949ab747fSPaolo Bonzini     default:
45049ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
45149ab747fSPaolo Bonzini                       "pl181_write: Bad offset %x\n", (int)offset);
45249ab747fSPaolo Bonzini     }
45349ab747fSPaolo Bonzini     pl181_update(s);
45449ab747fSPaolo Bonzini }
45549ab747fSPaolo Bonzini 
45649ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = {
45749ab747fSPaolo Bonzini     .read = pl181_read,
45849ab747fSPaolo Bonzini     .write = pl181_write,
45949ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
46049ab747fSPaolo Bonzini };
46149ab747fSPaolo Bonzini 
462*2762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level)
463*2762eed1SPhilippe Mathieu-Daudé {
464*2762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
465*2762eed1SPhilippe Mathieu-Daudé 
466*2762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_readonly, level);
467*2762eed1SPhilippe Mathieu-Daudé }
468*2762eed1SPhilippe Mathieu-Daudé 
469*2762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level)
470*2762eed1SPhilippe Mathieu-Daudé {
471*2762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
472*2762eed1SPhilippe Mathieu-Daudé 
473*2762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_inserted, level);
474*2762eed1SPhilippe Mathieu-Daudé }
475*2762eed1SPhilippe Mathieu-Daudé 
47649ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d)
47749ab747fSPaolo Bonzini {
478630f4442SAndreas Färber     PL181State *s = PL181(d);
47949ab747fSPaolo Bonzini 
48049ab747fSPaolo Bonzini     s->power = 0;
48149ab747fSPaolo Bonzini     s->cmdarg = 0;
48249ab747fSPaolo Bonzini     s->cmd = 0;
48349ab747fSPaolo Bonzini     s->datatimer = 0;
48449ab747fSPaolo Bonzini     s->datalength = 0;
48549ab747fSPaolo Bonzini     s->respcmd = 0;
48649ab747fSPaolo Bonzini     s->response[0] = 0;
48749ab747fSPaolo Bonzini     s->response[1] = 0;
48849ab747fSPaolo Bonzini     s->response[2] = 0;
48949ab747fSPaolo Bonzini     s->response[3] = 0;
49049ab747fSPaolo Bonzini     s->datatimer = 0;
49149ab747fSPaolo Bonzini     s->datalength = 0;
49249ab747fSPaolo Bonzini     s->datactrl = 0;
49349ab747fSPaolo Bonzini     s->datacnt = 0;
49449ab747fSPaolo Bonzini     s->status = 0;
49549ab747fSPaolo Bonzini     s->linux_hack = 0;
49649ab747fSPaolo Bonzini     s->mask[0] = 0;
49749ab747fSPaolo Bonzini     s->mask[1] = 0;
49849ab747fSPaolo Bonzini 
499*2762eed1SPhilippe Mathieu-Daudé     /* Reset other state based on current card insertion/readonly status */
500*2762eed1SPhilippe Mathieu-Daudé     pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus));
501*2762eed1SPhilippe Mathieu-Daudé     pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus));
50249ab747fSPaolo Bonzini }
50349ab747fSPaolo Bonzini 
5040d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
50549ab747fSPaolo Bonzini {
5060d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
5070d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
5080d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
50949ab747fSPaolo Bonzini 
5100d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
511630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
512630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
513630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
51426c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
51526c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
516*2762eed1SPhilippe Mathieu-Daudé 
517*2762eed1SPhilippe Mathieu-Daudé     qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
518*2762eed1SPhilippe Mathieu-Daudé                         TYPE_PL181_BUS, dev, "sd-bus");
5190d554cb0Sxiaoqiang zhao }
5200d554cb0Sxiaoqiang zhao 
5210d554cb0Sxiaoqiang zhao static void pl181_realize(DeviceState *dev, Error **errp)
5220d554cb0Sxiaoqiang zhao {
523*2762eed1SPhilippe Mathieu-Daudé     DeviceState *card;
5240d554cb0Sxiaoqiang zhao     DriveInfo *dinfo;
5250d554cb0Sxiaoqiang zhao 
526af9e40aaSMarkus Armbruster     /* FIXME use a qdev drive property instead of drive_get_next() */
527*2762eed1SPhilippe Mathieu-Daudé     card = qdev_new(TYPE_SD_CARD);
52849ab747fSPaolo Bonzini     dinfo = drive_get_next(IF_SD);
529*2762eed1SPhilippe Mathieu-Daudé     qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo),
530*2762eed1SPhilippe Mathieu-Daudé                             &error_fatal);
531*2762eed1SPhilippe Mathieu-Daudé     qdev_realize_and_unref(card,
532*2762eed1SPhilippe Mathieu-Daudé                            qdev_get_child_bus(dev, "sd-bus"),
533*2762eed1SPhilippe Mathieu-Daudé                            &error_fatal);
53449ab747fSPaolo Bonzini }
53549ab747fSPaolo Bonzini 
53649ab747fSPaolo Bonzini static void pl181_class_init(ObjectClass *klass, void *data)
53749ab747fSPaolo Bonzini {
53849ab747fSPaolo Bonzini     DeviceClass *k = DEVICE_CLASS(klass);
53949ab747fSPaolo Bonzini 
54049ab747fSPaolo Bonzini     k->vmsd = &vmstate_pl181;
54149ab747fSPaolo Bonzini     k->reset = pl181_reset;
5429f9bdf43SMarkus Armbruster     /* Reason: init() method uses drive_get_next() */
543e90f2a8cSEduardo Habkost     k->user_creatable = false;
5440d554cb0Sxiaoqiang zhao     k->realize = pl181_realize;
54549ab747fSPaolo Bonzini }
54649ab747fSPaolo Bonzini 
54749ab747fSPaolo Bonzini static const TypeInfo pl181_info = {
548630f4442SAndreas Färber     .name          = TYPE_PL181,
54949ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
5501d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5510d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
55249ab747fSPaolo Bonzini     .class_init    = pl181_class_init,
55349ab747fSPaolo Bonzini };
55449ab747fSPaolo Bonzini 
555*2762eed1SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, void *data)
556*2762eed1SPhilippe Mathieu-Daudé {
557*2762eed1SPhilippe Mathieu-Daudé     SDBusClass *sbc = SD_BUS_CLASS(klass);
558*2762eed1SPhilippe Mathieu-Daudé 
559*2762eed1SPhilippe Mathieu-Daudé     sbc->set_inserted = pl181_set_inserted;
560*2762eed1SPhilippe Mathieu-Daudé     sbc->set_readonly = pl181_set_readonly;
561*2762eed1SPhilippe Mathieu-Daudé }
562*2762eed1SPhilippe Mathieu-Daudé 
563*2762eed1SPhilippe Mathieu-Daudé static const TypeInfo pl181_bus_info = {
564*2762eed1SPhilippe Mathieu-Daudé     .name = TYPE_PL181_BUS,
565*2762eed1SPhilippe Mathieu-Daudé     .parent = TYPE_SD_BUS,
566*2762eed1SPhilippe Mathieu-Daudé     .instance_size = sizeof(SDBus),
567*2762eed1SPhilippe Mathieu-Daudé     .class_init = pl181_bus_class_init,
568*2762eed1SPhilippe Mathieu-Daudé };
569*2762eed1SPhilippe Mathieu-Daudé 
57049ab747fSPaolo Bonzini static void pl181_register_types(void)
57149ab747fSPaolo Bonzini {
57249ab747fSPaolo Bonzini     type_register_static(&pl181_info);
573*2762eed1SPhilippe Mathieu-Daudé     type_register_static(&pl181_bus_info);
57449ab747fSPaolo Bonzini }
57549ab747fSPaolo Bonzini 
57649ab747fSPaolo Bonzini type_init(pl181_register_types)
577