xref: /qemu/hw/sd/pl181.c (revision 26c5b0f4cbe19ad87d3aaba22cd1a8edc25f7ceb)
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"
20a1bb27b1Spbrook 
21a1bb27b1Spbrook //#define DEBUG_PL181 1
22a1bb27b1Spbrook 
23a1bb27b1Spbrook #ifdef DEBUG_PL181
24001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
25001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
26a1bb27b1Spbrook #else
27001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0)
28a1bb27b1Spbrook #endif
29a1bb27b1Spbrook 
30a1bb27b1Spbrook #define PL181_FIFO_LEN 16
31a1bb27b1Spbrook 
32630f4442SAndreas Färber #define TYPE_PL181 "pl181"
33630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
34630f4442SAndreas Färber 
351d998d93SAndreas Färber typedef struct PL181State {
36630f4442SAndreas Färber     SysBusDevice parent_obj;
37630f4442SAndreas Färber 
38ca45842aSAvi Kivity     MemoryRegion iomem;
3942a10898Spbrook     SDState *card;
40a1bb27b1Spbrook     uint32_t clock;
41a1bb27b1Spbrook     uint32_t power;
42a1bb27b1Spbrook     uint32_t cmdarg;
43a1bb27b1Spbrook     uint32_t cmd;
44a1bb27b1Spbrook     uint32_t datatimer;
45a1bb27b1Spbrook     uint32_t datalength;
46a1bb27b1Spbrook     uint32_t respcmd;
47a1bb27b1Spbrook     uint32_t response[4];
48a1bb27b1Spbrook     uint32_t datactrl;
49a1bb27b1Spbrook     uint32_t datacnt;
50a1bb27b1Spbrook     uint32_t status;
51a1bb27b1Spbrook     uint32_t mask[2];
52624923beSPeter Maydell     int32_t fifo_pos;
53624923beSPeter Maydell     int32_t fifo_len;
546361cdb6Spbrook     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5567cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
566361cdb6Spbrook        subsequent transfers until after the driver polls the status word.
576361cdb6Spbrook        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
586361cdb6Spbrook      */
59624923beSPeter Maydell     int32_t linux_hack;
600e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
61d537cf6cSpbrook     qemu_irq irq[2];
62c31a4724SPeter Maydell     /* GPIO outputs for 'card is readonly' and 'card inserted' */
63*26c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_readonly;
64*26c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_inserted;
651d998d93SAndreas Färber } PL181State;
66a1bb27b1Spbrook 
67624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
68624923beSPeter Maydell     .name = "pl181",
69624923beSPeter Maydell     .version_id = 1,
70624923beSPeter Maydell     .minimum_version_id = 1,
71624923beSPeter Maydell     .fields = (VMStateField[]) {
721d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
751d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
781d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
791d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
801d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
811d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
821d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
831d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
841d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
851d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
861d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
871d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
88624923beSPeter Maydell         VMSTATE_END_OF_LIST()
89624923beSPeter Maydell     }
90624923beSPeter Maydell };
91624923beSPeter Maydell 
92a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
93a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
94a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
95a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
96a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
97a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
98a1bb27b1Spbrook 
99a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
100a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
101a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
102a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
103a1bb27b1Spbrook 
104a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
105a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
106a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
107a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
108a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
109a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
110a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
111a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
112a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
113a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
114a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
115a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
116a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
117a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
118a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
119a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
120a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
121a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
122a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
123a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
124a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
125a1bb27b1Spbrook 
126a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
127a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
128a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
129a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
130a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
131a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
132a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
133a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
134a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
135a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
136a1bb27b1Spbrook 
137a1bb27b1Spbrook static const unsigned char pl181_id[] =
138a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
139a1bb27b1Spbrook 
1401d998d93SAndreas Färber static void pl181_update(PL181State *s)
141a1bb27b1Spbrook {
142a1bb27b1Spbrook     int i;
143a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
144d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
145a1bb27b1Spbrook     }
146a1bb27b1Spbrook }
147a1bb27b1Spbrook 
1481d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
149a1bb27b1Spbrook {
150a1bb27b1Spbrook     int n;
151a1bb27b1Spbrook 
152a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
1534858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
154a1bb27b1Spbrook         return;
155a1bb27b1Spbrook     }
156a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
157a1bb27b1Spbrook     s->fifo_len++;
158a1bb27b1Spbrook     s->fifo[n] = value;
159a1bb27b1Spbrook     DPRINTF("FIFO push %08x\n", (int)value);
160a1bb27b1Spbrook }
161a1bb27b1Spbrook 
1621d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
163a1bb27b1Spbrook {
164a1bb27b1Spbrook     uint32_t value;
165a1bb27b1Spbrook 
166a1bb27b1Spbrook     if (s->fifo_len == 0) {
1674858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
168a1bb27b1Spbrook         return 0;
169a1bb27b1Spbrook     }
170a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
171a1bb27b1Spbrook     s->fifo_len--;
172a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
173a1bb27b1Spbrook     DPRINTF("FIFO pop %08x\n", (int)value);
174a1bb27b1Spbrook     return value;
175a1bb27b1Spbrook }
176a1bb27b1Spbrook 
177b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
178a1bb27b1Spbrook {
179bc24a225SPaul Brook     SDRequest request;
180a1bb27b1Spbrook     uint8_t response[16];
181a1bb27b1Spbrook     int rlen;
182a1bb27b1Spbrook 
183a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
184a1bb27b1Spbrook     request.arg = s->cmdarg;
185a1bb27b1Spbrook     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
186a1bb27b1Spbrook     rlen = sd_do_command(s->card, &request, response);
187a1bb27b1Spbrook     if (rlen < 0)
188a1bb27b1Spbrook         goto error;
189a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
190a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
191a1bb27b1Spbrook             goto error;
192a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
193a1bb27b1Spbrook             goto error;
194b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
195a1bb27b1Spbrook         if (rlen == 4) {
196a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
197a1bb27b1Spbrook         } else {
198b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
199b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
200b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
201a1bb27b1Spbrook         }
202aa1f17c1Sths         DPRINTF("Response received\n");
203a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
204a1bb27b1Spbrook     } else {
205a1bb27b1Spbrook         DPRINTF("Command sent\n");
206a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
207a1bb27b1Spbrook     }
208a1bb27b1Spbrook     return;
209a1bb27b1Spbrook 
210a1bb27b1Spbrook error:
211a1bb27b1Spbrook     DPRINTF("Timeout\n");
212a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
213a1bb27b1Spbrook }
214a1bb27b1Spbrook 
215aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
216a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
217a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
218a1bb27b1Spbrook 
2191d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
220a1bb27b1Spbrook {
221a1bb27b1Spbrook     uint32_t bits;
222f21126dfSBlue Swirl     uint32_t value = 0;
223a1bb27b1Spbrook     int n;
224a1bb27b1Spbrook     int is_read;
225a1bb27b1Spbrook 
226a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2276361cdb6Spbrook     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
2286361cdb6Spbrook             && !s->linux_hack) {
229bc3b26f5SPaul Brook         if (is_read) {
230a1bb27b1Spbrook             n = 0;
231bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
232a1bb27b1Spbrook                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
233bc3b26f5SPaul Brook                 s->datacnt--;
234a1bb27b1Spbrook                 n++;
235a1bb27b1Spbrook                 if (n == 4) {
236a1bb27b1Spbrook                     pl181_fifo_push(s, value);
237a1bb27b1Spbrook                     n = 0;
238bc3b26f5SPaul Brook                     value = 0;
239a1bb27b1Spbrook                 }
240bc3b26f5SPaul Brook             }
241bc3b26f5SPaul Brook             if (n != 0) {
242bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
243bc3b26f5SPaul Brook             }
244bc3b26f5SPaul Brook         } else { /* write */
245bc3b26f5SPaul Brook             n = 0;
246bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
247a1bb27b1Spbrook                 if (n == 0) {
248a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
249a1bb27b1Spbrook                     n = 4;
250a1bb27b1Spbrook                 }
251bc3b26f5SPaul Brook                 n--;
252bc3b26f5SPaul Brook                 s->datacnt--;
253a1bb27b1Spbrook                 sd_write_data(s->card, value & 0xff);
254a1bb27b1Spbrook                 value >>= 8;
255a1bb27b1Spbrook             }
256a1bb27b1Spbrook         }
257a1bb27b1Spbrook     }
258a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
259a1bb27b1Spbrook     if (s->datacnt == 0) {
260a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
261a1bb27b1Spbrook         /* HACK: */
262a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
263a1bb27b1Spbrook         DPRINTF("Transfer Complete\n");
264a1bb27b1Spbrook     }
2656361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
266a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
267a1bb27b1Spbrook         DPRINTF("Data engine idle\n");
268a1bb27b1Spbrook     } else {
269a1bb27b1Spbrook         /* Update FIFO bits.  */
270a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
271a1bb27b1Spbrook         if (s->fifo_len == 0) {
272a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
273a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
274a1bb27b1Spbrook         } else {
275a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
276a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
277a1bb27b1Spbrook         }
278a1bb27b1Spbrook         if (s->fifo_len == 16) {
279a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
280a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
281a1bb27b1Spbrook         }
282a1bb27b1Spbrook         if (s->fifo_len <= 8) {
283a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
284a1bb27b1Spbrook         }
285a1bb27b1Spbrook         if (s->fifo_len >= 8) {
286a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
287a1bb27b1Spbrook         }
288a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
289a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
290a1bb27b1Spbrook         } else {
291a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
292a1bb27b1Spbrook         }
293a1bb27b1Spbrook         s->status |= bits;
294a1bb27b1Spbrook     }
295a1bb27b1Spbrook }
296a1bb27b1Spbrook 
297a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
298ca45842aSAvi Kivity                            unsigned size)
299a1bb27b1Spbrook {
3001d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
3016361cdb6Spbrook     uint32_t tmp;
302a1bb27b1Spbrook 
303a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
304a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
305a1bb27b1Spbrook     }
306a1bb27b1Spbrook     switch (offset) {
307a1bb27b1Spbrook     case 0x00: /* Power */
308a1bb27b1Spbrook         return s->power;
309a1bb27b1Spbrook     case 0x04: /* Clock */
310a1bb27b1Spbrook         return s->clock;
311a1bb27b1Spbrook     case 0x08: /* Argument */
312a1bb27b1Spbrook         return s->cmdarg;
313a1bb27b1Spbrook     case 0x0c: /* Command */
314a1bb27b1Spbrook         return s->cmd;
315a1bb27b1Spbrook     case 0x10: /* RespCmd */
316a1bb27b1Spbrook         return s->respcmd;
317a1bb27b1Spbrook     case 0x14: /* Response0 */
318a1bb27b1Spbrook         return s->response[0];
319a1bb27b1Spbrook     case 0x18: /* Response1 */
320a1bb27b1Spbrook         return s->response[1];
321a1bb27b1Spbrook     case 0x1c: /* Response2 */
322a1bb27b1Spbrook         return s->response[2];
323a1bb27b1Spbrook     case 0x20: /* Response3 */
324a1bb27b1Spbrook         return s->response[3];
325a1bb27b1Spbrook     case 0x24: /* DataTimer */
326a1bb27b1Spbrook         return s->datatimer;
327a1bb27b1Spbrook     case 0x28: /* DataLength */
328a1bb27b1Spbrook         return s->datalength;
329a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
330a1bb27b1Spbrook         return s->datactrl;
331a1bb27b1Spbrook     case 0x30: /* DataCnt */
332a1bb27b1Spbrook         return s->datacnt;
333a1bb27b1Spbrook     case 0x34: /* Status */
3346361cdb6Spbrook         tmp = s->status;
3356361cdb6Spbrook         if (s->linux_hack) {
3366361cdb6Spbrook             s->linux_hack = 0;
3376361cdb6Spbrook             pl181_fifo_run(s);
3386361cdb6Spbrook             pl181_update(s);
3396361cdb6Spbrook         }
3406361cdb6Spbrook         return tmp;
341a1bb27b1Spbrook     case 0x3c: /* Mask0 */
342a1bb27b1Spbrook         return s->mask[0];
343a1bb27b1Spbrook     case 0x40: /* Mask1 */
344a1bb27b1Spbrook         return s->mask[1];
345a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3466361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3476361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
34866a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3496361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
35066a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3516361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3526361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3536361cdb6Spbrook         if (s->linux_hack) {
3546361cdb6Spbrook             s->linux_hack = 0;
3556361cdb6Spbrook             pl181_fifo_run(s);
3566361cdb6Spbrook             pl181_update(s);
3576361cdb6Spbrook         }
3586361cdb6Spbrook         return tmp;
359a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
360a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
361a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
362a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3636361cdb6Spbrook         if (s->fifo_len == 0) {
3649351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
365a1bb27b1Spbrook             return 0;
366a1bb27b1Spbrook         } else {
367a1bb27b1Spbrook             uint32_t value;
368a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3696361cdb6Spbrook             s->linux_hack = 1;
370a1bb27b1Spbrook             pl181_fifo_run(s);
371a1bb27b1Spbrook             pl181_update(s);
372a1bb27b1Spbrook             return value;
373a1bb27b1Spbrook         }
374a1bb27b1Spbrook     default:
3759351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3769351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
377a1bb27b1Spbrook         return 0;
378a1bb27b1Spbrook     }
379a1bb27b1Spbrook }
380a1bb27b1Spbrook 
381a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
382ca45842aSAvi Kivity                         uint64_t value, unsigned size)
383a1bb27b1Spbrook {
3841d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
385a1bb27b1Spbrook 
386a1bb27b1Spbrook     switch (offset) {
387a1bb27b1Spbrook     case 0x00: /* Power */
388a1bb27b1Spbrook         s->power = value & 0xff;
389a1bb27b1Spbrook         break;
390a1bb27b1Spbrook     case 0x04: /* Clock */
391a1bb27b1Spbrook         s->clock = value & 0xff;
392a1bb27b1Spbrook         break;
393a1bb27b1Spbrook     case 0x08: /* Argument */
394a1bb27b1Spbrook         s->cmdarg = value;
395a1bb27b1Spbrook         break;
396a1bb27b1Spbrook     case 0x0c: /* Command */
397a1bb27b1Spbrook         s->cmd = value;
398a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
399a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
4009351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4019351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
402a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
4039351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4049351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
405a1bb27b1Spbrook             } else {
406b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
407a1bb27b1Spbrook                 pl181_fifo_run(s);
408a1bb27b1Spbrook             }
409a1bb27b1Spbrook             /* The command has completed one way or the other.  */
410a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
411a1bb27b1Spbrook         }
412a1bb27b1Spbrook         break;
413a1bb27b1Spbrook     case 0x24: /* DataTimer */
414a1bb27b1Spbrook         s->datatimer = value;
415a1bb27b1Spbrook         break;
416a1bb27b1Spbrook     case 0x28: /* DataLength */
417a1bb27b1Spbrook         s->datalength = value & 0xffff;
418a1bb27b1Spbrook         break;
419a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
420a1bb27b1Spbrook         s->datactrl = value & 0xff;
421a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
422a1bb27b1Spbrook             s->datacnt = s->datalength;
423a1bb27b1Spbrook             pl181_fifo_run(s);
424a1bb27b1Spbrook         }
425a1bb27b1Spbrook         break;
426a1bb27b1Spbrook     case 0x38: /* Clear */
427a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
428a1bb27b1Spbrook         break;
429a1bb27b1Spbrook     case 0x3c: /* Mask0 */
430a1bb27b1Spbrook         s->mask[0] = value;
431a1bb27b1Spbrook         break;
432a1bb27b1Spbrook     case 0x40: /* Mask1 */
433a1bb27b1Spbrook         s->mask[1] = value;
434a1bb27b1Spbrook         break;
435a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
436a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
437a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
438a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4396361cdb6Spbrook         if (s->datacnt == 0) {
4409351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
441a1bb27b1Spbrook         } else {
442a1bb27b1Spbrook             pl181_fifo_push(s, value);
443a1bb27b1Spbrook             pl181_fifo_run(s);
444a1bb27b1Spbrook         }
445a1bb27b1Spbrook         break;
446a1bb27b1Spbrook     default:
4479351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4489351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
449a1bb27b1Spbrook     }
450a1bb27b1Spbrook     pl181_update(s);
451a1bb27b1Spbrook }
452a1bb27b1Spbrook 
453ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
454ca45842aSAvi Kivity     .read = pl181_read,
455ca45842aSAvi Kivity     .write = pl181_write,
456ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
457a1bb27b1Spbrook };
458a1bb27b1Spbrook 
459624923beSPeter Maydell static void pl181_reset(DeviceState *d)
460a1bb27b1Spbrook {
461630f4442SAndreas Färber     PL181State *s = PL181(d);
462a1bb27b1Spbrook 
463a1bb27b1Spbrook     s->power = 0;
464a1bb27b1Spbrook     s->cmdarg = 0;
465a1bb27b1Spbrook     s->cmd = 0;
466a1bb27b1Spbrook     s->datatimer = 0;
467a1bb27b1Spbrook     s->datalength = 0;
468a1bb27b1Spbrook     s->respcmd = 0;
469a1bb27b1Spbrook     s->response[0] = 0;
470a1bb27b1Spbrook     s->response[1] = 0;
471a1bb27b1Spbrook     s->response[2] = 0;
472a1bb27b1Spbrook     s->response[3] = 0;
473a1bb27b1Spbrook     s->datatimer = 0;
474a1bb27b1Spbrook     s->datalength = 0;
475a1bb27b1Spbrook     s->datactrl = 0;
476a1bb27b1Spbrook     s->datacnt = 0;
477a1bb27b1Spbrook     s->status = 0;
4786361cdb6Spbrook     s->linux_hack = 0;
479a1bb27b1Spbrook     s->mask[0] = 0;
480a1bb27b1Spbrook     s->mask[1] = 0;
481c31a4724SPeter Maydell 
482c31a4724SPeter Maydell     /* We can assume our GPIO outputs have been wired up now */
483*26c5b0f4SPhilippe Mathieu-Daudé     sd_set_cb(s->card, s->card_readonly, s->card_inserted);
4840cb57cc7SPeter Maydell     /* Since we're still using the legacy SD API the card is not plugged
4850cb57cc7SPeter Maydell      * into any bus, and we must reset it manually.
4860cb57cc7SPeter Maydell      */
487f703a04cSDamien Hedde     device_legacy_reset(DEVICE(s->card));
488a1bb27b1Spbrook }
489a1bb27b1Spbrook 
4900d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
491a1bb27b1Spbrook {
4920d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4930d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
4940d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
495a1bb27b1Spbrook 
4960d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
497630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
498630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
499630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
500*26c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
501*26c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
5020d554cb0Sxiaoqiang zhao }
5030d554cb0Sxiaoqiang zhao 
5040d554cb0Sxiaoqiang zhao static void pl181_realize(DeviceState *dev, Error **errp)
5050d554cb0Sxiaoqiang zhao {
5060d554cb0Sxiaoqiang zhao     PL181State *s = PL181(dev);
5070d554cb0Sxiaoqiang zhao     DriveInfo *dinfo;
5080d554cb0Sxiaoqiang zhao 
509af9e40aaSMarkus Armbruster     /* FIXME use a qdev drive property instead of drive_get_next() */
51013839974SMarkus Armbruster     dinfo = drive_get_next(IF_SD);
5114be74634SMarkus Armbruster     s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
5124f8a066bSKevin Wolf     if (s->card == NULL) {
5130d554cb0Sxiaoqiang zhao         error_setg(errp, "sd_init failed");
5144f8a066bSKevin Wolf     }
515a1bb27b1Spbrook }
516aa9311d8SPaul Brook 
517999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data)
518999e12bbSAnthony Liguori {
51939bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
520999e12bbSAnthony Liguori 
52139bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
52239bffca2SAnthony Liguori     k->reset = pl181_reset;
5239f9bdf43SMarkus Armbruster     /* Reason: init() method uses drive_get_next() */
524e90f2a8cSEduardo Habkost     k->user_creatable = false;
5250d554cb0Sxiaoqiang zhao     k->realize = pl181_realize;
526999e12bbSAnthony Liguori }
527999e12bbSAnthony Liguori 
5288c43a6f0SAndreas Färber static const TypeInfo pl181_info = {
529630f4442SAndreas Färber     .name          = TYPE_PL181,
53039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
5311d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5320d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
533999e12bbSAnthony Liguori     .class_init    = pl181_class_init,
534624923beSPeter Maydell };
535624923beSPeter Maydell 
53683f7d43aSAndreas Färber static void pl181_register_types(void)
537aa9311d8SPaul Brook {
53839bffca2SAnthony Liguori     type_register_static(&pl181_info);
539aa9311d8SPaul Brook }
540aa9311d8SPaul Brook 
54183f7d43aSAndreas Färber type_init(pl181_register_types)
542