~ahs3/+junk/cq-qemu

« back to all changes in this revision

Viewing changes to hw/pl011.c

  • Committer: Al Stone
  • Date: 2012-02-09 01:17:20 UTC
  • Revision ID: albert.stone@canonical.com-20120209011720-tztl7ik3qayz80p4
first commit to bzr for qemu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
    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_fe_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)