Statistics
| Branch: | Revision:

root / hw / pl011.c @ 2507c12a

History | View | Annotate | Download (8.2 kB)

1
/*
2
 * Arm PrimeCell PL011 UART
3
 *
4
 * Copyright (c) 2006 CodeSourcery.
5
 * Written by Paul Brook
6
 *
7
 * This code is licenced under the GPL.
8
 */
9

    
10
#include "sysbus.h"
11
#include "qemu-char.h"
12

    
13
typedef struct {
14
    SysBusDevice busdev;
15
    uint32_t readbuff;
16
    uint32_t flags;
17
    uint32_t lcr;
18
    uint32_t cr;
19
    uint32_t dmacr;
20
    uint32_t int_enabled;
21
    uint32_t int_level;
22
    uint32_t read_fifo[16];
23
    uint32_t ilpr;
24
    uint32_t ibrd;
25
    uint32_t fbrd;
26
    uint32_t ifl;
27
    int read_pos;
28
    int read_count;
29
    int read_trigger;
30
    CharDriverState *chr;
31
    qemu_irq irq;
32
    const unsigned char *id;
33
} pl011_state;
34

    
35
#define PL011_INT_TX 0x20
36
#define PL011_INT_RX 0x10
37

    
38
#define PL011_FLAG_TXFE 0x80
39
#define PL011_FLAG_RXFF 0x40
40
#define PL011_FLAG_TXFF 0x20
41
#define PL011_FLAG_RXFE 0x10
42

    
43
static const unsigned char pl011_id_arm[8] =
44
  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45
static const unsigned char pl011_id_luminary[8] =
46
  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
47

    
48
static void pl011_update(pl011_state *s)
49
{
50
    uint32_t flags;
51

    
52
    flags = s->int_level & s->int_enabled;
53
    qemu_set_irq(s->irq, flags != 0);
54
}
55

    
56
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
57
{
58
    pl011_state *s = (pl011_state *)opaque;
59
    uint32_t c;
60

    
61
    if (offset >= 0xfe0 && offset < 0x1000) {
62
        return s->id[(offset - 0xfe0) >> 2];
63
    }
64
    switch (offset >> 2) {
65
    case 0: /* UARTDR */
66
        s->flags &= ~PL011_FLAG_RXFF;
67
        c = s->read_fifo[s->read_pos];
68
        if (s->read_count > 0) {
69
            s->read_count--;
70
            if (++s->read_pos == 16)
71
                s->read_pos = 0;
72
        }
73
        if (s->read_count == 0) {
74
            s->flags |= PL011_FLAG_RXFE;
75
        }
76
        if (s->read_count == s->read_trigger - 1)
77
            s->int_level &= ~ PL011_INT_RX;
78
        pl011_update(s);
79
        qemu_chr_accept_input(s->chr);
80
        return c;
81
    case 1: /* UARTCR */
82
        return 0;
83
    case 6: /* UARTFR */
84
        return s->flags;
85
    case 8: /* UARTILPR */
86
        return s->ilpr;
87
    case 9: /* UARTIBRD */
88
        return s->ibrd;
89
    case 10: /* UARTFBRD */
90
        return s->fbrd;
91
    case 11: /* UARTLCR_H */
92
        return s->lcr;
93
    case 12: /* UARTCR */
94
        return s->cr;
95
    case 13: /* UARTIFLS */
96
        return s->ifl;
97
    case 14: /* UARTIMSC */
98
        return s->int_enabled;
99
    case 15: /* UARTRIS */
100
        return s->int_level;
101
    case 16: /* UARTMIS */
102
        return s->int_level & s->int_enabled;
103
    case 18: /* UARTDMACR */
104
        return s->dmacr;
105
    default:
106
        hw_error("pl011_read: Bad offset %x\n", (int)offset);
107
        return 0;
108
    }
109
}
110

    
111
static void pl011_set_read_trigger(pl011_state *s)
112
{
113
#if 0
114
    /* The docs say the RX interrupt is triggered when the FIFO exceeds
115
       the threshold.  However linux only reads the FIFO in response to an
116
       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
117
       to make things work.  */
118
    if (s->lcr & 0x10)
119
        s->read_trigger = (s->ifl >> 1) & 0x1c;
120
    else
121
#endif
122
        s->read_trigger = 1;
123
}
124

    
125
static void pl011_write(void *opaque, target_phys_addr_t offset,
126
                          uint32_t value)
127
{
128
    pl011_state *s = (pl011_state *)opaque;
129
    unsigned char ch;
130

    
131
    switch (offset >> 2) {
132
    case 0: /* UARTDR */
133
        /* ??? Check if transmitter is enabled.  */
134
        ch = value;
135
        if (s->chr)
136
            qemu_chr_write(s->chr, &ch, 1);
137
        s->int_level |= PL011_INT_TX;
138
        pl011_update(s);
139
        break;
140
    case 1: /* UARTCR */
141
        s->cr = value;
142
        break;
143
    case 6: /* UARTFR */
144
        /* Writes to Flag register are ignored.  */
145
        break;
146
    case 8: /* UARTUARTILPR */
147
        s->ilpr = value;
148
        break;
149
    case 9: /* UARTIBRD */
150
        s->ibrd = value;
151
        break;
152
    case 10: /* UARTFBRD */
153
        s->fbrd = value;
154
        break;
155
    case 11: /* UARTLCR_H */
156
        s->lcr = value;
157
        pl011_set_read_trigger(s);
158
        break;
159
    case 12: /* UARTCR */
160
        /* ??? Need to implement the enable and loopback bits.  */
161
        s->cr = value;
162
        break;
163
    case 13: /* UARTIFS */
164
        s->ifl = value;
165
        pl011_set_read_trigger(s);
166
        break;
167
    case 14: /* UARTIMSC */
168
        s->int_enabled = value;
169
        pl011_update(s);
170
        break;
171
    case 17: /* UARTICR */
172
        s->int_level &= ~value;
173
        pl011_update(s);
174
        break;
175
    case 18: /* UARTDMACR */
176
        s->dmacr = value;
177
        if (value & 3)
178
            hw_error("PL011: DMA not implemented\n");
179
        break;
180
    default:
181
        hw_error("pl011_write: Bad offset %x\n", (int)offset);
182
    }
183
}
184

    
185
static int pl011_can_receive(void *opaque)
186
{
187
    pl011_state *s = (pl011_state *)opaque;
188

    
189
    if (s->lcr & 0x10)
190
        return s->read_count < 16;
191
    else
192
        return s->read_count < 1;
193
}
194

    
195
static void pl011_put_fifo(void *opaque, uint32_t value)
196
{
197
    pl011_state *s = (pl011_state *)opaque;
198
    int slot;
199

    
200
    slot = s->read_pos + s->read_count;
201
    if (slot >= 16)
202
        slot -= 16;
203
    s->read_fifo[slot] = value;
204
    s->read_count++;
205
    s->flags &= ~PL011_FLAG_RXFE;
206
    if (s->cr & 0x10 || s->read_count == 16) {
207
        s->flags |= PL011_FLAG_RXFF;
208
    }
209
    if (s->read_count == s->read_trigger) {
210
        s->int_level |= PL011_INT_RX;
211
        pl011_update(s);
212
    }
213
}
214

    
215
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
216
{
217
    pl011_put_fifo(opaque, *buf);
218
}
219

    
220
static void pl011_event(void *opaque, int event)
221
{
222
    if (event == CHR_EVENT_BREAK)
223
        pl011_put_fifo(opaque, 0x400);
224
}
225

    
226
static CPUReadMemoryFunc * const pl011_readfn[] = {
227
   pl011_read,
228
   pl011_read,
229
   pl011_read
230
};
231

    
232
static CPUWriteMemoryFunc * const pl011_writefn[] = {
233
   pl011_write,
234
   pl011_write,
235
   pl011_write
236
};
237

    
238
static void pl011_save(QEMUFile *f, void *opaque)
239
{
240
    pl011_state *s = (pl011_state *)opaque;
241
    int i;
242

    
243
    qemu_put_be32(f, s->readbuff);
244
    qemu_put_be32(f, s->flags);
245
    qemu_put_be32(f, s->lcr);
246
    qemu_put_be32(f, s->cr);
247
    qemu_put_be32(f, s->dmacr);
248
    qemu_put_be32(f, s->int_enabled);
249
    qemu_put_be32(f, s->int_level);
250
    for (i = 0; i < 16; i++)
251
        qemu_put_be32(f, s->read_fifo[i]);
252
    qemu_put_be32(f, s->ilpr);
253
    qemu_put_be32(f, s->ibrd);
254
    qemu_put_be32(f, s->fbrd);
255
    qemu_put_be32(f, s->ifl);
256
    qemu_put_be32(f, s->read_pos);
257
    qemu_put_be32(f, s->read_count);
258
    qemu_put_be32(f, s->read_trigger);
259
}
260

    
261
static int pl011_load(QEMUFile *f, void *opaque, int version_id)
262
{
263
    pl011_state *s = (pl011_state *)opaque;
264
    int i;
265

    
266
    if (version_id != 1)
267
        return -EINVAL;
268

    
269
    s->readbuff = qemu_get_be32(f);
270
    s->flags = qemu_get_be32(f);
271
    s->lcr = qemu_get_be32(f);
272
    s->cr = qemu_get_be32(f);
273
    s->dmacr = qemu_get_be32(f);
274
    s->int_enabled = qemu_get_be32(f);
275
    s->int_level = qemu_get_be32(f);
276
    for (i = 0; i < 16; i++)
277
        s->read_fifo[i] = qemu_get_be32(f);
278
    s->ilpr = qemu_get_be32(f);
279
    s->ibrd = qemu_get_be32(f);
280
    s->fbrd = qemu_get_be32(f);
281
    s->ifl = qemu_get_be32(f);
282
    s->read_pos = qemu_get_be32(f);
283
    s->read_count = qemu_get_be32(f);
284
    s->read_trigger = qemu_get_be32(f);
285

    
286
    return 0;
287
}
288

    
289
static int pl011_init(SysBusDevice *dev, const unsigned char *id)
290
{
291
    int iomemtype;
292
    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
293

    
294
    iomemtype = cpu_register_io_memory(pl011_readfn,
295
                                       pl011_writefn, s,
296
                                       DEVICE_NATIVE_ENDIAN);
297
    sysbus_init_mmio(dev, 0x1000,iomemtype);
298
    sysbus_init_irq(dev, &s->irq);
299
    s->id = id;
300
    s->chr = qdev_init_chardev(&dev->qdev);
301

    
302
    s->read_trigger = 1;
303
    s->ifl = 0x12;
304
    s->cr = 0x300;
305
    s->flags = 0x90;
306
    if (s->chr) {
307
        qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
308
                              pl011_event, s);
309
    }
310
    register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s);
311
    return 0;
312
}
313

    
314
static int pl011_init_arm(SysBusDevice *dev)
315
{
316
    return pl011_init(dev, pl011_id_arm);
317
}
318

    
319
static int pl011_init_luminary(SysBusDevice *dev)
320
{
321
    return pl011_init(dev, pl011_id_luminary);
322
}
323

    
324
static void pl011_register_devices(void)
325
{
326
    sysbus_register_dev("pl011", sizeof(pl011_state),
327
                        pl011_init_arm);
328
    sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
329
                        pl011_init_luminary);
330
}
331

    
332
device_init(pl011_register_devices)