xref: /illumos-kvm-cmd/hw/pl181.c (revision 68396ea9)
1 /*
2  * Arm PrimeCell PL181 MultiMedia Card Interface
3  *
4  * Copyright (c) 2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licenced under the GPL.
8  */
9 
10 #include "blockdev.h"
11 #include "sysbus.h"
12 #include "sd.h"
13 
14 //#define DEBUG_PL181 1
15 
16 #ifdef DEBUG_PL181
17 #define DPRINTF(fmt, ...) \
18 do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
19 #else
20 #define DPRINTF(fmt, ...) do {} while(0)
21 #endif
22 
23 #define PL181_FIFO_LEN 16
24 
25 typedef struct {
26     SysBusDevice busdev;
27     SDState *card;
28     uint32_t clock;
29     uint32_t power;
30     uint32_t cmdarg;
31     uint32_t cmd;
32     uint32_t datatimer;
33     uint32_t datalength;
34     uint32_t respcmd;
35     uint32_t response[4];
36     uint32_t datactrl;
37     uint32_t datacnt;
38     uint32_t status;
39     uint32_t mask[2];
40     int fifo_pos;
41     int fifo_len;
42     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
43        while it is reading the FIFO.  We hack around this be defering
44        subsequent transfers until after the driver polls the status word.
45        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
46      */
47     int linux_hack;
48     uint32_t fifo[PL181_FIFO_LEN];
49     qemu_irq irq[2];
50 } pl181_state;
51 
52 #define PL181_CMD_INDEX     0x3f
53 #define PL181_CMD_RESPONSE  (1 << 6)
54 #define PL181_CMD_LONGRESP  (1 << 7)
55 #define PL181_CMD_INTERRUPT (1 << 8)
56 #define PL181_CMD_PENDING   (1 << 9)
57 #define PL181_CMD_ENABLE    (1 << 10)
58 
59 #define PL181_DATA_ENABLE             (1 << 0)
60 #define PL181_DATA_DIRECTION          (1 << 1)
61 #define PL181_DATA_MODE               (1 << 2)
62 #define PL181_DATA_DMAENABLE          (1 << 3)
63 
64 #define PL181_STATUS_CMDCRCFAIL       (1 << 0)
65 #define PL181_STATUS_DATACRCFAIL      (1 << 1)
66 #define PL181_STATUS_CMDTIMEOUT       (1 << 2)
67 #define PL181_STATUS_DATATIMEOUT      (1 << 3)
68 #define PL181_STATUS_TXUNDERRUN       (1 << 4)
69 #define PL181_STATUS_RXOVERRUN        (1 << 5)
70 #define PL181_STATUS_CMDRESPEND       (1 << 6)
71 #define PL181_STATUS_CMDSENT          (1 << 7)
72 #define PL181_STATUS_DATAEND          (1 << 8)
73 #define PL181_STATUS_DATABLOCKEND     (1 << 10)
74 #define PL181_STATUS_CMDACTIVE        (1 << 11)
75 #define PL181_STATUS_TXACTIVE         (1 << 12)
76 #define PL181_STATUS_RXACTIVE         (1 << 13)
77 #define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
78 #define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
79 #define PL181_STATUS_TXFIFOFULL       (1 << 16)
80 #define PL181_STATUS_RXFIFOFULL       (1 << 17)
81 #define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
82 #define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
83 #define PL181_STATUS_TXDATAAVLBL      (1 << 20)
84 #define PL181_STATUS_RXDATAAVLBL      (1 << 21)
85 
86 #define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
87                              |PL181_STATUS_TXFIFOHALFEMPTY \
88                              |PL181_STATUS_TXFIFOFULL \
89                              |PL181_STATUS_TXFIFOEMPTY \
90                              |PL181_STATUS_TXDATAAVLBL)
91 #define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
92                              |PL181_STATUS_RXFIFOHALFFULL \
93                              |PL181_STATUS_RXFIFOFULL \
94                              |PL181_STATUS_RXFIFOEMPTY \
95                              |PL181_STATUS_RXDATAAVLBL)
96 
97 static const unsigned char pl181_id[] =
98 { 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
99 
pl181_update(pl181_state * s)100 static void pl181_update(pl181_state *s)
101 {
102     int i;
103     for (i = 0; i < 2; i++) {
104         qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
105     }
106 }
107 
pl181_fifo_push(pl181_state * s,uint32_t value)108 static void pl181_fifo_push(pl181_state *s, uint32_t value)
109 {
110     int n;
111 
112     if (s->fifo_len == PL181_FIFO_LEN) {
113         fprintf(stderr, "pl181: FIFO overflow\n");
114         return;
115     }
116     n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
117     s->fifo_len++;
118     s->fifo[n] = value;
119     DPRINTF("FIFO push %08x\n", (int)value);
120 }
121 
pl181_fifo_pop(pl181_state * s)122 static uint32_t pl181_fifo_pop(pl181_state *s)
123 {
124     uint32_t value;
125 
126     if (s->fifo_len == 0) {
127         fprintf(stderr, "pl181: FIFO underflow\n");
128         return 0;
129     }
130     value = s->fifo[s->fifo_pos];
131     s->fifo_len--;
132     s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
133     DPRINTF("FIFO pop %08x\n", (int)value);
134     return value;
135 }
136 
pl181_send_command(pl181_state * s)137 static void pl181_send_command(pl181_state *s)
138 {
139     SDRequest request;
140     uint8_t response[16];
141     int rlen;
142 
143     request.cmd = s->cmd & PL181_CMD_INDEX;
144     request.arg = s->cmdarg;
145     DPRINTF("Command %d %08x\n", request.cmd, request.arg);
146     rlen = sd_do_command(s->card, &request, response);
147     if (rlen < 0)
148         goto error;
149     if (s->cmd & PL181_CMD_RESPONSE) {
150 #define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
151                   | (response[n + 2] << 8) | response[n + 3])
152         if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
153             goto error;
154         if (rlen != 4 && rlen != 16)
155             goto error;
156         s->response[0] = RWORD(0);
157         if (rlen == 4) {
158             s->response[1] = s->response[2] = s->response[3] = 0;
159         } else {
160             s->response[1] = RWORD(4);
161             s->response[2] = RWORD(8);
162             s->response[3] = RWORD(12) & ~1;
163         }
164         DPRINTF("Response received\n");
165         s->status |= PL181_STATUS_CMDRESPEND;
166 #undef RWORD
167     } else {
168         DPRINTF("Command sent\n");
169         s->status |= PL181_STATUS_CMDSENT;
170     }
171     return;
172 
173 error:
174     DPRINTF("Timeout\n");
175     s->status |= PL181_STATUS_CMDTIMEOUT;
176 }
177 
178 /* Transfer data between the card and the FIFO.  This is complicated by
179    the FIFO holding 32-bit words and the card taking data in single byte
180    chunks.  FIFO bytes are transferred in little-endian order.  */
181 
pl181_fifo_run(pl181_state * s)182 static void pl181_fifo_run(pl181_state *s)
183 {
184     uint32_t bits;
185     uint32_t value = 0;
186     int n;
187     int is_read;
188 
189     is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
190     if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
191             && !s->linux_hack) {
192         if (is_read) {
193             n = 0;
194             while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
195                 value |= (uint32_t)sd_read_data(s->card) << (n * 8);
196                 s->datacnt--;
197                 n++;
198                 if (n == 4) {
199                     pl181_fifo_push(s, value);
200                     n = 0;
201                     value = 0;
202                 }
203             }
204             if (n != 0) {
205                 pl181_fifo_push(s, value);
206             }
207         } else { /* write */
208             n = 0;
209             while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
210                 if (n == 0) {
211                     value = pl181_fifo_pop(s);
212                     n = 4;
213                 }
214                 n--;
215                 s->datacnt--;
216                 sd_write_data(s->card, value & 0xff);
217                 value >>= 8;
218             }
219         }
220     }
221     s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
222     if (s->datacnt == 0) {
223         s->status |= PL181_STATUS_DATAEND;
224         /* HACK: */
225         s->status |= PL181_STATUS_DATABLOCKEND;
226         DPRINTF("Transfer Complete\n");
227     }
228     if (s->datacnt == 0 && s->fifo_len == 0) {
229         s->datactrl &= ~PL181_DATA_ENABLE;
230         DPRINTF("Data engine idle\n");
231     } else {
232         /* Update FIFO bits.  */
233         bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
234         if (s->fifo_len == 0) {
235             bits |= PL181_STATUS_TXFIFOEMPTY;
236             bits |= PL181_STATUS_RXFIFOEMPTY;
237         } else {
238             bits |= PL181_STATUS_TXDATAAVLBL;
239             bits |= PL181_STATUS_RXDATAAVLBL;
240         }
241         if (s->fifo_len == 16) {
242             bits |= PL181_STATUS_TXFIFOFULL;
243             bits |= PL181_STATUS_RXFIFOFULL;
244         }
245         if (s->fifo_len <= 8) {
246             bits |= PL181_STATUS_TXFIFOHALFEMPTY;
247         }
248         if (s->fifo_len >= 8) {
249             bits |= PL181_STATUS_RXFIFOHALFFULL;
250         }
251         if (s->datactrl & PL181_DATA_DIRECTION) {
252             bits &= PL181_STATUS_RX_FIFO;
253         } else {
254             bits &= PL181_STATUS_TX_FIFO;
255         }
256         s->status |= bits;
257     }
258 }
259 
pl181_read(void * opaque,target_phys_addr_t offset)260 static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
261 {
262     pl181_state *s = (pl181_state *)opaque;
263     uint32_t tmp;
264 
265     if (offset >= 0xfe0 && offset < 0x1000) {
266         return pl181_id[(offset - 0xfe0) >> 2];
267     }
268     switch (offset) {
269     case 0x00: /* Power */
270         return s->power;
271     case 0x04: /* Clock */
272         return s->clock;
273     case 0x08: /* Argument */
274         return s->cmdarg;
275     case 0x0c: /* Command */
276         return s->cmd;
277     case 0x10: /* RespCmd */
278         return s->respcmd;
279     case 0x14: /* Response0 */
280         return s->response[0];
281     case 0x18: /* Response1 */
282         return s->response[1];
283     case 0x1c: /* Response2 */
284         return s->response[2];
285     case 0x20: /* Response3 */
286         return s->response[3];
287     case 0x24: /* DataTimer */
288         return s->datatimer;
289     case 0x28: /* DataLength */
290         return s->datalength;
291     case 0x2c: /* DataCtrl */
292         return s->datactrl;
293     case 0x30: /* DataCnt */
294         return s->datacnt;
295     case 0x34: /* Status */
296         tmp = s->status;
297         if (s->linux_hack) {
298             s->linux_hack = 0;
299             pl181_fifo_run(s);
300             pl181_update(s);
301         }
302         return tmp;
303     case 0x3c: /* Mask0 */
304         return s->mask[0];
305     case 0x40: /* Mask1 */
306         return s->mask[1];
307     case 0x48: /* FifoCnt */
308         /* The documentation is somewhat vague about exactly what FifoCnt
309            does.  On real hardware it appears to be when decrememnted
310            when a word is transfered between the FIFO and the serial
311            data engine.  DataCnt is decremented after each byte is
312            transfered between the serial engine and the card.
313            We don't emulate this level of detail, so both can be the same.  */
314         tmp = (s->datacnt + 3) >> 2;
315         if (s->linux_hack) {
316             s->linux_hack = 0;
317             pl181_fifo_run(s);
318             pl181_update(s);
319         }
320         return tmp;
321     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
322     case 0x90: case 0x94: case 0x98: case 0x9c:
323     case 0xa0: case 0xa4: case 0xa8: case 0xac:
324     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
325         if (s->fifo_len == 0) {
326             fprintf(stderr, "pl181: Unexpected FIFO read\n");
327             return 0;
328         } else {
329             uint32_t value;
330             value = pl181_fifo_pop(s);
331             s->linux_hack = 1;
332             pl181_fifo_run(s);
333             pl181_update(s);
334             return value;
335         }
336     default:
337         hw_error("pl181_read: Bad offset %x\n", (int)offset);
338         return 0;
339     }
340 }
341 
pl181_write(void * opaque,target_phys_addr_t offset,uint32_t value)342 static void pl181_write(void *opaque, target_phys_addr_t offset,
343                           uint32_t value)
344 {
345     pl181_state *s = (pl181_state *)opaque;
346 
347     switch (offset) {
348     case 0x00: /* Power */
349         s->power = value & 0xff;
350         break;
351     case 0x04: /* Clock */
352         s->clock = value & 0xff;
353         break;
354     case 0x08: /* Argument */
355         s->cmdarg = value;
356         break;
357     case 0x0c: /* Command */
358         s->cmd = value;
359         if (s->cmd & PL181_CMD_ENABLE) {
360             if (s->cmd & PL181_CMD_INTERRUPT) {
361                 fprintf(stderr, "pl181: Interrupt mode not implemented\n");
362                 abort();
363             } if (s->cmd & PL181_CMD_PENDING) {
364                 fprintf(stderr, "pl181: Pending commands not implemented\n");
365                 abort();
366             } else {
367                 pl181_send_command(s);
368                 pl181_fifo_run(s);
369             }
370             /* The command has completed one way or the other.  */
371             s->cmd &= ~PL181_CMD_ENABLE;
372         }
373         break;
374     case 0x24: /* DataTimer */
375         s->datatimer = value;
376         break;
377     case 0x28: /* DataLength */
378         s->datalength = value & 0xffff;
379         break;
380     case 0x2c: /* DataCtrl */
381         s->datactrl = value & 0xff;
382         if (value & PL181_DATA_ENABLE) {
383             s->datacnt = s->datalength;
384             pl181_fifo_run(s);
385         }
386         break;
387     case 0x38: /* Clear */
388         s->status &= ~(value & 0x7ff);
389         break;
390     case 0x3c: /* Mask0 */
391         s->mask[0] = value;
392         break;
393     case 0x40: /* Mask1 */
394         s->mask[1] = value;
395         break;
396     case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
397     case 0x90: case 0x94: case 0x98: case 0x9c:
398     case 0xa0: case 0xa4: case 0xa8: case 0xac:
399     case 0xb0: case 0xb4: case 0xb8: case 0xbc:
400         if (s->datacnt == 0) {
401             fprintf(stderr, "pl181: Unexpected FIFO write\n");
402         } else {
403             pl181_fifo_push(s, value);
404             pl181_fifo_run(s);
405         }
406         break;
407     default:
408         hw_error("pl181_write: Bad offset %x\n", (int)offset);
409     }
410     pl181_update(s);
411 }
412 
413 static CPUReadMemoryFunc * const pl181_readfn[] = {
414    pl181_read,
415    pl181_read,
416    pl181_read
417 };
418 
419 static CPUWriteMemoryFunc * const pl181_writefn[] = {
420    pl181_write,
421    pl181_write,
422    pl181_write
423 };
424 
pl181_reset(void * opaque)425 static void pl181_reset(void *opaque)
426 {
427     pl181_state *s = (pl181_state *)opaque;
428 
429     s->power = 0;
430     s->cmdarg = 0;
431     s->cmd = 0;
432     s->datatimer = 0;
433     s->datalength = 0;
434     s->respcmd = 0;
435     s->response[0] = 0;
436     s->response[1] = 0;
437     s->response[2] = 0;
438     s->response[3] = 0;
439     s->datatimer = 0;
440     s->datalength = 0;
441     s->datactrl = 0;
442     s->datacnt = 0;
443     s->status = 0;
444     s->linux_hack = 0;
445     s->mask[0] = 0;
446     s->mask[1] = 0;
447 }
448 
pl181_init(SysBusDevice * dev)449 static int pl181_init(SysBusDevice *dev)
450 {
451     int iomemtype;
452     pl181_state *s = FROM_SYSBUS(pl181_state, dev);
453     DriveInfo *dinfo;
454 
455     iomemtype = cpu_register_io_memory(pl181_readfn, pl181_writefn, s,
456                                        DEVICE_NATIVE_ENDIAN);
457     sysbus_init_mmio(dev, 0x1000, iomemtype);
458     sysbus_init_irq(dev, &s->irq[0]);
459     sysbus_init_irq(dev, &s->irq[1]);
460     dinfo = drive_get_next(IF_SD);
461     s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
462     qemu_register_reset(pl181_reset, s);
463     pl181_reset(s);
464     /* ??? Save/restore.  */
465     return 0;
466 }
467 
pl181_register_devices(void)468 static void pl181_register_devices(void)
469 {
470     sysbus_register_dev("pl181", sizeof(pl181_state), pl181_init);
471 }
472 
473 device_init(pl181_register_devices)
474