]> git.proxmox.com Git - qemu.git/blame - hw/pl011.c
Merge remote-tracking branch 'qemu-kvm/uq/master' into staging
[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 *
8e31bf38 7 * This code is licensed under the GPL.
cdbdb648
PB
8 */
9
a7d518a6 10#include "sysbus.h"
87ecb68b 11#include "qemu-char.h"
cdbdb648
PB
12
13typedef struct {
a7d518a6 14 SysBusDevice busdev;
48484757 15 MemoryRegion iomem;
cdbdb648
PB
16 uint32_t readbuff;
17 uint32_t flags;
18 uint32_t lcr;
19 uint32_t cr;
20 uint32_t dmacr;
21 uint32_t int_enabled;
22 uint32_t int_level;
23 uint32_t read_fifo[16];
24 uint32_t ilpr;
25 uint32_t ibrd;
26 uint32_t fbrd;
27 uint32_t ifl;
28 int read_pos;
29 int read_count;
30 int read_trigger;
31 CharDriverState *chr;
d537cf6c 32 qemu_irq irq;
a7d518a6 33 const unsigned char *id;
cdbdb648
PB
34} pl011_state;
35
36#define PL011_INT_TX 0x20
37#define PL011_INT_RX 0x10
38
39#define PL011_FLAG_TXFE 0x80
40#define PL011_FLAG_RXFF 0x40
41#define PL011_FLAG_TXFF 0x20
42#define PL011_FLAG_RXFE 0x10
43
a7d518a6
PB
44static const unsigned char pl011_id_arm[8] =
45 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
46static const unsigned char pl011_id_luminary[8] =
47 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
cdbdb648
PB
48
49static void pl011_update(pl011_state *s)
50{
51 uint32_t flags;
3b46e624 52
cdbdb648 53 flags = s->int_level & s->int_enabled;
d537cf6c 54 qemu_set_irq(s->irq, flags != 0);
cdbdb648
PB
55}
56
48484757
AK
57static uint64_t pl011_read(void *opaque, target_phys_addr_t offset,
58 unsigned size)
cdbdb648
PB
59{
60 pl011_state *s = (pl011_state *)opaque;
61 uint32_t c;
62
cdbdb648 63 if (offset >= 0xfe0 && offset < 0x1000) {
a7d518a6 64 return s->id[(offset - 0xfe0) >> 2];
cdbdb648
PB
65 }
66 switch (offset >> 2) {
67 case 0: /* UARTDR */
68 s->flags &= ~PL011_FLAG_RXFF;
69 c = s->read_fifo[s->read_pos];
70 if (s->read_count > 0) {
71 s->read_count--;
72 if (++s->read_pos == 16)
73 s->read_pos = 0;
74 }
75 if (s->read_count == 0) {
76 s->flags |= PL011_FLAG_RXFE;
77 }
78 if (s->read_count == s->read_trigger - 1)
79 s->int_level &= ~ PL011_INT_RX;
80 pl011_update(s);
bd9bdce6 81 qemu_chr_accept_input(s->chr);
cdbdb648
PB
82 return c;
83 case 1: /* UARTCR */
84 return 0;
85 case 6: /* UARTFR */
86 return s->flags;
87 case 8: /* UARTILPR */
88 return s->ilpr;
89 case 9: /* UARTIBRD */
90 return s->ibrd;
91 case 10: /* UARTFBRD */
92 return s->fbrd;
93 case 11: /* UARTLCR_H */
94 return s->lcr;
95 case 12: /* UARTCR */
96 return s->cr;
97 case 13: /* UARTIFLS */
98 return s->ifl;
99 case 14: /* UARTIMSC */
100 return s->int_enabled;
101 case 15: /* UARTRIS */
102 return s->int_level;
103 case 16: /* UARTMIS */
104 return s->int_level & s->int_enabled;
105 case 18: /* UARTDMACR */
106 return s->dmacr;
107 default:
2ac71179 108 hw_error("pl011_read: Bad offset %x\n", (int)offset);
cdbdb648
PB
109 return 0;
110 }
111}
112
113static void pl011_set_read_trigger(pl011_state *s)
114{
115#if 0
116 /* The docs say the RX interrupt is triggered when the FIFO exceeds
117 the threshold. However linux only reads the FIFO in response to an
118 interrupt. Triggering the interrupt when the FIFO is non-empty seems
119 to make things work. */
120 if (s->lcr & 0x10)
121 s->read_trigger = (s->ifl >> 1) & 0x1c;
122 else
123#endif
124 s->read_trigger = 1;
125}
126
c227f099 127static void pl011_write(void *opaque, target_phys_addr_t offset,
48484757 128 uint64_t value, unsigned size)
cdbdb648
PB
129{
130 pl011_state *s = (pl011_state *)opaque;
131 unsigned char ch;
132
cdbdb648
PB
133 switch (offset >> 2) {
134 case 0: /* UARTDR */
135 /* ??? Check if transmitter is enabled. */
136 ch = value;
137 if (s->chr)
2cc6e0a1 138 qemu_chr_fe_write(s->chr, &ch, 1);
cdbdb648
PB
139 s->int_level |= PL011_INT_TX;
140 pl011_update(s);
141 break;
142 case 1: /* UARTCR */
143 s->cr = value;
144 break;
9ee6e8bb
PB
145 case 6: /* UARTFR */
146 /* Writes to Flag register are ignored. */
147 break;
cdbdb648
PB
148 case 8: /* UARTUARTILPR */
149 s->ilpr = value;
150 break;
151 case 9: /* UARTIBRD */
152 s->ibrd = value;
153 break;
154 case 10: /* UARTFBRD */
155 s->fbrd = value;
156 break;
157 case 11: /* UARTLCR_H */
158 s->lcr = value;
159 pl011_set_read_trigger(s);
160 break;
161 case 12: /* UARTCR */
162 /* ??? Need to implement the enable and loopback bits. */
163 s->cr = value;
164 break;
165 case 13: /* UARTIFS */
166 s->ifl = value;
167 pl011_set_read_trigger(s);
168 break;
169 case 14: /* UARTIMSC */
170 s->int_enabled = value;
171 pl011_update(s);
172 break;
173 case 17: /* UARTICR */
174 s->int_level &= ~value;
175 pl011_update(s);
176 break;
177 case 18: /* UARTDMACR */
178 s->dmacr = value;
179 if (value & 3)
2ac71179 180 hw_error("PL011: DMA not implemented\n");
cdbdb648
PB
181 break;
182 default:
2ac71179 183 hw_error("pl011_write: Bad offset %x\n", (int)offset);
cdbdb648
PB
184 }
185}
186
aa1f17c1 187static int pl011_can_receive(void *opaque)
cdbdb648
PB
188{
189 pl011_state *s = (pl011_state *)opaque;
190
191 if (s->lcr & 0x10)
192 return s->read_count < 16;
193 else
194 return s->read_count < 1;
195}
196
cc9c9ffc 197static void pl011_put_fifo(void *opaque, uint32_t value)
cdbdb648
PB
198{
199 pl011_state *s = (pl011_state *)opaque;
200 int slot;
201
202 slot = s->read_pos + s->read_count;
203 if (slot >= 16)
204 slot -= 16;
cc9c9ffc 205 s->read_fifo[slot] = value;
cdbdb648
PB
206 s->read_count++;
207 s->flags &= ~PL011_FLAG_RXFE;
208 if (s->cr & 0x10 || s->read_count == 16) {
209 s->flags |= PL011_FLAG_RXFF;
210 }
211 if (s->read_count == s->read_trigger) {
212 s->int_level |= PL011_INT_RX;
213 pl011_update(s);
214 }
215}
216
cc9c9ffc
AJ
217static void pl011_receive(void *opaque, const uint8_t *buf, int size)
218{
219 pl011_put_fifo(opaque, *buf);
220}
221
cdbdb648
PB
222static void pl011_event(void *opaque, int event)
223{
cc9c9ffc
AJ
224 if (event == CHR_EVENT_BREAK)
225 pl011_put_fifo(opaque, 0x400);
cdbdb648
PB
226}
227
48484757
AK
228static const MemoryRegionOps pl011_ops = {
229 .read = pl011_read,
230 .write = pl011_write,
231 .endianness = DEVICE_NATIVE_ENDIAN,
cdbdb648
PB
232};
233
02b68757
JQ
234static const VMStateDescription vmstate_pl011 = {
235 .name = "pl011",
236 .version_id = 1,
237 .minimum_version_id = 1,
238 .minimum_version_id_old = 1,
239 .fields = (VMStateField[]) {
240 VMSTATE_UINT32(readbuff, pl011_state),
241 VMSTATE_UINT32(flags, pl011_state),
242 VMSTATE_UINT32(lcr, pl011_state),
243 VMSTATE_UINT32(cr, pl011_state),
244 VMSTATE_UINT32(dmacr, pl011_state),
245 VMSTATE_UINT32(int_enabled, pl011_state),
246 VMSTATE_UINT32(int_level, pl011_state),
247 VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
248 VMSTATE_UINT32(ilpr, pl011_state),
249 VMSTATE_UINT32(ibrd, pl011_state),
250 VMSTATE_UINT32(fbrd, pl011_state),
251 VMSTATE_UINT32(ifl, pl011_state),
252 VMSTATE_INT32(read_pos, pl011_state),
253 VMSTATE_INT32(read_count, pl011_state),
254 VMSTATE_INT32(read_trigger, pl011_state),
255 VMSTATE_END_OF_LIST()
256 }
257};
23e39294 258
81a322d4 259static int pl011_init(SysBusDevice *dev, const unsigned char *id)
cdbdb648 260{
a7d518a6 261 pl011_state *s = FROM_SYSBUS(pl011_state, dev);
cdbdb648 262
48484757 263 memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
750ecd44 264 sysbus_init_mmio(dev, &s->iomem);
a7d518a6
PB
265 sysbus_init_irq(dev, &s->irq);
266 s->id = id;
0beb4942 267 s->chr = qemu_char_get_next_serial();
a7d518a6 268
cdbdb648
PB
269 s->read_trigger = 1;
270 s->ifl = 0x12;
271 s->cr = 0x300;
272 s->flags = 0x90;
a7d518a6
PB
273 if (s->chr) {
274 qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
e5b0bc44 275 pl011_event, s);
cdbdb648 276 }
02b68757 277 vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
81a322d4 278 return 0;
cdbdb648 279}
a7d518a6 280
999e12bb 281static int pl011_arm_init(SysBusDevice *dev)
a7d518a6 282{
81a322d4 283 return pl011_init(dev, pl011_id_arm);
a7d518a6
PB
284}
285
999e12bb 286static int pl011_luminary_init(SysBusDevice *dev)
a7d518a6 287{
81a322d4 288 return pl011_init(dev, pl011_id_luminary);
a7d518a6
PB
289}
290
999e12bb
AL
291static void pl011_arm_class_init(ObjectClass *klass, void *data)
292{
293 SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
294
295 sdc->init = pl011_arm_init;
296}
297
39bffca2
AL
298static TypeInfo pl011_arm_info = {
299 .name = "pl011",
300 .parent = TYPE_SYS_BUS_DEVICE,
301 .instance_size = sizeof(pl011_state),
302 .class_init = pl011_arm_class_init,
999e12bb
AL
303};
304
305static void pl011_luminary_class_init(ObjectClass *klass, void *data)
306{
307 SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
308
309 sdc->init = pl011_luminary_init;
310}
311
39bffca2
AL
312static TypeInfo pl011_luminary_info = {
313 .name = "pl011_luminary",
314 .parent = TYPE_SYS_BUS_DEVICE,
315 .instance_size = sizeof(pl011_state),
316 .class_init = pl011_luminary_class_init,
999e12bb
AL
317};
318
83f7d43a 319static void pl011_register_types(void)
a7d518a6 320{
39bffca2
AL
321 type_register_static(&pl011_arm_info);
322 type_register_static(&pl011_luminary_info);
a7d518a6
PB
323}
324
83f7d43a 325type_init(pl011_register_types)