Statistics
| Branch: | Revision:

root / hw / pl181.c @ a1bb27b1

History | View | Annotate | Download (12.6 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
    struct sd_state_s *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
    void *pic;
44
    int irq[2];
45
} pl181_state;
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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