xref: /qemu/hw/sd/pl181.c (revision 2762eed1f5534074fcce703bdda8702905dc4c61)
1a1bb27b1Spbrook /*
2a1bb27b1Spbrook  * Arm PrimeCell PL181 MultiMedia Card Interface
3a1bb27b1Spbrook  *
4a1bb27b1Spbrook  * Copyright (c) 2007 CodeSourcery.
5a1bb27b1Spbrook  * Written by Paul Brook
6a1bb27b1Spbrook  *
78e31bf38SMatthew Fernandez  * This code is licensed under the GPL.
8a1bb27b1Spbrook  */
9a1bb27b1Spbrook 
108ef94f0bSPeter Maydell #include "qemu/osdep.h"
119c17d615SPaolo Bonzini #include "sysemu/blockdev.h"
1283c9f4caSPaolo 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"
21a1bb27b1Spbrook 
22a1bb27b1Spbrook //#define DEBUG_PL181 1
23a1bb27b1Spbrook 
24a1bb27b1Spbrook #ifdef DEBUG_PL181
25001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
26001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
27a1bb27b1Spbrook #else
28001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0)
29a1bb27b1Spbrook #endif
30a1bb27b1Spbrook 
31a1bb27b1Spbrook #define PL181_FIFO_LEN 16
32a1bb27b1Spbrook 
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 
41ca45842aSAvi Kivity     MemoryRegion iomem;
42*2762eed1SPhilippe Mathieu-Daudé     SDBus sdbus;
43a1bb27b1Spbrook     uint32_t clock;
44a1bb27b1Spbrook     uint32_t power;
45a1bb27b1Spbrook     uint32_t cmdarg;
46a1bb27b1Spbrook     uint32_t cmd;
47a1bb27b1Spbrook     uint32_t datatimer;
48a1bb27b1Spbrook     uint32_t datalength;
49a1bb27b1Spbrook     uint32_t respcmd;
50a1bb27b1Spbrook     uint32_t response[4];
51a1bb27b1Spbrook     uint32_t datactrl;
52a1bb27b1Spbrook     uint32_t datacnt;
53a1bb27b1Spbrook     uint32_t status;
54a1bb27b1Spbrook     uint32_t mask[2];
55624923beSPeter Maydell     int32_t fifo_pos;
56624923beSPeter Maydell     int32_t fifo_len;
576361cdb6Spbrook     /* 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
596361cdb6Spbrook        subsequent transfers until after the driver polls the status word.
606361cdb6Spbrook        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
616361cdb6Spbrook      */
62624923beSPeter Maydell     int32_t linux_hack;
630e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
64d537cf6cSpbrook     qemu_irq irq[2];
65c31a4724SPeter Maydell     /* 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;
69a1bb27b1Spbrook 
70624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
71624923beSPeter Maydell     .name = "pl181",
72624923beSPeter Maydell     .version_id = 1,
73624923beSPeter Maydell     .minimum_version_id = 1,
74624923beSPeter Maydell     .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),
91624923beSPeter Maydell         VMSTATE_END_OF_LIST()
92624923beSPeter Maydell     }
93624923beSPeter Maydell };
94624923beSPeter Maydell 
95a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
96a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
97a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
98a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
99a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
100a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
101a1bb27b1Spbrook 
102a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
103a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
104a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
105a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
106a1bb27b1Spbrook 
107a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
108a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
109a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
110a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
111a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
112a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
113a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
114a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
115a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
116a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
117a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
118a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
119a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
120a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
121a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
122a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
123a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
124a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
125a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
126a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
127a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
128a1bb27b1Spbrook 
129a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
130a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
131a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
132a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
133a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
134a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
135a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
136a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
137a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
138a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
139a1bb27b1Spbrook 
140a1bb27b1Spbrook static const unsigned char pl181_id[] =
141a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
142a1bb27b1Spbrook 
1431d998d93SAndreas Färber static void pl181_update(PL181State *s)
144a1bb27b1Spbrook {
145a1bb27b1Spbrook     int i;
146a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
147d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
148a1bb27b1Spbrook     }
149a1bb27b1Spbrook }
150a1bb27b1Spbrook 
1511d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
152a1bb27b1Spbrook {
153a1bb27b1Spbrook     int n;
154a1bb27b1Spbrook 
155a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
1564858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
157a1bb27b1Spbrook         return;
158a1bb27b1Spbrook     }
159a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
160a1bb27b1Spbrook     s->fifo_len++;
161a1bb27b1Spbrook     s->fifo[n] = value;
162a1bb27b1Spbrook     DPRINTF("FIFO push %08x\n", (int)value);
163a1bb27b1Spbrook }
164a1bb27b1Spbrook 
1651d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
166a1bb27b1Spbrook {
167a1bb27b1Spbrook     uint32_t value;
168a1bb27b1Spbrook 
169a1bb27b1Spbrook     if (s->fifo_len == 0) {
1704858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
171a1bb27b1Spbrook         return 0;
172a1bb27b1Spbrook     }
173a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
174a1bb27b1Spbrook     s->fifo_len--;
175a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
176a1bb27b1Spbrook     DPRINTF("FIFO pop %08x\n", (int)value);
177a1bb27b1Spbrook     return value;
178a1bb27b1Spbrook }
179a1bb27b1Spbrook 
180b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
181a1bb27b1Spbrook {
182bc24a225SPaul Brook     SDRequest request;
183a1bb27b1Spbrook     uint8_t response[16];
184a1bb27b1Spbrook     int rlen;
185a1bb27b1Spbrook 
186a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
187a1bb27b1Spbrook     request.arg = s->cmdarg;
188a1bb27b1Spbrook     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
189*2762eed1SPhilippe Mathieu-Daudé     rlen = sdbus_do_command(&s->sdbus, &request, response);
190a1bb27b1Spbrook     if (rlen < 0)
191a1bb27b1Spbrook         goto error;
192a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
193a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
194a1bb27b1Spbrook             goto error;
195a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
196a1bb27b1Spbrook             goto error;
197b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
198a1bb27b1Spbrook         if (rlen == 4) {
199a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
200a1bb27b1Spbrook         } 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;
204a1bb27b1Spbrook         }
205aa1f17c1Sths         DPRINTF("Response received\n");
206a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
207a1bb27b1Spbrook     } else {
208a1bb27b1Spbrook         DPRINTF("Command sent\n");
209a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
210a1bb27b1Spbrook     }
211a1bb27b1Spbrook     return;
212a1bb27b1Spbrook 
213a1bb27b1Spbrook error:
214a1bb27b1Spbrook     DPRINTF("Timeout\n");
215a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
216a1bb27b1Spbrook }
217a1bb27b1Spbrook 
218aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
219a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
220a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
221a1bb27b1Spbrook 
2221d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
223a1bb27b1Spbrook {
224a1bb27b1Spbrook     uint32_t bits;
225f21126dfSBlue Swirl     uint32_t value = 0;
226a1bb27b1Spbrook     int n;
227a1bb27b1Spbrook     int is_read;
228a1bb27b1Spbrook 
229a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
230*2762eed1SPhilippe Mathieu-Daudé     if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
2316361cdb6Spbrook             && !s->linux_hack) {
232bc3b26f5SPaul Brook         if (is_read) {
233a1bb27b1Spbrook             n = 0;
234bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
235*2762eed1SPhilippe Mathieu-Daudé                 value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8);
236bc3b26f5SPaul Brook                 s->datacnt--;
237a1bb27b1Spbrook                 n++;
238a1bb27b1Spbrook                 if (n == 4) {
239a1bb27b1Spbrook                     pl181_fifo_push(s, value);
240a1bb27b1Spbrook                     n = 0;
241bc3b26f5SPaul Brook                     value = 0;
242a1bb27b1Spbrook                 }
243bc3b26f5SPaul Brook             }
244bc3b26f5SPaul Brook             if (n != 0) {
245bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
246bc3b26f5SPaul Brook             }
247bc3b26f5SPaul Brook         } else { /* write */
248bc3b26f5SPaul Brook             n = 0;
249bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
250a1bb27b1Spbrook                 if (n == 0) {
251a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
252a1bb27b1Spbrook                     n = 4;
253a1bb27b1Spbrook                 }
254bc3b26f5SPaul Brook                 n--;
255bc3b26f5SPaul Brook                 s->datacnt--;
256*2762eed1SPhilippe Mathieu-Daudé                 sdbus_write_data(&s->sdbus, value & 0xff);
257a1bb27b1Spbrook                 value >>= 8;
258a1bb27b1Spbrook             }
259a1bb27b1Spbrook         }
260a1bb27b1Spbrook     }
261a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
262a1bb27b1Spbrook     if (s->datacnt == 0) {
263a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
264a1bb27b1Spbrook         /* HACK: */
265a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
266a1bb27b1Spbrook         DPRINTF("Transfer Complete\n");
267a1bb27b1Spbrook     }
2686361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
269a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
270a1bb27b1Spbrook         DPRINTF("Data engine idle\n");
271a1bb27b1Spbrook     } else {
272a1bb27b1Spbrook         /* Update FIFO bits.  */
273a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
274a1bb27b1Spbrook         if (s->fifo_len == 0) {
275a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
276a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
277a1bb27b1Spbrook         } else {
278a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
279a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
280a1bb27b1Spbrook         }
281a1bb27b1Spbrook         if (s->fifo_len == 16) {
282a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
283a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
284a1bb27b1Spbrook         }
285a1bb27b1Spbrook         if (s->fifo_len <= 8) {
286a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
287a1bb27b1Spbrook         }
288a1bb27b1Spbrook         if (s->fifo_len >= 8) {
289a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
290a1bb27b1Spbrook         }
291a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
292a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
293a1bb27b1Spbrook         } else {
294a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
295a1bb27b1Spbrook         }
296a1bb27b1Spbrook         s->status |= bits;
297a1bb27b1Spbrook     }
298a1bb27b1Spbrook }
299a1bb27b1Spbrook 
300a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
301ca45842aSAvi Kivity                            unsigned size)
302a1bb27b1Spbrook {
3031d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
3046361cdb6Spbrook     uint32_t tmp;
305a1bb27b1Spbrook 
306a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
307a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
308a1bb27b1Spbrook     }
309a1bb27b1Spbrook     switch (offset) {
310a1bb27b1Spbrook     case 0x00: /* Power */
311a1bb27b1Spbrook         return s->power;
312a1bb27b1Spbrook     case 0x04: /* Clock */
313a1bb27b1Spbrook         return s->clock;
314a1bb27b1Spbrook     case 0x08: /* Argument */
315a1bb27b1Spbrook         return s->cmdarg;
316a1bb27b1Spbrook     case 0x0c: /* Command */
317a1bb27b1Spbrook         return s->cmd;
318a1bb27b1Spbrook     case 0x10: /* RespCmd */
319a1bb27b1Spbrook         return s->respcmd;
320a1bb27b1Spbrook     case 0x14: /* Response0 */
321a1bb27b1Spbrook         return s->response[0];
322a1bb27b1Spbrook     case 0x18: /* Response1 */
323a1bb27b1Spbrook         return s->response[1];
324a1bb27b1Spbrook     case 0x1c: /* Response2 */
325a1bb27b1Spbrook         return s->response[2];
326a1bb27b1Spbrook     case 0x20: /* Response3 */
327a1bb27b1Spbrook         return s->response[3];
328a1bb27b1Spbrook     case 0x24: /* DataTimer */
329a1bb27b1Spbrook         return s->datatimer;
330a1bb27b1Spbrook     case 0x28: /* DataLength */
331a1bb27b1Spbrook         return s->datalength;
332a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
333a1bb27b1Spbrook         return s->datactrl;
334a1bb27b1Spbrook     case 0x30: /* DataCnt */
335a1bb27b1Spbrook         return s->datacnt;
336a1bb27b1Spbrook     case 0x34: /* Status */
3376361cdb6Spbrook         tmp = s->status;
3386361cdb6Spbrook         if (s->linux_hack) {
3396361cdb6Spbrook             s->linux_hack = 0;
3406361cdb6Spbrook             pl181_fifo_run(s);
3416361cdb6Spbrook             pl181_update(s);
3426361cdb6Spbrook         }
3436361cdb6Spbrook         return tmp;
344a1bb27b1Spbrook     case 0x3c: /* Mask0 */
345a1bb27b1Spbrook         return s->mask[0];
346a1bb27b1Spbrook     case 0x40: /* Mask1 */
347a1bb27b1Spbrook         return s->mask[1];
348a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3496361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3506361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
35166a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3526361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
35366a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3546361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3556361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3566361cdb6Spbrook         if (s->linux_hack) {
3576361cdb6Spbrook             s->linux_hack = 0;
3586361cdb6Spbrook             pl181_fifo_run(s);
3596361cdb6Spbrook             pl181_update(s);
3606361cdb6Spbrook         }
3616361cdb6Spbrook         return tmp;
362a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
363a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
364a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
365a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3666361cdb6Spbrook         if (s->fifo_len == 0) {
3679351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
368a1bb27b1Spbrook             return 0;
369a1bb27b1Spbrook         } else {
370a1bb27b1Spbrook             uint32_t value;
371a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3726361cdb6Spbrook             s->linux_hack = 1;
373a1bb27b1Spbrook             pl181_fifo_run(s);
374a1bb27b1Spbrook             pl181_update(s);
375a1bb27b1Spbrook             return value;
376a1bb27b1Spbrook         }
377a1bb27b1Spbrook     default:
3789351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3799351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
380a1bb27b1Spbrook         return 0;
381a1bb27b1Spbrook     }
382a1bb27b1Spbrook }
383a1bb27b1Spbrook 
384a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
385ca45842aSAvi Kivity                         uint64_t value, unsigned size)
386a1bb27b1Spbrook {
3871d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
388a1bb27b1Spbrook 
389a1bb27b1Spbrook     switch (offset) {
390a1bb27b1Spbrook     case 0x00: /* Power */
391a1bb27b1Spbrook         s->power = value & 0xff;
392a1bb27b1Spbrook         break;
393a1bb27b1Spbrook     case 0x04: /* Clock */
394a1bb27b1Spbrook         s->clock = value & 0xff;
395a1bb27b1Spbrook         break;
396a1bb27b1Spbrook     case 0x08: /* Argument */
397a1bb27b1Spbrook         s->cmdarg = value;
398a1bb27b1Spbrook         break;
399a1bb27b1Spbrook     case 0x0c: /* Command */
400a1bb27b1Spbrook         s->cmd = value;
401a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
402a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
4039351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4049351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
405a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
4069351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4079351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
408a1bb27b1Spbrook             } else {
409b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
410a1bb27b1Spbrook                 pl181_fifo_run(s);
411a1bb27b1Spbrook             }
412a1bb27b1Spbrook             /* The command has completed one way or the other.  */
413a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
414a1bb27b1Spbrook         }
415a1bb27b1Spbrook         break;
416a1bb27b1Spbrook     case 0x24: /* DataTimer */
417a1bb27b1Spbrook         s->datatimer = value;
418a1bb27b1Spbrook         break;
419a1bb27b1Spbrook     case 0x28: /* DataLength */
420a1bb27b1Spbrook         s->datalength = value & 0xffff;
421a1bb27b1Spbrook         break;
422a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
423a1bb27b1Spbrook         s->datactrl = value & 0xff;
424a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
425a1bb27b1Spbrook             s->datacnt = s->datalength;
426a1bb27b1Spbrook             pl181_fifo_run(s);
427a1bb27b1Spbrook         }
428a1bb27b1Spbrook         break;
429a1bb27b1Spbrook     case 0x38: /* Clear */
430a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
431a1bb27b1Spbrook         break;
432a1bb27b1Spbrook     case 0x3c: /* Mask0 */
433a1bb27b1Spbrook         s->mask[0] = value;
434a1bb27b1Spbrook         break;
435a1bb27b1Spbrook     case 0x40: /* Mask1 */
436a1bb27b1Spbrook         s->mask[1] = value;
437a1bb27b1Spbrook         break;
438a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
439a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
440a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
441a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4426361cdb6Spbrook         if (s->datacnt == 0) {
4439351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
444a1bb27b1Spbrook         } else {
445a1bb27b1Spbrook             pl181_fifo_push(s, value);
446a1bb27b1Spbrook             pl181_fifo_run(s);
447a1bb27b1Spbrook         }
448a1bb27b1Spbrook         break;
449a1bb27b1Spbrook     default:
4509351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4519351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
452a1bb27b1Spbrook     }
453a1bb27b1Spbrook     pl181_update(s);
454a1bb27b1Spbrook }
455a1bb27b1Spbrook 
456ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
457ca45842aSAvi Kivity     .read = pl181_read,
458ca45842aSAvi Kivity     .write = pl181_write,
459ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
460a1bb27b1Spbrook };
461a1bb27b1Spbrook 
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é 
476624923beSPeter Maydell static void pl181_reset(DeviceState *d)
477a1bb27b1Spbrook {
478630f4442SAndreas Färber     PL181State *s = PL181(d);
479a1bb27b1Spbrook 
480a1bb27b1Spbrook     s->power = 0;
481a1bb27b1Spbrook     s->cmdarg = 0;
482a1bb27b1Spbrook     s->cmd = 0;
483a1bb27b1Spbrook     s->datatimer = 0;
484a1bb27b1Spbrook     s->datalength = 0;
485a1bb27b1Spbrook     s->respcmd = 0;
486a1bb27b1Spbrook     s->response[0] = 0;
487a1bb27b1Spbrook     s->response[1] = 0;
488a1bb27b1Spbrook     s->response[2] = 0;
489a1bb27b1Spbrook     s->response[3] = 0;
490a1bb27b1Spbrook     s->datatimer = 0;
491a1bb27b1Spbrook     s->datalength = 0;
492a1bb27b1Spbrook     s->datactrl = 0;
493a1bb27b1Spbrook     s->datacnt = 0;
494a1bb27b1Spbrook     s->status = 0;
4956361cdb6Spbrook     s->linux_hack = 0;
496a1bb27b1Spbrook     s->mask[0] = 0;
497a1bb27b1Spbrook     s->mask[1] = 0;
498c31a4724SPeter Maydell 
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));
502a1bb27b1Spbrook }
503a1bb27b1Spbrook 
5040d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
505a1bb27b1Spbrook {
5060d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
5070d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
5080d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
509a1bb27b1Spbrook 
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);
52813839974SMarkus Armbruster     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);
534a1bb27b1Spbrook }
535aa9311d8SPaul Brook 
536999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data)
537999e12bbSAnthony Liguori {
53839bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
539999e12bbSAnthony Liguori 
54039bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
54139bffca2SAnthony Liguori     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;
545999e12bbSAnthony Liguori }
546999e12bbSAnthony Liguori 
5478c43a6f0SAndreas Färber static const TypeInfo pl181_info = {
548630f4442SAndreas Färber     .name          = TYPE_PL181,
54939bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
5501d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5510d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
552999e12bbSAnthony Liguori     .class_init    = pl181_class_init,
553624923beSPeter Maydell };
554624923beSPeter Maydell 
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é 
57083f7d43aSAndreas Färber static void pl181_register_types(void)
571aa9311d8SPaul Brook {
57239bffca2SAnthony Liguori     type_register_static(&pl181_info);
573*2762eed1SPhilippe Mathieu-Daudé     type_register_static(&pl181_bus_info);
574aa9311d8SPaul Brook }
575aa9311d8SPaul Brook 
57683f7d43aSAndreas Färber type_init(pl181_register_types)
577