xref: /qemu/hw/sd/pl181.c (revision e3d08143)
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"
20583d09f0SPhilippe Mathieu-Daudé #include "trace.h"
21db1015e9SEduardo Habkost #include "qom/object.h"
2249ab747fSPaolo Bonzini 
2349ab747fSPaolo Bonzini #define PL181_FIFO_LEN 16
2449ab747fSPaolo Bonzini 
25630f4442SAndreas Färber #define TYPE_PL181 "pl181"
268063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PL181State, PL181)
27630f4442SAndreas Färber 
282762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus"
292762eed1SPhilippe Mathieu-Daudé 
30db1015e9SEduardo Habkost struct PL181State {
31630f4442SAndreas Färber     SysBusDevice parent_obj;
32630f4442SAndreas Färber 
3349ab747fSPaolo Bonzini     MemoryRegion iomem;
342762eed1SPhilippe Mathieu-Daudé     SDBus sdbus;
3549ab747fSPaolo Bonzini     uint32_t clock;
3649ab747fSPaolo Bonzini     uint32_t power;
3749ab747fSPaolo Bonzini     uint32_t cmdarg;
3849ab747fSPaolo Bonzini     uint32_t cmd;
3949ab747fSPaolo Bonzini     uint32_t datatimer;
4049ab747fSPaolo Bonzini     uint32_t datalength;
4149ab747fSPaolo Bonzini     uint32_t respcmd;
4249ab747fSPaolo Bonzini     uint32_t response[4];
4349ab747fSPaolo Bonzini     uint32_t datactrl;
4449ab747fSPaolo Bonzini     uint32_t datacnt;
4549ab747fSPaolo Bonzini     uint32_t status;
4649ab747fSPaolo Bonzini     uint32_t mask[2];
4749ab747fSPaolo Bonzini     int32_t fifo_pos;
4849ab747fSPaolo Bonzini     int32_t fifo_len;
4949ab747fSPaolo Bonzini     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5067cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
5149ab747fSPaolo Bonzini        subsequent transfers until after the driver polls the status word.
5249ab747fSPaolo Bonzini        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
5349ab747fSPaolo Bonzini      */
5449ab747fSPaolo Bonzini     int32_t linux_hack;
550e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
5649ab747fSPaolo Bonzini     qemu_irq irq[2];
5749ab747fSPaolo Bonzini     /* GPIO outputs for 'card is readonly' and 'card inserted' */
5826c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_readonly;
5926c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_inserted;
60db1015e9SEduardo Habkost };
6149ab747fSPaolo Bonzini 
6249ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl181 = {
6349ab747fSPaolo Bonzini     .name = "pl181",
6449ab747fSPaolo Bonzini     .version_id = 1,
6549ab747fSPaolo Bonzini     .minimum_version_id = 1,
66307119baSRichard Henderson     .fields = (const VMStateField[]) {
671d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
681d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
691d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
701d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
751d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
781d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
791d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
801d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
811d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
821d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
8349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
8449ab747fSPaolo Bonzini     }
8549ab747fSPaolo Bonzini };
8649ab747fSPaolo Bonzini 
8749ab747fSPaolo Bonzini #define PL181_CMD_INDEX     0x3f
8849ab747fSPaolo Bonzini #define PL181_CMD_RESPONSE  (1 << 6)
8949ab747fSPaolo Bonzini #define PL181_CMD_LONGRESP  (1 << 7)
9049ab747fSPaolo Bonzini #define PL181_CMD_INTERRUPT (1 << 8)
9149ab747fSPaolo Bonzini #define PL181_CMD_PENDING   (1 << 9)
9249ab747fSPaolo Bonzini #define PL181_CMD_ENABLE    (1 << 10)
9349ab747fSPaolo Bonzini 
9449ab747fSPaolo Bonzini #define PL181_DATA_ENABLE             (1 << 0)
9549ab747fSPaolo Bonzini #define PL181_DATA_DIRECTION          (1 << 1)
9649ab747fSPaolo Bonzini #define PL181_DATA_MODE               (1 << 2)
9749ab747fSPaolo Bonzini #define PL181_DATA_DMAENABLE          (1 << 3)
9849ab747fSPaolo Bonzini 
9949ab747fSPaolo Bonzini #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
10049ab747fSPaolo Bonzini #define PL181_STATUS_DATACRCFAIL      (1 << 1)
10149ab747fSPaolo Bonzini #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
10249ab747fSPaolo Bonzini #define PL181_STATUS_DATATIMEOUT      (1 << 3)
10349ab747fSPaolo Bonzini #define PL181_STATUS_TXUNDERRUN       (1 << 4)
10449ab747fSPaolo Bonzini #define PL181_STATUS_RXOVERRUN        (1 << 5)
10549ab747fSPaolo Bonzini #define PL181_STATUS_CMDRESPEND       (1 << 6)
10649ab747fSPaolo Bonzini #define PL181_STATUS_CMDSENT          (1 << 7)
10749ab747fSPaolo Bonzini #define PL181_STATUS_DATAEND          (1 << 8)
10849ab747fSPaolo Bonzini #define PL181_STATUS_DATABLOCKEND     (1 << 10)
10949ab747fSPaolo Bonzini #define PL181_STATUS_CMDACTIVE        (1 << 11)
11049ab747fSPaolo Bonzini #define PL181_STATUS_TXACTIVE         (1 << 12)
11149ab747fSPaolo Bonzini #define PL181_STATUS_RXACTIVE         (1 << 13)
11249ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
11349ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
11449ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOFULL       (1 << 16)
11549ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOFULL       (1 << 17)
11649ab747fSPaolo Bonzini #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
11749ab747fSPaolo Bonzini #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
11849ab747fSPaolo Bonzini #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
11949ab747fSPaolo Bonzini #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
12049ab747fSPaolo Bonzini 
12149ab747fSPaolo Bonzini #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
12249ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOHALFEMPTY \
12349ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOFULL \
12449ab747fSPaolo Bonzini                              |PL181_STATUS_TXFIFOEMPTY \
12549ab747fSPaolo Bonzini                              |PL181_STATUS_TXDATAAVLBL)
12649ab747fSPaolo Bonzini #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
12749ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOHALFFULL \
12849ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOFULL \
12949ab747fSPaolo Bonzini                              |PL181_STATUS_RXFIFOEMPTY \
13049ab747fSPaolo Bonzini                              |PL181_STATUS_RXDATAAVLBL)
13149ab747fSPaolo Bonzini 
13249ab747fSPaolo Bonzini static const unsigned char pl181_id[] =
13349ab747fSPaolo Bonzini { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
13449ab747fSPaolo Bonzini 
pl181_update(PL181State * s)1351d998d93SAndreas Färber static void pl181_update(PL181State *s)
13649ab747fSPaolo Bonzini {
13749ab747fSPaolo Bonzini     int i;
13849ab747fSPaolo Bonzini     for (i = 0; i < 2; i++) {
13949ab747fSPaolo Bonzini         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
14049ab747fSPaolo Bonzini     }
14149ab747fSPaolo Bonzini }
14249ab747fSPaolo Bonzini 
pl181_fifo_push(PL181State * s,uint32_t value)1431d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
14449ab747fSPaolo Bonzini {
14549ab747fSPaolo Bonzini     int n;
14649ab747fSPaolo Bonzini 
14749ab747fSPaolo Bonzini     if (s->fifo_len == PL181_FIFO_LEN) {
1484858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
14949ab747fSPaolo Bonzini         return;
15049ab747fSPaolo Bonzini     }
15149ab747fSPaolo Bonzini     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
15249ab747fSPaolo Bonzini     s->fifo_len++;
15349ab747fSPaolo Bonzini     s->fifo[n] = value;
154583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_push(value);
15549ab747fSPaolo Bonzini }
15649ab747fSPaolo Bonzini 
pl181_fifo_pop(PL181State * s)1571d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
15849ab747fSPaolo Bonzini {
15949ab747fSPaolo Bonzini     uint32_t value;
16049ab747fSPaolo Bonzini 
16149ab747fSPaolo Bonzini     if (s->fifo_len == 0) {
1624858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
16349ab747fSPaolo Bonzini         return 0;
16449ab747fSPaolo Bonzini     }
16549ab747fSPaolo Bonzini     value = s->fifo[s->fifo_pos];
16649ab747fSPaolo Bonzini     s->fifo_len--;
16749ab747fSPaolo Bonzini     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
168583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_pop(value);
16949ab747fSPaolo Bonzini     return value;
17049ab747fSPaolo Bonzini }
17149ab747fSPaolo Bonzini 
pl181_do_command(PL181State * s)172b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
17349ab747fSPaolo Bonzini {
17449ab747fSPaolo Bonzini     SDRequest request;
17549ab747fSPaolo Bonzini     uint8_t response[16];
17649ab747fSPaolo Bonzini     int rlen;
17749ab747fSPaolo Bonzini 
17849ab747fSPaolo Bonzini     request.cmd = s->cmd & PL181_CMD_INDEX;
17949ab747fSPaolo Bonzini     request.arg = s->cmdarg;
180583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_send(request.cmd, request.arg);
1812762eed1SPhilippe Mathieu-Daudé     rlen = sdbus_do_command(&s->sdbus, &request, response);
18249ab747fSPaolo Bonzini     if (rlen < 0)
18349ab747fSPaolo Bonzini         goto error;
18449ab747fSPaolo Bonzini     if (s->cmd & PL181_CMD_RESPONSE) {
18549ab747fSPaolo Bonzini         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
18649ab747fSPaolo Bonzini             goto error;
18749ab747fSPaolo Bonzini         if (rlen != 4 && rlen != 16)
18849ab747fSPaolo Bonzini             goto error;
189b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
19049ab747fSPaolo Bonzini         if (rlen == 4) {
19149ab747fSPaolo Bonzini             s->response[1] = s->response[2] = s->response[3] = 0;
19249ab747fSPaolo Bonzini         } else {
193b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
194b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
195b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
19649ab747fSPaolo Bonzini         }
197583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_response_pending();
19849ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDRESPEND;
19949ab747fSPaolo Bonzini     } else {
200583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_sent();
20149ab747fSPaolo Bonzini         s->status |= PL181_STATUS_CMDSENT;
20249ab747fSPaolo Bonzini     }
20349ab747fSPaolo Bonzini     return;
20449ab747fSPaolo Bonzini 
20549ab747fSPaolo Bonzini error:
206583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_timeout();
20749ab747fSPaolo Bonzini     s->status |= PL181_STATUS_CMDTIMEOUT;
20849ab747fSPaolo Bonzini }
20949ab747fSPaolo Bonzini 
21049ab747fSPaolo Bonzini /* Transfer data between the card and the FIFO.  This is complicated by
21149ab747fSPaolo Bonzini    the FIFO holding 32-bit words and the card taking data in single byte
21249ab747fSPaolo Bonzini    chunks.  FIFO bytes are transferred in little-endian order.  */
21349ab747fSPaolo Bonzini 
pl181_fifo_run(PL181State * s)2141d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
21549ab747fSPaolo Bonzini {
21649ab747fSPaolo Bonzini     uint32_t bits;
21749ab747fSPaolo Bonzini     uint32_t value = 0;
21849ab747fSPaolo Bonzini     int n;
21949ab747fSPaolo Bonzini     int is_read;
22049ab747fSPaolo Bonzini 
22149ab747fSPaolo Bonzini     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2222762eed1SPhilippe Mathieu-Daudé     if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
22349ab747fSPaolo Bonzini             && !s->linux_hack) {
22449ab747fSPaolo Bonzini         if (is_read) {
22549ab747fSPaolo Bonzini             n = 0;
22649ab747fSPaolo Bonzini             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
2278467f622SPhilippe Mathieu-Daudé                 value |= (uint32_t)sdbus_read_byte(&s->sdbus) << (n * 8);
22849ab747fSPaolo Bonzini                 s->datacnt--;
22949ab747fSPaolo Bonzini                 n++;
23049ab747fSPaolo Bonzini                 if (n == 4) {
23149ab747fSPaolo Bonzini                     pl181_fifo_push(s, value);
23249ab747fSPaolo Bonzini                     n = 0;
23349ab747fSPaolo Bonzini                     value = 0;
23449ab747fSPaolo Bonzini                 }
23549ab747fSPaolo Bonzini             }
23649ab747fSPaolo Bonzini             if (n != 0) {
23749ab747fSPaolo Bonzini                 pl181_fifo_push(s, value);
23849ab747fSPaolo Bonzini             }
23949ab747fSPaolo Bonzini         } else { /* write */
24049ab747fSPaolo Bonzini             n = 0;
24149ab747fSPaolo Bonzini             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
24249ab747fSPaolo Bonzini                 if (n == 0) {
24349ab747fSPaolo Bonzini                     value = pl181_fifo_pop(s);
24449ab747fSPaolo Bonzini                     n = 4;
24549ab747fSPaolo Bonzini                 }
24649ab747fSPaolo Bonzini                 n--;
24749ab747fSPaolo Bonzini                 s->datacnt--;
24839017143SPhilippe Mathieu-Daudé                 sdbus_write_byte(&s->sdbus, value & 0xff);
24949ab747fSPaolo Bonzini                 value >>= 8;
25049ab747fSPaolo Bonzini             }
25149ab747fSPaolo Bonzini         }
25249ab747fSPaolo Bonzini     }
25349ab747fSPaolo Bonzini     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
25449ab747fSPaolo Bonzini     if (s->datacnt == 0) {
25549ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATAEND;
25649ab747fSPaolo Bonzini         /* HACK: */
25749ab747fSPaolo Bonzini         s->status |= PL181_STATUS_DATABLOCKEND;
258583d09f0SPhilippe Mathieu-Daudé         trace_pl181_fifo_transfer_complete();
25949ab747fSPaolo Bonzini     }
26049ab747fSPaolo Bonzini     if (s->datacnt == 0 && s->fifo_len == 0) {
26149ab747fSPaolo Bonzini         s->datactrl &= ~PL181_DATA_ENABLE;
262583d09f0SPhilippe Mathieu-Daudé         trace_pl181_data_engine_idle();
26349ab747fSPaolo Bonzini     } else {
26449ab747fSPaolo Bonzini         /* Update FIFO bits.  */
26549ab747fSPaolo Bonzini         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
26649ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
26749ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOEMPTY;
26849ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOEMPTY;
26949ab747fSPaolo Bonzini         } else {
27049ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXDATAAVLBL;
27149ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXDATAAVLBL;
27249ab747fSPaolo Bonzini         }
27349ab747fSPaolo Bonzini         if (s->fifo_len == 16) {
27449ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOFULL;
27549ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOFULL;
27649ab747fSPaolo Bonzini         }
27749ab747fSPaolo Bonzini         if (s->fifo_len <= 8) {
27849ab747fSPaolo Bonzini             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
27949ab747fSPaolo Bonzini         }
28049ab747fSPaolo Bonzini         if (s->fifo_len >= 8) {
28149ab747fSPaolo Bonzini             bits |= PL181_STATUS_RXFIFOHALFFULL;
28249ab747fSPaolo Bonzini         }
28349ab747fSPaolo Bonzini         if (s->datactrl & PL181_DATA_DIRECTION) {
28449ab747fSPaolo Bonzini             bits &= PL181_STATUS_RX_FIFO;
28549ab747fSPaolo Bonzini         } else {
28649ab747fSPaolo Bonzini             bits &= PL181_STATUS_TX_FIFO;
28749ab747fSPaolo Bonzini         }
28849ab747fSPaolo Bonzini         s->status |= bits;
28949ab747fSPaolo Bonzini     }
29049ab747fSPaolo Bonzini }
29149ab747fSPaolo Bonzini 
pl181_read(void * opaque,hwaddr offset,unsigned size)29249ab747fSPaolo Bonzini static uint64_t pl181_read(void *opaque, hwaddr offset,
29349ab747fSPaolo Bonzini                            unsigned size)
29449ab747fSPaolo Bonzini {
2951d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
29649ab747fSPaolo Bonzini     uint32_t tmp;
29749ab747fSPaolo Bonzini 
29849ab747fSPaolo Bonzini     if (offset >= 0xfe0 && offset < 0x1000) {
29949ab747fSPaolo Bonzini         return pl181_id[(offset - 0xfe0) >> 2];
30049ab747fSPaolo Bonzini     }
30149ab747fSPaolo Bonzini     switch (offset) {
30249ab747fSPaolo Bonzini     case 0x00: /* Power */
30349ab747fSPaolo Bonzini         return s->power;
30449ab747fSPaolo Bonzini     case 0x04: /* Clock */
30549ab747fSPaolo Bonzini         return s->clock;
30649ab747fSPaolo Bonzini     case 0x08: /* Argument */
30749ab747fSPaolo Bonzini         return s->cmdarg;
30849ab747fSPaolo Bonzini     case 0x0c: /* Command */
30949ab747fSPaolo Bonzini         return s->cmd;
31049ab747fSPaolo Bonzini     case 0x10: /* RespCmd */
31149ab747fSPaolo Bonzini         return s->respcmd;
31249ab747fSPaolo Bonzini     case 0x14: /* Response0 */
31349ab747fSPaolo Bonzini         return s->response[0];
31449ab747fSPaolo Bonzini     case 0x18: /* Response1 */
31549ab747fSPaolo Bonzini         return s->response[1];
31649ab747fSPaolo Bonzini     case 0x1c: /* Response2 */
31749ab747fSPaolo Bonzini         return s->response[2];
31849ab747fSPaolo Bonzini     case 0x20: /* Response3 */
31949ab747fSPaolo Bonzini         return s->response[3];
32049ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
32149ab747fSPaolo Bonzini         return s->datatimer;
32249ab747fSPaolo Bonzini     case 0x28: /* DataLength */
32349ab747fSPaolo Bonzini         return s->datalength;
32449ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
32549ab747fSPaolo Bonzini         return s->datactrl;
32649ab747fSPaolo Bonzini     case 0x30: /* DataCnt */
32749ab747fSPaolo Bonzini         return s->datacnt;
32849ab747fSPaolo Bonzini     case 0x34: /* Status */
32949ab747fSPaolo Bonzini         tmp = s->status;
33049ab747fSPaolo Bonzini         if (s->linux_hack) {
33149ab747fSPaolo Bonzini             s->linux_hack = 0;
33249ab747fSPaolo Bonzini             pl181_fifo_run(s);
33349ab747fSPaolo Bonzini             pl181_update(s);
33449ab747fSPaolo Bonzini         }
33549ab747fSPaolo Bonzini         return tmp;
33649ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
33749ab747fSPaolo Bonzini         return s->mask[0];
33849ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
33949ab747fSPaolo Bonzini         return s->mask[1];
34049ab747fSPaolo Bonzini     case 0x48: /* FifoCnt */
34149ab747fSPaolo Bonzini         /* The documentation is somewhat vague about exactly what FifoCnt
34249ab747fSPaolo Bonzini            does.  On real hardware it appears to be when decrememnted
34349ab747fSPaolo Bonzini            when a word is transferred between the FIFO and the serial
34449ab747fSPaolo Bonzini            data engine.  DataCnt is decremented after each byte is
34549ab747fSPaolo Bonzini            transferred between the serial engine and the card.
34649ab747fSPaolo Bonzini            We don't emulate this level of detail, so both can be the same.  */
34749ab747fSPaolo Bonzini         tmp = (s->datacnt + 3) >> 2;
34849ab747fSPaolo Bonzini         if (s->linux_hack) {
34949ab747fSPaolo Bonzini             s->linux_hack = 0;
35049ab747fSPaolo Bonzini             pl181_fifo_run(s);
35149ab747fSPaolo Bonzini             pl181_update(s);
35249ab747fSPaolo Bonzini         }
35349ab747fSPaolo Bonzini         return tmp;
35449ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
35549ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
35649ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
35749ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
35849ab747fSPaolo Bonzini         if (s->fifo_len == 0) {
35949ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
36049ab747fSPaolo Bonzini             return 0;
36149ab747fSPaolo Bonzini         } else {
36249ab747fSPaolo Bonzini             uint32_t value;
36349ab747fSPaolo Bonzini             value = pl181_fifo_pop(s);
36449ab747fSPaolo Bonzini             s->linux_hack = 1;
36549ab747fSPaolo Bonzini             pl181_fifo_run(s);
36649ab747fSPaolo Bonzini             pl181_update(s);
36749ab747fSPaolo Bonzini             return value;
36849ab747fSPaolo Bonzini         }
36949ab747fSPaolo Bonzini     default:
37049ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
37149ab747fSPaolo Bonzini                       "pl181_read: Bad offset %x\n", (int)offset);
37249ab747fSPaolo Bonzini         return 0;
37349ab747fSPaolo Bonzini     }
37449ab747fSPaolo Bonzini }
37549ab747fSPaolo Bonzini 
pl181_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)37649ab747fSPaolo Bonzini static void pl181_write(void *opaque, hwaddr offset,
37749ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
37849ab747fSPaolo Bonzini {
3791d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
38049ab747fSPaolo Bonzini 
38149ab747fSPaolo Bonzini     switch (offset) {
38249ab747fSPaolo Bonzini     case 0x00: /* Power */
38349ab747fSPaolo Bonzini         s->power = value & 0xff;
38449ab747fSPaolo Bonzini         break;
38549ab747fSPaolo Bonzini     case 0x04: /* Clock */
38649ab747fSPaolo Bonzini         s->clock = value & 0xff;
38749ab747fSPaolo Bonzini         break;
38849ab747fSPaolo Bonzini     case 0x08: /* Argument */
38949ab747fSPaolo Bonzini         s->cmdarg = value;
39049ab747fSPaolo Bonzini         break;
39149ab747fSPaolo Bonzini     case 0x0c: /* Command */
39249ab747fSPaolo Bonzini         s->cmd = value;
39349ab747fSPaolo Bonzini         if (s->cmd & PL181_CMD_ENABLE) {
39449ab747fSPaolo Bonzini             if (s->cmd & PL181_CMD_INTERRUPT) {
39549ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
39649ab747fSPaolo Bonzini                               "pl181: Interrupt mode not implemented\n");
39749ab747fSPaolo Bonzini             } if (s->cmd & PL181_CMD_PENDING) {
39849ab747fSPaolo Bonzini                 qemu_log_mask(LOG_UNIMP,
39949ab747fSPaolo Bonzini                               "pl181: Pending commands not implemented\n");
40049ab747fSPaolo Bonzini             } else {
401b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
40249ab747fSPaolo Bonzini                 pl181_fifo_run(s);
40349ab747fSPaolo Bonzini             }
40449ab747fSPaolo Bonzini             /* The command has completed one way or the other.  */
40549ab747fSPaolo Bonzini             s->cmd &= ~PL181_CMD_ENABLE;
40649ab747fSPaolo Bonzini         }
40749ab747fSPaolo Bonzini         break;
40849ab747fSPaolo Bonzini     case 0x24: /* DataTimer */
40949ab747fSPaolo Bonzini         s->datatimer = value;
41049ab747fSPaolo Bonzini         break;
41149ab747fSPaolo Bonzini     case 0x28: /* DataLength */
41249ab747fSPaolo Bonzini         s->datalength = value & 0xffff;
41349ab747fSPaolo Bonzini         break;
41449ab747fSPaolo Bonzini     case 0x2c: /* DataCtrl */
41549ab747fSPaolo Bonzini         s->datactrl = value & 0xff;
41649ab747fSPaolo Bonzini         if (value & PL181_DATA_ENABLE) {
41749ab747fSPaolo Bonzini             s->datacnt = s->datalength;
41849ab747fSPaolo Bonzini             pl181_fifo_run(s);
41949ab747fSPaolo Bonzini         }
42049ab747fSPaolo Bonzini         break;
42149ab747fSPaolo Bonzini     case 0x38: /* Clear */
42249ab747fSPaolo Bonzini         s->status &= ~(value & 0x7ff);
42349ab747fSPaolo Bonzini         break;
42449ab747fSPaolo Bonzini     case 0x3c: /* Mask0 */
42549ab747fSPaolo Bonzini         s->mask[0] = value;
42649ab747fSPaolo Bonzini         break;
42749ab747fSPaolo Bonzini     case 0x40: /* Mask1 */
42849ab747fSPaolo Bonzini         s->mask[1] = value;
42949ab747fSPaolo Bonzini         break;
43049ab747fSPaolo Bonzini     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
43149ab747fSPaolo Bonzini     case 0x90: case 0x94: case 0x98: case 0x9c:
43249ab747fSPaolo Bonzini     case 0xa0: case 0xa4: case 0xa8: case 0xac:
43349ab747fSPaolo Bonzini     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
43449ab747fSPaolo Bonzini         if (s->datacnt == 0) {
43549ab747fSPaolo Bonzini             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
43649ab747fSPaolo Bonzini         } else {
43749ab747fSPaolo Bonzini             pl181_fifo_push(s, value);
43849ab747fSPaolo Bonzini             pl181_fifo_run(s);
43949ab747fSPaolo Bonzini         }
44049ab747fSPaolo Bonzini         break;
44149ab747fSPaolo Bonzini     default:
44249ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
44349ab747fSPaolo Bonzini                       "pl181_write: Bad offset %x\n", (int)offset);
44449ab747fSPaolo Bonzini     }
44549ab747fSPaolo Bonzini     pl181_update(s);
44649ab747fSPaolo Bonzini }
44749ab747fSPaolo Bonzini 
44849ab747fSPaolo Bonzini static const MemoryRegionOps pl181_ops = {
44949ab747fSPaolo Bonzini     .read = pl181_read,
45049ab747fSPaolo Bonzini     .write = pl181_write,
45149ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
45249ab747fSPaolo Bonzini };
45349ab747fSPaolo Bonzini 
pl181_set_readonly(DeviceState * dev,bool level)4542762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level)
4552762eed1SPhilippe Mathieu-Daudé {
4562762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4572762eed1SPhilippe Mathieu-Daudé 
4582762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_readonly, level);
4592762eed1SPhilippe Mathieu-Daudé }
4602762eed1SPhilippe Mathieu-Daudé 
pl181_set_inserted(DeviceState * dev,bool level)4612762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level)
4622762eed1SPhilippe Mathieu-Daudé {
4632762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4642762eed1SPhilippe Mathieu-Daudé 
4652762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_inserted, level);
4662762eed1SPhilippe Mathieu-Daudé }
4672762eed1SPhilippe Mathieu-Daudé 
pl181_reset(DeviceState * d)46849ab747fSPaolo Bonzini static void pl181_reset(DeviceState *d)
46949ab747fSPaolo Bonzini {
470630f4442SAndreas Färber     PL181State *s = PL181(d);
47149ab747fSPaolo Bonzini 
47249ab747fSPaolo Bonzini     s->power = 0;
47349ab747fSPaolo Bonzini     s->cmdarg = 0;
47449ab747fSPaolo Bonzini     s->cmd = 0;
47549ab747fSPaolo Bonzini     s->datatimer = 0;
47649ab747fSPaolo Bonzini     s->datalength = 0;
47749ab747fSPaolo Bonzini     s->respcmd = 0;
47849ab747fSPaolo Bonzini     s->response[0] = 0;
47949ab747fSPaolo Bonzini     s->response[1] = 0;
48049ab747fSPaolo Bonzini     s->response[2] = 0;
48149ab747fSPaolo Bonzini     s->response[3] = 0;
48249ab747fSPaolo Bonzini     s->datatimer = 0;
48349ab747fSPaolo Bonzini     s->datalength = 0;
48449ab747fSPaolo Bonzini     s->datactrl = 0;
48549ab747fSPaolo Bonzini     s->datacnt = 0;
48649ab747fSPaolo Bonzini     s->status = 0;
48749ab747fSPaolo Bonzini     s->linux_hack = 0;
48849ab747fSPaolo Bonzini     s->mask[0] = 0;
48949ab747fSPaolo Bonzini     s->mask[1] = 0;
49049ab747fSPaolo Bonzini 
4912762eed1SPhilippe Mathieu-Daudé     /* Reset other state based on current card insertion/readonly status */
4922762eed1SPhilippe Mathieu-Daudé     pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus));
4932762eed1SPhilippe Mathieu-Daudé     pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus));
49449ab747fSPaolo Bonzini }
49549ab747fSPaolo Bonzini 
pl181_init(Object * obj)4960d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
49749ab747fSPaolo Bonzini {
4980d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4990d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
5000d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
50149ab747fSPaolo Bonzini 
5020d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
503630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
504630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
505630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
50626c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
50726c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
5082762eed1SPhilippe Mathieu-Daudé 
509d637e1dcSPeter Maydell     qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus");
5100d554cb0Sxiaoqiang zhao }
5110d554cb0Sxiaoqiang zhao 
pl181_class_init(ObjectClass * klass,void * data)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;
517*e3d08143SPeter Maydell     device_class_set_legacy_reset(k, pl181_reset);
51826c607b8SPhilippe Mathieu-Daudé     /* Reason: output IRQs should be wired up */
519e90f2a8cSEduardo Habkost     k->user_creatable = false;
52049ab747fSPaolo Bonzini }
52149ab747fSPaolo Bonzini 
pl181_bus_class_init(ObjectClass * klass,void * data)5222762eed1SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, void *data)
5232762eed1SPhilippe Mathieu-Daudé {
5242762eed1SPhilippe Mathieu-Daudé     SDBusClass *sbc = SD_BUS_CLASS(klass);
5252762eed1SPhilippe Mathieu-Daudé 
5262762eed1SPhilippe Mathieu-Daudé     sbc->set_inserted = pl181_set_inserted;
5272762eed1SPhilippe Mathieu-Daudé     sbc->set_readonly = pl181_set_readonly;
5282762eed1SPhilippe Mathieu-Daudé }
5292762eed1SPhilippe Mathieu-Daudé 
53088d2198cSPhilippe Mathieu-Daudé static const TypeInfo pl181_info[] = {
53188d2198cSPhilippe Mathieu-Daudé     {
53288d2198cSPhilippe Mathieu-Daudé         .name           = TYPE_PL181,
53388d2198cSPhilippe Mathieu-Daudé         .parent         = TYPE_SYS_BUS_DEVICE,
53488d2198cSPhilippe Mathieu-Daudé         .instance_size  = sizeof(PL181State),
53588d2198cSPhilippe Mathieu-Daudé         .instance_init  = pl181_init,
53688d2198cSPhilippe Mathieu-Daudé         .class_init     = pl181_class_init,
53788d2198cSPhilippe Mathieu-Daudé     },
53888d2198cSPhilippe Mathieu-Daudé     {
5392762eed1SPhilippe Mathieu-Daudé         .name           = TYPE_PL181_BUS,
5402762eed1SPhilippe Mathieu-Daudé         .parent         = TYPE_SD_BUS,
5412762eed1SPhilippe Mathieu-Daudé         .instance_size  = sizeof(SDBus),
5422762eed1SPhilippe Mathieu-Daudé         .class_init     = pl181_bus_class_init,
54388d2198cSPhilippe Mathieu-Daudé     },
5442762eed1SPhilippe Mathieu-Daudé };
5452762eed1SPhilippe Mathieu-Daudé 
54688d2198cSPhilippe Mathieu-Daudé DEFINE_TYPES(pl181_info)
547