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