Statistics
| Branch: | Revision:

root / hw / pl011.c @ afe3ef1d

History | View | Annotate | Download (8.2 kB)

1 5fafdf24 ths
/*
2 cdbdb648 pbrook
 * Arm PrimeCell PL011 UART
3 cdbdb648 pbrook
 *
4 cdbdb648 pbrook
 * Copyright (c) 2006 CodeSourcery.
5 cdbdb648 pbrook
 * Written by Paul Brook
6 cdbdb648 pbrook
 *
7 cdbdb648 pbrook
 * This code is licenced under the GPL.
8 cdbdb648 pbrook
 */
9 cdbdb648 pbrook
10 a7d518a6 Paul Brook
#include "sysbus.h"
11 87ecb68b pbrook
#include "qemu-char.h"
12 cdbdb648 pbrook
13 cdbdb648 pbrook
typedef struct {
14 a7d518a6 Paul Brook
    SysBusDevice busdev;
15 cdbdb648 pbrook
    uint32_t readbuff;
16 cdbdb648 pbrook
    uint32_t flags;
17 cdbdb648 pbrook
    uint32_t lcr;
18 cdbdb648 pbrook
    uint32_t cr;
19 cdbdb648 pbrook
    uint32_t dmacr;
20 cdbdb648 pbrook
    uint32_t int_enabled;
21 cdbdb648 pbrook
    uint32_t int_level;
22 cdbdb648 pbrook
    uint32_t read_fifo[16];
23 cdbdb648 pbrook
    uint32_t ilpr;
24 cdbdb648 pbrook
    uint32_t ibrd;
25 cdbdb648 pbrook
    uint32_t fbrd;
26 cdbdb648 pbrook
    uint32_t ifl;
27 cdbdb648 pbrook
    int read_pos;
28 cdbdb648 pbrook
    int read_count;
29 cdbdb648 pbrook
    int read_trigger;
30 cdbdb648 pbrook
    CharDriverState *chr;
31 d537cf6c pbrook
    qemu_irq irq;
32 a7d518a6 Paul Brook
    const unsigned char *id;
33 cdbdb648 pbrook
} pl011_state;
34 cdbdb648 pbrook
35 cdbdb648 pbrook
#define PL011_INT_TX 0x20
36 cdbdb648 pbrook
#define PL011_INT_RX 0x10
37 cdbdb648 pbrook
38 cdbdb648 pbrook
#define PL011_FLAG_TXFE 0x80
39 cdbdb648 pbrook
#define PL011_FLAG_RXFF 0x40
40 cdbdb648 pbrook
#define PL011_FLAG_TXFF 0x20
41 cdbdb648 pbrook
#define PL011_FLAG_RXFE 0x10
42 cdbdb648 pbrook
43 a7d518a6 Paul Brook
static const unsigned char pl011_id_arm[8] =
44 a7d518a6 Paul Brook
  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45 a7d518a6 Paul Brook
static const unsigned char pl011_id_luminary[8] =
46 a7d518a6 Paul Brook
  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
47 cdbdb648 pbrook
48 cdbdb648 pbrook
static void pl011_update(pl011_state *s)
49 cdbdb648 pbrook
{
50 cdbdb648 pbrook
    uint32_t flags;
51 3b46e624 ths
52 cdbdb648 pbrook
    flags = s->int_level & s->int_enabled;
53 d537cf6c pbrook
    qemu_set_irq(s->irq, flags != 0);
54 cdbdb648 pbrook
}
55 cdbdb648 pbrook
56 c227f099 Anthony Liguori
static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
57 cdbdb648 pbrook
{
58 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
59 cdbdb648 pbrook
    uint32_t c;
60 cdbdb648 pbrook
61 cdbdb648 pbrook
    if (offset >= 0xfe0 && offset < 0x1000) {
62 a7d518a6 Paul Brook
        return s->id[(offset - 0xfe0) >> 2];
63 cdbdb648 pbrook
    }
64 cdbdb648 pbrook
    switch (offset >> 2) {
65 cdbdb648 pbrook
    case 0: /* UARTDR */
66 cdbdb648 pbrook
        s->flags &= ~PL011_FLAG_RXFF;
67 cdbdb648 pbrook
        c = s->read_fifo[s->read_pos];
68 cdbdb648 pbrook
        if (s->read_count > 0) {
69 cdbdb648 pbrook
            s->read_count--;
70 cdbdb648 pbrook
            if (++s->read_pos == 16)
71 cdbdb648 pbrook
                s->read_pos = 0;
72 cdbdb648 pbrook
        }
73 cdbdb648 pbrook
        if (s->read_count == 0) {
74 cdbdb648 pbrook
            s->flags |= PL011_FLAG_RXFE;
75 cdbdb648 pbrook
        }
76 cdbdb648 pbrook
        if (s->read_count == s->read_trigger - 1)
77 cdbdb648 pbrook
            s->int_level &= ~ PL011_INT_RX;
78 cdbdb648 pbrook
        pl011_update(s);
79 bd9bdce6 balrog
        qemu_chr_accept_input(s->chr);
80 cdbdb648 pbrook
        return c;
81 cdbdb648 pbrook
    case 1: /* UARTCR */
82 cdbdb648 pbrook
        return 0;
83 cdbdb648 pbrook
    case 6: /* UARTFR */
84 cdbdb648 pbrook
        return s->flags;
85 cdbdb648 pbrook
    case 8: /* UARTILPR */
86 cdbdb648 pbrook
        return s->ilpr;
87 cdbdb648 pbrook
    case 9: /* UARTIBRD */
88 cdbdb648 pbrook
        return s->ibrd;
89 cdbdb648 pbrook
    case 10: /* UARTFBRD */
90 cdbdb648 pbrook
        return s->fbrd;
91 cdbdb648 pbrook
    case 11: /* UARTLCR_H */
92 cdbdb648 pbrook
        return s->lcr;
93 cdbdb648 pbrook
    case 12: /* UARTCR */
94 cdbdb648 pbrook
        return s->cr;
95 cdbdb648 pbrook
    case 13: /* UARTIFLS */
96 cdbdb648 pbrook
        return s->ifl;
97 cdbdb648 pbrook
    case 14: /* UARTIMSC */
98 cdbdb648 pbrook
        return s->int_enabled;
99 cdbdb648 pbrook
    case 15: /* UARTRIS */
100 cdbdb648 pbrook
        return s->int_level;
101 cdbdb648 pbrook
    case 16: /* UARTMIS */
102 cdbdb648 pbrook
        return s->int_level & s->int_enabled;
103 cdbdb648 pbrook
    case 18: /* UARTDMACR */
104 cdbdb648 pbrook
        return s->dmacr;
105 cdbdb648 pbrook
    default:
106 2ac71179 Paul Brook
        hw_error("pl011_read: Bad offset %x\n", (int)offset);
107 cdbdb648 pbrook
        return 0;
108 cdbdb648 pbrook
    }
109 cdbdb648 pbrook
}
110 cdbdb648 pbrook
111 cdbdb648 pbrook
static void pl011_set_read_trigger(pl011_state *s)
112 cdbdb648 pbrook
{
113 cdbdb648 pbrook
#if 0
114 cdbdb648 pbrook
    /* The docs say the RX interrupt is triggered when the FIFO exceeds
115 cdbdb648 pbrook
       the threshold.  However linux only reads the FIFO in response to an
116 cdbdb648 pbrook
       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
117 cdbdb648 pbrook
       to make things work.  */
118 cdbdb648 pbrook
    if (s->lcr & 0x10)
119 cdbdb648 pbrook
        s->read_trigger = (s->ifl >> 1) & 0x1c;
120 cdbdb648 pbrook
    else
121 cdbdb648 pbrook
#endif
122 cdbdb648 pbrook
        s->read_trigger = 1;
123 cdbdb648 pbrook
}
124 cdbdb648 pbrook
125 c227f099 Anthony Liguori
static void pl011_write(void *opaque, target_phys_addr_t offset,
126 cdbdb648 pbrook
                          uint32_t value)
127 cdbdb648 pbrook
{
128 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
129 cdbdb648 pbrook
    unsigned char ch;
130 cdbdb648 pbrook
131 cdbdb648 pbrook
    switch (offset >> 2) {
132 cdbdb648 pbrook
    case 0: /* UARTDR */
133 cdbdb648 pbrook
        /* ??? Check if transmitter is enabled.  */
134 cdbdb648 pbrook
        ch = value;
135 cdbdb648 pbrook
        if (s->chr)
136 cdbdb648 pbrook
            qemu_chr_write(s->chr, &ch, 1);
137 cdbdb648 pbrook
        s->int_level |= PL011_INT_TX;
138 cdbdb648 pbrook
        pl011_update(s);
139 cdbdb648 pbrook
        break;
140 cdbdb648 pbrook
    case 1: /* UARTCR */
141 cdbdb648 pbrook
        s->cr = value;
142 cdbdb648 pbrook
        break;
143 9ee6e8bb pbrook
    case 6: /* UARTFR */
144 9ee6e8bb pbrook
        /* Writes to Flag register are ignored.  */
145 9ee6e8bb pbrook
        break;
146 cdbdb648 pbrook
    case 8: /* UARTUARTILPR */
147 cdbdb648 pbrook
        s->ilpr = value;
148 cdbdb648 pbrook
        break;
149 cdbdb648 pbrook
    case 9: /* UARTIBRD */
150 cdbdb648 pbrook
        s->ibrd = value;
151 cdbdb648 pbrook
        break;
152 cdbdb648 pbrook
    case 10: /* UARTFBRD */
153 cdbdb648 pbrook
        s->fbrd = value;
154 cdbdb648 pbrook
        break;
155 cdbdb648 pbrook
    case 11: /* UARTLCR_H */
156 cdbdb648 pbrook
        s->lcr = value;
157 cdbdb648 pbrook
        pl011_set_read_trigger(s);
158 cdbdb648 pbrook
        break;
159 cdbdb648 pbrook
    case 12: /* UARTCR */
160 cdbdb648 pbrook
        /* ??? Need to implement the enable and loopback bits.  */
161 cdbdb648 pbrook
        s->cr = value;
162 cdbdb648 pbrook
        break;
163 cdbdb648 pbrook
    case 13: /* UARTIFS */
164 cdbdb648 pbrook
        s->ifl = value;
165 cdbdb648 pbrook
        pl011_set_read_trigger(s);
166 cdbdb648 pbrook
        break;
167 cdbdb648 pbrook
    case 14: /* UARTIMSC */
168 cdbdb648 pbrook
        s->int_enabled = value;
169 cdbdb648 pbrook
        pl011_update(s);
170 cdbdb648 pbrook
        break;
171 cdbdb648 pbrook
    case 17: /* UARTICR */
172 cdbdb648 pbrook
        s->int_level &= ~value;
173 cdbdb648 pbrook
        pl011_update(s);
174 cdbdb648 pbrook
        break;
175 cdbdb648 pbrook
    case 18: /* UARTDMACR */
176 cdbdb648 pbrook
        s->dmacr = value;
177 cdbdb648 pbrook
        if (value & 3)
178 2ac71179 Paul Brook
            hw_error("PL011: DMA not implemented\n");
179 cdbdb648 pbrook
        break;
180 cdbdb648 pbrook
    default:
181 2ac71179 Paul Brook
        hw_error("pl011_write: Bad offset %x\n", (int)offset);
182 cdbdb648 pbrook
    }
183 cdbdb648 pbrook
}
184 cdbdb648 pbrook
185 aa1f17c1 ths
static int pl011_can_receive(void *opaque)
186 cdbdb648 pbrook
{
187 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
188 cdbdb648 pbrook
189 cdbdb648 pbrook
    if (s->lcr & 0x10)
190 cdbdb648 pbrook
        return s->read_count < 16;
191 cdbdb648 pbrook
    else
192 cdbdb648 pbrook
        return s->read_count < 1;
193 cdbdb648 pbrook
}
194 cdbdb648 pbrook
195 cc9c9ffc aurel32
static void pl011_put_fifo(void *opaque, uint32_t value)
196 cdbdb648 pbrook
{
197 cdbdb648 pbrook
    pl011_state *s = (pl011_state *)opaque;
198 cdbdb648 pbrook
    int slot;
199 cdbdb648 pbrook
200 cdbdb648 pbrook
    slot = s->read_pos + s->read_count;
201 cdbdb648 pbrook
    if (slot >= 16)
202 cdbdb648 pbrook
        slot -= 16;
203 cc9c9ffc aurel32
    s->read_fifo[slot] = value;
204 cdbdb648 pbrook
    s->read_count++;
205 cdbdb648 pbrook
    s->flags &= ~PL011_FLAG_RXFE;
206 cdbdb648 pbrook
    if (s->cr & 0x10 || s->read_count == 16) {
207 cdbdb648 pbrook
        s->flags |= PL011_FLAG_RXFF;
208 cdbdb648 pbrook
    }
209 cdbdb648 pbrook
    if (s->read_count == s->read_trigger) {
210 cdbdb648 pbrook
        s->int_level |= PL011_INT_RX;
211 cdbdb648 pbrook
        pl011_update(s);
212 cdbdb648 pbrook
    }
213 cdbdb648 pbrook
}
214 cdbdb648 pbrook
215 cc9c9ffc aurel32
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
216 cc9c9ffc aurel32
{
217 cc9c9ffc aurel32
    pl011_put_fifo(opaque, *buf);
218 cc9c9ffc aurel32
}
219 cc9c9ffc aurel32
220 cdbdb648 pbrook
static void pl011_event(void *opaque, int event)
221 cdbdb648 pbrook
{
222 cc9c9ffc aurel32
    if (event == CHR_EVENT_BREAK)
223 cc9c9ffc aurel32
        pl011_put_fifo(opaque, 0x400);
224 cdbdb648 pbrook
}
225 cdbdb648 pbrook
226 d60efc6b Blue Swirl
static CPUReadMemoryFunc * const pl011_readfn[] = {
227 cdbdb648 pbrook
   pl011_read,
228 cdbdb648 pbrook
   pl011_read,
229 cdbdb648 pbrook
   pl011_read
230 cdbdb648 pbrook
};
231 cdbdb648 pbrook
232 d60efc6b Blue Swirl
static CPUWriteMemoryFunc * const pl011_writefn[] = {
233 cdbdb648 pbrook
   pl011_write,
234 cdbdb648 pbrook
   pl011_write,
235 cdbdb648 pbrook
   pl011_write
236 cdbdb648 pbrook
};
237 cdbdb648 pbrook
238 23e39294 pbrook
static void pl011_save(QEMUFile *f, void *opaque)
239 23e39294 pbrook
{
240 23e39294 pbrook
    pl011_state *s = (pl011_state *)opaque;
241 23e39294 pbrook
    int i;
242 23e39294 pbrook
243 23e39294 pbrook
    qemu_put_be32(f, s->readbuff);
244 23e39294 pbrook
    qemu_put_be32(f, s->flags);
245 23e39294 pbrook
    qemu_put_be32(f, s->lcr);
246 23e39294 pbrook
    qemu_put_be32(f, s->cr);
247 23e39294 pbrook
    qemu_put_be32(f, s->dmacr);
248 23e39294 pbrook
    qemu_put_be32(f, s->int_enabled);
249 23e39294 pbrook
    qemu_put_be32(f, s->int_level);
250 23e39294 pbrook
    for (i = 0; i < 16; i++)
251 23e39294 pbrook
        qemu_put_be32(f, s->read_fifo[i]);
252 23e39294 pbrook
    qemu_put_be32(f, s->ilpr);
253 23e39294 pbrook
    qemu_put_be32(f, s->ibrd);
254 23e39294 pbrook
    qemu_put_be32(f, s->fbrd);
255 23e39294 pbrook
    qemu_put_be32(f, s->ifl);
256 23e39294 pbrook
    qemu_put_be32(f, s->read_pos);
257 23e39294 pbrook
    qemu_put_be32(f, s->read_count);
258 23e39294 pbrook
    qemu_put_be32(f, s->read_trigger);
259 23e39294 pbrook
}
260 23e39294 pbrook
261 23e39294 pbrook
static int pl011_load(QEMUFile *f, void *opaque, int version_id)
262 23e39294 pbrook
{
263 23e39294 pbrook
    pl011_state *s = (pl011_state *)opaque;
264 23e39294 pbrook
    int i;
265 23e39294 pbrook
266 23e39294 pbrook
    if (version_id != 1)
267 23e39294 pbrook
        return -EINVAL;
268 23e39294 pbrook
269 23e39294 pbrook
    s->readbuff = qemu_get_be32(f);
270 23e39294 pbrook
    s->flags = qemu_get_be32(f);
271 23e39294 pbrook
    s->lcr = qemu_get_be32(f);
272 23e39294 pbrook
    s->cr = qemu_get_be32(f);
273 23e39294 pbrook
    s->dmacr = qemu_get_be32(f);
274 23e39294 pbrook
    s->int_enabled = qemu_get_be32(f);
275 23e39294 pbrook
    s->int_level = qemu_get_be32(f);
276 23e39294 pbrook
    for (i = 0; i < 16; i++)
277 23e39294 pbrook
        s->read_fifo[i] = qemu_get_be32(f);
278 23e39294 pbrook
    s->ilpr = qemu_get_be32(f);
279 23e39294 pbrook
    s->ibrd = qemu_get_be32(f);
280 23e39294 pbrook
    s->fbrd = qemu_get_be32(f);
281 23e39294 pbrook
    s->ifl = qemu_get_be32(f);
282 23e39294 pbrook
    s->read_pos = qemu_get_be32(f);
283 23e39294 pbrook
    s->read_count = qemu_get_be32(f);
284 23e39294 pbrook
    s->read_trigger = qemu_get_be32(f);
285 23e39294 pbrook
286 23e39294 pbrook
    return 0;
287 23e39294 pbrook
}
288 23e39294 pbrook
289 81a322d4 Gerd Hoffmann
static int pl011_init(SysBusDevice *dev, const unsigned char *id)
290 cdbdb648 pbrook
{
291 cdbdb648 pbrook
    int iomemtype;
292 a7d518a6 Paul Brook
    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
293 cdbdb648 pbrook
294 1eed09cb Avi Kivity
    iomemtype = cpu_register_io_memory(pl011_readfn,
295 2507c12a Alexander Graf
                                       pl011_writefn, s,
296 2507c12a Alexander Graf
                                       DEVICE_NATIVE_ENDIAN);
297 a7d518a6 Paul Brook
    sysbus_init_mmio(dev, 0x1000,iomemtype);
298 a7d518a6 Paul Brook
    sysbus_init_irq(dev, &s->irq);
299 a7d518a6 Paul Brook
    s->id = id;
300 a7d518a6 Paul Brook
    s->chr = qdev_init_chardev(&dev->qdev);
301 a7d518a6 Paul Brook
302 cdbdb648 pbrook
    s->read_trigger = 1;
303 cdbdb648 pbrook
    s->ifl = 0x12;
304 cdbdb648 pbrook
    s->cr = 0x300;
305 cdbdb648 pbrook
    s->flags = 0x90;
306 a7d518a6 Paul Brook
    if (s->chr) {
307 a7d518a6 Paul Brook
        qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
308 e5b0bc44 pbrook
                              pl011_event, s);
309 cdbdb648 pbrook
    }
310 0be71e32 Alex Williamson
    register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s);
311 81a322d4 Gerd Hoffmann
    return 0;
312 cdbdb648 pbrook
}
313 a7d518a6 Paul Brook
314 81a322d4 Gerd Hoffmann
static int pl011_init_arm(SysBusDevice *dev)
315 a7d518a6 Paul Brook
{
316 81a322d4 Gerd Hoffmann
    return pl011_init(dev, pl011_id_arm);
317 a7d518a6 Paul Brook
}
318 a7d518a6 Paul Brook
319 81a322d4 Gerd Hoffmann
static int pl011_init_luminary(SysBusDevice *dev)
320 a7d518a6 Paul Brook
{
321 81a322d4 Gerd Hoffmann
    return pl011_init(dev, pl011_id_luminary);
322 a7d518a6 Paul Brook
}
323 a7d518a6 Paul Brook
324 a7d518a6 Paul Brook
static void pl011_register_devices(void)
325 a7d518a6 Paul Brook
{
326 a7d518a6 Paul Brook
    sysbus_register_dev("pl011", sizeof(pl011_state),
327 a7d518a6 Paul Brook
                        pl011_init_arm);
328 a7d518a6 Paul Brook
    sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
329 a7d518a6 Paul Brook
                        pl011_init_luminary);
330 a7d518a6 Paul Brook
}
331 a7d518a6 Paul Brook
332 a7d518a6 Paul Brook
device_init(pl011_register_devices)