Statistics
| Branch: | Revision:

root / hw / pl011.c @ 086cf4d3

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

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

    
268
    iomemtype = cpu_register_io_memory(pl011_readfn,
269
                                       pl011_writefn, s,
270
                                       DEVICE_NATIVE_ENDIAN);
271
    sysbus_init_mmio(dev, 0x1000,iomemtype);
272
    sysbus_init_irq(dev, &s->irq);
273
    s->id = id;
274
    s->chr = qdev_init_chardev(&dev->qdev);
275

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

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

    
293
static int pl011_init_luminary(SysBusDevice *dev)
294
{
295
    return pl011_init(dev, pl011_id_luminary);
296
}
297

    
298
static void pl011_register_devices(void)
299
{
300
    sysbus_register_dev("pl011", sizeof(pl011_state),
301
                        pl011_init_arm);
302
    sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
303
                        pl011_init_luminary);
304
}
305

    
306
device_init(pl011_register_devices)