blob: a2da375528c1a69083679408eafe63215514744b [file] [log] [blame]
ths5fafdf22007-09-16 21:08:06 +00001/*
pbrookcdbdb642006-04-09 01:32:52 +00002 * Arm PrimeCell PL011 UART
3 *
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
6 *
Matthew Fernandez8e31bf32011-06-26 12:21:35 +10007 * This code is licensed under the GPL.
pbrookcdbdb642006-04-09 01:32:52 +00008 */
9
Peter Maydella3c1ca52019-02-21 18:17:46 +000010/*
11 * QEMU interface:
12 * + sysbus MMIO region 0: device registers
13 * + sysbus IRQ 0: UARTINTR (combined interrupt line)
14 * + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line)
15 * + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line)
16 * + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line)
17 * + sysbus IRQ 4: UARTMSINTR (momem status interrupt line)
18 * + sysbus IRQ 5: UARTEINTR (error interrupt line)
19 */
20
Peter Maydell8ef94f02016-01-26 18:17:05 +000021#include "qemu/osdep.h"
Peter Maydell694cf202019-02-21 18:17:46 +000022#include "hw/char/pl011.h"
Markus Armbruster64552b62019-08-12 07:23:42 +020023#include "hw/irq.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010024#include "hw/sysbus.h"
Marc-André Lureau4d43a602017-01-26 18:26:44 +040025#include "chardev/char-fe.h"
Paolo Bonzini03dd0242015-12-15 13:16:16 +010026#include "qemu/log.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020027#include "qemu/module.h"
Peter Maydell041ac052016-10-12 18:54:36 +010028#include "trace.h"
pbrookcdbdb642006-04-09 01:32:52 +000029
pbrookcdbdb642006-04-09 01:32:52 +000030#define PL011_INT_TX 0x20
31#define PL011_INT_RX 0x10
32
33#define PL011_FLAG_TXFE 0x80
34#define PL011_FLAG_RXFF 0x40
35#define PL011_FLAG_TXFF 0x20
36#define PL011_FLAG_RXFE 0x10
37
Peter Maydella3c1ca52019-02-21 18:17:46 +000038/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
39#define INT_OE (1 << 10)
40#define INT_BE (1 << 9)
41#define INT_PE (1 << 8)
42#define INT_FE (1 << 7)
43#define INT_RT (1 << 6)
44#define INT_TX (1 << 5)
45#define INT_RX (1 << 4)
46#define INT_DSR (1 << 3)
47#define INT_DCD (1 << 2)
48#define INT_CTS (1 << 1)
49#define INT_RI (1 << 0)
50#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
51#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
52
Paul Brooka7d518a2009-05-14 22:35:07 +010053static const unsigned char pl011_id_arm[8] =
54 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
55static const unsigned char pl011_id_luminary[8] =
56 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
pbrookcdbdb642006-04-09 01:32:52 +000057
Peter Maydella3c1ca52019-02-21 18:17:46 +000058/* Which bits in the interrupt status matter for each outbound IRQ line ? */
59static const uint32_t irqmask[] = {
60 INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
61 INT_RX,
62 INT_TX,
63 INT_RT,
64 INT_MS,
65 INT_E,
66};
67
Andreas Färberab640bf2013-07-24 23:13:57 +020068static void pl011_update(PL011State *s)
pbrookcdbdb642006-04-09 01:32:52 +000069{
70 uint32_t flags;
Peter Maydella3c1ca52019-02-21 18:17:46 +000071 int i;
ths3b46e622007-09-17 08:09:54 +000072
pbrookcdbdb642006-04-09 01:32:52 +000073 flags = s->int_level & s->int_enabled;
Peter Maydell041ac052016-10-12 18:54:36 +010074 trace_pl011_irq_state(flags != 0);
Peter Maydella3c1ca52019-02-21 18:17:46 +000075 for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
76 qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
77 }
pbrookcdbdb642006-04-09 01:32:52 +000078}
79
Avi Kivitya8170e52012-10-23 12:30:10 +020080static uint64_t pl011_read(void *opaque, hwaddr offset,
Avi Kivity48484752011-10-10 17:08:49 +020081 unsigned size)
pbrookcdbdb642006-04-09 01:32:52 +000082{
Andreas Färberab640bf2013-07-24 23:13:57 +020083 PL011State *s = (PL011State *)opaque;
pbrookcdbdb642006-04-09 01:32:52 +000084 uint32_t c;
Peter Maydell041ac052016-10-12 18:54:36 +010085 uint64_t r;
pbrookcdbdb642006-04-09 01:32:52 +000086
pbrookcdbdb642006-04-09 01:32:52 +000087 switch (offset >> 2) {
88 case 0: /* UARTDR */
89 s->flags &= ~PL011_FLAG_RXFF;
90 c = s->read_fifo[s->read_pos];
91 if (s->read_count > 0) {
92 s->read_count--;
93 if (++s->read_pos == 16)
94 s->read_pos = 0;
95 }
96 if (s->read_count == 0) {
97 s->flags |= PL011_FLAG_RXFE;
98 }
99 if (s->read_count == s->read_trigger - 1)
100 s->int_level &= ~ PL011_INT_RX;
Peter Maydell041ac052016-10-12 18:54:36 +0100101 trace_pl011_read_fifo(s->read_count);
Rob Herringce8f0902014-03-18 13:18:40 -0500102 s->rsr = c >> 8;
pbrookcdbdb642006-04-09 01:32:52 +0000103 pl011_update(s);
Marc-André Lureaufa394ed2016-10-22 12:52:59 +0300104 qemu_chr_fe_accept_input(&s->chr);
Peter Maydell041ac052016-10-12 18:54:36 +0100105 r = c;
106 break;
Rob Herringce8f0902014-03-18 13:18:40 -0500107 case 1: /* UARTRSR */
Peter Maydell041ac052016-10-12 18:54:36 +0100108 r = s->rsr;
109 break;
pbrookcdbdb642006-04-09 01:32:52 +0000110 case 6: /* UARTFR */
Peter Maydell041ac052016-10-12 18:54:36 +0100111 r = s->flags;
112 break;
pbrookcdbdb642006-04-09 01:32:52 +0000113 case 8: /* UARTILPR */
Peter Maydell041ac052016-10-12 18:54:36 +0100114 r = s->ilpr;
115 break;
pbrookcdbdb642006-04-09 01:32:52 +0000116 case 9: /* UARTIBRD */
Peter Maydell041ac052016-10-12 18:54:36 +0100117 r = s->ibrd;
118 break;
pbrookcdbdb642006-04-09 01:32:52 +0000119 case 10: /* UARTFBRD */
Peter Maydell041ac052016-10-12 18:54:36 +0100120 r = s->fbrd;
121 break;
pbrookcdbdb642006-04-09 01:32:52 +0000122 case 11: /* UARTLCR_H */
Peter Maydell041ac052016-10-12 18:54:36 +0100123 r = s->lcr;
124 break;
pbrookcdbdb642006-04-09 01:32:52 +0000125 case 12: /* UARTCR */
Peter Maydell041ac052016-10-12 18:54:36 +0100126 r = s->cr;
127 break;
pbrookcdbdb642006-04-09 01:32:52 +0000128 case 13: /* UARTIFLS */
Peter Maydell041ac052016-10-12 18:54:36 +0100129 r = s->ifl;
130 break;
pbrookcdbdb642006-04-09 01:32:52 +0000131 case 14: /* UARTIMSC */
Peter Maydell041ac052016-10-12 18:54:36 +0100132 r = s->int_enabled;
133 break;
pbrookcdbdb642006-04-09 01:32:52 +0000134 case 15: /* UARTRIS */
Peter Maydell041ac052016-10-12 18:54:36 +0100135 r = s->int_level;
136 break;
pbrookcdbdb642006-04-09 01:32:52 +0000137 case 16: /* UARTMIS */
Peter Maydell041ac052016-10-12 18:54:36 +0100138 r = s->int_level & s->int_enabled;
139 break;
pbrookcdbdb642006-04-09 01:32:52 +0000140 case 18: /* UARTDMACR */
Peter Maydell041ac052016-10-12 18:54:36 +0100141 r = s->dmacr;
142 break;
143 case 0x3f8 ... 0x400:
144 r = s->id[(offset - 0xfe0) >> 2];
145 break;
pbrookcdbdb642006-04-09 01:32:52 +0000146 default:
Peter Maydell6d5433e2012-10-18 14:11:40 +0100147 qemu_log_mask(LOG_GUEST_ERROR,
Peter Maydell76b09fa2019-02-21 18:17:46 +0000148 "pl011_read: Bad offset 0x%x\n", (int)offset);
Peter Maydell041ac052016-10-12 18:54:36 +0100149 r = 0;
150 break;
pbrookcdbdb642006-04-09 01:32:52 +0000151 }
Peter Maydell041ac052016-10-12 18:54:36 +0100152
153 trace_pl011_read(offset, r);
154 return r;
pbrookcdbdb642006-04-09 01:32:52 +0000155}
156
Andreas Färberab640bf2013-07-24 23:13:57 +0200157static void pl011_set_read_trigger(PL011State *s)
pbrookcdbdb642006-04-09 01:32:52 +0000158{
159#if 0
160 /* The docs say the RX interrupt is triggered when the FIFO exceeds
161 the threshold. However linux only reads the FIFO in response to an
162 interrupt. Triggering the interrupt when the FIFO is non-empty seems
163 to make things work. */
164 if (s->lcr & 0x10)
165 s->read_trigger = (s->ifl >> 1) & 0x1c;
166 else
167#endif
168 s->read_trigger = 1;
169}
170
Avi Kivitya8170e52012-10-23 12:30:10 +0200171static void pl011_write(void *opaque, hwaddr offset,
Avi Kivity48484752011-10-10 17:08:49 +0200172 uint64_t value, unsigned size)
pbrookcdbdb642006-04-09 01:32:52 +0000173{
Andreas Färberab640bf2013-07-24 23:13:57 +0200174 PL011State *s = (PL011State *)opaque;
pbrookcdbdb642006-04-09 01:32:52 +0000175 unsigned char ch;
176
Peter Maydell041ac052016-10-12 18:54:36 +0100177 trace_pl011_write(offset, value);
178
pbrookcdbdb642006-04-09 01:32:52 +0000179 switch (offset >> 2) {
180 case 0: /* UARTDR */
181 /* ??? Check if transmitter is enabled. */
182 ch = value;
Marc-André Lureaufa394ed2016-10-22 12:52:59 +0300183 /* XXX this blocks entire thread. Rewrite to use
184 * qemu_chr_fe_write and background I/O callbacks */
185 qemu_chr_fe_write_all(&s->chr, &ch, 1);
pbrookcdbdb642006-04-09 01:32:52 +0000186 s->int_level |= PL011_INT_TX;
187 pl011_update(s);
188 break;
Rob Herringce8f0902014-03-18 13:18:40 -0500189 case 1: /* UARTRSR/UARTECR */
190 s->rsr = 0;
pbrookcdbdb642006-04-09 01:32:52 +0000191 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000192 case 6: /* UARTFR */
193 /* Writes to Flag register are ignored. */
194 break;
pbrookcdbdb642006-04-09 01:32:52 +0000195 case 8: /* UARTUARTILPR */
196 s->ilpr = value;
197 break;
198 case 9: /* UARTIBRD */
199 s->ibrd = value;
200 break;
201 case 10: /* UARTFBRD */
202 s->fbrd = value;
203 break;
204 case 11: /* UARTLCR_H */
Rob Herring22709e92014-03-18 13:18:39 -0500205 /* Reset the FIFO state on FIFO enable or disable */
206 if ((s->lcr ^ value) & 0x10) {
207 s->read_count = 0;
208 s->read_pos = 0;
209 }
pbrookcdbdb642006-04-09 01:32:52 +0000210 s->lcr = value;
211 pl011_set_read_trigger(s);
212 break;
213 case 12: /* UARTCR */
214 /* ??? Need to implement the enable and loopback bits. */
215 s->cr = value;
216 break;
217 case 13: /* UARTIFS */
218 s->ifl = value;
219 pl011_set_read_trigger(s);
220 break;
221 case 14: /* UARTIMSC */
222 s->int_enabled = value;
223 pl011_update(s);
224 break;
225 case 17: /* UARTICR */
226 s->int_level &= ~value;
227 pl011_update(s);
228 break;
229 case 18: /* UARTDMACR */
230 s->dmacr = value;
Peter Maydell6d5433e2012-10-18 14:11:40 +0100231 if (value & 3) {
232 qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
233 }
pbrookcdbdb642006-04-09 01:32:52 +0000234 break;
235 default:
Peter Maydell6d5433e2012-10-18 14:11:40 +0100236 qemu_log_mask(LOG_GUEST_ERROR,
Peter Maydell76b09fa2019-02-21 18:17:46 +0000237 "pl011_write: Bad offset 0x%x\n", (int)offset);
pbrookcdbdb642006-04-09 01:32:52 +0000238 }
239}
240
thsaa1f17c2007-07-11 22:48:58 +0000241static int pl011_can_receive(void *opaque)
pbrookcdbdb642006-04-09 01:32:52 +0000242{
Andreas Färberab640bf2013-07-24 23:13:57 +0200243 PL011State *s = (PL011State *)opaque;
Peter Maydell041ac052016-10-12 18:54:36 +0100244 int r;
pbrookcdbdb642006-04-09 01:32:52 +0000245
Peter Maydell041ac052016-10-12 18:54:36 +0100246 if (s->lcr & 0x10) {
247 r = s->read_count < 16;
248 } else {
249 r = s->read_count < 1;
250 }
251 trace_pl011_can_receive(s->lcr, s->read_count, r);
252 return r;
pbrookcdbdb642006-04-09 01:32:52 +0000253}
254
aurel32cc9c9ff2008-04-08 19:51:43 +0000255static void pl011_put_fifo(void *opaque, uint32_t value)
pbrookcdbdb642006-04-09 01:32:52 +0000256{
Andreas Färberab640bf2013-07-24 23:13:57 +0200257 PL011State *s = (PL011State *)opaque;
pbrookcdbdb642006-04-09 01:32:52 +0000258 int slot;
259
260 slot = s->read_pos + s->read_count;
261 if (slot >= 16)
262 slot -= 16;
aurel32cc9c9ff2008-04-08 19:51:43 +0000263 s->read_fifo[slot] = value;
pbrookcdbdb642006-04-09 01:32:52 +0000264 s->read_count++;
265 s->flags &= ~PL011_FLAG_RXFE;
Peter Maydell041ac052016-10-12 18:54:36 +0100266 trace_pl011_put_fifo(value, s->read_count);
Rob Herringf72dbf32014-03-18 13:18:41 -0500267 if (!(s->lcr & 0x10) || s->read_count == 16) {
Peter Maydell041ac052016-10-12 18:54:36 +0100268 trace_pl011_put_fifo_full();
pbrookcdbdb642006-04-09 01:32:52 +0000269 s->flags |= PL011_FLAG_RXFF;
270 }
271 if (s->read_count == s->read_trigger) {
272 s->int_level |= PL011_INT_RX;
273 pl011_update(s);
274 }
275}
276
aurel32cc9c9ff2008-04-08 19:51:43 +0000277static void pl011_receive(void *opaque, const uint8_t *buf, int size)
278{
279 pl011_put_fifo(opaque, *buf);
280}
281
pbrookcdbdb642006-04-09 01:32:52 +0000282static void pl011_event(void *opaque, int event)
283{
aurel32cc9c9ff2008-04-08 19:51:43 +0000284 if (event == CHR_EVENT_BREAK)
285 pl011_put_fifo(opaque, 0x400);
pbrookcdbdb642006-04-09 01:32:52 +0000286}
287
Avi Kivity48484752011-10-10 17:08:49 +0200288static const MemoryRegionOps pl011_ops = {
289 .read = pl011_read,
290 .write = pl011_write,
291 .endianness = DEVICE_NATIVE_ENDIAN,
pbrookcdbdb642006-04-09 01:32:52 +0000292};
293
Juan Quintela02b68752010-12-02 01:50:33 +0100294static const VMStateDescription vmstate_pl011 = {
295 .name = "pl011",
Rob Herringce8f0902014-03-18 13:18:40 -0500296 .version_id = 2,
297 .minimum_version_id = 2,
Juan Quintela8f1e8842014-05-13 16:09:35 +0100298 .fields = (VMStateField[]) {
Andreas Färberab640bf2013-07-24 23:13:57 +0200299 VMSTATE_UINT32(readbuff, PL011State),
300 VMSTATE_UINT32(flags, PL011State),
301 VMSTATE_UINT32(lcr, PL011State),
Rob Herringce8f0902014-03-18 13:18:40 -0500302 VMSTATE_UINT32(rsr, PL011State),
Andreas Färberab640bf2013-07-24 23:13:57 +0200303 VMSTATE_UINT32(cr, PL011State),
304 VMSTATE_UINT32(dmacr, PL011State),
305 VMSTATE_UINT32(int_enabled, PL011State),
306 VMSTATE_UINT32(int_level, PL011State),
307 VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16),
308 VMSTATE_UINT32(ilpr, PL011State),
309 VMSTATE_UINT32(ibrd, PL011State),
310 VMSTATE_UINT32(fbrd, PL011State),
311 VMSTATE_UINT32(ifl, PL011State),
312 VMSTATE_INT32(read_pos, PL011State),
313 VMSTATE_INT32(read_count, PL011State),
314 VMSTATE_INT32(read_trigger, PL011State),
Juan Quintela02b68752010-12-02 01:50:33 +0100315 VMSTATE_END_OF_LIST()
316 }
317};
pbrook23e39292008-07-02 16:48:32 +0000318
xiaoqiang zhaof0d1d2c2016-06-06 16:59:31 +0100319static Property pl011_properties[] = {
320 DEFINE_PROP_CHR("chardev", PL011State, chr),
321 DEFINE_PROP_END_OF_LIST(),
322};
323
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200324static void pl011_init(Object *obj)
pbrookcdbdb642006-04-09 01:32:52 +0000325{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200326 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
327 PL011State *s = PL011(obj);
Peter Maydella3c1ca52019-02-21 18:17:46 +0000328 int i;
pbrookcdbdb642006-04-09 01:32:52 +0000329
Paolo Bonzini300b1fc2013-06-06 21:25:08 -0400330 memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200331 sysbus_init_mmio(sbd, &s->iomem);
Peter Maydella3c1ca52019-02-21 18:17:46 +0000332 for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
333 sysbus_init_irq(sbd, &s->irq[i]);
334 }
Paul Brooka7d518a2009-05-14 22:35:07 +0100335
pbrookcdbdb642006-04-09 01:32:52 +0000336 s->read_trigger = 1;
337 s->ifl = 0x12;
338 s->cr = 0x300;
339 s->flags = 0x90;
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200340
341 s->id = pl011_id_arm;
342}
343
344static void pl011_realize(DeviceState *dev, Error **errp)
345{
346 PL011State *s = PL011(dev);
347
Marc-André Lureaufa394ed2016-10-22 12:52:59 +0300348 qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
Anton Nefedov81517ba2017-07-06 15:08:49 +0300349 pl011_event, NULL, s, NULL, true);
pbrookcdbdb642006-04-09 01:32:52 +0000350}
Paul Brooka7d518a2009-05-14 22:35:07 +0100351
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200352static void pl011_class_init(ObjectClass *oc, void *data)
Paul Brooka7d518a2009-05-14 22:35:07 +0100353{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200354 DeviceClass *dc = DEVICE_CLASS(oc);
Paul Brooka7d518a2009-05-14 22:35:07 +0100355
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200356 dc->realize = pl011_realize;
357 dc->vmsd = &vmstate_pl011;
xiaoqiang zhaof0d1d2c2016-06-06 16:59:31 +0100358 dc->props = pl011_properties;
Anthony Liguori999e12b2012-01-24 13:12:29 -0600359}
360
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100361static const TypeInfo pl011_arm_info = {
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200362 .name = TYPE_PL011,
Anthony Liguori39bffca2011-12-07 21:34:16 -0600363 .parent = TYPE_SYS_BUS_DEVICE,
Andreas Färberab640bf2013-07-24 23:13:57 +0200364 .instance_size = sizeof(PL011State),
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200365 .instance_init = pl011_init,
366 .class_init = pl011_class_init,
Anthony Liguori999e12b2012-01-24 13:12:29 -0600367};
368
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200369static void pl011_luminary_init(Object *obj)
Anthony Liguori999e12b2012-01-24 13:12:29 -0600370{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200371 PL011State *s = PL011(obj);
Anthony Liguori999e12b2012-01-24 13:12:29 -0600372
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200373 s->id = pl011_id_luminary;
Anthony Liguori999e12b2012-01-24 13:12:29 -0600374}
375
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100376static const TypeInfo pl011_luminary_info = {
Peter Maydell694cf202019-02-21 18:17:46 +0000377 .name = TYPE_PL011_LUMINARY,
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200378 .parent = TYPE_PL011,
379 .instance_init = pl011_luminary_init,
Anthony Liguori999e12b2012-01-24 13:12:29 -0600380};
381
Andreas Färber83f7d432012-02-09 15:20:55 +0100382static void pl011_register_types(void)
Paul Brooka7d518a2009-05-14 22:35:07 +0100383{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600384 type_register_static(&pl011_arm_info);
385 type_register_static(&pl011_luminary_info);
Paul Brooka7d518a2009-05-14 22:35:07 +0100386}
387
Andreas Färber83f7d432012-02-09 15:20:55 +0100388type_init(pl011_register_types)