Statistics
| Branch: | Revision:

root / hw / pl011.c @ a8170e5e

History | View | Annotate | Download (8.4 kB)

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

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

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

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

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

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

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

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

    
57
static uint64_t pl011_read(void *opaque, hwaddr offset,
58
                           unsigned size)
59
{
60
    pl011_state *s = (pl011_state *)opaque;
61
    uint32_t c;
62

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

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

    
130
static void pl011_write(void *opaque, hwaddr offset,
131
                        uint64_t value, unsigned size)
132
{
133
    pl011_state *s = (pl011_state *)opaque;
134
    unsigned char ch;
135

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

    
192
static int pl011_can_receive(void *opaque)
193
{
194
    pl011_state *s = (pl011_state *)opaque;
195

    
196
    if (s->lcr & 0x10)
197
        return s->read_count < 16;
198
    else
199
        return s->read_count < 1;
200
}
201

    
202
static void pl011_put_fifo(void *opaque, uint32_t value)
203
{
204
    pl011_state *s = (pl011_state *)opaque;
205
    int slot;
206

    
207
    slot = s->read_pos + s->read_count;
208
    if (slot >= 16)
209
        slot -= 16;
210
    s->read_fifo[slot] = value;
211
    s->read_count++;
212
    s->flags &= ~PL011_FLAG_RXFE;
213
    if (s->cr & 0x10 || s->read_count == 16) {
214
        s->flags |= PL011_FLAG_RXFF;
215
    }
216
    if (s->read_count == s->read_trigger) {
217
        s->int_level |= PL011_INT_RX;
218
        pl011_update(s);
219
    }
220
}
221

    
222
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
223
{
224
    pl011_put_fifo(opaque, *buf);
225
}
226

    
227
static void pl011_event(void *opaque, int event)
228
{
229
    if (event == CHR_EVENT_BREAK)
230
        pl011_put_fifo(opaque, 0x400);
231
}
232

    
233
static const MemoryRegionOps pl011_ops = {
234
    .read = pl011_read,
235
    .write = pl011_write,
236
    .endianness = DEVICE_NATIVE_ENDIAN,
237
};
238

    
239
static const VMStateDescription vmstate_pl011 = {
240
    .name = "pl011",
241
    .version_id = 1,
242
    .minimum_version_id = 1,
243
    .minimum_version_id_old = 1,
244
    .fields      = (VMStateField[]) {
245
        VMSTATE_UINT32(readbuff, pl011_state),
246
        VMSTATE_UINT32(flags, pl011_state),
247
        VMSTATE_UINT32(lcr, pl011_state),
248
        VMSTATE_UINT32(cr, pl011_state),
249
        VMSTATE_UINT32(dmacr, pl011_state),
250
        VMSTATE_UINT32(int_enabled, pl011_state),
251
        VMSTATE_UINT32(int_level, pl011_state),
252
        VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
253
        VMSTATE_UINT32(ilpr, pl011_state),
254
        VMSTATE_UINT32(ibrd, pl011_state),
255
        VMSTATE_UINT32(fbrd, pl011_state),
256
        VMSTATE_UINT32(ifl, pl011_state),
257
        VMSTATE_INT32(read_pos, pl011_state),
258
        VMSTATE_INT32(read_count, pl011_state),
259
        VMSTATE_INT32(read_trigger, pl011_state),
260
        VMSTATE_END_OF_LIST()
261
    }
262
};
263

    
264
static int pl011_init(SysBusDevice *dev, const unsigned char *id)
265
{
266
    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
267

    
268
    memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
269
    sysbus_init_mmio(dev, &s->iomem);
270
    sysbus_init_irq(dev, &s->irq);
271
    s->id = id;
272
    s->chr = qemu_char_get_next_serial();
273

    
274
    s->read_trigger = 1;
275
    s->ifl = 0x12;
276
    s->cr = 0x300;
277
    s->flags = 0x90;
278
    if (s->chr) {
279
        qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
280
                              pl011_event, s);
281
    }
282
    vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
283
    return 0;
284
}
285

    
286
static int pl011_arm_init(SysBusDevice *dev)
287
{
288
    return pl011_init(dev, pl011_id_arm);
289
}
290

    
291
static int pl011_luminary_init(SysBusDevice *dev)
292
{
293
    return pl011_init(dev, pl011_id_luminary);
294
}
295

    
296
static void pl011_arm_class_init(ObjectClass *klass, void *data)
297
{
298
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
299

    
300
    sdc->init = pl011_arm_init;
301
}
302

    
303
static TypeInfo pl011_arm_info = {
304
    .name          = "pl011",
305
    .parent        = TYPE_SYS_BUS_DEVICE,
306
    .instance_size = sizeof(pl011_state),
307
    .class_init    = pl011_arm_class_init,
308
};
309

    
310
static void pl011_luminary_class_init(ObjectClass *klass, void *data)
311
{
312
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
313

    
314
    sdc->init = pl011_luminary_init;
315
}
316

    
317
static TypeInfo pl011_luminary_info = {
318
    .name          = "pl011_luminary",
319
    .parent        = TYPE_SYS_BUS_DEVICE,
320
    .instance_size = sizeof(pl011_state),
321
    .class_init    = pl011_luminary_class_init,
322
};
323

    
324
static void pl011_register_types(void)
325
{
326
    type_register_static(&pl011_arm_info);
327
    type_register_static(&pl011_luminary_info);
328
}
329

    
330
type_init(pl011_register_types)