]> git.proxmox.com Git - qemu.git/blame - hw/pl011.c
tcg-sparc: Use TCG_TARGET_REG_BITS in conditional compilation.
[qemu.git] / hw / pl011.c
CommitLineData
5fafdf24 1/*
cdbdb648
PB
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
a7d518a6 10#include "sysbus.h"
87ecb68b 11#include "qemu-char.h"
cdbdb648
PB
12
13typedef struct {
a7d518a6 14 SysBusDevice busdev;
cdbdb648
PB
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;
d537cf6c 31 qemu_irq irq;
a7d518a6 32 const unsigned char *id;
cdbdb648
PB
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
a7d518a6
PB
43static const unsigned char pl011_id_arm[8] =
44 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
45static const unsigned char pl011_id_luminary[8] =
46 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
cdbdb648
PB
47
48static void pl011_update(pl011_state *s)
49{
50 uint32_t flags;
3b46e624 51
cdbdb648 52 flags = s->int_level & s->int_enabled;
d537cf6c 53 qemu_set_irq(s->irq, flags != 0);
cdbdb648
PB
54}
55
c227f099 56static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
cdbdb648
PB
57{
58 pl011_state *s = (pl011_state *)opaque;
59 uint32_t c;
60
cdbdb648 61 if (offset >= 0xfe0 && offset < 0x1000) {
a7d518a6 62 return s->id[(offset - 0xfe0) >> 2];
cdbdb648
PB
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);
bd9bdce6 79 qemu_chr_accept_input(s->chr);
cdbdb648
PB
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:
2ac71179 106 hw_error("pl011_read: Bad offset %x\n", (int)offset);
cdbdb648
PB
107 return 0;
108 }
109}
110
111static 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
c227f099 125static void pl011_write(void *opaque, target_phys_addr_t offset,
cdbdb648
PB
126 uint32_t value)
127{
128 pl011_state *s = (pl011_state *)opaque;
129 unsigned char ch;
130
cdbdb648
PB
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;
9ee6e8bb
PB
143 case 6: /* UARTFR */
144 /* Writes to Flag register are ignored. */
145 break;
cdbdb648
PB
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)
2ac71179 178 hw_error("PL011: DMA not implemented\n");
cdbdb648
PB
179 break;
180 default:
2ac71179 181 hw_error("pl011_write: Bad offset %x\n", (int)offset);
cdbdb648
PB
182 }
183}
184
aa1f17c1 185static int pl011_can_receive(void *opaque)
cdbdb648
PB
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
cc9c9ffc 195static void pl011_put_fifo(void *opaque, uint32_t value)
cdbdb648
PB
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;
cc9c9ffc 203 s->read_fifo[slot] = value;
cdbdb648
PB
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
cc9c9ffc
AJ
215static void pl011_receive(void *opaque, const uint8_t *buf, int size)
216{
217 pl011_put_fifo(opaque, *buf);
218}
219
cdbdb648
PB
220static void pl011_event(void *opaque, int event)
221{
cc9c9ffc
AJ
222 if (event == CHR_EVENT_BREAK)
223 pl011_put_fifo(opaque, 0x400);
cdbdb648
PB
224}
225
d60efc6b 226static CPUReadMemoryFunc * const pl011_readfn[] = {
cdbdb648
PB
227 pl011_read,
228 pl011_read,
229 pl011_read
230};
231
d60efc6b 232static CPUWriteMemoryFunc * const pl011_writefn[] = {
cdbdb648
PB
233 pl011_write,
234 pl011_write,
235 pl011_write
236};
237
23e39294
PB
238static void pl011_save(QEMUFile *f, void *opaque)
239{
240 pl011_state *s = (pl011_state *)opaque;
241 int i;
242
243 qemu_put_be32(f, s->readbuff);
244 qemu_put_be32(f, s->flags);
245 qemu_put_be32(f, s->lcr);
246 qemu_put_be32(f, s->cr);
247 qemu_put_be32(f, s->dmacr);
248 qemu_put_be32(f, s->int_enabled);
249 qemu_put_be32(f, s->int_level);
250 for (i = 0; i < 16; i++)
251 qemu_put_be32(f, s->read_fifo[i]);
252 qemu_put_be32(f, s->ilpr);
253 qemu_put_be32(f, s->ibrd);
254 qemu_put_be32(f, s->fbrd);
255 qemu_put_be32(f, s->ifl);
256 qemu_put_be32(f, s->read_pos);
257 qemu_put_be32(f, s->read_count);
258 qemu_put_be32(f, s->read_trigger);
259}
260
261static int pl011_load(QEMUFile *f, void *opaque, int version_id)
262{
263 pl011_state *s = (pl011_state *)opaque;
264 int i;
265
266 if (version_id != 1)
267 return -EINVAL;
268
269 s->readbuff = qemu_get_be32(f);
270 s->flags = qemu_get_be32(f);
271 s->lcr = qemu_get_be32(f);
272 s->cr = qemu_get_be32(f);
273 s->dmacr = qemu_get_be32(f);
274 s->int_enabled = qemu_get_be32(f);
275 s->int_level = qemu_get_be32(f);
276 for (i = 0; i < 16; i++)
277 s->read_fifo[i] = qemu_get_be32(f);
278 s->ilpr = qemu_get_be32(f);
279 s->ibrd = qemu_get_be32(f);
280 s->fbrd = qemu_get_be32(f);
281 s->ifl = qemu_get_be32(f);
282 s->read_pos = qemu_get_be32(f);
283 s->read_count = qemu_get_be32(f);
284 s->read_trigger = qemu_get_be32(f);
285
286 return 0;
287}
288
81a322d4 289static int pl011_init(SysBusDevice *dev, const unsigned char *id)
cdbdb648
PB
290{
291 int iomemtype;
a7d518a6 292 pl011_state *s = FROM_SYSBUS(pl011_state, dev);
cdbdb648 293
1eed09cb 294 iomemtype = cpu_register_io_memory(pl011_readfn,
cdbdb648 295 pl011_writefn, s);
a7d518a6
PB
296 sysbus_init_mmio(dev, 0x1000,iomemtype);
297 sysbus_init_irq(dev, &s->irq);
298 s->id = id;
299 s->chr = qdev_init_chardev(&dev->qdev);
300
cdbdb648
PB
301 s->read_trigger = 1;
302 s->ifl = 0x12;
303 s->cr = 0x300;
304 s->flags = 0x90;
a7d518a6
PB
305 if (s->chr) {
306 qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
e5b0bc44 307 pl011_event, s);
cdbdb648 308 }
23e39294 309 register_savevm("pl011_uart", -1, 1, pl011_save, pl011_load, s);
81a322d4 310 return 0;
cdbdb648 311}
a7d518a6 312
81a322d4 313static int pl011_init_arm(SysBusDevice *dev)
a7d518a6 314{
81a322d4 315 return pl011_init(dev, pl011_id_arm);
a7d518a6
PB
316}
317
81a322d4 318static int pl011_init_luminary(SysBusDevice *dev)
a7d518a6 319{
81a322d4 320 return pl011_init(dev, pl011_id_luminary);
a7d518a6
PB
321}
322
323static void pl011_register_devices(void)
324{
325 sysbus_register_dev("pl011", sizeof(pl011_state),
326 pl011_init_arm);
327 sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
328 pl011_init_luminary);
329}
330
331device_init(pl011_register_devices)