Blame view

hw/pl011.c 7.83 KB
1
/*
2
3
4
5
6
7
8
9
 * Arm PrimeCell PL011 UART
 *
 * Copyright (c) 2006 CodeSourcery.
 * Written by Paul Brook
 *
 * This code is licenced under the GPL.
 */
pbrook authored
10
11
12
#include "hw.h"
#include "qemu-char.h"
#include "primecell.h"
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

typedef struct {
    uint32_t base;
    uint32_t readbuff;
    uint32_t flags;
    uint32_t lcr;
    uint32_t cr;
    uint32_t dmacr;
    uint32_t int_enabled;
    uint32_t int_level;
    uint32_t read_fifo[16];
    uint32_t ilpr;
    uint32_t ibrd;
    uint32_t fbrd;
    uint32_t ifl;
    int read_pos;
    int read_count;
    int read_trigger;
    CharDriverState *chr;
pbrook authored
32
    qemu_irq irq;
pbrook authored
33
    enum pl011_type type;
34
35
36
37
38
39
40
41
42
43
} pl011_state;

#define PL011_INT_TX 0x20
#define PL011_INT_RX 0x10

#define PL011_FLAG_TXFE 0x80
#define PL011_FLAG_RXFF 0x40
#define PL011_FLAG_TXFF 0x20
#define PL011_FLAG_RXFE 0x10
pbrook authored
44
45
46
47
static const unsigned char pl011_id[2][8] = {
  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
};
48
49
50
51

static void pl011_update(pl011_state *s)
{
    uint32_t flags;
52
53
    flags = s->int_level & s->int_enabled;
pbrook authored
54
    qemu_set_irq(s->irq, flags != 0);
55
56
57
58
59
60
61
62
63
}

static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
{
    pl011_state *s = (pl011_state *)opaque;
    uint32_t c;

    offset -= s->base;
    if (offset >= 0xfe0 && offset < 0x1000) {
pbrook authored
64
        return pl011_id[s->type][(offset - 0xfe0) >> 2];
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    }
    switch (offset >> 2) {
    case 0: /* UARTDR */
        s->flags &= ~PL011_FLAG_RXFF;
        c = s->read_fifo[s->read_pos];
        if (s->read_count > 0) {
            s->read_count--;
            if (++s->read_pos == 16)
                s->read_pos = 0;
        }
        if (s->read_count == 0) {
            s->flags |= PL011_FLAG_RXFE;
        }
        if (s->read_count == s->read_trigger - 1)
            s->int_level &= ~ PL011_INT_RX;
        pl011_update(s);
81
        qemu_chr_accept_input(s->chr);
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
        return c;
    case 1: /* UARTCR */
        return 0;
    case 6: /* UARTFR */
        return s->flags;
    case 8: /* UARTILPR */
        return s->ilpr;
    case 9: /* UARTIBRD */
        return s->ibrd;
    case 10: /* UARTFBRD */
        return s->fbrd;
    case 11: /* UARTLCR_H */
        return s->lcr;
    case 12: /* UARTCR */
        return s->cr;
    case 13: /* UARTIFLS */
        return s->ifl;
    case 14: /* UARTIMSC */
        return s->int_enabled;
    case 15: /* UARTRIS */
        return s->int_level;
    case 16: /* UARTMIS */
        return s->int_level & s->int_enabled;
    case 18: /* UARTDMACR */
        return s->dmacr;
    default:
108
        cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
        return 0;
    }
}

static void pl011_set_read_trigger(pl011_state *s)
{
#if 0
    /* The docs say the RX interrupt is triggered when the FIFO exceeds
       the threshold.  However linux only reads the FIFO in response to an
       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
       to make things work.  */
    if (s->lcr & 0x10)
        s->read_trigger = (s->ifl >> 1) & 0x1c;
    else
#endif
        s->read_trigger = 1;
}

static void pl011_write(void *opaque, target_phys_addr_t offset,
                          uint32_t value)
{
    pl011_state *s = (pl011_state *)opaque;
    unsigned char ch;

    offset -= s->base;
    switch (offset >> 2) {
    case 0: /* UARTDR */
        /* ??? Check if transmitter is enabled.  */
        ch = value;
        if (s->chr)
            qemu_chr_write(s->chr, &ch, 1);
        s->int_level |= PL011_INT_TX;
        pl011_update(s);
        break;
    case 1: /* UARTCR */
        s->cr = value;
        break;
pbrook authored
146
147
148
    case 6: /* UARTFR */
        /* Writes to Flag register are ignored.  */
        break;
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
    case 8: /* UARTUARTILPR */
        s->ilpr = value;
        break;
    case 9: /* UARTIBRD */
        s->ibrd = value;
        break;
    case 10: /* UARTFBRD */
        s->fbrd = value;
        break;
    case 11: /* UARTLCR_H */
        s->lcr = value;
        pl011_set_read_trigger(s);
        break;
    case 12: /* UARTCR */
        /* ??? Need to implement the enable and loopback bits.  */
        s->cr = value;
        break;
    case 13: /* UARTIFS */
        s->ifl = value;
        pl011_set_read_trigger(s);
        break;
    case 14: /* UARTIMSC */
        s->int_enabled = value;
        pl011_update(s);
        break;
    case 17: /* UARTICR */
        s->int_level &= ~value;
        pl011_update(s);
        break;
    case 18: /* UARTDMACR */
        s->dmacr = value;
        if (value & 3)
            cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
        break;
    default:
184
        cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
185
186
187
    }
}
ths authored
188
static int pl011_can_receive(void *opaque)
189
190
191
192
193
194
195
196
197
{
    pl011_state *s = (pl011_state *)opaque;

    if (s->lcr & 0x10)
        return s->read_count < 16;
    else
        return s->read_count < 1;
}
198
static void pl011_put_fifo(void *opaque, uint32_t value)
199
200
201
202
203
204
205
{
    pl011_state *s = (pl011_state *)opaque;
    int slot;

    slot = s->read_pos + s->read_count;
    if (slot >= 16)
        slot -= 16;
206
    s->read_fifo[slot] = value;
207
208
209
210
211
212
213
214
215
216
217
    s->read_count++;
    s->flags &= ~PL011_FLAG_RXFE;
    if (s->cr & 0x10 || s->read_count == 16) {
        s->flags |= PL011_FLAG_RXFF;
    }
    if (s->read_count == s->read_trigger) {
        s->int_level |= PL011_INT_RX;
        pl011_update(s);
    }
}
218
219
220
221
222
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
{
    pl011_put_fifo(opaque, *buf);
}
223
224
static void pl011_event(void *opaque, int event)
{
225
226
    if (event == CHR_EVENT_BREAK)
        pl011_put_fifo(opaque, 0x400);
227
228
229
230
231
232
233
234
235
236
237
238
239
240
}

static CPUReadMemoryFunc *pl011_readfn[] = {
   pl011_read,
   pl011_read,
   pl011_read
};

static CPUWriteMemoryFunc *pl011_writefn[] = {
   pl011_write,
   pl011_write,
   pl011_write
};
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
static void pl011_save(QEMUFile *f, void *opaque)
{
    pl011_state *s = (pl011_state *)opaque;
    int i;

    qemu_put_be32(f, s->readbuff);
    qemu_put_be32(f, s->flags);
    qemu_put_be32(f, s->lcr);
    qemu_put_be32(f, s->cr);
    qemu_put_be32(f, s->dmacr);
    qemu_put_be32(f, s->int_enabled);
    qemu_put_be32(f, s->int_level);
    for (i = 0; i < 16; i++)
        qemu_put_be32(f, s->read_fifo[i]);
    qemu_put_be32(f, s->ilpr);
    qemu_put_be32(f, s->ibrd);
    qemu_put_be32(f, s->fbrd);
    qemu_put_be32(f, s->ifl);
    qemu_put_be32(f, s->read_pos);
    qemu_put_be32(f, s->read_count);
    qemu_put_be32(f, s->read_trigger);
}

static int pl011_load(QEMUFile *f, void *opaque, int version_id)
{
    pl011_state *s = (pl011_state *)opaque;
    int i;

    if (version_id != 1)
        return -EINVAL;

    s->readbuff = qemu_get_be32(f);
    s->flags = qemu_get_be32(f);
    s->lcr = qemu_get_be32(f);
    s->cr = qemu_get_be32(f);
    s->dmacr = qemu_get_be32(f);
    s->int_enabled = qemu_get_be32(f);
    s->int_level = qemu_get_be32(f);
    for (i = 0; i < 16; i++)
        s->read_fifo[i] = qemu_get_be32(f);
    s->ilpr = qemu_get_be32(f);
    s->ibrd = qemu_get_be32(f);
    s->fbrd = qemu_get_be32(f);
    s->ifl = qemu_get_be32(f);
    s->read_pos = qemu_get_be32(f);
    s->read_count = qemu_get_be32(f);
    s->read_trigger = qemu_get_be32(f);

    return 0;
}
pbrook authored
292
void pl011_init(uint32_t base, qemu_irq irq,
pbrook authored
293
                CharDriverState *chr, enum pl011_type type)
294
295
296
297
298
299
300
{
    int iomemtype;
    pl011_state *s;

    s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
    iomemtype = cpu_register_io_memory(0, pl011_readfn,
                                       pl011_writefn, s);
301
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
302
303
    s->base = base;
    s->irq = irq;
pbrook authored
304
    s->type = type;
305
306
307
308
309
    s->chr = chr;
    s->read_trigger = 1;
    s->ifl = 0x12;
    s->cr = 0x300;
    s->flags = 0x90;
310
    if (chr){
ths authored
311
        qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
312
                              pl011_event, s);
313
    }
314
    register_savevm("pl011_uart", -1, 1, pl011_save, pl011_load, s);
315
316
}