Statistics
| Branch: | Revision:

root / hw / pl011.c @ ab034c26

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 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
        qemu_chr_accept_input(s->chr);
82
        return c;
83
    case 1: /* UARTCR */
84
        return 0;
85
    case 6: /* UARTFR */
86
        return s->flags;
87
    case 8: /* UARTILPR */
88
        return s->ilpr;
89
    case 9: /* UARTIBRD */
90
        return s->ibrd;
91
    case 10: /* UARTFBRD */
92
        return s->fbrd;
93
    case 11: /* UARTLCR_H */
94
        return s->lcr;
95
    case 12: /* UARTCR */
96
        return s->cr;
97
    case 13: /* UARTIFLS */
98
        return s->ifl;
99
    case 14: /* UARTIMSC */
100
        return s->int_enabled;
101
    case 15: /* UARTRIS */
102
        return s->int_level;
103
    case 16: /* UARTMIS */
104
        return s->int_level & s->int_enabled;
105
    case 18: /* UARTDMACR */
106
        return s->dmacr;
107
    default:
108
        hw_error("pl011_read: Bad offset %x\n", (int)offset);
109
        return 0;
110
    }
111
}
112

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

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

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

    
187
static int pl011_can_receive(void *opaque)
188
{
189
    pl011_state *s = (pl011_state *)opaque;
190

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

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

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

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

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

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

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

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

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

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

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

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

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

    
295
    sdc->init = pl011_arm_init;
296
}
297

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

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

    
309
    sdc->init = pl011_luminary_init;
310
}
311

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

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

    
325
type_init(pl011_register_types)