xref: /qemu/hw/sd/pl181.c (revision f703a04ce558ac3c7c0587a2d919c39efb8ca3ba)
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"
180d554cb0Sxiaoqiang zhao #include "qapi/error.h"
19a1bb27b1Spbrook 
20a1bb27b1Spbrook //#define DEBUG_PL181 1
21a1bb27b1Spbrook 
22a1bb27b1Spbrook #ifdef DEBUG_PL181
23001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
24001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
25a1bb27b1Spbrook #else
26001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0)
27a1bb27b1Spbrook #endif
28a1bb27b1Spbrook 
29a1bb27b1Spbrook #define PL181_FIFO_LEN 16
30a1bb27b1Spbrook 
31630f4442SAndreas Färber #define TYPE_PL181 "pl181"
32630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
33630f4442SAndreas Färber 
341d998d93SAndreas Färber typedef struct PL181State {
35630f4442SAndreas Färber     SysBusDevice parent_obj;
36630f4442SAndreas Färber 
37ca45842aSAvi Kivity     MemoryRegion iomem;
3842a10898Spbrook     SDState *card;
39a1bb27b1Spbrook     uint32_t clock;
40a1bb27b1Spbrook     uint32_t power;
41a1bb27b1Spbrook     uint32_t cmdarg;
42a1bb27b1Spbrook     uint32_t cmd;
43a1bb27b1Spbrook     uint32_t datatimer;
44a1bb27b1Spbrook     uint32_t datalength;
45a1bb27b1Spbrook     uint32_t respcmd;
46a1bb27b1Spbrook     uint32_t response[4];
47a1bb27b1Spbrook     uint32_t datactrl;
48a1bb27b1Spbrook     uint32_t datacnt;
49a1bb27b1Spbrook     uint32_t status;
50a1bb27b1Spbrook     uint32_t mask[2];
51624923beSPeter Maydell     int32_t fifo_pos;
52624923beSPeter Maydell     int32_t fifo_len;
536361cdb6Spbrook     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5467cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
556361cdb6Spbrook        subsequent transfers until after the driver polls the status word.
566361cdb6Spbrook        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
576361cdb6Spbrook      */
58624923beSPeter Maydell     int32_t linux_hack;
59a1bb27b1Spbrook     uint32_t fifo[PL181_FIFO_LEN];
60d537cf6cSpbrook     qemu_irq irq[2];
61c31a4724SPeter Maydell     /* GPIO outputs for 'card is readonly' and 'card inserted' */
62c31a4724SPeter Maydell     qemu_irq cardstatus[2];
631d998d93SAndreas Färber } PL181State;
64a1bb27b1Spbrook 
65624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
66624923beSPeter Maydell     .name = "pl181",
67624923beSPeter Maydell     .version_id = 1,
68624923beSPeter Maydell     .minimum_version_id = 1,
69624923beSPeter Maydell     .fields = (VMStateField[]) {
701d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
751d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
781d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
791d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
801d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
811d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
821d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
831d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
841d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
851d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
86624923beSPeter Maydell         VMSTATE_END_OF_LIST()
87624923beSPeter Maydell     }
88624923beSPeter Maydell };
89624923beSPeter Maydell 
90a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
91a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
92a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
93a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
94a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
95a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
96a1bb27b1Spbrook 
97a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
98a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
99a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
100a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
101a1bb27b1Spbrook 
102a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
103a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
104a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
105a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
106a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
107a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
108a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
109a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
110a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
111a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
112a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
113a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
114a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
115a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
116a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
117a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
118a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
119a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
120a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
121a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
122a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
123a1bb27b1Spbrook 
124a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
125a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
126a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
127a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
128a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
129a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
130a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
131a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
132a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
133a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
134a1bb27b1Spbrook 
135a1bb27b1Spbrook static const unsigned char pl181_id[] =
136a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
137a1bb27b1Spbrook 
1381d998d93SAndreas Färber static void pl181_update(PL181State *s)
139a1bb27b1Spbrook {
140a1bb27b1Spbrook     int i;
141a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
142d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
143a1bb27b1Spbrook     }
144a1bb27b1Spbrook }
145a1bb27b1Spbrook 
1461d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
147a1bb27b1Spbrook {
148a1bb27b1Spbrook     int n;
149a1bb27b1Spbrook 
150a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
151a1bb27b1Spbrook         fprintf(stderr, "pl181: FIFO overflow\n");
152a1bb27b1Spbrook         return;
153a1bb27b1Spbrook     }
154a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
155a1bb27b1Spbrook     s->fifo_len++;
156a1bb27b1Spbrook     s->fifo[n] = value;
157a1bb27b1Spbrook     DPRINTF("FIFO push %08x\n", (int)value);
158a1bb27b1Spbrook }
159a1bb27b1Spbrook 
1601d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
161a1bb27b1Spbrook {
162a1bb27b1Spbrook     uint32_t value;
163a1bb27b1Spbrook 
164a1bb27b1Spbrook     if (s->fifo_len == 0) {
165a1bb27b1Spbrook         fprintf(stderr, "pl181: FIFO underflow\n");
166a1bb27b1Spbrook         return 0;
167a1bb27b1Spbrook     }
168a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
169a1bb27b1Spbrook     s->fifo_len--;
170a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
171a1bb27b1Spbrook     DPRINTF("FIFO pop %08x\n", (int)value);
172a1bb27b1Spbrook     return value;
173a1bb27b1Spbrook }
174a1bb27b1Spbrook 
1751d998d93SAndreas Färber static void pl181_send_command(PL181State *s)
176a1bb27b1Spbrook {
177bc24a225SPaul Brook     SDRequest request;
178a1bb27b1Spbrook     uint8_t response[16];
179a1bb27b1Spbrook     int rlen;
180a1bb27b1Spbrook 
181a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
182a1bb27b1Spbrook     request.arg = s->cmdarg;
183a1bb27b1Spbrook     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
184a1bb27b1Spbrook     rlen = sd_do_command(s->card, &request, response);
185a1bb27b1Spbrook     if (rlen < 0)
186a1bb27b1Spbrook         goto error;
187a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
188a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
189a1bb27b1Spbrook             goto error;
190a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
191a1bb27b1Spbrook             goto error;
192b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
193a1bb27b1Spbrook         if (rlen == 4) {
194a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
195a1bb27b1Spbrook         } else {
196b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
197b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
198b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
199a1bb27b1Spbrook         }
200aa1f17c1Sths         DPRINTF("Response received\n");
201a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
202a1bb27b1Spbrook     } else {
203a1bb27b1Spbrook         DPRINTF("Command sent\n");
204a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
205a1bb27b1Spbrook     }
206a1bb27b1Spbrook     return;
207a1bb27b1Spbrook 
208a1bb27b1Spbrook error:
209a1bb27b1Spbrook     DPRINTF("Timeout\n");
210a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
211a1bb27b1Spbrook }
212a1bb27b1Spbrook 
213aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
214a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
215a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
216a1bb27b1Spbrook 
2171d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
218a1bb27b1Spbrook {
219a1bb27b1Spbrook     uint32_t bits;
220f21126dfSBlue Swirl     uint32_t value = 0;
221a1bb27b1Spbrook     int n;
222a1bb27b1Spbrook     int is_read;
223a1bb27b1Spbrook 
224a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2256361cdb6Spbrook     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
2266361cdb6Spbrook             && !s->linux_hack) {
227bc3b26f5SPaul Brook         if (is_read) {
228a1bb27b1Spbrook             n = 0;
229bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
230a1bb27b1Spbrook                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
231bc3b26f5SPaul Brook                 s->datacnt--;
232a1bb27b1Spbrook                 n++;
233a1bb27b1Spbrook                 if (n == 4) {
234a1bb27b1Spbrook                     pl181_fifo_push(s, value);
235a1bb27b1Spbrook                     n = 0;
236bc3b26f5SPaul Brook                     value = 0;
237a1bb27b1Spbrook                 }
238bc3b26f5SPaul Brook             }
239bc3b26f5SPaul Brook             if (n != 0) {
240bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
241bc3b26f5SPaul Brook             }
242bc3b26f5SPaul Brook         } else { /* write */
243bc3b26f5SPaul Brook             n = 0;
244bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
245a1bb27b1Spbrook                 if (n == 0) {
246a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
247a1bb27b1Spbrook                     n = 4;
248a1bb27b1Spbrook                 }
249bc3b26f5SPaul Brook                 n--;
250bc3b26f5SPaul Brook                 s->datacnt--;
251a1bb27b1Spbrook                 sd_write_data(s->card, value & 0xff);
252a1bb27b1Spbrook                 value >>= 8;
253a1bb27b1Spbrook             }
254a1bb27b1Spbrook         }
255a1bb27b1Spbrook     }
256a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
257a1bb27b1Spbrook     if (s->datacnt == 0) {
258a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
259a1bb27b1Spbrook         /* HACK: */
260a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
261a1bb27b1Spbrook         DPRINTF("Transfer Complete\n");
262a1bb27b1Spbrook     }
2636361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
264a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
265a1bb27b1Spbrook         DPRINTF("Data engine idle\n");
266a1bb27b1Spbrook     } else {
267a1bb27b1Spbrook         /* Update FIFO bits.  */
268a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
269a1bb27b1Spbrook         if (s->fifo_len == 0) {
270a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
271a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
272a1bb27b1Spbrook         } else {
273a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
274a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
275a1bb27b1Spbrook         }
276a1bb27b1Spbrook         if (s->fifo_len == 16) {
277a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
278a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
279a1bb27b1Spbrook         }
280a1bb27b1Spbrook         if (s->fifo_len <= 8) {
281a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
282a1bb27b1Spbrook         }
283a1bb27b1Spbrook         if (s->fifo_len >= 8) {
284a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
285a1bb27b1Spbrook         }
286a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
287a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
288a1bb27b1Spbrook         } else {
289a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
290a1bb27b1Spbrook         }
291a1bb27b1Spbrook         s->status |= bits;
292a1bb27b1Spbrook     }
293a1bb27b1Spbrook }
294a1bb27b1Spbrook 
295a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
296ca45842aSAvi Kivity                            unsigned size)
297a1bb27b1Spbrook {
2981d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
2996361cdb6Spbrook     uint32_t tmp;
300a1bb27b1Spbrook 
301a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
302a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
303a1bb27b1Spbrook     }
304a1bb27b1Spbrook     switch (offset) {
305a1bb27b1Spbrook     case 0x00: /* Power */
306a1bb27b1Spbrook         return s->power;
307a1bb27b1Spbrook     case 0x04: /* Clock */
308a1bb27b1Spbrook         return s->clock;
309a1bb27b1Spbrook     case 0x08: /* Argument */
310a1bb27b1Spbrook         return s->cmdarg;
311a1bb27b1Spbrook     case 0x0c: /* Command */
312a1bb27b1Spbrook         return s->cmd;
313a1bb27b1Spbrook     case 0x10: /* RespCmd */
314a1bb27b1Spbrook         return s->respcmd;
315a1bb27b1Spbrook     case 0x14: /* Response0 */
316a1bb27b1Spbrook         return s->response[0];
317a1bb27b1Spbrook     case 0x18: /* Response1 */
318a1bb27b1Spbrook         return s->response[1];
319a1bb27b1Spbrook     case 0x1c: /* Response2 */
320a1bb27b1Spbrook         return s->response[2];
321a1bb27b1Spbrook     case 0x20: /* Response3 */
322a1bb27b1Spbrook         return s->response[3];
323a1bb27b1Spbrook     case 0x24: /* DataTimer */
324a1bb27b1Spbrook         return s->datatimer;
325a1bb27b1Spbrook     case 0x28: /* DataLength */
326a1bb27b1Spbrook         return s->datalength;
327a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
328a1bb27b1Spbrook         return s->datactrl;
329a1bb27b1Spbrook     case 0x30: /* DataCnt */
330a1bb27b1Spbrook         return s->datacnt;
331a1bb27b1Spbrook     case 0x34: /* Status */
3326361cdb6Spbrook         tmp = s->status;
3336361cdb6Spbrook         if (s->linux_hack) {
3346361cdb6Spbrook             s->linux_hack = 0;
3356361cdb6Spbrook             pl181_fifo_run(s);
3366361cdb6Spbrook             pl181_update(s);
3376361cdb6Spbrook         }
3386361cdb6Spbrook         return tmp;
339a1bb27b1Spbrook     case 0x3c: /* Mask0 */
340a1bb27b1Spbrook         return s->mask[0];
341a1bb27b1Spbrook     case 0x40: /* Mask1 */
342a1bb27b1Spbrook         return s->mask[1];
343a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3446361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3456361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
34666a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3476361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
34866a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3496361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3506361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3516361cdb6Spbrook         if (s->linux_hack) {
3526361cdb6Spbrook             s->linux_hack = 0;
3536361cdb6Spbrook             pl181_fifo_run(s);
3546361cdb6Spbrook             pl181_update(s);
3556361cdb6Spbrook         }
3566361cdb6Spbrook         return tmp;
357a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
358a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
359a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
360a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3616361cdb6Spbrook         if (s->fifo_len == 0) {
3629351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
363a1bb27b1Spbrook             return 0;
364a1bb27b1Spbrook         } else {
365a1bb27b1Spbrook             uint32_t value;
366a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3676361cdb6Spbrook             s->linux_hack = 1;
368a1bb27b1Spbrook             pl181_fifo_run(s);
369a1bb27b1Spbrook             pl181_update(s);
370a1bb27b1Spbrook             return value;
371a1bb27b1Spbrook         }
372a1bb27b1Spbrook     default:
3739351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3749351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
375a1bb27b1Spbrook         return 0;
376a1bb27b1Spbrook     }
377a1bb27b1Spbrook }
378a1bb27b1Spbrook 
379a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
380ca45842aSAvi Kivity                         uint64_t value, unsigned size)
381a1bb27b1Spbrook {
3821d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
383a1bb27b1Spbrook 
384a1bb27b1Spbrook     switch (offset) {
385a1bb27b1Spbrook     case 0x00: /* Power */
386a1bb27b1Spbrook         s->power = value & 0xff;
387a1bb27b1Spbrook         break;
388a1bb27b1Spbrook     case 0x04: /* Clock */
389a1bb27b1Spbrook         s->clock = value & 0xff;
390a1bb27b1Spbrook         break;
391a1bb27b1Spbrook     case 0x08: /* Argument */
392a1bb27b1Spbrook         s->cmdarg = value;
393a1bb27b1Spbrook         break;
394a1bb27b1Spbrook     case 0x0c: /* Command */
395a1bb27b1Spbrook         s->cmd = value;
396a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
397a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
3989351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3999351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
400a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
4019351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4029351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
403a1bb27b1Spbrook             } else {
404a1bb27b1Spbrook                 pl181_send_command(s);
405a1bb27b1Spbrook                 pl181_fifo_run(s);
406a1bb27b1Spbrook             }
407a1bb27b1Spbrook             /* The command has completed one way or the other.  */
408a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
409a1bb27b1Spbrook         }
410a1bb27b1Spbrook         break;
411a1bb27b1Spbrook     case 0x24: /* DataTimer */
412a1bb27b1Spbrook         s->datatimer = value;
413a1bb27b1Spbrook         break;
414a1bb27b1Spbrook     case 0x28: /* DataLength */
415a1bb27b1Spbrook         s->datalength = value & 0xffff;
416a1bb27b1Spbrook         break;
417a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
418a1bb27b1Spbrook         s->datactrl = value & 0xff;
419a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
420a1bb27b1Spbrook             s->datacnt = s->datalength;
421a1bb27b1Spbrook             pl181_fifo_run(s);
422a1bb27b1Spbrook         }
423a1bb27b1Spbrook         break;
424a1bb27b1Spbrook     case 0x38: /* Clear */
425a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
426a1bb27b1Spbrook         break;
427a1bb27b1Spbrook     case 0x3c: /* Mask0 */
428a1bb27b1Spbrook         s->mask[0] = value;
429a1bb27b1Spbrook         break;
430a1bb27b1Spbrook     case 0x40: /* Mask1 */
431a1bb27b1Spbrook         s->mask[1] = value;
432a1bb27b1Spbrook         break;
433a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
434a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
435a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
436a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4376361cdb6Spbrook         if (s->datacnt == 0) {
4389351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
439a1bb27b1Spbrook         } else {
440a1bb27b1Spbrook             pl181_fifo_push(s, value);
441a1bb27b1Spbrook             pl181_fifo_run(s);
442a1bb27b1Spbrook         }
443a1bb27b1Spbrook         break;
444a1bb27b1Spbrook     default:
4459351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4469351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
447a1bb27b1Spbrook     }
448a1bb27b1Spbrook     pl181_update(s);
449a1bb27b1Spbrook }
450a1bb27b1Spbrook 
451ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
452ca45842aSAvi Kivity     .read = pl181_read,
453ca45842aSAvi Kivity     .write = pl181_write,
454ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
455a1bb27b1Spbrook };
456a1bb27b1Spbrook 
457624923beSPeter Maydell static void pl181_reset(DeviceState *d)
458a1bb27b1Spbrook {
459630f4442SAndreas Färber     PL181State *s = PL181(d);
460a1bb27b1Spbrook 
461a1bb27b1Spbrook     s->power = 0;
462a1bb27b1Spbrook     s->cmdarg = 0;
463a1bb27b1Spbrook     s->cmd = 0;
464a1bb27b1Spbrook     s->datatimer = 0;
465a1bb27b1Spbrook     s->datalength = 0;
466a1bb27b1Spbrook     s->respcmd = 0;
467a1bb27b1Spbrook     s->response[0] = 0;
468a1bb27b1Spbrook     s->response[1] = 0;
469a1bb27b1Spbrook     s->response[2] = 0;
470a1bb27b1Spbrook     s->response[3] = 0;
471a1bb27b1Spbrook     s->datatimer = 0;
472a1bb27b1Spbrook     s->datalength = 0;
473a1bb27b1Spbrook     s->datactrl = 0;
474a1bb27b1Spbrook     s->datacnt = 0;
475a1bb27b1Spbrook     s->status = 0;
4766361cdb6Spbrook     s->linux_hack = 0;
477a1bb27b1Spbrook     s->mask[0] = 0;
478a1bb27b1Spbrook     s->mask[1] = 0;
479c31a4724SPeter Maydell 
480c31a4724SPeter Maydell     /* We can assume our GPIO outputs have been wired up now */
481c31a4724SPeter Maydell     sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
4820cb57cc7SPeter Maydell     /* Since we're still using the legacy SD API the card is not plugged
4830cb57cc7SPeter Maydell      * into any bus, and we must reset it manually.
4840cb57cc7SPeter Maydell      */
485*f703a04cSDamien Hedde     device_legacy_reset(DEVICE(s->card));
486a1bb27b1Spbrook }
487a1bb27b1Spbrook 
4880d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
489a1bb27b1Spbrook {
4900d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4910d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
4920d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
493a1bb27b1Spbrook 
4940d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
495630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
496630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
497630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
498630f4442SAndreas Färber     qdev_init_gpio_out(dev, s->cardstatus, 2);
4990d554cb0Sxiaoqiang zhao }
5000d554cb0Sxiaoqiang zhao 
5010d554cb0Sxiaoqiang zhao static void pl181_realize(DeviceState *dev, Error **errp)
5020d554cb0Sxiaoqiang zhao {
5030d554cb0Sxiaoqiang zhao     PL181State *s = PL181(dev);
5040d554cb0Sxiaoqiang zhao     DriveInfo *dinfo;
5050d554cb0Sxiaoqiang zhao 
506af9e40aaSMarkus Armbruster     /* FIXME use a qdev drive property instead of drive_get_next() */
50713839974SMarkus Armbruster     dinfo = drive_get_next(IF_SD);
5084be74634SMarkus Armbruster     s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
5094f8a066bSKevin Wolf     if (s->card == NULL) {
5100d554cb0Sxiaoqiang zhao         error_setg(errp, "sd_init failed");
5114f8a066bSKevin Wolf     }
512a1bb27b1Spbrook }
513aa9311d8SPaul Brook 
514999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data)
515999e12bbSAnthony Liguori {
51639bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
517999e12bbSAnthony Liguori 
51839bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
51939bffca2SAnthony Liguori     k->reset = pl181_reset;
5209f9bdf43SMarkus Armbruster     /* Reason: init() method uses drive_get_next() */
521e90f2a8cSEduardo Habkost     k->user_creatable = false;
5220d554cb0Sxiaoqiang zhao     k->realize = pl181_realize;
523999e12bbSAnthony Liguori }
524999e12bbSAnthony Liguori 
5258c43a6f0SAndreas Färber static const TypeInfo pl181_info = {
526630f4442SAndreas Färber     .name          = TYPE_PL181,
52739bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
5281d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5290d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
530999e12bbSAnthony Liguori     .class_init    = pl181_class_init,
531624923beSPeter Maydell };
532624923beSPeter Maydell 
53383f7d43aSAndreas Färber static void pl181_register_types(void)
534aa9311d8SPaul Brook {
53539bffca2SAnthony Liguori     type_register_static(&pl181_info);
536aa9311d8SPaul Brook }
537aa9311d8SPaul Brook 
53883f7d43aSAndreas Färber type_init(pl181_register_types)
539