xref: /qemu/hw/sd/pl181.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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"
1132cad1ffSPhilippe Mathieu-Daudé #include "system/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"
20583d09f0SPhilippe Mathieu-Daudé #include "trace.h"
21db1015e9SEduardo Habkost #include "qom/object.h"
22a1bb27b1Spbrook 
23a1bb27b1Spbrook #define PL181_FIFO_LEN 16
24a1bb27b1Spbrook 
25630f4442SAndreas Färber #define TYPE_PL181 "pl181"
268063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PL181State, PL181)
27630f4442SAndreas Färber 
282762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus"
292762eed1SPhilippe Mathieu-Daudé 
30db1015e9SEduardo Habkost struct PL181State {
31630f4442SAndreas Färber     SysBusDevice parent_obj;
32630f4442SAndreas Färber 
33ca45842aSAvi Kivity     MemoryRegion iomem;
342762eed1SPhilippe Mathieu-Daudé     SDBus sdbus;
35a1bb27b1Spbrook     uint32_t clock;
36a1bb27b1Spbrook     uint32_t power;
37a1bb27b1Spbrook     uint32_t cmdarg;
38a1bb27b1Spbrook     uint32_t cmd;
39a1bb27b1Spbrook     uint32_t datatimer;
40a1bb27b1Spbrook     uint32_t datalength;
41a1bb27b1Spbrook     uint32_t respcmd;
42a1bb27b1Spbrook     uint32_t response[4];
43a1bb27b1Spbrook     uint32_t datactrl;
44a1bb27b1Spbrook     uint32_t datacnt;
45a1bb27b1Spbrook     uint32_t status;
46a1bb27b1Spbrook     uint32_t mask[2];
47624923beSPeter Maydell     int32_t fifo_pos;
48624923beSPeter Maydell     int32_t fifo_len;
496361cdb6Spbrook     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
5067cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
516361cdb6Spbrook        subsequent transfers until after the driver polls the status word.
526361cdb6Spbrook        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
536361cdb6Spbrook      */
54624923beSPeter Maydell     int32_t linux_hack;
550e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
56d537cf6cSpbrook     qemu_irq irq[2];
57c31a4724SPeter Maydell     /* GPIO outputs for 'card is readonly' and 'card inserted' */
5826c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_readonly;
5926c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_inserted;
60db1015e9SEduardo Habkost };
61a1bb27b1Spbrook 
62624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
63624923beSPeter Maydell     .name = "pl181",
64624923beSPeter Maydell     .version_id = 1,
65624923beSPeter Maydell     .minimum_version_id = 1,
66307119baSRichard Henderson     .fields = (const VMStateField[]) {
671d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
681d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
691d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
701d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
741d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
751d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
781d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
791d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
801d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
811d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
821d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
83624923beSPeter Maydell         VMSTATE_END_OF_LIST()
84624923beSPeter Maydell     }
85624923beSPeter Maydell };
86624923beSPeter Maydell 
87a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
88a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
89a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
90a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
91a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
92a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
93a1bb27b1Spbrook 
94a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
95a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
96a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
97a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
98a1bb27b1Spbrook 
99a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
100a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
101a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
102a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
103a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
104a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
105a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
106a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
107a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
108a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
109a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
110a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
111a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
112a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
113a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
114a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
115a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
116a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
117a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
118a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
119a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
120a1bb27b1Spbrook 
121a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
122a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
123a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
124a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
125a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
126a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
127a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
128a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
129a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
130a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
131a1bb27b1Spbrook 
132a1bb27b1Spbrook static const unsigned char pl181_id[] =
133a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
134a1bb27b1Spbrook 
pl181_update(PL181State * s)1351d998d93SAndreas Färber static void pl181_update(PL181State *s)
136a1bb27b1Spbrook {
137a1bb27b1Spbrook     int i;
138a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
139d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
140a1bb27b1Spbrook     }
141a1bb27b1Spbrook }
142a1bb27b1Spbrook 
pl181_fifo_push(PL181State * s,uint32_t value)1431d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
144a1bb27b1Spbrook {
145a1bb27b1Spbrook     int n;
146a1bb27b1Spbrook 
147a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
1484858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
149a1bb27b1Spbrook         return;
150a1bb27b1Spbrook     }
151a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
152a1bb27b1Spbrook     s->fifo_len++;
153a1bb27b1Spbrook     s->fifo[n] = value;
154583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_push(value);
155a1bb27b1Spbrook }
156a1bb27b1Spbrook 
pl181_fifo_pop(PL181State * s)1571d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
158a1bb27b1Spbrook {
159a1bb27b1Spbrook     uint32_t value;
160a1bb27b1Spbrook 
161a1bb27b1Spbrook     if (s->fifo_len == 0) {
1624858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
163a1bb27b1Spbrook         return 0;
164a1bb27b1Spbrook     }
165a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
166a1bb27b1Spbrook     s->fifo_len--;
167a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
168583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_pop(value);
169a1bb27b1Spbrook     return value;
170a1bb27b1Spbrook }
171a1bb27b1Spbrook 
pl181_do_command(PL181State * s)172b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
173a1bb27b1Spbrook {
174bc24a225SPaul Brook     SDRequest request;
175a1bb27b1Spbrook     uint8_t response[16];
176a1bb27b1Spbrook     int rlen;
177a1bb27b1Spbrook 
178a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
179a1bb27b1Spbrook     request.arg = s->cmdarg;
180583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_send(request.cmd, request.arg);
1812762eed1SPhilippe Mathieu-Daudé     rlen = sdbus_do_command(&s->sdbus, &request, response);
182a1bb27b1Spbrook     if (rlen < 0)
183a1bb27b1Spbrook         goto error;
184a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
185a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
186a1bb27b1Spbrook             goto error;
187a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
188a1bb27b1Spbrook             goto error;
189b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
190a1bb27b1Spbrook         if (rlen == 4) {
191a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
192a1bb27b1Spbrook         } else {
193b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
194b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
195b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
196a1bb27b1Spbrook         }
197583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_response_pending();
198a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
199a1bb27b1Spbrook     } else {
200583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_sent();
201a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
202a1bb27b1Spbrook     }
203a1bb27b1Spbrook     return;
204a1bb27b1Spbrook 
205a1bb27b1Spbrook error:
206583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_timeout();
207a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
208a1bb27b1Spbrook }
209a1bb27b1Spbrook 
210aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
211a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
212a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
213a1bb27b1Spbrook 
pl181_fifo_run(PL181State * s)2141d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
215a1bb27b1Spbrook {
216a1bb27b1Spbrook     uint32_t bits;
217f21126dfSBlue Swirl     uint32_t value = 0;
218a1bb27b1Spbrook     int n;
219a1bb27b1Spbrook     int is_read;
220a1bb27b1Spbrook 
221a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2222762eed1SPhilippe Mathieu-Daudé     if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
2236361cdb6Spbrook             && !s->linux_hack) {
224bc3b26f5SPaul Brook         if (is_read) {
225a1bb27b1Spbrook             n = 0;
226bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
2278467f622SPhilippe Mathieu-Daudé                 value |= (uint32_t)sdbus_read_byte(&s->sdbus) << (n * 8);
228bc3b26f5SPaul Brook                 s->datacnt--;
229a1bb27b1Spbrook                 n++;
230a1bb27b1Spbrook                 if (n == 4) {
231a1bb27b1Spbrook                     pl181_fifo_push(s, value);
232a1bb27b1Spbrook                     n = 0;
233bc3b26f5SPaul Brook                     value = 0;
234a1bb27b1Spbrook                 }
235bc3b26f5SPaul Brook             }
236bc3b26f5SPaul Brook             if (n != 0) {
237bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
238bc3b26f5SPaul Brook             }
239bc3b26f5SPaul Brook         } else { /* write */
240bc3b26f5SPaul Brook             n = 0;
241bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
242a1bb27b1Spbrook                 if (n == 0) {
243a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
244a1bb27b1Spbrook                     n = 4;
245a1bb27b1Spbrook                 }
246bc3b26f5SPaul Brook                 n--;
247bc3b26f5SPaul Brook                 s->datacnt--;
24839017143SPhilippe Mathieu-Daudé                 sdbus_write_byte(&s->sdbus, value & 0xff);
249a1bb27b1Spbrook                 value >>= 8;
250a1bb27b1Spbrook             }
251a1bb27b1Spbrook         }
252a1bb27b1Spbrook     }
253a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
254a1bb27b1Spbrook     if (s->datacnt == 0) {
255a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
256a1bb27b1Spbrook         /* HACK: */
257a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
258583d09f0SPhilippe Mathieu-Daudé         trace_pl181_fifo_transfer_complete();
259a1bb27b1Spbrook     }
2606361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
261a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
262583d09f0SPhilippe Mathieu-Daudé         trace_pl181_data_engine_idle();
263a1bb27b1Spbrook     } else {
264a1bb27b1Spbrook         /* Update FIFO bits.  */
265a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
266a1bb27b1Spbrook         if (s->fifo_len == 0) {
267a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
268a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
269a1bb27b1Spbrook         } else {
270a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
271a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
272a1bb27b1Spbrook         }
273a1bb27b1Spbrook         if (s->fifo_len == 16) {
274a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
275a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
276a1bb27b1Spbrook         }
277a1bb27b1Spbrook         if (s->fifo_len <= 8) {
278a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
279a1bb27b1Spbrook         }
280a1bb27b1Spbrook         if (s->fifo_len >= 8) {
281a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
282a1bb27b1Spbrook         }
283a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
284a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
285a1bb27b1Spbrook         } else {
286a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
287a1bb27b1Spbrook         }
288a1bb27b1Spbrook         s->status |= bits;
289a1bb27b1Spbrook     }
290a1bb27b1Spbrook }
291a1bb27b1Spbrook 
pl181_read(void * opaque,hwaddr offset,unsigned size)292a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
293ca45842aSAvi Kivity                            unsigned size)
294a1bb27b1Spbrook {
2951d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
2966361cdb6Spbrook     uint32_t tmp;
297a1bb27b1Spbrook 
298a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
299a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
300a1bb27b1Spbrook     }
301a1bb27b1Spbrook     switch (offset) {
302a1bb27b1Spbrook     case 0x00: /* Power */
303a1bb27b1Spbrook         return s->power;
304a1bb27b1Spbrook     case 0x04: /* Clock */
305a1bb27b1Spbrook         return s->clock;
306a1bb27b1Spbrook     case 0x08: /* Argument */
307a1bb27b1Spbrook         return s->cmdarg;
308a1bb27b1Spbrook     case 0x0c: /* Command */
309a1bb27b1Spbrook         return s->cmd;
310a1bb27b1Spbrook     case 0x10: /* RespCmd */
311a1bb27b1Spbrook         return s->respcmd;
312a1bb27b1Spbrook     case 0x14: /* Response0 */
313a1bb27b1Spbrook         return s->response[0];
314a1bb27b1Spbrook     case 0x18: /* Response1 */
315a1bb27b1Spbrook         return s->response[1];
316a1bb27b1Spbrook     case 0x1c: /* Response2 */
317a1bb27b1Spbrook         return s->response[2];
318a1bb27b1Spbrook     case 0x20: /* Response3 */
319a1bb27b1Spbrook         return s->response[3];
320a1bb27b1Spbrook     case 0x24: /* DataTimer */
321a1bb27b1Spbrook         return s->datatimer;
322a1bb27b1Spbrook     case 0x28: /* DataLength */
323a1bb27b1Spbrook         return s->datalength;
324a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
325a1bb27b1Spbrook         return s->datactrl;
326a1bb27b1Spbrook     case 0x30: /* DataCnt */
327a1bb27b1Spbrook         return s->datacnt;
328a1bb27b1Spbrook     case 0x34: /* Status */
3296361cdb6Spbrook         tmp = s->status;
3306361cdb6Spbrook         if (s->linux_hack) {
3316361cdb6Spbrook             s->linux_hack = 0;
3326361cdb6Spbrook             pl181_fifo_run(s);
3336361cdb6Spbrook             pl181_update(s);
3346361cdb6Spbrook         }
3356361cdb6Spbrook         return tmp;
336a1bb27b1Spbrook     case 0x3c: /* Mask0 */
337a1bb27b1Spbrook         return s->mask[0];
338a1bb27b1Spbrook     case 0x40: /* Mask1 */
339a1bb27b1Spbrook         return s->mask[1];
340a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3416361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3426361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
34366a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3446361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
34566a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3466361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3476361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3486361cdb6Spbrook         if (s->linux_hack) {
3496361cdb6Spbrook             s->linux_hack = 0;
3506361cdb6Spbrook             pl181_fifo_run(s);
3516361cdb6Spbrook             pl181_update(s);
3526361cdb6Spbrook         }
3536361cdb6Spbrook         return tmp;
354a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
355a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
356a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
357a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3586361cdb6Spbrook         if (s->fifo_len == 0) {
3599351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
360a1bb27b1Spbrook             return 0;
361a1bb27b1Spbrook         } else {
362a1bb27b1Spbrook             uint32_t value;
363a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3646361cdb6Spbrook             s->linux_hack = 1;
365a1bb27b1Spbrook             pl181_fifo_run(s);
366a1bb27b1Spbrook             pl181_update(s);
367a1bb27b1Spbrook             return value;
368a1bb27b1Spbrook         }
369a1bb27b1Spbrook     default:
3709351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3719351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
372a1bb27b1Spbrook         return 0;
373a1bb27b1Spbrook     }
374a1bb27b1Spbrook }
375a1bb27b1Spbrook 
pl181_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)376a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
377ca45842aSAvi Kivity                         uint64_t value, unsigned size)
378a1bb27b1Spbrook {
3791d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
380a1bb27b1Spbrook 
381a1bb27b1Spbrook     switch (offset) {
382a1bb27b1Spbrook     case 0x00: /* Power */
383a1bb27b1Spbrook         s->power = value & 0xff;
384a1bb27b1Spbrook         break;
385a1bb27b1Spbrook     case 0x04: /* Clock */
386a1bb27b1Spbrook         s->clock = value & 0xff;
387a1bb27b1Spbrook         break;
388a1bb27b1Spbrook     case 0x08: /* Argument */
389a1bb27b1Spbrook         s->cmdarg = value;
390a1bb27b1Spbrook         break;
391a1bb27b1Spbrook     case 0x0c: /* Command */
392a1bb27b1Spbrook         s->cmd = value;
393a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
394a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
3959351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3969351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
397a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
3989351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3999351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
400a1bb27b1Spbrook             } else {
401b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
402a1bb27b1Spbrook                 pl181_fifo_run(s);
403a1bb27b1Spbrook             }
404a1bb27b1Spbrook             /* The command has completed one way or the other.  */
405a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
406a1bb27b1Spbrook         }
407a1bb27b1Spbrook         break;
408a1bb27b1Spbrook     case 0x24: /* DataTimer */
409a1bb27b1Spbrook         s->datatimer = value;
410a1bb27b1Spbrook         break;
411a1bb27b1Spbrook     case 0x28: /* DataLength */
412a1bb27b1Spbrook         s->datalength = value & 0xffff;
413a1bb27b1Spbrook         break;
414a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
415a1bb27b1Spbrook         s->datactrl = value & 0xff;
416a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
417a1bb27b1Spbrook             s->datacnt = s->datalength;
418a1bb27b1Spbrook             pl181_fifo_run(s);
419a1bb27b1Spbrook         }
420a1bb27b1Spbrook         break;
421a1bb27b1Spbrook     case 0x38: /* Clear */
422a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
423a1bb27b1Spbrook         break;
424a1bb27b1Spbrook     case 0x3c: /* Mask0 */
425a1bb27b1Spbrook         s->mask[0] = value;
426a1bb27b1Spbrook         break;
427a1bb27b1Spbrook     case 0x40: /* Mask1 */
428a1bb27b1Spbrook         s->mask[1] = value;
429a1bb27b1Spbrook         break;
430a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
431a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
432a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
433a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4346361cdb6Spbrook         if (s->datacnt == 0) {
4359351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
436a1bb27b1Spbrook         } else {
437a1bb27b1Spbrook             pl181_fifo_push(s, value);
438a1bb27b1Spbrook             pl181_fifo_run(s);
439a1bb27b1Spbrook         }
440a1bb27b1Spbrook         break;
441a1bb27b1Spbrook     default:
4429351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4439351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
444a1bb27b1Spbrook     }
445a1bb27b1Spbrook     pl181_update(s);
446a1bb27b1Spbrook }
447a1bb27b1Spbrook 
448ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
449ca45842aSAvi Kivity     .read = pl181_read,
450ca45842aSAvi Kivity     .write = pl181_write,
451ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
452a1bb27b1Spbrook };
453a1bb27b1Spbrook 
pl181_set_readonly(DeviceState * dev,bool level)4542762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level)
4552762eed1SPhilippe Mathieu-Daudé {
4562762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4572762eed1SPhilippe Mathieu-Daudé 
4582762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_readonly, level);
4592762eed1SPhilippe Mathieu-Daudé }
4602762eed1SPhilippe Mathieu-Daudé 
pl181_set_inserted(DeviceState * dev,bool level)4612762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level)
4622762eed1SPhilippe Mathieu-Daudé {
4632762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4642762eed1SPhilippe Mathieu-Daudé 
4652762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_inserted, level);
4662762eed1SPhilippe Mathieu-Daudé }
4672762eed1SPhilippe Mathieu-Daudé 
pl181_reset(DeviceState * d)468624923beSPeter Maydell static void pl181_reset(DeviceState *d)
469a1bb27b1Spbrook {
470630f4442SAndreas Färber     PL181State *s = PL181(d);
471a1bb27b1Spbrook 
472a1bb27b1Spbrook     s->power = 0;
473a1bb27b1Spbrook     s->cmdarg = 0;
474a1bb27b1Spbrook     s->cmd = 0;
475a1bb27b1Spbrook     s->datatimer = 0;
476a1bb27b1Spbrook     s->datalength = 0;
477a1bb27b1Spbrook     s->respcmd = 0;
478a1bb27b1Spbrook     s->response[0] = 0;
479a1bb27b1Spbrook     s->response[1] = 0;
480a1bb27b1Spbrook     s->response[2] = 0;
481a1bb27b1Spbrook     s->response[3] = 0;
482a1bb27b1Spbrook     s->datatimer = 0;
483a1bb27b1Spbrook     s->datalength = 0;
484a1bb27b1Spbrook     s->datactrl = 0;
485a1bb27b1Spbrook     s->datacnt = 0;
486a1bb27b1Spbrook     s->status = 0;
4876361cdb6Spbrook     s->linux_hack = 0;
488a1bb27b1Spbrook     s->mask[0] = 0;
489a1bb27b1Spbrook     s->mask[1] = 0;
490c31a4724SPeter Maydell 
4912762eed1SPhilippe Mathieu-Daudé     /* Reset other state based on current card insertion/readonly status */
4922762eed1SPhilippe Mathieu-Daudé     pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus));
4932762eed1SPhilippe Mathieu-Daudé     pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus));
494a1bb27b1Spbrook }
495a1bb27b1Spbrook 
pl181_init(Object * obj)4960d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
497a1bb27b1Spbrook {
4980d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4990d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
5000d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
501a1bb27b1Spbrook 
5020d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
503630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
504630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
505630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
50626c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
50726c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
5082762eed1SPhilippe Mathieu-Daudé 
509d637e1dcSPeter Maydell     qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus");
5100d554cb0Sxiaoqiang zhao }
5110d554cb0Sxiaoqiang zhao 
pl181_class_init(ObjectClass * klass,const void * data)512*12d1a768SPhilippe Mathieu-Daudé static void pl181_class_init(ObjectClass *klass, const void *data)
513999e12bbSAnthony Liguori {
51439bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
515999e12bbSAnthony Liguori 
51639bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
517e3d08143SPeter Maydell     device_class_set_legacy_reset(k, pl181_reset);
51826c607b8SPhilippe Mathieu-Daudé     /* Reason: output IRQs should be wired up */
519e90f2a8cSEduardo Habkost     k->user_creatable = false;
520999e12bbSAnthony Liguori }
521999e12bbSAnthony Liguori 
pl181_bus_class_init(ObjectClass * klass,const void * data)522*12d1a768SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, const void *data)
5232762eed1SPhilippe Mathieu-Daudé {
5242762eed1SPhilippe Mathieu-Daudé     SDBusClass *sbc = SD_BUS_CLASS(klass);
5252762eed1SPhilippe Mathieu-Daudé 
5262762eed1SPhilippe Mathieu-Daudé     sbc->set_inserted = pl181_set_inserted;
5272762eed1SPhilippe Mathieu-Daudé     sbc->set_readonly = pl181_set_readonly;
5282762eed1SPhilippe Mathieu-Daudé }
5292762eed1SPhilippe Mathieu-Daudé 
53088d2198cSPhilippe Mathieu-Daudé static const TypeInfo pl181_info[] = {
53188d2198cSPhilippe Mathieu-Daudé     {
53288d2198cSPhilippe Mathieu-Daudé         .name           = TYPE_PL181,
53388d2198cSPhilippe Mathieu-Daudé         .parent         = TYPE_SYS_BUS_DEVICE,
53488d2198cSPhilippe Mathieu-Daudé         .instance_size  = sizeof(PL181State),
53588d2198cSPhilippe Mathieu-Daudé         .instance_init  = pl181_init,
53688d2198cSPhilippe Mathieu-Daudé         .class_init     = pl181_class_init,
53788d2198cSPhilippe Mathieu-Daudé     },
53888d2198cSPhilippe Mathieu-Daudé     {
5392762eed1SPhilippe Mathieu-Daudé         .name           = TYPE_PL181_BUS,
5402762eed1SPhilippe Mathieu-Daudé         .parent         = TYPE_SD_BUS,
5412762eed1SPhilippe Mathieu-Daudé         .instance_size  = sizeof(SDBus),
5422762eed1SPhilippe Mathieu-Daudé         .class_init     = pl181_bus_class_init,
54388d2198cSPhilippe Mathieu-Daudé     },
5442762eed1SPhilippe Mathieu-Daudé };
5452762eed1SPhilippe Mathieu-Daudé 
54688d2198cSPhilippe Mathieu-Daudé DEFINE_TYPES(pl181_info)
547