Statistics
| Branch: | Revision:

root / hw / pl011.c @ 9d926598

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