xref: /qemu/hw/sd/pl181.c (revision 583d09f078c54862cd57f230a01e61b376bcde8a)
1a1bb27b1Spbrook /*
2a1bb27b1Spbrook  * Arm PrimeCell PL181 MultiMedia Card Interface
3a1bb27b1Spbrook  *
4a1bb27b1Spbrook  * Copyright (c) 2007 CodeSourcery.
5a1bb27b1Spbrook  * Written by Paul Brook
6a1bb27b1Spbrook  *
78e31bf38SMatthew Fernandez  * This code is licensed under the GPL.
8a1bb27b1Spbrook  */
9a1bb27b1Spbrook 
108ef94f0bSPeter Maydell #include "qemu/osdep.h"
119c17d615SPaolo Bonzini #include "sysemu/blockdev.h"
1283c9f4caSPaolo Bonzini #include "hw/sysbus.h"
13d6454270SMarkus Armbruster #include "migration/vmstate.h"
1464552b6bSMarkus Armbruster #include "hw/irq.h"
15e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h"
1603dd024fSPaolo Bonzini #include "qemu/log.h"
170b8fa32fSMarkus Armbruster #include "qemu/module.h"
184858e256SAlistair Francis #include "qemu/error-report.h"
190d554cb0Sxiaoqiang zhao #include "qapi/error.h"
20*583d09f0SPhilippe Mathieu-Daudé #include "trace.h"
21a1bb27b1Spbrook 
22a1bb27b1Spbrook #define PL181_FIFO_LEN 16
23a1bb27b1Spbrook 
24630f4442SAndreas Färber #define TYPE_PL181 "pl181"
25630f4442SAndreas Färber #define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181)
26630f4442SAndreas Färber 
272762eed1SPhilippe Mathieu-Daudé #define TYPE_PL181_BUS "pl181-bus"
282762eed1SPhilippe Mathieu-Daudé 
291d998d93SAndreas Färber typedef struct PL181State {
30630f4442SAndreas Färber     SysBusDevice parent_obj;
31630f4442SAndreas Färber 
32ca45842aSAvi Kivity     MemoryRegion iomem;
332762eed1SPhilippe Mathieu-Daudé     SDBus sdbus;
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
4967cc32ebSVeres Lajos        while it is reading the FIFO.  We hack around this by deferring
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;
540e33730cSPhilippe Mathieu-Daudé     uint32_t fifo[PL181_FIFO_LEN]; /* TODO use Fifo32 */
55d537cf6cSpbrook     qemu_irq irq[2];
56c31a4724SPeter Maydell     /* GPIO outputs for 'card is readonly' and 'card inserted' */
5726c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_readonly;
5826c5b0f4SPhilippe Mathieu-Daudé     qemu_irq card_inserted;
591d998d93SAndreas Färber } PL181State;
60a1bb27b1Spbrook 
61624923beSPeter Maydell static const VMStateDescription vmstate_pl181 = {
62624923beSPeter Maydell     .name = "pl181",
63624923beSPeter Maydell     .version_id = 1,
64624923beSPeter Maydell     .minimum_version_id = 1,
65624923beSPeter Maydell     .fields = (VMStateField[]) {
661d998d93SAndreas Färber         VMSTATE_UINT32(clock, PL181State),
671d998d93SAndreas Färber         VMSTATE_UINT32(power, PL181State),
681d998d93SAndreas Färber         VMSTATE_UINT32(cmdarg, PL181State),
691d998d93SAndreas Färber         VMSTATE_UINT32(cmd, PL181State),
701d998d93SAndreas Färber         VMSTATE_UINT32(datatimer, PL181State),
711d998d93SAndreas Färber         VMSTATE_UINT32(datalength, PL181State),
721d998d93SAndreas Färber         VMSTATE_UINT32(respcmd, PL181State),
731d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(response, PL181State, 4),
741d998d93SAndreas Färber         VMSTATE_UINT32(datactrl, PL181State),
751d998d93SAndreas Färber         VMSTATE_UINT32(datacnt, PL181State),
761d998d93SAndreas Färber         VMSTATE_UINT32(status, PL181State),
771d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(mask, PL181State, 2),
781d998d93SAndreas Färber         VMSTATE_INT32(fifo_pos, PL181State),
791d998d93SAndreas Färber         VMSTATE_INT32(fifo_len, PL181State),
801d998d93SAndreas Färber         VMSTATE_INT32(linux_hack, PL181State),
811d998d93SAndreas Färber         VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN),
82624923beSPeter Maydell         VMSTATE_END_OF_LIST()
83624923beSPeter Maydell     }
84624923beSPeter Maydell };
85624923beSPeter Maydell 
86a1bb27b1Spbrook #define PL181_CMD_INDEX     0x3f
87a1bb27b1Spbrook #define PL181_CMD_RESPONSE  (1 << 6)
88a1bb27b1Spbrook #define PL181_CMD_LONGRESP  (1 << 7)
89a1bb27b1Spbrook #define PL181_CMD_INTERRUPT (1 << 8)
90a1bb27b1Spbrook #define PL181_CMD_PENDING   (1 << 9)
91a1bb27b1Spbrook #define PL181_CMD_ENABLE    (1 << 10)
92a1bb27b1Spbrook 
93a1bb27b1Spbrook #define PL181_DATA_ENABLE             (1 << 0)
94a1bb27b1Spbrook #define PL181_DATA_DIRECTION          (1 << 1)
95a1bb27b1Spbrook #define PL181_DATA_MODE               (1 << 2)
96a1bb27b1Spbrook #define PL181_DATA_DMAENABLE          (1 << 3)
97a1bb27b1Spbrook 
98a1bb27b1Spbrook #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
99a1bb27b1Spbrook #define PL181_STATUS_DATACRCFAIL      (1 << 1)
100a1bb27b1Spbrook #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
101a1bb27b1Spbrook #define PL181_STATUS_DATATIMEOUT      (1 << 3)
102a1bb27b1Spbrook #define PL181_STATUS_TXUNDERRUN       (1 << 4)
103a1bb27b1Spbrook #define PL181_STATUS_RXOVERRUN        (1 << 5)
104a1bb27b1Spbrook #define PL181_STATUS_CMDRESPEND       (1 << 6)
105a1bb27b1Spbrook #define PL181_STATUS_CMDSENT          (1 << 7)
106a1bb27b1Spbrook #define PL181_STATUS_DATAEND          (1 << 8)
107a1bb27b1Spbrook #define PL181_STATUS_DATABLOCKEND     (1 << 10)
108a1bb27b1Spbrook #define PL181_STATUS_CMDACTIVE        (1 << 11)
109a1bb27b1Spbrook #define PL181_STATUS_TXACTIVE         (1 << 12)
110a1bb27b1Spbrook #define PL181_STATUS_RXACTIVE         (1 << 13)
111a1bb27b1Spbrook #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
112a1bb27b1Spbrook #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
113a1bb27b1Spbrook #define PL181_STATUS_TXFIFOFULL       (1 << 16)
114a1bb27b1Spbrook #define PL181_STATUS_RXFIFOFULL       (1 << 17)
115a1bb27b1Spbrook #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
116a1bb27b1Spbrook #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
117a1bb27b1Spbrook #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
118a1bb27b1Spbrook #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
119a1bb27b1Spbrook 
120a1bb27b1Spbrook #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
121a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOHALFEMPTY \
122a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOFULL \
123a1bb27b1Spbrook                              |PL181_STATUS_TXFIFOEMPTY \
124a1bb27b1Spbrook                              |PL181_STATUS_TXDATAAVLBL)
125a1bb27b1Spbrook #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
126a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOHALFFULL \
127a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOFULL \
128a1bb27b1Spbrook                              |PL181_STATUS_RXFIFOEMPTY \
129a1bb27b1Spbrook                              |PL181_STATUS_RXDATAAVLBL)
130a1bb27b1Spbrook 
131a1bb27b1Spbrook static const unsigned char pl181_id[] =
132a1bb27b1Spbrook { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
133a1bb27b1Spbrook 
1341d998d93SAndreas Färber static void pl181_update(PL181State *s)
135a1bb27b1Spbrook {
136a1bb27b1Spbrook     int i;
137a1bb27b1Spbrook     for (i = 0; i < 2; i++) {
138d537cf6cSpbrook         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
139a1bb27b1Spbrook     }
140a1bb27b1Spbrook }
141a1bb27b1Spbrook 
1421d998d93SAndreas Färber static void pl181_fifo_push(PL181State *s, uint32_t value)
143a1bb27b1Spbrook {
144a1bb27b1Spbrook     int n;
145a1bb27b1Spbrook 
146a1bb27b1Spbrook     if (s->fifo_len == PL181_FIFO_LEN) {
1474858e256SAlistair Francis         error_report("%s: FIFO overflow", __func__);
148a1bb27b1Spbrook         return;
149a1bb27b1Spbrook     }
150a1bb27b1Spbrook     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
151a1bb27b1Spbrook     s->fifo_len++;
152a1bb27b1Spbrook     s->fifo[n] = value;
153*583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_push(value);
154a1bb27b1Spbrook }
155a1bb27b1Spbrook 
1561d998d93SAndreas Färber static uint32_t pl181_fifo_pop(PL181State *s)
157a1bb27b1Spbrook {
158a1bb27b1Spbrook     uint32_t value;
159a1bb27b1Spbrook 
160a1bb27b1Spbrook     if (s->fifo_len == 0) {
1614858e256SAlistair Francis         error_report("%s: FIFO underflow", __func__);
162a1bb27b1Spbrook         return 0;
163a1bb27b1Spbrook     }
164a1bb27b1Spbrook     value = s->fifo[s->fifo_pos];
165a1bb27b1Spbrook     s->fifo_len--;
166a1bb27b1Spbrook     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
167*583d09f0SPhilippe Mathieu-Daudé     trace_pl181_fifo_pop(value);
168a1bb27b1Spbrook     return value;
169a1bb27b1Spbrook }
170a1bb27b1Spbrook 
171b67cd8f5SPhilippe Mathieu-Daudé static void pl181_do_command(PL181State *s)
172a1bb27b1Spbrook {
173bc24a225SPaul Brook     SDRequest request;
174a1bb27b1Spbrook     uint8_t response[16];
175a1bb27b1Spbrook     int rlen;
176a1bb27b1Spbrook 
177a1bb27b1Spbrook     request.cmd = s->cmd & PL181_CMD_INDEX;
178a1bb27b1Spbrook     request.arg = s->cmdarg;
179*583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_send(request.cmd, request.arg);
1802762eed1SPhilippe Mathieu-Daudé     rlen = sdbus_do_command(&s->sdbus, &request, response);
181a1bb27b1Spbrook     if (rlen < 0)
182a1bb27b1Spbrook         goto error;
183a1bb27b1Spbrook     if (s->cmd & PL181_CMD_RESPONSE) {
184a1bb27b1Spbrook         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
185a1bb27b1Spbrook             goto error;
186a1bb27b1Spbrook         if (rlen != 4 && rlen != 16)
187a1bb27b1Spbrook             goto error;
188b3141c06SPhilippe Mathieu-Daudé         s->response[0] = ldl_be_p(&response[0]);
189a1bb27b1Spbrook         if (rlen == 4) {
190a1bb27b1Spbrook             s->response[1] = s->response[2] = s->response[3] = 0;
191a1bb27b1Spbrook         } else {
192b3141c06SPhilippe Mathieu-Daudé             s->response[1] = ldl_be_p(&response[4]);
193b3141c06SPhilippe Mathieu-Daudé             s->response[2] = ldl_be_p(&response[8]);
194b3141c06SPhilippe Mathieu-Daudé             s->response[3] = ldl_be_p(&response[12]) & ~1;
195a1bb27b1Spbrook         }
196*583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_response_pending();
197a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDRESPEND;
198a1bb27b1Spbrook     } else {
199*583d09f0SPhilippe Mathieu-Daudé         trace_pl181_command_sent();
200a1bb27b1Spbrook         s->status |= PL181_STATUS_CMDSENT;
201a1bb27b1Spbrook     }
202a1bb27b1Spbrook     return;
203a1bb27b1Spbrook 
204a1bb27b1Spbrook error:
205*583d09f0SPhilippe Mathieu-Daudé     trace_pl181_command_timeout();
206a1bb27b1Spbrook     s->status |= PL181_STATUS_CMDTIMEOUT;
207a1bb27b1Spbrook }
208a1bb27b1Spbrook 
209aa1f17c1Sths /* Transfer data between the card and the FIFO.  This is complicated by
210a1bb27b1Spbrook    the FIFO holding 32-bit words and the card taking data in single byte
211a1bb27b1Spbrook    chunks.  FIFO bytes are transferred in little-endian order.  */
212a1bb27b1Spbrook 
2131d998d93SAndreas Färber static void pl181_fifo_run(PL181State *s)
214a1bb27b1Spbrook {
215a1bb27b1Spbrook     uint32_t bits;
216f21126dfSBlue Swirl     uint32_t value = 0;
217a1bb27b1Spbrook     int n;
218a1bb27b1Spbrook     int is_read;
219a1bb27b1Spbrook 
220a1bb27b1Spbrook     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
2212762eed1SPhilippe Mathieu-Daudé     if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))
2226361cdb6Spbrook             && !s->linux_hack) {
223bc3b26f5SPaul Brook         if (is_read) {
224a1bb27b1Spbrook             n = 0;
225bc3b26f5SPaul Brook             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
2262762eed1SPhilippe Mathieu-Daudé                 value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8);
227bc3b26f5SPaul Brook                 s->datacnt--;
228a1bb27b1Spbrook                 n++;
229a1bb27b1Spbrook                 if (n == 4) {
230a1bb27b1Spbrook                     pl181_fifo_push(s, value);
231a1bb27b1Spbrook                     n = 0;
232bc3b26f5SPaul Brook                     value = 0;
233a1bb27b1Spbrook                 }
234bc3b26f5SPaul Brook             }
235bc3b26f5SPaul Brook             if (n != 0) {
236bc3b26f5SPaul Brook                 pl181_fifo_push(s, value);
237bc3b26f5SPaul Brook             }
238bc3b26f5SPaul Brook         } else { /* write */
239bc3b26f5SPaul Brook             n = 0;
240bc3b26f5SPaul Brook             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
241a1bb27b1Spbrook                 if (n == 0) {
242a1bb27b1Spbrook                     value = pl181_fifo_pop(s);
243a1bb27b1Spbrook                     n = 4;
244a1bb27b1Spbrook                 }
245bc3b26f5SPaul Brook                 n--;
246bc3b26f5SPaul Brook                 s->datacnt--;
2472762eed1SPhilippe Mathieu-Daudé                 sdbus_write_data(&s->sdbus, value & 0xff);
248a1bb27b1Spbrook                 value >>= 8;
249a1bb27b1Spbrook             }
250a1bb27b1Spbrook         }
251a1bb27b1Spbrook     }
252a1bb27b1Spbrook     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
253a1bb27b1Spbrook     if (s->datacnt == 0) {
254a1bb27b1Spbrook         s->status |= PL181_STATUS_DATAEND;
255a1bb27b1Spbrook         /* HACK: */
256a1bb27b1Spbrook         s->status |= PL181_STATUS_DATABLOCKEND;
257*583d09f0SPhilippe Mathieu-Daudé         trace_pl181_fifo_transfer_complete();
258a1bb27b1Spbrook     }
2596361cdb6Spbrook     if (s->datacnt == 0 && s->fifo_len == 0) {
260a1bb27b1Spbrook         s->datactrl &= ~PL181_DATA_ENABLE;
261*583d09f0SPhilippe Mathieu-Daudé         trace_pl181_data_engine_idle();
262a1bb27b1Spbrook     } else {
263a1bb27b1Spbrook         /* Update FIFO bits.  */
264a1bb27b1Spbrook         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
265a1bb27b1Spbrook         if (s->fifo_len == 0) {
266a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOEMPTY;
267a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOEMPTY;
268a1bb27b1Spbrook         } else {
269a1bb27b1Spbrook             bits |= PL181_STATUS_TXDATAAVLBL;
270a1bb27b1Spbrook             bits |= PL181_STATUS_RXDATAAVLBL;
271a1bb27b1Spbrook         }
272a1bb27b1Spbrook         if (s->fifo_len == 16) {
273a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOFULL;
274a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOFULL;
275a1bb27b1Spbrook         }
276a1bb27b1Spbrook         if (s->fifo_len <= 8) {
277a1bb27b1Spbrook             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
278a1bb27b1Spbrook         }
279a1bb27b1Spbrook         if (s->fifo_len >= 8) {
280a1bb27b1Spbrook             bits |= PL181_STATUS_RXFIFOHALFFULL;
281a1bb27b1Spbrook         }
282a1bb27b1Spbrook         if (s->datactrl & PL181_DATA_DIRECTION) {
283a1bb27b1Spbrook             bits &= PL181_STATUS_RX_FIFO;
284a1bb27b1Spbrook         } else {
285a1bb27b1Spbrook             bits &= PL181_STATUS_TX_FIFO;
286a1bb27b1Spbrook         }
287a1bb27b1Spbrook         s->status |= bits;
288a1bb27b1Spbrook     }
289a1bb27b1Spbrook }
290a1bb27b1Spbrook 
291a8170e5eSAvi Kivity static uint64_t pl181_read(void *opaque, hwaddr offset,
292ca45842aSAvi Kivity                            unsigned size)
293a1bb27b1Spbrook {
2941d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
2956361cdb6Spbrook     uint32_t tmp;
296a1bb27b1Spbrook 
297a1bb27b1Spbrook     if (offset >= 0xfe0 && offset < 0x1000) {
298a1bb27b1Spbrook         return pl181_id[(offset - 0xfe0) >> 2];
299a1bb27b1Spbrook     }
300a1bb27b1Spbrook     switch (offset) {
301a1bb27b1Spbrook     case 0x00: /* Power */
302a1bb27b1Spbrook         return s->power;
303a1bb27b1Spbrook     case 0x04: /* Clock */
304a1bb27b1Spbrook         return s->clock;
305a1bb27b1Spbrook     case 0x08: /* Argument */
306a1bb27b1Spbrook         return s->cmdarg;
307a1bb27b1Spbrook     case 0x0c: /* Command */
308a1bb27b1Spbrook         return s->cmd;
309a1bb27b1Spbrook     case 0x10: /* RespCmd */
310a1bb27b1Spbrook         return s->respcmd;
311a1bb27b1Spbrook     case 0x14: /* Response0 */
312a1bb27b1Spbrook         return s->response[0];
313a1bb27b1Spbrook     case 0x18: /* Response1 */
314a1bb27b1Spbrook         return s->response[1];
315a1bb27b1Spbrook     case 0x1c: /* Response2 */
316a1bb27b1Spbrook         return s->response[2];
317a1bb27b1Spbrook     case 0x20: /* Response3 */
318a1bb27b1Spbrook         return s->response[3];
319a1bb27b1Spbrook     case 0x24: /* DataTimer */
320a1bb27b1Spbrook         return s->datatimer;
321a1bb27b1Spbrook     case 0x28: /* DataLength */
322a1bb27b1Spbrook         return s->datalength;
323a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
324a1bb27b1Spbrook         return s->datactrl;
325a1bb27b1Spbrook     case 0x30: /* DataCnt */
326a1bb27b1Spbrook         return s->datacnt;
327a1bb27b1Spbrook     case 0x34: /* Status */
3286361cdb6Spbrook         tmp = s->status;
3296361cdb6Spbrook         if (s->linux_hack) {
3306361cdb6Spbrook             s->linux_hack = 0;
3316361cdb6Spbrook             pl181_fifo_run(s);
3326361cdb6Spbrook             pl181_update(s);
3336361cdb6Spbrook         }
3346361cdb6Spbrook         return tmp;
335a1bb27b1Spbrook     case 0x3c: /* Mask0 */
336a1bb27b1Spbrook         return s->mask[0];
337a1bb27b1Spbrook     case 0x40: /* Mask1 */
338a1bb27b1Spbrook         return s->mask[1];
339a1bb27b1Spbrook     case 0x48: /* FifoCnt */
3406361cdb6Spbrook         /* The documentation is somewhat vague about exactly what FifoCnt
3416361cdb6Spbrook            does.  On real hardware it appears to be when decrememnted
34266a0a2cbSDong Xu Wang            when a word is transferred between the FIFO and the serial
3436361cdb6Spbrook            data engine.  DataCnt is decremented after each byte is
34466a0a2cbSDong Xu Wang            transferred between the serial engine and the card.
3456361cdb6Spbrook            We don't emulate this level of detail, so both can be the same.  */
3466361cdb6Spbrook         tmp = (s->datacnt + 3) >> 2;
3476361cdb6Spbrook         if (s->linux_hack) {
3486361cdb6Spbrook             s->linux_hack = 0;
3496361cdb6Spbrook             pl181_fifo_run(s);
3506361cdb6Spbrook             pl181_update(s);
3516361cdb6Spbrook         }
3526361cdb6Spbrook         return tmp;
353a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
354a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
355a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
356a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
3576361cdb6Spbrook         if (s->fifo_len == 0) {
3589351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
359a1bb27b1Spbrook             return 0;
360a1bb27b1Spbrook         } else {
361a1bb27b1Spbrook             uint32_t value;
362a1bb27b1Spbrook             value = pl181_fifo_pop(s);
3636361cdb6Spbrook             s->linux_hack = 1;
364a1bb27b1Spbrook             pl181_fifo_run(s);
365a1bb27b1Spbrook             pl181_update(s);
366a1bb27b1Spbrook             return value;
367a1bb27b1Spbrook         }
368a1bb27b1Spbrook     default:
3699351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
3709351d708SPeter Maydell                       "pl181_read: Bad offset %x\n", (int)offset);
371a1bb27b1Spbrook         return 0;
372a1bb27b1Spbrook     }
373a1bb27b1Spbrook }
374a1bb27b1Spbrook 
375a8170e5eSAvi Kivity static void pl181_write(void *opaque, hwaddr offset,
376ca45842aSAvi Kivity                         uint64_t value, unsigned size)
377a1bb27b1Spbrook {
3781d998d93SAndreas Färber     PL181State *s = (PL181State *)opaque;
379a1bb27b1Spbrook 
380a1bb27b1Spbrook     switch (offset) {
381a1bb27b1Spbrook     case 0x00: /* Power */
382a1bb27b1Spbrook         s->power = value & 0xff;
383a1bb27b1Spbrook         break;
384a1bb27b1Spbrook     case 0x04: /* Clock */
385a1bb27b1Spbrook         s->clock = value & 0xff;
386a1bb27b1Spbrook         break;
387a1bb27b1Spbrook     case 0x08: /* Argument */
388a1bb27b1Spbrook         s->cmdarg = value;
389a1bb27b1Spbrook         break;
390a1bb27b1Spbrook     case 0x0c: /* Command */
391a1bb27b1Spbrook         s->cmd = value;
392a1bb27b1Spbrook         if (s->cmd & PL181_CMD_ENABLE) {
393a1bb27b1Spbrook             if (s->cmd & PL181_CMD_INTERRUPT) {
3949351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3959351d708SPeter Maydell                               "pl181: Interrupt mode not implemented\n");
396a1bb27b1Spbrook             } if (s->cmd & PL181_CMD_PENDING) {
3979351d708SPeter Maydell                 qemu_log_mask(LOG_UNIMP,
3989351d708SPeter Maydell                               "pl181: Pending commands not implemented\n");
399a1bb27b1Spbrook             } else {
400b67cd8f5SPhilippe Mathieu-Daudé                 pl181_do_command(s);
401a1bb27b1Spbrook                 pl181_fifo_run(s);
402a1bb27b1Spbrook             }
403a1bb27b1Spbrook             /* The command has completed one way or the other.  */
404a1bb27b1Spbrook             s->cmd &= ~PL181_CMD_ENABLE;
405a1bb27b1Spbrook         }
406a1bb27b1Spbrook         break;
407a1bb27b1Spbrook     case 0x24: /* DataTimer */
408a1bb27b1Spbrook         s->datatimer = value;
409a1bb27b1Spbrook         break;
410a1bb27b1Spbrook     case 0x28: /* DataLength */
411a1bb27b1Spbrook         s->datalength = value & 0xffff;
412a1bb27b1Spbrook         break;
413a1bb27b1Spbrook     case 0x2c: /* DataCtrl */
414a1bb27b1Spbrook         s->datactrl = value & 0xff;
415a1bb27b1Spbrook         if (value & PL181_DATA_ENABLE) {
416a1bb27b1Spbrook             s->datacnt = s->datalength;
417a1bb27b1Spbrook             pl181_fifo_run(s);
418a1bb27b1Spbrook         }
419a1bb27b1Spbrook         break;
420a1bb27b1Spbrook     case 0x38: /* Clear */
421a1bb27b1Spbrook         s->status &= ~(value & 0x7ff);
422a1bb27b1Spbrook         break;
423a1bb27b1Spbrook     case 0x3c: /* Mask0 */
424a1bb27b1Spbrook         s->mask[0] = value;
425a1bb27b1Spbrook         break;
426a1bb27b1Spbrook     case 0x40: /* Mask1 */
427a1bb27b1Spbrook         s->mask[1] = value;
428a1bb27b1Spbrook         break;
429a1bb27b1Spbrook     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
430a1bb27b1Spbrook     case 0x90: case 0x94: case 0x98: case 0x9c:
431a1bb27b1Spbrook     case 0xa0: case 0xa4: case 0xa8: case 0xac:
432a1bb27b1Spbrook     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
4336361cdb6Spbrook         if (s->datacnt == 0) {
4349351d708SPeter Maydell             qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
435a1bb27b1Spbrook         } else {
436a1bb27b1Spbrook             pl181_fifo_push(s, value);
437a1bb27b1Spbrook             pl181_fifo_run(s);
438a1bb27b1Spbrook         }
439a1bb27b1Spbrook         break;
440a1bb27b1Spbrook     default:
4419351d708SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
4429351d708SPeter Maydell                       "pl181_write: Bad offset %x\n", (int)offset);
443a1bb27b1Spbrook     }
444a1bb27b1Spbrook     pl181_update(s);
445a1bb27b1Spbrook }
446a1bb27b1Spbrook 
447ca45842aSAvi Kivity static const MemoryRegionOps pl181_ops = {
448ca45842aSAvi Kivity     .read = pl181_read,
449ca45842aSAvi Kivity     .write = pl181_write,
450ca45842aSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
451a1bb27b1Spbrook };
452a1bb27b1Spbrook 
4532762eed1SPhilippe Mathieu-Daudé static void pl181_set_readonly(DeviceState *dev, bool level)
4542762eed1SPhilippe Mathieu-Daudé {
4552762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4562762eed1SPhilippe Mathieu-Daudé 
4572762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_readonly, level);
4582762eed1SPhilippe Mathieu-Daudé }
4592762eed1SPhilippe Mathieu-Daudé 
4602762eed1SPhilippe Mathieu-Daudé static void pl181_set_inserted(DeviceState *dev, bool level)
4612762eed1SPhilippe Mathieu-Daudé {
4622762eed1SPhilippe Mathieu-Daudé     PL181State *s = (PL181State *)dev;
4632762eed1SPhilippe Mathieu-Daudé 
4642762eed1SPhilippe Mathieu-Daudé     qemu_set_irq(s->card_inserted, level);
4652762eed1SPhilippe Mathieu-Daudé }
4662762eed1SPhilippe Mathieu-Daudé 
467624923beSPeter Maydell static void pl181_reset(DeviceState *d)
468a1bb27b1Spbrook {
469630f4442SAndreas Färber     PL181State *s = PL181(d);
470a1bb27b1Spbrook 
471a1bb27b1Spbrook     s->power = 0;
472a1bb27b1Spbrook     s->cmdarg = 0;
473a1bb27b1Spbrook     s->cmd = 0;
474a1bb27b1Spbrook     s->datatimer = 0;
475a1bb27b1Spbrook     s->datalength = 0;
476a1bb27b1Spbrook     s->respcmd = 0;
477a1bb27b1Spbrook     s->response[0] = 0;
478a1bb27b1Spbrook     s->response[1] = 0;
479a1bb27b1Spbrook     s->response[2] = 0;
480a1bb27b1Spbrook     s->response[3] = 0;
481a1bb27b1Spbrook     s->datatimer = 0;
482a1bb27b1Spbrook     s->datalength = 0;
483a1bb27b1Spbrook     s->datactrl = 0;
484a1bb27b1Spbrook     s->datacnt = 0;
485a1bb27b1Spbrook     s->status = 0;
4866361cdb6Spbrook     s->linux_hack = 0;
487a1bb27b1Spbrook     s->mask[0] = 0;
488a1bb27b1Spbrook     s->mask[1] = 0;
489c31a4724SPeter Maydell 
4902762eed1SPhilippe Mathieu-Daudé     /* Reset other state based on current card insertion/readonly status */
4912762eed1SPhilippe Mathieu-Daudé     pl181_set_inserted(DEVICE(s), sdbus_get_inserted(&s->sdbus));
4922762eed1SPhilippe Mathieu-Daudé     pl181_set_readonly(DEVICE(s), sdbus_get_readonly(&s->sdbus));
493a1bb27b1Spbrook }
494a1bb27b1Spbrook 
4950d554cb0Sxiaoqiang zhao static void pl181_init(Object *obj)
496a1bb27b1Spbrook {
4970d554cb0Sxiaoqiang zhao     DeviceState *dev = DEVICE(obj);
4980d554cb0Sxiaoqiang zhao     PL181State *s = PL181(obj);
4990d554cb0Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
500a1bb27b1Spbrook 
5010d554cb0Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
502630f4442SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
503630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[0]);
504630f4442SAndreas Färber     sysbus_init_irq(sbd, &s->irq[1]);
50526c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_readonly, "card-read-only", 1);
50626c5b0f4SPhilippe Mathieu-Daudé     qdev_init_gpio_out_named(dev, &s->card_inserted, "card-inserted", 1);
5072762eed1SPhilippe Mathieu-Daudé 
5082762eed1SPhilippe Mathieu-Daudé     qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
5092762eed1SPhilippe Mathieu-Daudé                         TYPE_PL181_BUS, dev, "sd-bus");
5100d554cb0Sxiaoqiang zhao }
5110d554cb0Sxiaoqiang zhao 
512999e12bbSAnthony Liguori static void pl181_class_init(ObjectClass *klass, void *data)
513999e12bbSAnthony Liguori {
51439bffca2SAnthony Liguori     DeviceClass *k = DEVICE_CLASS(klass);
515999e12bbSAnthony Liguori 
51639bffca2SAnthony Liguori     k->vmsd = &vmstate_pl181;
51739bffca2SAnthony Liguori     k->reset = pl181_reset;
51826c607b8SPhilippe Mathieu-Daudé     /* Reason: output IRQs should be wired up */
519e90f2a8cSEduardo Habkost     k->user_creatable = false;
520999e12bbSAnthony Liguori }
521999e12bbSAnthony Liguori 
5228c43a6f0SAndreas Färber static const TypeInfo pl181_info = {
523630f4442SAndreas Färber     .name          = TYPE_PL181,
52439bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
5251d998d93SAndreas Färber     .instance_size = sizeof(PL181State),
5260d554cb0Sxiaoqiang zhao     .instance_init = pl181_init,
527999e12bbSAnthony Liguori     .class_init    = pl181_class_init,
528624923beSPeter Maydell };
529624923beSPeter Maydell 
5302762eed1SPhilippe Mathieu-Daudé static void pl181_bus_class_init(ObjectClass *klass, void *data)
5312762eed1SPhilippe Mathieu-Daudé {
5322762eed1SPhilippe Mathieu-Daudé     SDBusClass *sbc = SD_BUS_CLASS(klass);
5332762eed1SPhilippe Mathieu-Daudé 
5342762eed1SPhilippe Mathieu-Daudé     sbc->set_inserted = pl181_set_inserted;
5352762eed1SPhilippe Mathieu-Daudé     sbc->set_readonly = pl181_set_readonly;
5362762eed1SPhilippe Mathieu-Daudé }
5372762eed1SPhilippe Mathieu-Daudé 
5382762eed1SPhilippe Mathieu-Daudé static const TypeInfo pl181_bus_info = {
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,
5432762eed1SPhilippe Mathieu-Daudé };
5442762eed1SPhilippe Mathieu-Daudé 
54583f7d43aSAndreas Färber static void pl181_register_types(void)
546aa9311d8SPaul Brook {
54739bffca2SAnthony Liguori     type_register_static(&pl181_info);
5482762eed1SPhilippe Mathieu-Daudé     type_register_static(&pl181_bus_info);
549aa9311d8SPaul Brook }
550aa9311d8SPaul Brook 
55183f7d43aSAndreas Färber type_init(pl181_register_types)
552