xref: /openbmc/qemu/hw/char/pl011.c (revision 10e3edd9b3b745ca7772a046c06a27ef539fba33)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Arm PrimeCell PL011 UART
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2006 CodeSourcery.
549ab747fSPaolo Bonzini  * Written by Paul Brook
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GPL.
849ab747fSPaolo Bonzini  */
949ab747fSPaolo Bonzini 
10a3c1ca56SPeter Maydell /*
11a3c1ca56SPeter Maydell  * QEMU interface:
12a3c1ca56SPeter Maydell  *  + sysbus MMIO region 0: device registers
13a3c1ca56SPeter Maydell  *  + sysbus IRQ 0: UARTINTR (combined interrupt line)
14a3c1ca56SPeter Maydell  *  + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line)
15a3c1ca56SPeter Maydell  *  + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line)
16a3c1ca56SPeter Maydell  *  + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line)
17a3c1ca56SPeter Maydell  *  + sysbus IRQ 4: UARTMSINTR (momem status interrupt line)
18a3c1ca56SPeter Maydell  *  + sysbus IRQ 5: UARTEINTR (error interrupt line)
19a3c1ca56SPeter Maydell  */
20a3c1ca56SPeter Maydell 
218ef94f0bSPeter Maydell #include "qemu/osdep.h"
2211f2ee1dSPhilippe Mathieu-Daudé #include "qapi/error.h"
23694cf209SPeter Maydell #include "hw/char/pl011.h"
2464552b6bSMarkus Armbruster #include "hw/irq.h"
2549ab747fSPaolo Bonzini #include "hw/sysbus.h"
26aac63e0eSLuc Michel #include "hw/qdev-clock.h"
2711f2ee1dSPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
28ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
29d6454270SMarkus Armbruster #include "migration/vmstate.h"
304d43a603SMarc-André Lureau #include "chardev/char-fe.h"
31d60af909SJan Luebbe #include "chardev/char-serial.h"
3203dd024fSPaolo Bonzini #include "qemu/log.h"
330b8fa32fSMarkus Armbruster #include "qemu/module.h"
34041ac056SPeter Maydell #include "trace.h"
3549ab747fSPaolo Bonzini 
pl011_create(hwaddr addr,qemu_irq irq,Chardev * chr)3611f2ee1dSPhilippe Mathieu-Daudé DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
3711f2ee1dSPhilippe Mathieu-Daudé {
3811f2ee1dSPhilippe Mathieu-Daudé     DeviceState *dev;
3911f2ee1dSPhilippe Mathieu-Daudé     SysBusDevice *s;
4011f2ee1dSPhilippe Mathieu-Daudé 
4111f2ee1dSPhilippe Mathieu-Daudé     dev = qdev_new("pl011");
4211f2ee1dSPhilippe Mathieu-Daudé     s = SYS_BUS_DEVICE(dev);
4311f2ee1dSPhilippe Mathieu-Daudé     qdev_prop_set_chr(dev, "chardev", chr);
4411f2ee1dSPhilippe Mathieu-Daudé     sysbus_realize_and_unref(s, &error_fatal);
4511f2ee1dSPhilippe Mathieu-Daudé     sysbus_mmio_map(s, 0, addr);
4611f2ee1dSPhilippe Mathieu-Daudé     sysbus_connect_irq(s, 0, irq);
4711f2ee1dSPhilippe Mathieu-Daudé 
4811f2ee1dSPhilippe Mathieu-Daudé     return dev;
4911f2ee1dSPhilippe Mathieu-Daudé }
5011f2ee1dSPhilippe Mathieu-Daudé 
5151141cabSPhilippe Mathieu-Daudé /* Flag Register, UARTFR */
52f576e073STong Ho #define PL011_FLAG_RI   0x100
5349ab747fSPaolo Bonzini #define PL011_FLAG_TXFE 0x80
5449ab747fSPaolo Bonzini #define PL011_FLAG_RXFF 0x40
5549ab747fSPaolo Bonzini #define PL011_FLAG_TXFF 0x20
5649ab747fSPaolo Bonzini #define PL011_FLAG_RXFE 0x10
57f576e073STong Ho #define PL011_FLAG_DCD  0x04
58f576e073STong Ho #define PL011_FLAG_DSR  0x02
59f576e073STong Ho #define PL011_FLAG_CTS  0x01
6049ab747fSPaolo Bonzini 
6107738852SPhilippe Mathieu-Daudé /* Data Register, UARTDR */
6207738852SPhilippe Mathieu-Daudé #define DR_BE   (1 << 10)
6307738852SPhilippe Mathieu-Daudé 
64a3c1ca56SPeter Maydell /* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
65a3c1ca56SPeter Maydell #define INT_OE (1 << 10)
66a3c1ca56SPeter Maydell #define INT_BE (1 << 9)
67a3c1ca56SPeter Maydell #define INT_PE (1 << 8)
68a3c1ca56SPeter Maydell #define INT_FE (1 << 7)
69a3c1ca56SPeter Maydell #define INT_RT (1 << 6)
70a3c1ca56SPeter Maydell #define INT_TX (1 << 5)
71a3c1ca56SPeter Maydell #define INT_RX (1 << 4)
72a3c1ca56SPeter Maydell #define INT_DSR (1 << 3)
73a3c1ca56SPeter Maydell #define INT_DCD (1 << 2)
74a3c1ca56SPeter Maydell #define INT_CTS (1 << 1)
75a3c1ca56SPeter Maydell #define INT_RI (1 << 0)
76a3c1ca56SPeter Maydell #define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
77a3c1ca56SPeter Maydell #define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
78a3c1ca56SPeter Maydell 
7907738852SPhilippe Mathieu-Daudé /* Line Control Register, UARTLCR_H */
8007738852SPhilippe Mathieu-Daudé #define LCR_FEN     (1 << 4)
8107738852SPhilippe Mathieu-Daudé #define LCR_BRK     (1 << 0)
8207738852SPhilippe Mathieu-Daudé 
83f576e073STong Ho /* Control Register, UARTCR */
84f576e073STong Ho #define CR_OUT2     (1 << 13)
85f576e073STong Ho #define CR_OUT1     (1 << 12)
86f576e073STong Ho #define CR_RTS      (1 << 11)
87f576e073STong Ho #define CR_DTR      (1 << 10)
88f576e073STong Ho #define CR_TXE      (1 << 8)
89f576e073STong Ho #define CR_LBE      (1 << 7)
90b88cfee9SZheyu Ma #define CR_UARTEN   (1 << 0)
91*10e3edd9SPeter Maydell 
92b88cfee9SZheyu Ma /* Integer Baud Rate Divider, UARTIBRD */
93b88cfee9SZheyu Ma #define IBRD_MASK 0xffff
94*10e3edd9SPeter Maydell 
95b88cfee9SZheyu Ma /* Fractional Baud Rate Divider, UARTFBRD */
9649ab747fSPaolo Bonzini #define FBRD_MASK 0x3f
9749ab747fSPaolo Bonzini 
9849ab747fSPaolo Bonzini static const unsigned char pl011_id_arm[8] =
9949ab747fSPaolo Bonzini   { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
10049ab747fSPaolo Bonzini static const unsigned char pl011_id_luminary[8] =
10151141cabSPhilippe Mathieu-Daudé   { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
10251141cabSPhilippe Mathieu-Daudé 
pl011_regname(hwaddr offset)10351141cabSPhilippe Mathieu-Daudé static const char *pl011_regname(hwaddr offset)
10451141cabSPhilippe Mathieu-Daudé {
10551141cabSPhilippe Mathieu-Daudé     static const char *const rname[] = {
10651141cabSPhilippe Mathieu-Daudé         [0] = "DR", [1] = "RSR", [6] = "FR", [8] = "ILPR", [9] = "IBRD",
10751141cabSPhilippe Mathieu-Daudé         [10] = "FBRD", [11] = "LCRH", [12] = "CR", [13] = "IFLS", [14] = "IMSC",
10851141cabSPhilippe Mathieu-Daudé         [15] = "RIS", [16] = "MIS", [17] = "ICR", [18] = "DMACR",
10951141cabSPhilippe Mathieu-Daudé     };
11051141cabSPhilippe Mathieu-Daudé     unsigned idx = offset >> 2;
11151141cabSPhilippe Mathieu-Daudé 
11251141cabSPhilippe Mathieu-Daudé     if (idx < ARRAY_SIZE(rname) && rname[idx]) {
11351141cabSPhilippe Mathieu-Daudé         return rname[idx];
11451141cabSPhilippe Mathieu-Daudé     }
11551141cabSPhilippe Mathieu-Daudé     if (idx >= 0x3f8 && idx <= 0x400) {
11651141cabSPhilippe Mathieu-Daudé         return "ID";
11751141cabSPhilippe Mathieu-Daudé     }
11851141cabSPhilippe Mathieu-Daudé     return "UNKN";
119a3c1ca56SPeter Maydell }
120a3c1ca56SPeter Maydell 
121a3c1ca56SPeter Maydell /* Which bits in the interrupt status matter for each outbound IRQ line ? */
122a3c1ca56SPeter Maydell static const uint32_t irqmask[] = {
123a3c1ca56SPeter Maydell     INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
124a3c1ca56SPeter Maydell     INT_RX,
125a3c1ca56SPeter Maydell     INT_TX,
126a3c1ca56SPeter Maydell     INT_RT,
127a3c1ca56SPeter Maydell     INT_MS,
128a3c1ca56SPeter Maydell     INT_E,
129ab640bfcSAndreas Färber };
13049ab747fSPaolo Bonzini 
pl011_update(PL011State * s)13149ab747fSPaolo Bonzini static void pl011_update(PL011State *s)
132a3c1ca56SPeter Maydell {
13349ab747fSPaolo Bonzini     uint32_t flags;
13449ab747fSPaolo Bonzini     int i;
135041ac056SPeter Maydell 
136a3c1ca56SPeter Maydell     flags = s->int_level & s->int_enabled;
137a3c1ca56SPeter Maydell     trace_pl011_irq_state(flags != 0);
138a3c1ca56SPeter Maydell     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
13949ab747fSPaolo Bonzini         qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
14049ab747fSPaolo Bonzini     }
1419d88935cSEvgeny Iakovlev }
1429d88935cSEvgeny Iakovlev 
pl011_loopback_enabled(PL011State * s)14307738852SPhilippe Mathieu-Daudé static bool pl011_loopback_enabled(PL011State *s)
1449d88935cSEvgeny Iakovlev {
1459d88935cSEvgeny Iakovlev     return !!(s->cr & CR_LBE);
1469d88935cSEvgeny Iakovlev }
1479d88935cSEvgeny Iakovlev 
pl011_is_fifo_enabled(PL011State * s)1489d88935cSEvgeny Iakovlev static bool pl011_is_fifo_enabled(PL011State *s)
1499d88935cSEvgeny Iakovlev {
1509d88935cSEvgeny Iakovlev     return (s->lcr & LCR_FEN) != 0;
1519d88935cSEvgeny Iakovlev }
15223dcbfc0SEvgeny Iakovlev 
pl011_get_fifo_depth(PL011State * s)15323dcbfc0SEvgeny Iakovlev static inline unsigned pl011_get_fifo_depth(PL011State *s)
15423dcbfc0SEvgeny Iakovlev {
15523dcbfc0SEvgeny Iakovlev     /* Note: FIFO depth is expected to be power-of-2 */
15623dcbfc0SEvgeny Iakovlev     return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1;
15723dcbfc0SEvgeny Iakovlev }
15823dcbfc0SEvgeny Iakovlev 
pl011_reset_rx_fifo(PL011State * s)15923dcbfc0SEvgeny Iakovlev static inline void pl011_reset_rx_fifo(PL011State *s)
16023dcbfc0SEvgeny Iakovlev {
16123dcbfc0SEvgeny Iakovlev     s->read_count = 0;
16249ab747fSPaolo Bonzini     s->read_pos = 0;
16349ab747fSPaolo Bonzini 
16449ab747fSPaolo Bonzini     /* Reset FIFO flags */
165ab640bfcSAndreas Färber     s->flags &= ~PL011_FLAG_RXFF;
16649ab747fSPaolo Bonzini     s->flags |= PL011_FLAG_RXFE;
167041ac056SPeter Maydell }
16849ab747fSPaolo Bonzini 
pl011_reset_tx_fifo(PL011State * s)16949ab747fSPaolo Bonzini static inline void pl011_reset_tx_fifo(PL011State *s)
17049ab747fSPaolo Bonzini {
17149ab747fSPaolo Bonzini     /* Reset FIFO flags */
17249ab747fSPaolo Bonzini     s->flags &= ~PL011_FLAG_TXFF;
17349ab747fSPaolo Bonzini     s->flags |= PL011_FLAG_TXFE;
17449ab747fSPaolo Bonzini }
1759d88935cSEvgeny Iakovlev 
pl011_fifo_rx_put(void * opaque,uint32_t value)17649ab747fSPaolo Bonzini static void pl011_fifo_rx_put(void *opaque, uint32_t value)
17749ab747fSPaolo Bonzini {
17849ab747fSPaolo Bonzini     PL011State *s = (PL011State *)opaque;
17949ab747fSPaolo Bonzini     int slot;
18049ab747fSPaolo Bonzini     unsigned pipe_depth;
18122f7ff7fSPhilippe Mathieu-Daudé 
182041ac056SPeter Maydell     pipe_depth = pl011_get_fifo_depth(s);
183ce8f0905SRob Herring     slot = (s->read_pos + s->read_count) & (pipe_depth - 1);
18449ab747fSPaolo Bonzini     s->read_fifo[slot] = value;
1855345fdb4SMarc-André Lureau     s->read_count++;
186041ac056SPeter Maydell     s->flags &= ~PL011_FLAG_RXFE;
187041ac056SPeter Maydell     trace_pl011_fifo_rx_put(value, s->read_count);
188ce8f0905SRob Herring     if (s->read_count == pipe_depth) {
189041ac056SPeter Maydell         trace_pl011_fifo_rx_full();
190041ac056SPeter Maydell         s->flags |= PL011_FLAG_RXFF;
19149ab747fSPaolo Bonzini     }
192041ac056SPeter Maydell     if (s->read_count == s->read_trigger) {
193041ac056SPeter Maydell         s->int_level |= INT_RX;
19449ab747fSPaolo Bonzini         pl011_update(s);
195041ac056SPeter Maydell     }
196041ac056SPeter Maydell }
19749ab747fSPaolo Bonzini 
pl011_loopback_tx(PL011State * s,uint32_t value)198041ac056SPeter Maydell static void pl011_loopback_tx(PL011State *s, uint32_t value)
199041ac056SPeter Maydell {
20049ab747fSPaolo Bonzini     if (!pl011_loopback_enabled(s)) {
201041ac056SPeter Maydell         return;
202041ac056SPeter Maydell     }
20349ab747fSPaolo Bonzini 
204041ac056SPeter Maydell     /*
205041ac056SPeter Maydell      * Caveat:
20649ab747fSPaolo Bonzini      *
207041ac056SPeter Maydell      * In real hardware, TX loopback happens at the serial-bit level
208041ac056SPeter Maydell      * and then reassembled by the RX logics back into bytes and placed
20949ab747fSPaolo Bonzini      * into the RX fifo. That is, loopback happens after TX fifo.
210041ac056SPeter Maydell      *
211041ac056SPeter Maydell      * Because the real hardware TX fifo is time-drained at the frame
21249ab747fSPaolo Bonzini      * rate governed by the configured serial format, some loopback
213041ac056SPeter Maydell      * bytes in TX fifo may still be able to get into the RX fifo
214041ac056SPeter Maydell      * that could be full at times while being drained at software
21549ab747fSPaolo Bonzini      * pace.
216041ac056SPeter Maydell      *
217041ac056SPeter Maydell      * In such scenario, the RX draining pace is the major factor
21849ab747fSPaolo Bonzini      * deciding which loopback bytes get into the RX fifo, unless
219041ac056SPeter Maydell      * hardware flow-control is enabled.
220041ac056SPeter Maydell      *
22149ab747fSPaolo Bonzini      * For simplicity, the above described is not emulated.
222041ac056SPeter Maydell      */
223041ac056SPeter Maydell     pl011_fifo_rx_put(s, value);
224041ac056SPeter Maydell }
225041ac056SPeter Maydell 
pl011_write_txdata(PL011State * s,uint8_t data)226041ac056SPeter Maydell static void pl011_write_txdata(PL011State *s, uint8_t data)
22749ab747fSPaolo Bonzini {
22849ab747fSPaolo Bonzini     if (!(s->cr & CR_UARTEN)) {
22976b09fafSPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
230041ac056SPeter Maydell                       "PL011 data written to disabled UART\n");
231041ac056SPeter Maydell     }
23249ab747fSPaolo Bonzini     if (!(s->cr & CR_TXE)) {
233041ac056SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
23451141cabSPhilippe Mathieu-Daudé                       "PL011 data written to disabled TX UART\n");
235041ac056SPeter Maydell     }
23649ab747fSPaolo Bonzini 
23749ab747fSPaolo Bonzini     /*
238ab640bfcSAndreas Färber      * XXX this blocks entire thread. Rewrite to use
23949ab747fSPaolo Bonzini      * qemu_chr_fe_write and background I/O callbacks
24049ab747fSPaolo Bonzini      */
24149ab747fSPaolo Bonzini     qemu_chr_fe_write_all(&s->chr, &data, 1);
24249ab747fSPaolo Bonzini     pl011_loopback_tx(s, data);
24349ab747fSPaolo Bonzini     s->int_level |= INT_TX;
24449ab747fSPaolo Bonzini     pl011_update(s);
24507738852SPhilippe Mathieu-Daudé }
24649ab747fSPaolo Bonzini 
pl011_read_rxdata(PL011State * s)24749ab747fSPaolo Bonzini static uint32_t pl011_read_rxdata(PL011State *s)
24849ab747fSPaolo Bonzini {
24949ab747fSPaolo Bonzini     uint32_t c;
25049ab747fSPaolo Bonzini 
25149ab747fSPaolo Bonzini     s->flags &= ~PL011_FLAG_RXFF;
252aac63e0eSLuc Michel     c = s->read_fifo[s->read_pos];
253aac63e0eSLuc Michel     if (s->read_count > 0) {
254aac63e0eSLuc Michel         s->read_count--;
255aac63e0eSLuc Michel         s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1);
25631cb769cSBaruch Siach     }
257aac63e0eSLuc Michel     if (s->read_count == 0) {
258aac63e0eSLuc Michel         s->flags |= PL011_FLAG_RXFE;
259aac63e0eSLuc Michel     }
260aac63e0eSLuc Michel     if (s->read_count == s->read_trigger - 1) {
261aac63e0eSLuc Michel         s->int_level &= ~INT_RX;
262aac63e0eSLuc Michel     }
263aac63e0eSLuc Michel     trace_pl011_read_fifo(s->read_count);
264aac63e0eSLuc Michel     s->rsr = c >> 8;
265aac63e0eSLuc Michel     pl011_update(s);
266aac63e0eSLuc Michel     qemu_chr_fe_accept_input(&s->chr);
267aac63e0eSLuc Michel     return c;
268aac63e0eSLuc Michel }
269aac63e0eSLuc Michel 
pl011_read(void * opaque,hwaddr offset,unsigned size)270aac63e0eSLuc Michel static uint64_t pl011_read(void *opaque, hwaddr offset,
271f576e073STong Ho                            unsigned size)
272f576e073STong Ho {
273f576e073STong Ho     PL011State *s = (PL011State *)opaque;
274f576e073STong Ho     uint64_t r;
275f576e073STong Ho 
276f576e073STong Ho     switch (offset >> 2) {
277f576e073STong Ho     case 0: /* UARTDR */
278f576e073STong Ho         r = pl011_read_rxdata(s);
279f576e073STong Ho         break;
280f576e073STong Ho     case 1: /* UARTRSR */
281f576e073STong Ho         r = s->rsr;
282f576e073STong Ho         break;
283f576e073STong Ho     case 6: /* UARTFR */
284f576e073STong Ho         r = s->flags;
285f576e073STong Ho         break;
286f576e073STong Ho     case 8: /* UARTILPR */
287f576e073STong Ho         r = s->ilpr;
288f576e073STong Ho         break;
289f576e073STong Ho     case 9: /* UARTIBRD */
290f576e073STong Ho         r = s->ibrd;
291f576e073STong Ho         break;
292f576e073STong Ho     case 10: /* UARTFBRD */
293f576e073STong Ho         r = s->fbrd;
294f576e073STong Ho         break;
295f576e073STong Ho     case 11: /* UARTLCR_H */
296f576e073STong Ho         r = s->lcr;
297f576e073STong Ho         break;
298f576e073STong Ho     case 12: /* UARTCR */
299f576e073STong Ho         r = s->cr;
300f576e073STong Ho         break;
301f576e073STong Ho     case 13: /* UARTIFLS */
302f576e073STong Ho         r = s->ifl;
303f576e073STong Ho         break;
304f576e073STong Ho     case 14: /* UARTIMSC */
305f576e073STong Ho         r = s->int_enabled;
306f576e073STong Ho         break;
307f576e073STong Ho     case 15: /* UARTRIS */
308f576e073STong Ho         r = s->int_level;
309f576e073STong Ho         break;
310f576e073STong Ho     case 16: /* UARTMIS */
311f576e073STong Ho         r = s->int_level & s->int_enabled;
312f576e073STong Ho         break;
313f576e073STong Ho     case 18: /* UARTDMACR */
314f576e073STong Ho         r = s->dmacr;
315f576e073STong Ho         break;
316f576e073STong Ho     case 0x3f8 ... 0x400:
317f576e073STong Ho         r = s->id[(offset - 0xfe0) >> 2];
318f576e073STong Ho         break;
319f576e073STong Ho     default:
320f576e073STong Ho         qemu_log_mask(LOG_GUEST_ERROR,
321f576e073STong Ho                       "pl011_read: Bad offset 0x%x\n", (int)offset);
322f576e073STong Ho         r = 0;
323f576e073STong Ho         break;
324f576e073STong Ho     }
325f576e073STong Ho 
326f576e073STong Ho     trace_pl011_read(offset, r, pl011_regname(offset));
327f576e073STong Ho     return r;
328f576e073STong Ho }
329f576e073STong Ho 
pl011_set_read_trigger(PL011State * s)330f576e073STong Ho static void pl011_set_read_trigger(PL011State *s)
331f576e073STong Ho {
332f576e073STong Ho #if 0
333f576e073STong Ho     /* The docs say the RX interrupt is triggered when the FIFO exceeds
334f576e073STong Ho        the threshold.  However linux only reads the FIFO in response to an
335f576e073STong Ho        interrupt.  Triggering the interrupt when the FIFO is non-empty seems
336f576e073STong Ho        to make things work.  */
337f576e073STong Ho     if (s->lcr & LCR_FEN)
338f576e073STong Ho         s->read_trigger = (s->ifl >> 1) & 0x1c;
339f576e073STong Ho     else
340f576e073STong Ho #endif
341f576e073STong Ho         s->read_trigger = 1;
342f576e073STong Ho }
343f576e073STong Ho 
pl011_get_baudrate(const PL011State * s)344f576e073STong Ho static unsigned int pl011_get_baudrate(const PL011State *s)
345f576e073STong Ho {
346f576e073STong Ho     uint64_t clk;
347f576e073STong Ho 
348f576e073STong Ho     if (s->ibrd == 0) {
349f576e073STong Ho         return 0;
350f576e073STong Ho     }
351f576e073STong Ho 
352f576e073STong Ho     clk = clock_get_hz(s->clk);
353f576e073STong Ho     return (clk / ((s->ibrd << 6) + s->fbrd)) << 2;
35449ab747fSPaolo Bonzini }
35549ab747fSPaolo Bonzini 
pl011_trace_baudrate_change(const PL011State * s)35649ab747fSPaolo Bonzini static void pl011_trace_baudrate_change(const PL011State *s)
357ab640bfcSAndreas Färber {
35849ab747fSPaolo Bonzini     trace_pl011_baudrate_change(pl011_get_baudrate(s),
35949ab747fSPaolo Bonzini                                 clock_get_hz(s->clk),
36051141cabSPhilippe Mathieu-Daudé                                 s->ibrd, s->fbrd);
361041ac056SPeter Maydell }
36249ab747fSPaolo Bonzini 
pl011_loopback_mdmctrl(PL011State * s)36349ab747fSPaolo Bonzini static void pl011_loopback_mdmctrl(PL011State *s)
36449ab747fSPaolo Bonzini {
36549ab747fSPaolo Bonzini     uint32_t cr, fr, il;
3666ab3fc32SDaniel P. Berrange 
3676ab3fc32SDaniel P. Berrange     if (!pl011_loopback_enabled(s)) {
3685345fdb4SMarc-André Lureau         return;
369f576e073STong Ho     }
37022f7ff7fSPhilippe Mathieu-Daudé 
37149ab747fSPaolo Bonzini     /*
37249ab747fSPaolo Bonzini      * Loopback software-driven modem control outputs to modem status inputs:
373ce8f0905SRob Herring      *   FR.RI  <= CR.Out2
374ce8f0905SRob Herring      *   FR.DCD <= CR.Out1
37549ab747fSPaolo Bonzini      *   FR.CTS <= CR.RTS
37649ab747fSPaolo Bonzini      *   FR.DSR <= CR.DTR
37749ab747fSPaolo Bonzini      *
37849ab747fSPaolo Bonzini      * The loopback happens immediately even if this call is triggered
37951141cabSPhilippe Mathieu-Daudé      * by setting only CR.LBE.
38049ab747fSPaolo Bonzini      *
38149ab747fSPaolo Bonzini      * CTS/RTS updates due to enabled hardware flow controls are not
38249ab747fSPaolo Bonzini      * dealt with here.
383b88cfee9SZheyu Ma      */
384aac63e0eSLuc Michel     cr = s->cr;
38549ab747fSPaolo Bonzini     fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
38649ab747fSPaolo Bonzini                       PL011_FLAG_DSR | PL011_FLAG_CTS);
387b88cfee9SZheyu Ma     fr |= (cr & CR_OUT2) ? PL011_FLAG_RI  : 0;
388aac63e0eSLuc Michel     fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
38949ab747fSPaolo Bonzini     fr |= (cr & CR_RTS)  ? PL011_FLAG_CTS : 0;
39049ab747fSPaolo Bonzini     fr |= (cr & CR_DTR)  ? PL011_FLAG_DSR : 0;
39122709e90SRob Herring 
39207738852SPhilippe Mathieu-Daudé     /* Change interrupts based on updated FR */
39323dcbfc0SEvgeny Iakovlev     il = s->int_level & ~(INT_DSR | INT_DCD | INT_CTS | INT_RI);
39422709e90SRob Herring     il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
39507738852SPhilippe Mathieu-Daudé     il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
39607738852SPhilippe Mathieu-Daudé     il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
397d60af909SJan Luebbe     il |= (fr & PL011_FLAG_RI)  ? INT_RI  : 0;
398d60af909SJan Luebbe 
399f576e073STong Ho     s->flags = fr;
400d60af909SJan Luebbe     s->int_level = il;
40149ab747fSPaolo Bonzini     pl011_update(s);
40249ab747fSPaolo Bonzini }
40349ab747fSPaolo Bonzini 
pl011_loopback_break(PL011State * s,int brk_enable)40449ab747fSPaolo Bonzini static void pl011_loopback_break(PL011State *s, int brk_enable)
405f576e073STong Ho {
40649ab747fSPaolo Bonzini     if (brk_enable) {
407f576e073STong Ho         pl011_loopback_tx(s, DR_BE);
40849ab747fSPaolo Bonzini     }
40949ab747fSPaolo Bonzini }
41049ab747fSPaolo Bonzini 
pl011_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)41149ab747fSPaolo Bonzini static void pl011_write(void *opaque, hwaddr offset,
41249ab747fSPaolo Bonzini                         uint64_t value, unsigned size)
41349ab747fSPaolo Bonzini {
41449ab747fSPaolo Bonzini     PL011State *s = (PL011State *)opaque;
41549ab747fSPaolo Bonzini     unsigned char ch;
41649ab747fSPaolo Bonzini 
41749ab747fSPaolo Bonzini     trace_pl011_write(offset, value, pl011_regname(offset));
41849ab747fSPaolo Bonzini 
41949ab747fSPaolo Bonzini     switch (offset >> 2) {
42049ab747fSPaolo Bonzini     case 0: /* UARTDR */
42149ab747fSPaolo Bonzini         ch = value;
42249ab747fSPaolo Bonzini         pl011_write_txdata(s, ch);
42349ab747fSPaolo Bonzini         break;
42449ab747fSPaolo Bonzini     case 1: /* UARTRSR/UARTECR */
42549ab747fSPaolo Bonzini         s->rsr = 0;
42649ab747fSPaolo Bonzini         break;
42749ab747fSPaolo Bonzini     case 6: /* UARTFR */
42849ab747fSPaolo Bonzini         /* Writes to Flag register are ignored.  */
42976b09fafSPeter Maydell         break;
43049ab747fSPaolo Bonzini     case 8: /* UARTILPR */
43149ab747fSPaolo Bonzini         s->ilpr = value;
43249ab747fSPaolo Bonzini         break;
43349ab747fSPaolo Bonzini     case 9: /* UARTIBRD */
43449ab747fSPaolo Bonzini         s->ibrd = value & IBRD_MASK;
435ab640bfcSAndreas Färber         pl011_trace_baudrate_change(s);
436041ac056SPeter Maydell         break;
43749ab747fSPaolo Bonzini     case 10: /* UARTFBRD */
4389d88935cSEvgeny Iakovlev         s->fbrd = value & FBRD_MASK;
439041ac056SPeter Maydell         pl011_trace_baudrate_change(s);
440041ac056SPeter Maydell         break;
44149ab747fSPaolo Bonzini     case 11: /* UARTLCR_H */
44249ab747fSPaolo Bonzini         /* Reset the FIFO state on FIFO enable or disable */
44349ab747fSPaolo Bonzini         if ((s->lcr ^ value) & LCR_FEN) {
44449ab747fSPaolo Bonzini             pl011_reset_rx_fifo(s);
445ab640bfcSAndreas Färber             pl011_reset_tx_fifo(s);
44649ab747fSPaolo Bonzini         }
4479d88935cSEvgeny Iakovlev         if ((s->lcr ^ value) & LCR_BRK) {
44849ab747fSPaolo Bonzini             int break_enable = value & LCR_BRK;
4499d88935cSEvgeny Iakovlev             qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
4509d88935cSEvgeny Iakovlev                               &break_enable);
45149ab747fSPaolo Bonzini             pl011_loopback_break(s, break_enable);
45249ab747fSPaolo Bonzini         }
45349ab747fSPaolo Bonzini         s->lcr = value;
454041ac056SPeter Maydell         pl011_set_read_trigger(s);
4559d88935cSEvgeny Iakovlev         break;
456041ac056SPeter Maydell     case 12: /* UARTCR */
45749ab747fSPaolo Bonzini         /* ??? Need to implement the enable bit.  */
45849ab747fSPaolo Bonzini         s->cr = value;
45949ab747fSPaolo Bonzini         pl011_loopback_mdmctrl(s);
46022f7ff7fSPhilippe Mathieu-Daudé         break;
46149ab747fSPaolo Bonzini     case 13: /* UARTIFS */
46249ab747fSPaolo Bonzini         s->ifl = value;
46349ab747fSPaolo Bonzini         pl011_set_read_trigger(s);
46449ab747fSPaolo Bonzini         break;
46549ab747fSPaolo Bonzini     case 14: /* UARTIMSC */
46649ab747fSPaolo Bonzini         s->int_enabled = value;
467f576e073STong Ho         pl011_update(s);
468f576e073STong Ho         break;
469f576e073STong Ho     case 17: /* UARTICR */
470f576e073STong Ho         s->int_level &= ~value;
471f576e073STong Ho         pl011_update(s);
472f576e073STong Ho         break;
473f576e073STong Ho     case 18: /* UARTDMACR */
474f576e073STong Ho         s->dmacr = value;
475f576e073STong Ho         if (value & 3) {
47649ab747fSPaolo Bonzini             qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
47749ab747fSPaolo Bonzini         }
47849ab747fSPaolo Bonzini         break;
479083b266fSPhilippe Mathieu-Daudé     default:
48049ab747fSPaolo Bonzini         qemu_log_mask(LOG_GUEST_ERROR,
481f576e073STong Ho                       "pl011_write: Bad offset 0x%x\n", (int)offset);
48207738852SPhilippe Mathieu-Daudé     }
48307738852SPhilippe Mathieu-Daudé }
48449ab747fSPaolo Bonzini 
pl011_can_receive(void * opaque)48549ab747fSPaolo Bonzini static int pl011_can_receive(void *opaque)
4865ee0abedSPeter Maydell {
487aac63e0eSLuc Michel     PL011State *s = (PL011State *)opaque;
488aac63e0eSLuc Michel     int r;
489aac63e0eSLuc Michel 
490aac63e0eSLuc Michel     r = s->read_count < pl011_get_fifo_depth(s);
491aac63e0eSLuc Michel     trace_pl011_can_receive(s->lcr, s->read_count, r);
492aac63e0eSLuc Michel     return r;
49349ab747fSPaolo Bonzini }
49449ab747fSPaolo Bonzini 
pl011_receive(void * opaque,const uint8_t * buf,int size)49549ab747fSPaolo Bonzini static void pl011_receive(void *opaque, const uint8_t *buf, int size)
49649ab747fSPaolo Bonzini {
4977e66d52bSPhilippe Mathieu-Daudé     /*
4987e66d52bSPhilippe Mathieu-Daudé      * In loopback mode, the RX input signal is internally disconnected
49949ab747fSPaolo Bonzini      * from the entire receiving logics; thus, all inputs are ignored,
50049ab747fSPaolo Bonzini      * and BREAK detection on RX input signal is also not performed.
501e6fa978dSGavin Shan      */
502e6fa978dSGavin Shan     if (pl011_loopback_enabled(opaque)) {
503e6fa978dSGavin Shan         return;
504e6fa978dSGavin Shan     }
505e6fa978dSGavin Shan 
506e6fa978dSGavin Shan     pl011_fifo_rx_put(opaque, *buf);
507e6fa978dSGavin Shan }
508aac63e0eSLuc Michel 
pl011_event(void * opaque,QEMUChrEvent event)509aac63e0eSLuc Michel static void pl011_event(void *opaque, QEMUChrEvent event)
510aac63e0eSLuc Michel {
511aac63e0eSLuc Michel     if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) {
512e6fa978dSGavin Shan         pl011_fifo_rx_put(opaque, DR_BE);
5132f6cab05SRichard Henderson     }
514aac63e0eSLuc Michel }
515aac63e0eSLuc Michel 
pl011_clock_update(void * opaque,ClockEvent event)516aac63e0eSLuc Michel static void pl011_clock_update(void *opaque, ClockEvent event)
517aac63e0eSLuc Michel {
518aac63e0eSLuc Michel     PL011State *s = PL011(opaque);
51913ea96faSEvgeny Iakovlev 
52013ea96faSEvgeny Iakovlev     pl011_trace_baudrate_change(s);
52113ea96faSEvgeny Iakovlev }
52213ea96faSEvgeny Iakovlev 
52313ea96faSEvgeny Iakovlev static const MemoryRegionOps pl011_ops = {
52413ea96faSEvgeny Iakovlev     .read = pl011_read,
52513ea96faSEvgeny Iakovlev     .write = pl011_write,
52613ea96faSEvgeny Iakovlev     .endianness = DEVICE_NATIVE_ENDIAN,
52713ea96faSEvgeny Iakovlev     .impl.min_access_size = 4,
52813ea96faSEvgeny Iakovlev     .impl.max_access_size = 4,
52913ea96faSEvgeny Iakovlev };
53013ea96faSEvgeny Iakovlev 
pl011_clock_needed(void * opaque)53113ea96faSEvgeny Iakovlev static bool pl011_clock_needed(void *opaque)
53213ea96faSEvgeny Iakovlev {
53313ea96faSEvgeny Iakovlev     PL011State *s = PL011(opaque);
53413ea96faSEvgeny Iakovlev 
53513ea96faSEvgeny Iakovlev     return s->migrate_clk;
53613ea96faSEvgeny Iakovlev }
53713ea96faSEvgeny Iakovlev 
53813ea96faSEvgeny Iakovlev static const VMStateDescription vmstate_pl011_clock = {
53913ea96faSEvgeny Iakovlev     .name = "pl011/clock",
540b88cfee9SZheyu Ma     .version_id = 1,
541b88cfee9SZheyu Ma     .minimum_version_id = 1,
542b88cfee9SZheyu Ma     .needed = pl011_clock_needed,
54313ea96faSEvgeny Iakovlev     .fields = (const VMStateField[]) {
54413ea96faSEvgeny Iakovlev         VMSTATE_CLOCK(clk, PL011State),
54513ea96faSEvgeny Iakovlev         VMSTATE_END_OF_LIST()
54649ab747fSPaolo Bonzini     }
54749ab747fSPaolo Bonzini };
548ce8f0905SRob Herring 
pl011_post_load(void * opaque,int version_id)549ce8f0905SRob Herring static int pl011_post_load(void *opaque, int version_id)
55013ea96faSEvgeny Iakovlev {
5512f6cab05SRichard Henderson     PL011State* s = opaque;
552ab640bfcSAndreas Färber 
553ab640bfcSAndreas Färber     /* Sanity-check input state */
554ab640bfcSAndreas Färber     if (s->read_pos >= ARRAY_SIZE(s->read_fifo) ||
555ce8f0905SRob Herring         s->read_count > ARRAY_SIZE(s->read_fifo)) {
556ab640bfcSAndreas Färber         return -1;
557ab640bfcSAndreas Färber     }
558ab640bfcSAndreas Färber 
559ab640bfcSAndreas Färber     if (!pl011_is_fifo_enabled(s) && s->read_count > 0 && s->read_pos > 0) {
5609d88935cSEvgeny Iakovlev         /*
561ab640bfcSAndreas Färber          * Older versions of PL011 didn't ensure that the single
562ab640bfcSAndreas Färber          * character in the FIFO in FIFO-disabled mode is in
563ab640bfcSAndreas Färber          * element 0 of the array; convert to follow the current
564ab640bfcSAndreas Färber          * code's assumptions.
565ab640bfcSAndreas Färber          */
566ab640bfcSAndreas Färber         s->read_fifo[0] = s->read_fifo[s->read_pos];
567ab640bfcSAndreas Färber         s->read_pos = 0;
56849ab747fSPaolo Bonzini     }
569aac63e0eSLuc Michel 
5702f6cab05SRichard Henderson     s->ibrd &= IBRD_MASK;
571aac63e0eSLuc Michel     s->fbrd &= FBRD_MASK;
572aac63e0eSLuc Michel 
57349ab747fSPaolo Bonzini     return 0;
57449ab747fSPaolo Bonzini }
57549ab747fSPaolo Bonzini 
576f0d1d2c1Sxiaoqiang zhao static const VMStateDescription vmstate_pl011 = {
577f0d1d2c1Sxiaoqiang zhao     .name = "pl011",
578e6fa978dSGavin Shan     .version_id = 2,
579f0d1d2c1Sxiaoqiang zhao     .minimum_version_id = 2,
580f0d1d2c1Sxiaoqiang zhao     .post_load = pl011_post_load,
581f0d1d2c1Sxiaoqiang zhao     .fields = (const VMStateField[]) {
58271ffe1a0SAndreas Färber         VMSTATE_UNUSED(sizeof(uint32_t)),
58349ab747fSPaolo Bonzini         VMSTATE_UINT32(flags, PL011State),
58471ffe1a0SAndreas Färber         VMSTATE_UINT32(lcr, PL011State),
58571ffe1a0SAndreas Färber         VMSTATE_UINT32(rsr, PL011State),
586a3c1ca56SPeter Maydell         VMSTATE_UINT32(cr, PL011State),
58749ab747fSPaolo Bonzini         VMSTATE_UINT32(dmacr, PL011State),
588300b1fc6SPaolo Bonzini         VMSTATE_UINT32(int_enabled, PL011State),
58971ffe1a0SAndreas Färber         VMSTATE_UINT32(int_level, PL011State),
590a3c1ca56SPeter Maydell         VMSTATE_UINT32_ARRAY(read_fifo, PL011State, PL011_FIFO_DEPTH),
591a3c1ca56SPeter Maydell         VMSTATE_UINT32(ilpr, PL011State),
592a3c1ca56SPeter Maydell         VMSTATE_UINT32(ibrd, PL011State),
59349ab747fSPaolo Bonzini         VMSTATE_UINT32(fbrd, PL011State),
5945ee0abedSPeter Maydell         VMSTATE_UINT32(ifl, PL011State),
5955ee0abedSPeter Maydell         VMSTATE_INT32(read_pos, PL011State),
596aac63e0eSLuc Michel         VMSTATE_INT32(read_count, PL011State),
59771ffe1a0SAndreas Färber         VMSTATE_INT32(read_trigger, PL011State),
59871ffe1a0SAndreas Färber         VMSTATE_END_OF_LIST()
59971ffe1a0SAndreas Färber     },
60071ffe1a0SAndreas Färber     .subsections = (const VMStateDescription * const []) {
60171ffe1a0SAndreas Färber         &vmstate_pl011_clock,
60271ffe1a0SAndreas Färber         NULL
60371ffe1a0SAndreas Färber     }
6045345fdb4SMarc-André Lureau };
60581517ba3SAnton Nefedov 
60649ab747fSPaolo Bonzini static Property pl011_properties[] = {
60749ab747fSPaolo Bonzini     DEFINE_PROP_CHR("chardev", PL011State, chr),
6083b7a165eSEvgeny Iakovlev     DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true),
6093b7a165eSEvgeny Iakovlev     DEFINE_PROP_END_OF_LIST(),
6103b7a165eSEvgeny Iakovlev };
6113b7a165eSEvgeny Iakovlev 
pl011_init(Object * obj)6123b7a165eSEvgeny Iakovlev static void pl011_init(Object *obj)
6133b7a165eSEvgeny Iakovlev {
6143b7a165eSEvgeny Iakovlev     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
6153b7a165eSEvgeny Iakovlev     PL011State *s = PL011(obj);
6163b7a165eSEvgeny Iakovlev     int i;
6173b7a165eSEvgeny Iakovlev 
6183b7a165eSEvgeny Iakovlev     memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
6193b7a165eSEvgeny Iakovlev     sysbus_init_mmio(sbd, &s->iomem);
6203b7a165eSEvgeny Iakovlev     for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
6213b7a165eSEvgeny Iakovlev         sysbus_init_irq(sbd, &s->irq[i]);
6223b7a165eSEvgeny Iakovlev     }
62323dcbfc0SEvgeny Iakovlev 
62423dcbfc0SEvgeny Iakovlev     s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
6253b7a165eSEvgeny Iakovlev                                 ClockUpdate);
6263b7a165eSEvgeny Iakovlev 
62771ffe1a0SAndreas Färber     s->id = pl011_id_arm;
62849ab747fSPaolo Bonzini }
62971ffe1a0SAndreas Färber 
pl011_realize(DeviceState * dev,Error ** errp)63049ab747fSPaolo Bonzini static void pl011_realize(DeviceState *dev, Error **errp)
63171ffe1a0SAndreas Färber {
6323b7a165eSEvgeny Iakovlev     PL011State *s = PL011(dev);
63371ffe1a0SAndreas Färber 
6344f67d30bSMarc-André Lureau     qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
63549ab747fSPaolo Bonzini                              pl011_event, NULL, s, NULL, true);
63649ab747fSPaolo Bonzini }
63749ab747fSPaolo Bonzini 
pl011_reset(DeviceState * dev)63871ffe1a0SAndreas Färber static void pl011_reset(DeviceState *dev)
63949ab747fSPaolo Bonzini {
640ab640bfcSAndreas Färber     PL011State *s = PL011(dev);
64171ffe1a0SAndreas Färber 
64271ffe1a0SAndreas Färber     s->lcr = 0;
64349ab747fSPaolo Bonzini     s->rsr = 0;
64449ab747fSPaolo Bonzini     s->dmacr = 0;
64571ffe1a0SAndreas Färber     s->int_enabled = 0;
64649ab747fSPaolo Bonzini     s->int_level = 0;
64771ffe1a0SAndreas Färber     s->ilpr = 0;
64849ab747fSPaolo Bonzini     s->ibrd = 0;
64971ffe1a0SAndreas Färber     s->fbrd = 0;
65049ab747fSPaolo Bonzini     s->read_trigger = 1;
65149ab747fSPaolo Bonzini     s->ifl = 0x12;
65249ab747fSPaolo Bonzini     s->cr = 0x300;
653694cf209SPeter Maydell     s->flags = 0;
65471ffe1a0SAndreas Färber     pl011_reset_rx_fifo(s);
65571ffe1a0SAndreas Färber     pl011_reset_tx_fifo(s);
65649ab747fSPaolo Bonzini }
65749ab747fSPaolo Bonzini 
pl011_class_init(ObjectClass * oc,void * data)65849ab747fSPaolo Bonzini static void pl011_class_init(ObjectClass *oc, void *data)
65949ab747fSPaolo Bonzini {
66049ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(oc);
66149ab747fSPaolo Bonzini 
66249ab747fSPaolo Bonzini     dc->realize = pl011_realize;
66349ab747fSPaolo Bonzini     device_class_set_legacy_reset(dc, pl011_reset);
66449ab747fSPaolo Bonzini     dc->vmsd = &vmstate_pl011;
665     device_class_set_props(dc, pl011_properties);
666 }
667 
668 static const TypeInfo pl011_arm_info = {
669     .name          = TYPE_PL011,
670     .parent        = TYPE_SYS_BUS_DEVICE,
671     .instance_size = sizeof(PL011State),
672     .instance_init = pl011_init,
673     .class_init    = pl011_class_init,
674 };
675 
pl011_luminary_init(Object * obj)676 static void pl011_luminary_init(Object *obj)
677 {
678     PL011State *s = PL011(obj);
679 
680     s->id = pl011_id_luminary;
681 }
682 
683 static const TypeInfo pl011_luminary_info = {
684     .name          = TYPE_PL011_LUMINARY,
685     .parent        = TYPE_PL011,
686     .instance_init = pl011_luminary_init,
687 };
688 
pl011_register_types(void)689 static void pl011_register_types(void)
690 {
691     type_register_static(&pl011_arm_info);
692     type_register_static(&pl011_luminary_info);
693 }
694 
695 type_init(pl011_register_types)
696