xref: /qemu/hw/sd/pl181.c (revision af9e40aa8f36e30e89f16323b3d341ee59309b7e)
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 
10fa1d36dfSMarkus Armbruster #include "sysemu/block-backend.h"
119c17d615SPaolo Bonzini #include "sysemu/blockdev.h"
1283c9f4caSPaolo Bonzini #include "hw/sysbus.h"
1383c9f4caSPaolo Bonzini #include "hw/sd.h"
14a1bb27b1Spbrook 
15a1bb27b1Spbrook //#define DEBUG_PL181 1
16a1bb27b1Spbrook 
17a1bb27b1Spbrook #ifdef DEBUG_PL181
18001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
19001faf32SBlue Swirl do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
20a1bb27b1Spbrook #else
21001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0)
22a1bb27b1Spbrook #endif
23a1bb27b1Spbrook 
24a1bb27b1Spbrook #define PL181_FIFO_LEN 16
25a1bb27b1Spbrook 
26630f4442SAndreas Färber #define TYPE_PL181 "pl181"
27630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
28630f4442SAndreas Färber 
291d998d93SAndreas Färber typedef struct PL181State {
30630f4442SAndreas Färber     SysBusDevice parent_obj;
31630f4442SAndreas Färber 
32ca45842aSAvi Kivity     MemoryRegion iomem;
3342a10898Spbrook     SDState *card;
34a1bb27b1Spbrook     uint32_t clock;
35a1bb27b1Spbrook     uint32_t power;
36a1bb27b1Spbrook     uint32_t cmdarg;
37a1bb27b1Spbrook     uint32_t cmd;
38a1bb27b1Spbrook     uint32_t datatimer;
39a1bb27b1Spbrook     uint32_t datalength;
40a1bb27b1Spbrook     uint32_t respcmd;
41a1bb27b1Spbrook     uint32_t response[4];
42a1bb27b1Spbrook     uint32_t datactrl;
43a1bb27b1Spbrook     uint32_t datacnt;
44a1bb27b1Spbrook     uint32_t status;
45a1bb27b1Spbrook     uint32_t mask[2];
46624923beSPeter Maydell     int32_t fifo_pos;
47624923beSPeter Maydell     int32_t fifo_len;
486361cdb6Spbrook     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
496361cdb6Spbrook        while it is reading the FIFO.  We hack around this be defering
506361cdb6Spbrook        subsequent transfers until after the driver polls the status word.
516361cdb6Spbrook        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
526361cdb6Spbrook      */
53624923beSPeter Maydell     int32_t linux_hack;
54a1bb27b1Spbrook     uint32_t fifo[PL181_FIFO_LEN];
55d537cf6cSpbrook     qemu_irq irq[2];
56c31a4724SPeter Maydell     /* GPIO outputs for 'card is readonly' and 'card inserted' */
57c31a4724SPeter Maydell     qemu_irq cardstatus[2];
581d998d93SAndreas Färber } PL181State;
59a1bb27b1Spbrook 
60624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
61624923beSPeter Maydell     .name = "pl181",
62624923beSPeter Maydell     .version_id = 1,
63624923beSPeter Maydell     .minimum_version_id = 1,
64624923beSPeter Maydell     .fields = (VMStateField[]) {
651d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
661d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
671d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
681d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
691d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
701d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
731d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
751d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
771d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
781d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
791d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
801d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
81624923beSPeter Maydell         VMSTATE_END_OF_LIST()
82624923beSPeter Maydell     }
83624923beSPeter Maydell };
84624923beSPeter Maydell 
85a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
86a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
87a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
88a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
89a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
90a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
91a1bb27b1Spbrook 
92a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
93a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
94a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
95a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
96a1bb27b1Spbrook 
97a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
98a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
99a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
100a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
101a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
102a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
103a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
104a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
105a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
106a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
107a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
108a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
109a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
110a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
111a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
112a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
113a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
114a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
115a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
116a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
117a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
118a1bb27b1Spbrook 
119a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
120a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
121a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
122a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
123a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
124a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
125a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
126a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
127a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
128a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
129a1bb27b1Spbrook 
130a1bb27b1Spbrook static const unsigned char pl181_id[] =
131a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
132a1bb27b1Spbrook 
1331d998d93SAndreas Färber static void pl181_update(PL181State *s)
134a1bb27b1Spbrook {
135a1bb27b1Spbrook     int i;
136a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
137d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
138a1bb27b1Spbrook     }
139a1bb27b1Spbrook }
140a1bb27b1Spbrook 
1411d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
142a1bb27b1Spbrook {
143a1bb27b1Spbrook     int n;
144a1bb27b1Spbrook 
145a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
146a1bb27b1Spbrook         fprintf(stderr, "pl181: FIFO overflow\n");
147a1bb27b1Spbrook         return;
148a1bb27b1Spbrook     }
149a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
150a1bb27b1Spbrook     s->fifo_len++;
151a1bb27b1Spbrook     s->fifo[n] = value;
152a1bb27b1Spbrook     DPRINTF("FIFO push %08x\n", (int)value);
153a1bb27b1Spbrook }
154a1bb27b1Spbrook 
1551d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
156a1bb27b1Spbrook {
157a1bb27b1Spbrook     uint32_t value;
158a1bb27b1Spbrook 
159a1bb27b1Spbrook     if (s->fifo_len == 0) {
160a1bb27b1Spbrook         fprintf(stderr, "pl181: FIFO underflow\n");
161a1bb27b1Spbrook         return 0;
162a1bb27b1Spbrook     }
163a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
164a1bb27b1Spbrook     s->fifo_len--;
165a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
166a1bb27b1Spbrook     DPRINTF("FIFO pop %08x\n", (int)value);
167a1bb27b1Spbrook     return value;
168a1bb27b1Spbrook }
169a1bb27b1Spbrook 
1701d998d93SAndreas Färber static void pl181_send_command(PL181State *s)
171a1bb27b1Spbrook {
172bc24a225SPaul Brook     SDRequest request;
173a1bb27b1Spbrook     uint8_t response[16];
174a1bb27b1Spbrook     int rlen;
175a1bb27b1Spbrook 
176a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
177a1bb27b1Spbrook     request.arg = s->cmdarg;
178a1bb27b1Spbrook     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
179a1bb27b1Spbrook     rlen = sd_do_command(s->card, &request, response);
180a1bb27b1Spbrook     if (rlen < 0)
181a1bb27b1Spbrook         goto error;
182a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
1838827b0fbSPeter Maydell #define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \
184a1bb27b1Spbrook                   | (response[n + 2] << 8) | response[n + 3])
185a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
186a1bb27b1Spbrook             goto error;
187a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
188a1bb27b1Spbrook             goto error;
189a1bb27b1Spbrook         s->response[0] = RWORD(0);
190a1bb27b1Spbrook         if (rlen == 4) {
191a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
192a1bb27b1Spbrook         } else {
193a1bb27b1Spbrook             s->response[1] = RWORD(4);
194a1bb27b1Spbrook             s->response[2] = RWORD(8);
195a1bb27b1Spbrook             s->response[3] = RWORD(12) & ~1;
196a1bb27b1Spbrook         }
197aa1f17c1Sths         DPRINTF("Response received\n");
198a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
199a1bb27b1Spbrook #undef RWORD
200a1bb27b1Spbrook     } else {
201a1bb27b1Spbrook         DPRINTF("Command sent\n");
202a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
203a1bb27b1Spbrook     }
204a1bb27b1Spbrook     return;
205a1bb27b1Spbrook 
206a1bb27b1Spbrook error:
207a1bb27b1Spbrook     DPRINTF("Timeout\n");
208a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
209a1bb27b1Spbrook }
210a1bb27b1Spbrook 
211aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
212a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
213a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
214a1bb27b1Spbrook 
2151d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
216a1bb27b1Spbrook {
217a1bb27b1Spbrook     uint32_t bits;
218f21126dfSBlue Swirl     uint32_t value = 0;
219a1bb27b1Spbrook     int n;
220a1bb27b1Spbrook     int is_read;
221a1bb27b1Spbrook 
222a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2236361cdb6Spbrook     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
2246361cdb6Spbrook             && !s->linux_hack) {
225bc3b26f5SPaul Brook         if (is_read) {
226a1bb27b1Spbrook             n = 0;
227bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
228a1bb27b1Spbrook                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
229bc3b26f5SPaul Brook                 s->datacnt--;
230a1bb27b1Spbrook                 n++;
231a1bb27b1Spbrook                 if (n == 4) {
232a1bb27b1Spbrook                     pl181_fifo_push(s, value);
233a1bb27b1Spbrook                     n = 0;
234bc3b26f5SPaul Brook                     value = 0;
235a1bb27b1Spbrook                 }
236bc3b26f5SPaul Brook             }
237bc3b26f5SPaul Brook             if (n != 0) {
238bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
239bc3b26f5SPaul Brook             }
240bc3b26f5SPaul Brook         } else { /* write */
241bc3b26f5SPaul Brook             n = 0;
242bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
243a1bb27b1Spbrook                 if (n == 0) {
244a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
245a1bb27b1Spbrook                     n = 4;
246a1bb27b1Spbrook                 }
247bc3b26f5SPaul Brook                 n--;
248bc3b26f5SPaul Brook                 s->datacnt--;
249a1bb27b1Spbrook                 sd_write_data(s->card, value & 0xff);
250a1bb27b1Spbrook                 value >>= 8;
251a1bb27b1Spbrook             }
252a1bb27b1Spbrook         }
253a1bb27b1Spbrook     }
254a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
255a1bb27b1Spbrook     if (s->datacnt == 0) {
256a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
257a1bb27b1Spbrook         /* HACK: */
258a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
259a1bb27b1Spbrook         DPRINTF("Transfer Complete\n");
260a1bb27b1Spbrook     }
2616361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
262a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
263a1bb27b1Spbrook         DPRINTF("Data engine idle\n");
264a1bb27b1Spbrook     } else {
265a1bb27b1Spbrook         /* Update FIFO bits.  */
266a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
267a1bb27b1Spbrook         if (s->fifo_len == 0) {
268a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
269a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
270a1bb27b1Spbrook         } else {
271a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
272a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
273a1bb27b1Spbrook         }
274a1bb27b1Spbrook         if (s->fifo_len == 16) {
275a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
276a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
277a1bb27b1Spbrook         }
278a1bb27b1Spbrook         if (s->fifo_len <= 8) {
279a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
280a1bb27b1Spbrook         }
281a1bb27b1Spbrook         if (s->fifo_len >= 8) {
282a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
283a1bb27b1Spbrook         }
284a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
285a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
286a1bb27b1Spbrook         } else {
287a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
288a1bb27b1Spbrook         }
289a1bb27b1Spbrook         s->status |= bits;
290a1bb27b1Spbrook     }
291a1bb27b1Spbrook }
292a1bb27b1Spbrook 
293a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
294ca45842aSAvi Kivity                            unsigned size)
295a1bb27b1Spbrook {
2961d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
2976361cdb6Spbrook     uint32_t tmp;
298a1bb27b1Spbrook 
299a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
300a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
301a1bb27b1Spbrook     }
302a1bb27b1Spbrook     switch (offset) {
303a1bb27b1Spbrook     case 0x00: /* Power */
304a1bb27b1Spbrook         return s->power;
305a1bb27b1Spbrook     case 0x04: /* Clock */
306a1bb27b1Spbrook         return s->clock;
307a1bb27b1Spbrook     case 0x08: /* Argument */
308a1bb27b1Spbrook         return s->cmdarg;
309a1bb27b1Spbrook     case 0x0c: /* Command */
310a1bb27b1Spbrook         return s->cmd;
311a1bb27b1Spbrook     case 0x10: /* RespCmd */
312a1bb27b1Spbrook         return s->respcmd;
313a1bb27b1Spbrook     case 0x14: /* Response0 */
314a1bb27b1Spbrook         return s->response[0];
315a1bb27b1Spbrook     case 0x18: /* Response1 */
316a1bb27b1Spbrook         return s->response[1];
317a1bb27b1Spbrook     case 0x1c: /* Response2 */
318a1bb27b1Spbrook         return s->response[2];
319a1bb27b1Spbrook     case 0x20: /* Response3 */
320a1bb27b1Spbrook         return s->response[3];
321a1bb27b1Spbrook     case 0x24: /* DataTimer */
322a1bb27b1Spbrook         return s->datatimer;
323a1bb27b1Spbrook     case 0x28: /* DataLength */
324a1bb27b1Spbrook         return s->datalength;
325a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
326a1bb27b1Spbrook         return s->datactrl;
327a1bb27b1Spbrook     case 0x30: /* DataCnt */
328a1bb27b1Spbrook         return s->datacnt;
329a1bb27b1Spbrook     case 0x34: /* Status */
3306361cdb6Spbrook         tmp = s->status;
3316361cdb6Spbrook         if (s->linux_hack) {
3326361cdb6Spbrook             s->linux_hack = 0;
3336361cdb6Spbrook             pl181_fifo_run(s);
3346361cdb6Spbrook             pl181_update(s);
3356361cdb6Spbrook         }
3366361cdb6Spbrook         return tmp;
337a1bb27b1Spbrook     case 0x3c: /* Mask0 */
338a1bb27b1Spbrook         return s->mask[0];
339a1bb27b1Spbrook     case 0x40: /* Mask1 */
340a1bb27b1Spbrook         return s->mask[1];
341a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3426361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3436361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
34466a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3456361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
34666a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3476361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3486361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3496361cdb6Spbrook         if (s->linux_hack) {
3506361cdb6Spbrook             s->linux_hack = 0;
3516361cdb6Spbrook             pl181_fifo_run(s);
3526361cdb6Spbrook             pl181_update(s);
3536361cdb6Spbrook         }
3546361cdb6Spbrook         return tmp;
355a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
356a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
357a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
358a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3596361cdb6Spbrook         if (s->fifo_len == 0) {
3609351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
361a1bb27b1Spbrook             return 0;
362a1bb27b1Spbrook         } else {
363a1bb27b1Spbrook             uint32_t value;
364a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3656361cdb6Spbrook             s->linux_hack = 1;
366a1bb27b1Spbrook             pl181_fifo_run(s);
367a1bb27b1Spbrook             pl181_update(s);
368a1bb27b1Spbrook             return value;
369a1bb27b1Spbrook         }
370a1bb27b1Spbrook     default:
3719351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3729351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
373a1bb27b1Spbrook         return 0;
374a1bb27b1Spbrook     }
375a1bb27b1Spbrook }
376a1bb27b1Spbrook 
377a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
378ca45842aSAvi Kivity                         uint64_t value, unsigned size)
379a1bb27b1Spbrook {
3801d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
381a1bb27b1Spbrook 
382a1bb27b1Spbrook     switch (offset) {
383a1bb27b1Spbrook     case 0x00: /* Power */
384a1bb27b1Spbrook         s->power = value & 0xff;
385a1bb27b1Spbrook         break;
386a1bb27b1Spbrook     case 0x04: /* Clock */
387a1bb27b1Spbrook         s->clock = value & 0xff;
388a1bb27b1Spbrook         break;
389a1bb27b1Spbrook     case 0x08: /* Argument */
390a1bb27b1Spbrook         s->cmdarg = value;
391a1bb27b1Spbrook         break;
392a1bb27b1Spbrook     case 0x0c: /* Command */
393a1bb27b1Spbrook         s->cmd = value;
394a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
395a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
3969351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3979351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
398a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
3999351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
4009351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
401a1bb27b1Spbrook             } else {
402a1bb27b1Spbrook                 pl181_send_command(s);
403a1bb27b1Spbrook                 pl181_fifo_run(s);
404a1bb27b1Spbrook             }
405a1bb27b1Spbrook             /* The command has completed one way or the other.  */
406a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
407a1bb27b1Spbrook         }
408a1bb27b1Spbrook         break;
409a1bb27b1Spbrook     case 0x24: /* DataTimer */
410a1bb27b1Spbrook         s->datatimer = value;
411a1bb27b1Spbrook         break;
412a1bb27b1Spbrook     case 0x28: /* DataLength */
413a1bb27b1Spbrook         s->datalength = value & 0xffff;
414a1bb27b1Spbrook         break;
415a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
416a1bb27b1Spbrook         s->datactrl = value & 0xff;
417a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
418a1bb27b1Spbrook             s->datacnt = s->datalength;
419a1bb27b1Spbrook             pl181_fifo_run(s);
420a1bb27b1Spbrook         }
421a1bb27b1Spbrook         break;
422a1bb27b1Spbrook     case 0x38: /* Clear */
423a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
424a1bb27b1Spbrook         break;
425a1bb27b1Spbrook     case 0x3c: /* Mask0 */
426a1bb27b1Spbrook         s->mask[0] = value;
427a1bb27b1Spbrook         break;
428a1bb27b1Spbrook     case 0x40: /* Mask1 */
429a1bb27b1Spbrook         s->mask[1] = value;
430a1bb27b1Spbrook         break;
431a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
432a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
433a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
434a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4356361cdb6Spbrook         if (s->datacnt == 0) {
4369351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
437a1bb27b1Spbrook         } else {
438a1bb27b1Spbrook             pl181_fifo_push(s, value);
439a1bb27b1Spbrook             pl181_fifo_run(s);
440a1bb27b1Spbrook         }
441a1bb27b1Spbrook         break;
442a1bb27b1Spbrook     default:
4439351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4449351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
445a1bb27b1Spbrook     }
446a1bb27b1Spbrook     pl181_update(s);
447a1bb27b1Spbrook }
448a1bb27b1Spbrook 
449ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
450ca45842aSAvi Kivity     .read = pl181_read,
451ca45842aSAvi Kivity     .write = pl181_write,
452ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
453a1bb27b1Spbrook };
454a1bb27b1Spbrook 
455624923beSPeter Maydell static void pl181_reset(DeviceState *d)
456a1bb27b1Spbrook {
457630f4442SAndreas Färber     PL181State *s = PL181(d);
458a1bb27b1Spbrook 
459a1bb27b1Spbrook     s->power = 0;
460a1bb27b1Spbrook     s->cmdarg = 0;
461a1bb27b1Spbrook     s->cmd = 0;
462a1bb27b1Spbrook     s->datatimer = 0;
463a1bb27b1Spbrook     s->datalength = 0;
464a1bb27b1Spbrook     s->respcmd = 0;
465a1bb27b1Spbrook     s->response[0] = 0;
466a1bb27b1Spbrook     s->response[1] = 0;
467a1bb27b1Spbrook     s->response[2] = 0;
468a1bb27b1Spbrook     s->response[3] = 0;
469a1bb27b1Spbrook     s->datatimer = 0;
470a1bb27b1Spbrook     s->datalength = 0;
471a1bb27b1Spbrook     s->datactrl = 0;
472a1bb27b1Spbrook     s->datacnt = 0;
473a1bb27b1Spbrook     s->status = 0;
4746361cdb6Spbrook     s->linux_hack = 0;
475a1bb27b1Spbrook     s->mask[0] = 0;
476a1bb27b1Spbrook     s->mask[1] = 0;
477c31a4724SPeter Maydell 
478c31a4724SPeter Maydell     /* We can assume our GPIO outputs have been wired up now */
479c31a4724SPeter Maydell     sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
480a1bb27b1Spbrook }
481a1bb27b1Spbrook 
482630f4442SAndreas Färber static int pl181_init(SysBusDevice *sbd)
483a1bb27b1Spbrook {
484630f4442SAndreas Färber     DeviceState *dev = DEVICE(sbd);
485630f4442SAndreas Färber     PL181State *s = PL181(dev);
48613839974SMarkus Armbruster     DriveInfo *dinfo;
487a1bb27b1Spbrook 
48829776739SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000);
489630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
490630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
491630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
492630f4442SAndreas Färber     qdev_init_gpio_out(dev, s->cardstatus, 2);
493*af9e40aaSMarkus Armbruster     /* FIXME use a qdev drive property instead of drive_get_next() */
49413839974SMarkus Armbruster     dinfo = drive_get_next(IF_SD);
4954be74634SMarkus Armbruster     s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
4964f8a066bSKevin Wolf     if (s->card == NULL) {
4974f8a066bSKevin Wolf         return -1;
4984f8a066bSKevin Wolf     }
4994f8a066bSKevin Wolf 
50081a322d4SGerd Hoffmann     return 0;
501a1bb27b1Spbrook }
502aa9311d8SPaul Brook 
503999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data)
504999e12bbSAnthony Liguori {
505999e12bbSAnthony Liguori     SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
50639bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
507999e12bbSAnthony Liguori 
508999e12bbSAnthony Liguori     sdc->init = pl181_init;
50939bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
51039bffca2SAnthony Liguori     k->reset = pl181_reset;
511999e12bbSAnthony Liguori }
512999e12bbSAnthony Liguori 
5138c43a6f0SAndreas Färber static const TypeInfo pl181_info = {
514630f4442SAndreas Färber     .name          = TYPE_PL181,
51539bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
5161d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
517999e12bbSAnthony Liguori     .class_init    = pl181_class_init,
518624923beSPeter Maydell };
519624923beSPeter Maydell 
52083f7d43aSAndreas Färber static void pl181_register_types(void)
521aa9311d8SPaul Brook {
52239bffca2SAnthony Liguori     type_register_static(&pl181_info);
523aa9311d8SPaul Brook }
524aa9311d8SPaul Brook 
52583f7d43aSAndreas Färber type_init(pl181_register_types)
526