Statistics
| Branch: | Revision:

root / hw / pl011.c @ fa2ddcb4

History | View | Annotate | Download (8.3 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, target_phys_addr_t 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
        hw_error("pl011_read: Bad offset %x\n", (int)offset);
111
        return 0;
112
    }
113
}
114

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

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

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

    
189
static int pl011_can_receive(void *opaque)
190
{
191
    pl011_state *s = (pl011_state *)opaque;
192

    
193
    if (s->lcr & 0x10)
194
        return s->read_count < 16;
195
    else
196
        return s->read_count < 1;
197
}
198

    
199
static void pl011_put_fifo(void *opaque, uint32_t value)
200
{
201
    pl011_state *s = (pl011_state *)opaque;
202
    int slot;
203

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

    
219
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
220
{
221
    pl011_put_fifo(opaque, *buf);
222
}
223

    
224
static void pl011_event(void *opaque, int event)
225
{
226
    if (event == CHR_EVENT_BREAK)
227
        pl011_put_fifo(opaque, 0x400);
228
}
229

    
230
static const MemoryRegionOps pl011_ops = {
231
    .read = pl011_read,
232
    .write = pl011_write,
233
    .endianness = DEVICE_NATIVE_ENDIAN,
234
};
235

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

    
261
static int pl011_init(SysBusDevice *dev, const unsigned char *id)
262
{
263
    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
264

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

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

    
283
static int pl011_arm_init(SysBusDevice *dev)
284
{
285
    return pl011_init(dev, pl011_id_arm);
286
}
287

    
288
static int pl011_luminary_init(SysBusDevice *dev)
289
{
290
    return pl011_init(dev, pl011_id_luminary);
291
}
292

    
293
static void pl011_arm_class_init(ObjectClass *klass, void *data)
294
{
295
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
296

    
297
    sdc->init = pl011_arm_init;
298
}
299

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

    
307
static void pl011_luminary_class_init(ObjectClass *klass, void *data)
308
{
309
    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
310

    
311
    sdc->init = pl011_luminary_init;
312
}
313

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

    
321
static void pl011_register_types(void)
322
{
323
    type_register_static(&pl011_arm_info);
324
    type_register_static(&pl011_luminary_info);
325
}
326

    
327
type_init(pl011_register_types)