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