Statistics
| Branch: | Revision:

root / hw / pl181.c @ d537cf6c

History | View | Annotate | Download (12.5 kB)

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 "vl.h"
11
#include "sd.h"
12

    
13
//#define DEBUG_PL181 1
14

    
15
#ifdef DEBUG_PL181
16
#define DPRINTF(fmt, args...) \
17
do { printf("pl181: " fmt , ##args); } while (0)
18
#else
19
#define DPRINTF(fmt, args...) do {} while(0)
20
#endif
21

    
22
#define PL181_FIFO_LEN 16
23

    
24
typedef struct {
25
    SDState *card;
26
    uint32_t base;
27
    uint32_t clock;
28
    uint32_t power;
29
    uint32_t cmdarg;
30
    uint32_t cmd;
31
    uint32_t datatimer;
32
    uint32_t datalength;
33
    uint32_t respcmd;
34
    uint32_t response[4];
35
    uint32_t datactrl;
36
    uint32_t datacnt;
37
    uint32_t status;
38
    uint32_t mask[2];
39
    uint32_t fifocnt;
40
    int fifo_pos;
41
    int fifo_len;
42
    uint32_t fifo[PL181_FIFO_LEN];
43
    qemu_irq irq[2];
44
} pl181_state;
45

    
46
#define PL181_CMD_INDEX     0x3f
47
#define PL181_CMD_RESPONSE  (1 << 6)
48
#define PL181_CMD_LONGRESP  (1 << 7)
49
#define PL181_CMD_INTERRUPT (1 << 8)
50
#define PL181_CMD_PENDING   (1 << 9)
51
#define PL181_CMD_ENABLE    (1 << 10)
52

    
53
#define PL181_DATA_ENABLE             (1 << 0)
54
#define PL181_DATA_DIRECTION          (1 << 1)
55
#define PL181_DATA_MODE               (1 << 2)
56
#define PL181_DATA_DMAENABLE          (1 << 3)
57

    
58
#define PL181_STATUS_CMDCRCFAIL       (1 << 0)
59
#define PL181_STATUS_DATACRCFAIL      (1 << 1)
60
#define PL181_STATUS_CMDTIMEOUT       (1 << 2)
61
#define PL181_STATUS_DATATIMEOUT      (1 << 3)
62
#define PL181_STATUS_TXUNDERRUN       (1 << 4)
63
#define PL181_STATUS_RXOVERRUN        (1 << 5)
64
#define PL181_STATUS_CMDRESPEND       (1 << 6)
65
#define PL181_STATUS_CMDSENT          (1 << 7)
66
#define PL181_STATUS_DATAEND          (1 << 8)
67
#define PL181_STATUS_DATABLOCKEND     (1 << 10)
68
#define PL181_STATUS_CMDACTIVE        (1 << 11)
69
#define PL181_STATUS_TXACTIVE         (1 << 12)
70
#define PL181_STATUS_RXACTIVE         (1 << 13)
71
#define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
72
#define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
73
#define PL181_STATUS_TXFIFOFULL       (1 << 16)
74
#define PL181_STATUS_RXFIFOFULL       (1 << 17)
75
#define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
76
#define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
77
#define PL181_STATUS_TXDATAAVLBL      (1 << 20)
78
#define PL181_STATUS_RXDATAAVLBL      (1 << 21)
79

    
80
#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
81
                             |PL181_STATUS_TXFIFOHALFEMPTY \
82
                             |PL181_STATUS_TXFIFOFULL \
83
                             |PL181_STATUS_TXFIFOEMPTY \
84
                             |PL181_STATUS_TXDATAAVLBL)
85
#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
86
                             |PL181_STATUS_RXFIFOHALFFULL \
87
                             |PL181_STATUS_RXFIFOFULL \
88
                             |PL181_STATUS_RXFIFOEMPTY \
89
                             |PL181_STATUS_RXDATAAVLBL)
90

    
91
static const unsigned char pl181_id[] =
92
{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
93

    
94
static void pl181_update(pl181_state *s)
95
{
96
    int i;
97
    for (i = 0; i < 2; i++) {
98
        qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
99
    }
100
}
101

    
102
static void pl181_fifo_push(pl181_state *s, uint32_t value)
103
{
104
    int n;
105

    
106
    if (s->fifo_len == PL181_FIFO_LEN) {
107
        fprintf(stderr, "pl181: FIFO overflow\n");
108
        return;
109
    }
110
    n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
111
    s->fifo_len++;
112
    s->fifo[n] = value;
113
    DPRINTF("FIFO push %08x\n", (int)value);
114
}
115

    
116
static uint32_t pl181_fifo_pop(pl181_state *s)
117
{
118
    uint32_t value;
119

    
120
    if (s->fifo_len == 0) {
121
        fprintf(stderr, "pl181: FIFO underflow\n");
122
        return 0;
123
    }
124
    value = s->fifo[s->fifo_pos];
125
    s->fifo_len--;
126
    s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
127
    DPRINTF("FIFO pop %08x\n", (int)value);
128
    return value;
129
}
130

    
131
static void pl181_send_command(pl181_state *s)
132
{
133
    struct sd_request_s request;
134
    uint8_t response[16];
135
    int rlen;
136

    
137
    request.cmd = s->cmd & PL181_CMD_INDEX;
138
    request.arg = s->cmdarg;
139
    DPRINTF("Command %d %08x\n", request.cmd, request.arg);
140
    rlen = sd_do_command(s->card, &request, response);
141
    if (rlen < 0)
142
        goto error;
143
    if (s->cmd & PL181_CMD_RESPONSE) {
144
#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
145
                  | (response[n + 2] << 8) | response[n + 3])
146
        if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
147
            goto error;
148
        if (rlen != 4 && rlen != 16)
149
            goto error;
150
        s->response[0] = RWORD(0);
151
        if (rlen == 4) {
152
            s->response[1] = s->response[2] = s->response[3] = 0;
153
        } else {
154
            s->response[1] = RWORD(4);
155
            s->response[2] = RWORD(8);
156
            s->response[3] = RWORD(12) & ~1;
157
        }
158
        DPRINTF("Response recieved\n");
159
        s->status |= PL181_STATUS_CMDRESPEND;
160
#undef RWORD
161
    } else {
162
        DPRINTF("Command sent\n");
163
        s->status |= PL181_STATUS_CMDSENT;
164
    }
165
    return;
166

    
167
error:
168
    DPRINTF("Timeout\n");
169
    s->status |= PL181_STATUS_CMDTIMEOUT;
170
}
171

    
172
/* Transfer data between teh card and the FIFO.  This is complicated by
173
   the FIFO holding 32-bit words and the card taking data in single byte
174
   chunks.  FIFO bytes are transferred in little-endian order.  */
175
   
176
static void pl181_fifo_run(pl181_state *s)
177
{
178
    uint32_t bits;
179
    uint32_t value;
180
    int n;
181
    int limit;
182
    int is_read;
183

    
184
    is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
185
    if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))) {
186
        limit = is_read ? PL181_FIFO_LEN : 0;
187
        n = 0;
188
        value = 0;
189
        while (s->datacnt && s->fifo_len != limit) {
190
            if (is_read) {
191
                value |= (uint32_t)sd_read_data(s->card) << (n * 8);
192
                n++;
193
                if (n == 4) {
194
                    pl181_fifo_push(s, value);
195
                    value = 0;
196
                    n = 0;
197
                }
198
            } else {
199
                if (n == 0) {
200
                    value = pl181_fifo_pop(s);
201
                    n = 4;
202
                }
203
                sd_write_data(s->card, value & 0xff);
204
                value >>= 8;
205
                n--;
206
            }
207
            s->datacnt--;
208
        }
209
        if (n && is_read) {
210
            pl181_fifo_push(s, value);
211
        }
212
    }
213
    s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
214
    if (s->datacnt == 0) {
215
        s->status |= PL181_STATUS_DATAEND;
216
        /* HACK: */
217
        s->status |= PL181_STATUS_DATABLOCKEND;
218
        DPRINTF("Transfer Complete\n");
219
    }
220
    if (s->datacnt == 0 && s->fifocnt == 0) {
221
        s->datactrl &= ~PL181_DATA_ENABLE;
222
        DPRINTF("Data engine idle\n");
223
    } else {
224
        /* Update FIFO bits.  */
225
        bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
226
        if (s->fifo_len == 0) {
227
            bits |= PL181_STATUS_TXFIFOEMPTY;
228
            bits |= PL181_STATUS_RXFIFOEMPTY;
229
        } else {
230
            bits |= PL181_STATUS_TXDATAAVLBL;
231
            bits |= PL181_STATUS_RXDATAAVLBL;
232
        }
233
        if (s->fifo_len == 16) {
234
            bits |= PL181_STATUS_TXFIFOFULL;
235
            bits |= PL181_STATUS_RXFIFOFULL;
236
        }
237
        if (s->fifo_len <= 8) {
238
            bits |= PL181_STATUS_TXFIFOHALFEMPTY;
239
        }
240
        if (s->fifo_len >= 8) {
241
            bits |= PL181_STATUS_RXFIFOHALFFULL;
242
        }
243
        if (s->datactrl & PL181_DATA_DIRECTION) {
244
            bits &= PL181_STATUS_RX_FIFO;
245
        } else {
246
            bits &= PL181_STATUS_TX_FIFO;
247
        }
248
        s->status |= bits;
249
    }
250
}
251

    
252
static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
253
{
254
    pl181_state *s = (pl181_state *)opaque;
255

    
256
    offset -= s->base;
257
    if (offset >= 0xfe0 && offset < 0x1000) {
258
        return pl181_id[(offset - 0xfe0) >> 2];
259
    }
260
    switch (offset) {
261
    case 0x00: /* Power */
262
        return s->power;
263
    case 0x04: /* Clock */
264
        return s->clock;
265
    case 0x08: /* Argument */
266
        return s->cmdarg;
267
    case 0x0c: /* Command */
268
        return s->cmd;
269
    case 0x10: /* RespCmd */
270
        return s->respcmd;
271
    case 0x14: /* Response0 */
272
        return s->response[0];
273
    case 0x18: /* Response1 */
274
        return s->response[1];
275
    case 0x1c: /* Response2 */
276
        return s->response[2];
277
    case 0x20: /* Response3 */
278
        return s->response[3];
279
    case 0x24: /* DataTimer */
280
        return s->datatimer;
281
    case 0x28: /* DataLength */
282
        return s->datalength;
283
    case 0x2c: /* DataCtrl */
284
        return s->datactrl;
285
    case 0x30: /* DataCnt */
286
        return s->datacnt;
287
    case 0x34: /* Status */
288
        return s->status;
289
    case 0x3c: /* Mask0 */
290
        return s->mask[0];
291
    case 0x40: /* Mask1 */
292
        return s->mask[1];
293
    case 0x48: /* FifoCnt */
294
        return s->fifocnt;
295
    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
296
    case 0x90: case 0x94: case 0x98: case 0x9c:
297
    case 0xa0: case 0xa4: case 0xa8: case 0xac:
298
    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
299
        if (s->fifocnt == 0) {
300
            fprintf(stderr, "pl181: Unexpected FIFO read\n");
301
            return 0;
302
        } else {
303
            uint32_t value;
304
            s->fifocnt--;
305
            value = pl181_fifo_pop(s);
306
            pl181_fifo_run(s);
307
            pl181_update(s);
308
            return value;
309
        }
310
    default:
311
        cpu_abort (cpu_single_env, "pl181_read: Bad offset %x\n", offset);
312
        return 0;
313
    }
314
}
315

    
316
static void pl181_write(void *opaque, target_phys_addr_t offset,
317
                          uint32_t value)
318
{
319
    pl181_state *s = (pl181_state *)opaque;
320

    
321
    offset -= s->base;
322
    switch (offset) {
323
    case 0x00: /* Power */
324
        s->power = value & 0xff;
325
        break;
326
    case 0x04: /* Clock */
327
        s->clock = value & 0xff;
328
        break;
329
    case 0x08: /* Argument */
330
        s->cmdarg = value;
331
        break;
332
    case 0x0c: /* Command */
333
        s->cmd = value;
334
        if (s->cmd & PL181_CMD_ENABLE) {
335
            if (s->cmd & PL181_CMD_INTERRUPT) {
336
                fprintf(stderr, "pl181: Interrupt mode not implemented\n");
337
                abort();
338
            } if (s->cmd & PL181_CMD_PENDING) {
339
                fprintf(stderr, "pl181: Pending commands not implemented\n");
340
                abort();
341
            } else {
342
                pl181_send_command(s);
343
                pl181_fifo_run(s);
344
            }
345
            /* The command has completed one way or the other.  */
346
            s->cmd &= ~PL181_CMD_ENABLE;
347
        }
348
        break;
349
    case 0x24: /* DataTimer */
350
        s->datatimer = value;
351
        break;
352
    case 0x28: /* DataLength */
353
        s->datalength = value & 0xffff;
354
        break;
355
    case 0x2c: /* DataCtrl */
356
        s->datactrl = value & 0xff;
357
        if (value & PL181_DATA_ENABLE) {
358
            s->datacnt = s->datalength;
359
            s->fifocnt = (s->datalength + 3) >> 2;
360
            pl181_fifo_run(s);
361
        }
362
        break;
363
    case 0x38: /* Clear */
364
        s->status &= ~(value & 0x7ff);
365
        break;
366
    case 0x3c: /* Mask0 */
367
        s->mask[0] = value;
368
        break;
369
    case 0x40: /* Mask1 */
370
        s->mask[1] = value;
371
        break;
372
    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
373
    case 0x90: case 0x94: case 0x98: case 0x9c:
374
    case 0xa0: case 0xa4: case 0xa8: case 0xac:
375
    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
376
        if (s->fifocnt == 0) {
377
            fprintf(stderr, "pl181: Unexpected FIFO write\n");
378
        } else {
379
            s->fifocnt--;
380
            pl181_fifo_push(s, value);
381
            pl181_fifo_run(s);
382
        }
383
        break;
384
    default:
385
        cpu_abort (cpu_single_env, "pl181_write: Bad offset %x\n", offset);
386
    }
387
    pl181_update(s);
388
}
389

    
390
static CPUReadMemoryFunc *pl181_readfn[] = {
391
   pl181_read,
392
   pl181_read,
393
   pl181_read
394
};
395

    
396
static CPUWriteMemoryFunc *pl181_writefn[] = {
397
   pl181_write,
398
   pl181_write,
399
   pl181_write
400
};
401

    
402
static void pl181_reset(void *opaque)
403
{
404
    pl181_state *s = (pl181_state *)opaque;
405

    
406
    s->power = 0;
407
    s->cmdarg = 0;
408
    s->cmd = 0;
409
    s->datatimer = 0;
410
    s->datalength = 0;
411
    s->respcmd = 0;
412
    s->response[0] = 0;
413
    s->response[1] = 0;
414
    s->response[2] = 0;
415
    s->response[3] = 0;
416
    s->datatimer = 0;
417
    s->datalength = 0;
418
    s->datactrl = 0;
419
    s->datacnt = 0;
420
    s->status = 0;
421
    s->mask[0] = 0;
422
    s->mask[1] = 0;
423
    s->fifocnt = 0;
424
}
425

    
426
void pl181_init(uint32_t base, BlockDriverState *bd,
427
                qemu_irq irq0, qemu_irq irq1)
428
{
429
    int iomemtype;
430
    pl181_state *s;
431

    
432
    s = (pl181_state *)qemu_mallocz(sizeof(pl181_state));
433
    iomemtype = cpu_register_io_memory(0, pl181_readfn,
434
                                       pl181_writefn, s);
435
    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
436
    s->base = base;
437
    s->card = sd_init(bd);
438
    s->irq[0] = irq0;
439
    s->irq[1] = irq1;
440
    qemu_register_reset(pl181_reset, s);
441
    pl181_reset(s);
442
    /* ??? Save/restore.  */
443
}