blob: 0fd1334fab44d39f73678f48837e5c7f9f891a46 [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"
Philippe Mathieu-Daudé11f2ee12023-02-20 12:51:08 +010022#include "qapi/error.h"
Peter Maydell694cf202019-02-21 18:17:46 +000023#include "hw/char/pl011.h"
Markus Armbruster64552b62019-08-12 07:23:42 +020024#include "hw/irq.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010025#include "hw/sysbus.h"
Luc Michelaac63e02020-10-10 15:57:58 +020026#include "hw/qdev-clock.h"
Philippe Mathieu-Daudé11f2ee12023-02-20 12:51:08 +010027#include "hw/qdev-properties.h"
Eduardo Habkostce35e222020-12-11 17:05:12 -050028#include "hw/qdev-properties-system.h"
Markus Armbrusterd6454272019-08-12 07:23:45 +020029#include "migration/vmstate.h"
Marc-André Lureau4d43a602017-01-26 18:26:44 +040030#include "chardev/char-fe.h"
Jan Luebbed60af902021-08-06 16:47:00 +020031#include "chardev/char-serial.h"
Paolo Bonzini03dd0242015-12-15 13:16:16 +010032#include "qemu/log.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +020033#include "qemu/module.h"
Peter Maydell041ac052016-10-12 18:54:36 +010034#include "trace.h"
pbrookcdbdb642006-04-09 01:32:52 +000035
Philippe Mathieu-Daudé11f2ee12023-02-20 12:51:08 +010036DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
37{
38 DeviceState *dev;
39 SysBusDevice *s;
40
41 dev = qdev_new("pl011");
42 s = SYS_BUS_DEVICE(dev);
43 qdev_prop_set_chr(dev, "chardev", chr);
44 sysbus_realize_and_unref(s, &error_fatal);
45 sysbus_mmio_map(s, 0, addr);
46 sysbus_connect_irq(s, 0, irq);
47
48 return dev;
49}
50
Philippe Mathieu-Daudé51141ca2023-05-22 10:31:40 +020051/* Flag Register, UARTFR */
Tong Hof576e072024-02-26 21:48:55 -080052#define PL011_FLAG_RI 0x100
pbrookcdbdb642006-04-09 01:32:52 +000053#define PL011_FLAG_TXFE 0x80
54#define PL011_FLAG_RXFF 0x40
55#define PL011_FLAG_TXFF 0x20
56#define PL011_FLAG_RXFE 0x10
Tong Hof576e072024-02-26 21:48:55 -080057#define PL011_FLAG_DCD 0x04
58#define PL011_FLAG_DSR 0x02
59#define PL011_FLAG_CTS 0x01
pbrookcdbdb642006-04-09 01:32:52 +000060
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +020061/* Data Register, UARTDR */
62#define DR_BE (1 << 10)
63
Peter Maydella3c1ca52019-02-21 18:17:46 +000064/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
65#define INT_OE (1 << 10)
66#define INT_BE (1 << 9)
67#define INT_PE (1 << 8)
68#define INT_FE (1 << 7)
69#define INT_RT (1 << 6)
70#define INT_TX (1 << 5)
71#define INT_RX (1 << 4)
72#define INT_DSR (1 << 3)
73#define INT_DCD (1 << 2)
74#define INT_CTS (1 << 1)
75#define INT_RI (1 << 0)
76#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
77#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
78
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +020079/* Line Control Register, UARTLCR_H */
80#define LCR_FEN (1 << 4)
81#define LCR_BRK (1 << 0)
82
Tong Hof576e072024-02-26 21:48:55 -080083/* Control Register, UARTCR */
84#define CR_OUT2 (1 << 13)
85#define CR_OUT1 (1 << 12)
86#define CR_RTS (1 << 11)
87#define CR_DTR (1 << 10)
Philippe Mathieu-Daudé51b61dd2024-07-18 17:23:42 +020088#define CR_TXE (1 << 8)
Tong Hof576e072024-02-26 21:48:55 -080089#define CR_LBE (1 << 7)
Philippe Mathieu-Daudé51b61dd2024-07-18 17:23:42 +020090#define CR_UARTEN (1 << 0)
Tong Hof576e072024-02-26 21:48:55 -080091
Zheyu Mab88cfee2024-07-02 17:57:52 +020092/* Integer Baud Rate Divider, UARTIBRD */
Peter Maydellcd247ea2024-10-14 17:05:53 +010093#define IBRD_MASK 0xffff
Zheyu Mab88cfee2024-07-02 17:57:52 +020094
95/* Fractional Baud Rate Divider, UARTFBRD */
Peter Maydellcd247ea2024-10-14 17:05:53 +010096#define FBRD_MASK 0x3f
Zheyu Mab88cfee2024-07-02 17:57:52 +020097
Paul Brooka7d518a2009-05-14 22:35:07 +010098static const unsigned char pl011_id_arm[8] =
99 { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
100static const unsigned char pl011_id_luminary[8] =
101 { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
pbrookcdbdb642006-04-09 01:32:52 +0000102
Philippe Mathieu-Daudé51141ca2023-05-22 10:31:40 +0200103static const char *pl011_regname(hwaddr offset)
104{
105 static const char *const rname[] = {
106 [0] = "DR", [1] = "RSR", [6] = "FR", [8] = "ILPR", [9] = "IBRD",
107 [10] = "FBRD", [11] = "LCRH", [12] = "CR", [13] = "IFLS", [14] = "IMSC",
108 [15] = "RIS", [16] = "MIS", [17] = "ICR", [18] = "DMACR",
109 };
110 unsigned idx = offset >> 2;
111
112 if (idx < ARRAY_SIZE(rname) && rname[idx]) {
113 return rname[idx];
114 }
115 if (idx >= 0x3f8 && idx <= 0x400) {
116 return "ID";
117 }
118 return "UNKN";
119}
120
Peter Maydella3c1ca52019-02-21 18:17:46 +0000121/* Which bits in the interrupt status matter for each outbound IRQ line ? */
122static const uint32_t irqmask[] = {
123 INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
124 INT_RX,
125 INT_TX,
126 INT_RT,
127 INT_MS,
128 INT_E,
129};
130
Andreas Färberab640bf2013-07-24 23:13:57 +0200131static void pl011_update(PL011State *s)
pbrookcdbdb642006-04-09 01:32:52 +0000132{
133 uint32_t flags;
Peter Maydella3c1ca52019-02-21 18:17:46 +0000134 int i;
ths3b46e622007-09-17 08:09:54 +0000135
pbrookcdbdb642006-04-09 01:32:52 +0000136 flags = s->int_level & s->int_enabled;
Peter Maydell041ac052016-10-12 18:54:36 +0100137 trace_pl011_irq_state(flags != 0);
Peter Maydella3c1ca52019-02-21 18:17:46 +0000138 for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
139 qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0);
140 }
pbrookcdbdb642006-04-09 01:32:52 +0000141}
142
Philippe Mathieu-Daudé1fb27212024-07-18 17:47:28 +0200143static bool pl011_loopback_enabled(PL011State *s)
144{
145 return !!(s->cr & CR_LBE);
146}
147
Evgeny Iakovlev9d889352023-01-23 17:23:00 +0100148static bool pl011_is_fifo_enabled(PL011State *s)
149{
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +0200150 return (s->lcr & LCR_FEN) != 0;
Evgeny Iakovlev9d889352023-01-23 17:23:00 +0100151}
152
153static inline unsigned pl011_get_fifo_depth(PL011State *s)
154{
155 /* Note: FIFO depth is expected to be power-of-2 */
156 return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1;
157}
158
Philippe Mathieu-Daudé02b1f7f2023-05-22 15:52:06 +0200159static inline void pl011_reset_rx_fifo(PL011State *s)
Evgeny Iakovlev23dcbfc2023-01-23 17:23:03 +0100160{
161 s->read_count = 0;
162 s->read_pos = 0;
163
164 /* Reset FIFO flags */
Philippe Mathieu-Daudé02b1f7f2023-05-22 15:52:06 +0200165 s->flags &= ~PL011_FLAG_RXFF;
166 s->flags |= PL011_FLAG_RXFE;
167}
168
169static inline void pl011_reset_tx_fifo(PL011State *s)
170{
171 /* Reset FIFO flags */
172 s->flags &= ~PL011_FLAG_TXFF;
173 s->flags |= PL011_FLAG_TXFE;
Evgeny Iakovlev23dcbfc2023-01-23 17:23:03 +0100174}
175
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200176static void pl011_fifo_rx_put(void *opaque, uint32_t value)
Philippe Mathieu-Daudé56608022024-07-18 17:21:46 +0200177{
178 PL011State *s = (PL011State *)opaque;
179 int slot;
180 unsigned pipe_depth;
181
182 pipe_depth = pl011_get_fifo_depth(s);
183 slot = (s->read_pos + s->read_count) & (pipe_depth - 1);
184 s->read_fifo[slot] = value;
185 s->read_count++;
186 s->flags &= ~PL011_FLAG_RXFE;
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200187 trace_pl011_fifo_rx_put(value, s->read_count);
Philippe Mathieu-Daudé56608022024-07-18 17:21:46 +0200188 if (s->read_count == pipe_depth) {
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200189 trace_pl011_fifo_rx_full();
Philippe Mathieu-Daudé56608022024-07-18 17:21:46 +0200190 s->flags |= PL011_FLAG_RXFF;
191 }
192 if (s->read_count == s->read_trigger) {
193 s->int_level |= INT_RX;
194 pl011_update(s);
195 }
196}
197
Philippe Mathieu-Daudé1fb27212024-07-18 17:47:28 +0200198static void pl011_loopback_tx(PL011State *s, uint32_t value)
199{
200 if (!pl011_loopback_enabled(s)) {
201 return;
202 }
203
204 /*
205 * Caveat:
206 *
207 * In real hardware, TX loopback happens at the serial-bit level
208 * and then reassembled by the RX logics back into bytes and placed
209 * into the RX fifo. That is, loopback happens after TX fifo.
210 *
211 * Because the real hardware TX fifo is time-drained at the frame
212 * rate governed by the configured serial format, some loopback
213 * bytes in TX fifo may still be able to get into the RX fifo
214 * that could be full at times while being drained at software
215 * pace.
216 *
217 * In such scenario, the RX draining pace is the major factor
218 * deciding which loopback bytes get into the RX fifo, unless
219 * hardware flow-control is enabled.
220 *
221 * For simplicity, the above described is not emulated.
222 */
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200223 pl011_fifo_rx_put(s, value);
Philippe Mathieu-Daudé1fb27212024-07-18 17:47:28 +0200224}
225
Philippe Mathieu-Daudébd6051b2023-05-22 10:41:04 +0200226static void pl011_write_txdata(PL011State *s, uint8_t data)
227{
Philippe Mathieu-Daudé51b61dd2024-07-18 17:23:42 +0200228 if (!(s->cr & CR_UARTEN)) {
229 qemu_log_mask(LOG_GUEST_ERROR,
230 "PL011 data written to disabled UART\n");
231 }
232 if (!(s->cr & CR_TXE)) {
233 qemu_log_mask(LOG_GUEST_ERROR,
234 "PL011 data written to disabled TX UART\n");
235 }
Philippe Mathieu-Daudébd6051b2023-05-22 10:41:04 +0200236
237 /*
238 * XXX this blocks entire thread. Rewrite to use
239 * qemu_chr_fe_write and background I/O callbacks
240 */
241 qemu_chr_fe_write_all(&s->chr, &data, 1);
242 pl011_loopback_tx(s, data);
243 s->int_level |= INT_TX;
244 pl011_update(s);
245}
246
Philippe Mathieu-Daudédee82842024-07-18 17:41:44 +0200247static uint32_t pl011_read_rxdata(PL011State *s)
248{
249 uint32_t c;
250
251 s->flags &= ~PL011_FLAG_RXFF;
252 c = s->read_fifo[s->read_pos];
253 if (s->read_count > 0) {
254 s->read_count--;
255 s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1);
256 }
257 if (s->read_count == 0) {
258 s->flags |= PL011_FLAG_RXFE;
259 }
260 if (s->read_count == s->read_trigger - 1) {
261 s->int_level &= ~INT_RX;
262 }
263 trace_pl011_read_fifo(s->read_count);
264 s->rsr = c >> 8;
265 pl011_update(s);
266 qemu_chr_fe_accept_input(&s->chr);
267 return c;
268}
269
Avi Kivitya8170e52012-10-23 12:30:10 +0200270static uint64_t pl011_read(void *opaque, hwaddr offset,
Avi Kivity48484752011-10-10 17:08:49 +0200271 unsigned size)
pbrookcdbdb642006-04-09 01:32:52 +0000272{
Andreas Färberab640bf2013-07-24 23:13:57 +0200273 PL011State *s = (PL011State *)opaque;
Peter Maydell041ac052016-10-12 18:54:36 +0100274 uint64_t r;
pbrookcdbdb642006-04-09 01:32:52 +0000275
pbrookcdbdb642006-04-09 01:32:52 +0000276 switch (offset >> 2) {
277 case 0: /* UARTDR */
Philippe Mathieu-Daudédee82842024-07-18 17:41:44 +0200278 r = pl011_read_rxdata(s);
Peter Maydell041ac052016-10-12 18:54:36 +0100279 break;
Rob Herringce8f0902014-03-18 13:18:40 -0500280 case 1: /* UARTRSR */
Peter Maydell041ac052016-10-12 18:54:36 +0100281 r = s->rsr;
282 break;
pbrookcdbdb642006-04-09 01:32:52 +0000283 case 6: /* UARTFR */
Peter Maydell041ac052016-10-12 18:54:36 +0100284 r = s->flags;
285 break;
pbrookcdbdb642006-04-09 01:32:52 +0000286 case 8: /* UARTILPR */
Peter Maydell041ac052016-10-12 18:54:36 +0100287 r = s->ilpr;
288 break;
pbrookcdbdb642006-04-09 01:32:52 +0000289 case 9: /* UARTIBRD */
Peter Maydell041ac052016-10-12 18:54:36 +0100290 r = s->ibrd;
291 break;
pbrookcdbdb642006-04-09 01:32:52 +0000292 case 10: /* UARTFBRD */
Peter Maydell041ac052016-10-12 18:54:36 +0100293 r = s->fbrd;
294 break;
pbrookcdbdb642006-04-09 01:32:52 +0000295 case 11: /* UARTLCR_H */
Peter Maydell041ac052016-10-12 18:54:36 +0100296 r = s->lcr;
297 break;
pbrookcdbdb642006-04-09 01:32:52 +0000298 case 12: /* UARTCR */
Peter Maydell041ac052016-10-12 18:54:36 +0100299 r = s->cr;
300 break;
pbrookcdbdb642006-04-09 01:32:52 +0000301 case 13: /* UARTIFLS */
Peter Maydell041ac052016-10-12 18:54:36 +0100302 r = s->ifl;
303 break;
pbrookcdbdb642006-04-09 01:32:52 +0000304 case 14: /* UARTIMSC */
Peter Maydell041ac052016-10-12 18:54:36 +0100305 r = s->int_enabled;
306 break;
pbrookcdbdb642006-04-09 01:32:52 +0000307 case 15: /* UARTRIS */
Peter Maydell041ac052016-10-12 18:54:36 +0100308 r = s->int_level;
309 break;
pbrookcdbdb642006-04-09 01:32:52 +0000310 case 16: /* UARTMIS */
Peter Maydell041ac052016-10-12 18:54:36 +0100311 r = s->int_level & s->int_enabled;
312 break;
pbrookcdbdb642006-04-09 01:32:52 +0000313 case 18: /* UARTDMACR */
Peter Maydell041ac052016-10-12 18:54:36 +0100314 r = s->dmacr;
315 break;
316 case 0x3f8 ... 0x400:
317 r = s->id[(offset - 0xfe0) >> 2];
318 break;
pbrookcdbdb642006-04-09 01:32:52 +0000319 default:
Peter Maydell6d5433e2012-10-18 14:11:40 +0100320 qemu_log_mask(LOG_GUEST_ERROR,
Peter Maydell76b09fa2019-02-21 18:17:46 +0000321 "pl011_read: Bad offset 0x%x\n", (int)offset);
Peter Maydell041ac052016-10-12 18:54:36 +0100322 r = 0;
323 break;
pbrookcdbdb642006-04-09 01:32:52 +0000324 }
Peter Maydell041ac052016-10-12 18:54:36 +0100325
Philippe Mathieu-Daudé51141ca2023-05-22 10:31:40 +0200326 trace_pl011_read(offset, r, pl011_regname(offset));
Peter Maydell041ac052016-10-12 18:54:36 +0100327 return r;
pbrookcdbdb642006-04-09 01:32:52 +0000328}
329
Andreas Färberab640bf2013-07-24 23:13:57 +0200330static void pl011_set_read_trigger(PL011State *s)
pbrookcdbdb642006-04-09 01:32:52 +0000331{
332#if 0
333 /* The docs say the RX interrupt is triggered when the FIFO exceeds
334 the threshold. However linux only reads the FIFO in response to an
335 interrupt. Triggering the interrupt when the FIFO is non-empty seems
336 to make things work. */
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +0200337 if (s->lcr & LCR_FEN)
pbrookcdbdb642006-04-09 01:32:52 +0000338 s->read_trigger = (s->ifl >> 1) & 0x1c;
339 else
340#endif
341 s->read_trigger = 1;
342}
343
Luc Michelaac63e02020-10-10 15:57:58 +0200344static unsigned int pl011_get_baudrate(const PL011State *s)
345{
346 uint64_t clk;
347
Baruch Siach31cb7692022-10-06 13:19:48 +0300348 if (s->ibrd == 0) {
Luc Michelaac63e02020-10-10 15:57:58 +0200349 return 0;
350 }
351
352 clk = clock_get_hz(s->clk);
353 return (clk / ((s->ibrd << 6) + s->fbrd)) << 2;
354}
355
356static void pl011_trace_baudrate_change(const PL011State *s)
357{
358 trace_pl011_baudrate_change(pl011_get_baudrate(s),
359 clock_get_hz(s->clk),
360 s->ibrd, s->fbrd);
361}
362
Tong Hof576e072024-02-26 21:48:55 -0800363static void pl011_loopback_mdmctrl(PL011State *s)
364{
365 uint32_t cr, fr, il;
366
367 if (!pl011_loopback_enabled(s)) {
368 return;
369 }
370
371 /*
372 * Loopback software-driven modem control outputs to modem status inputs:
373 * FR.RI <= CR.Out2
374 * FR.DCD <= CR.Out1
375 * FR.CTS <= CR.RTS
376 * FR.DSR <= CR.DTR
377 *
378 * The loopback happens immediately even if this call is triggered
379 * by setting only CR.LBE.
380 *
381 * CTS/RTS updates due to enabled hardware flow controls are not
382 * dealt with here.
383 */
384 cr = s->cr;
385 fr = s->flags & ~(PL011_FLAG_RI | PL011_FLAG_DCD |
386 PL011_FLAG_DSR | PL011_FLAG_CTS);
387 fr |= (cr & CR_OUT2) ? PL011_FLAG_RI : 0;
388 fr |= (cr & CR_OUT1) ? PL011_FLAG_DCD : 0;
389 fr |= (cr & CR_RTS) ? PL011_FLAG_CTS : 0;
390 fr |= (cr & CR_DTR) ? PL011_FLAG_DSR : 0;
391
392 /* Change interrupts based on updated FR */
393 il = s->int_level & ~(INT_DSR | INT_DCD | INT_CTS | INT_RI);
394 il |= (fr & PL011_FLAG_DSR) ? INT_DSR : 0;
395 il |= (fr & PL011_FLAG_DCD) ? INT_DCD : 0;
396 il |= (fr & PL011_FLAG_CTS) ? INT_CTS : 0;
397 il |= (fr & PL011_FLAG_RI) ? INT_RI : 0;
398
399 s->flags = fr;
400 s->int_level = il;
401 pl011_update(s);
402}
403
Tong Hof576e072024-02-26 21:48:55 -0800404static void pl011_loopback_break(PL011State *s, int brk_enable)
405{
406 if (brk_enable) {
407 pl011_loopback_tx(s, DR_BE);
408 }
409}
410
Avi Kivitya8170e52012-10-23 12:30:10 +0200411static void pl011_write(void *opaque, hwaddr offset,
Avi Kivity48484752011-10-10 17:08:49 +0200412 uint64_t value, unsigned size)
pbrookcdbdb642006-04-09 01:32:52 +0000413{
Andreas Färberab640bf2013-07-24 23:13:57 +0200414 PL011State *s = (PL011State *)opaque;
pbrookcdbdb642006-04-09 01:32:52 +0000415 unsigned char ch;
416
Philippe Mathieu-Daudé51141ca2023-05-22 10:31:40 +0200417 trace_pl011_write(offset, value, pl011_regname(offset));
Peter Maydell041ac052016-10-12 18:54:36 +0100418
pbrookcdbdb642006-04-09 01:32:52 +0000419 switch (offset >> 2) {
420 case 0: /* UARTDR */
pbrookcdbdb642006-04-09 01:32:52 +0000421 ch = value;
Philippe Mathieu-Daudébd6051b2023-05-22 10:41:04 +0200422 pl011_write_txdata(s, ch);
pbrookcdbdb642006-04-09 01:32:52 +0000423 break;
Rob Herringce8f0902014-03-18 13:18:40 -0500424 case 1: /* UARTRSR/UARTECR */
425 s->rsr = 0;
pbrookcdbdb642006-04-09 01:32:52 +0000426 break;
pbrook9ee6e8b2007-11-11 00:04:49 +0000427 case 6: /* UARTFR */
428 /* Writes to Flag register are ignored. */
429 break;
Philippe Mathieu-Daudé51141ca2023-05-22 10:31:40 +0200430 case 8: /* UARTILPR */
pbrookcdbdb642006-04-09 01:32:52 +0000431 s->ilpr = value;
432 break;
433 case 9: /* UARTIBRD */
Zheyu Mab88cfee2024-07-02 17:57:52 +0200434 s->ibrd = value & IBRD_MASK;
Luc Michelaac63e02020-10-10 15:57:58 +0200435 pl011_trace_baudrate_change(s);
pbrookcdbdb642006-04-09 01:32:52 +0000436 break;
437 case 10: /* UARTFBRD */
Zheyu Mab88cfee2024-07-02 17:57:52 +0200438 s->fbrd = value & FBRD_MASK;
Luc Michelaac63e02020-10-10 15:57:58 +0200439 pl011_trace_baudrate_change(s);
pbrookcdbdb642006-04-09 01:32:52 +0000440 break;
441 case 11: /* UARTLCR_H */
Rob Herring22709e92014-03-18 13:18:39 -0500442 /* Reset the FIFO state on FIFO enable or disable */
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +0200443 if ((s->lcr ^ value) & LCR_FEN) {
Philippe Mathieu-Daudé02b1f7f2023-05-22 15:52:06 +0200444 pl011_reset_rx_fifo(s);
445 pl011_reset_tx_fifo(s);
Rob Herring22709e92014-03-18 13:18:39 -0500446 }
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +0200447 if ((s->lcr ^ value) & LCR_BRK) {
448 int break_enable = value & LCR_BRK;
Jan Luebbed60af902021-08-06 16:47:00 +0200449 qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
450 &break_enable);
Tong Hof576e072024-02-26 21:48:55 -0800451 pl011_loopback_break(s, break_enable);
Jan Luebbed60af902021-08-06 16:47:00 +0200452 }
pbrookcdbdb642006-04-09 01:32:52 +0000453 s->lcr = value;
454 pl011_set_read_trigger(s);
455 break;
456 case 12: /* UARTCR */
Tong Hof576e072024-02-26 21:48:55 -0800457 /* ??? Need to implement the enable bit. */
pbrookcdbdb642006-04-09 01:32:52 +0000458 s->cr = value;
Tong Hof576e072024-02-26 21:48:55 -0800459 pl011_loopback_mdmctrl(s);
pbrookcdbdb642006-04-09 01:32:52 +0000460 break;
461 case 13: /* UARTIFS */
462 s->ifl = value;
463 pl011_set_read_trigger(s);
464 break;
465 case 14: /* UARTIMSC */
466 s->int_enabled = value;
467 pl011_update(s);
468 break;
469 case 17: /* UARTICR */
470 s->int_level &= ~value;
471 pl011_update(s);
472 break;
473 case 18: /* UARTDMACR */
474 s->dmacr = value;
Peter Maydell6d5433e2012-10-18 14:11:40 +0100475 if (value & 3) {
476 qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
477 }
pbrookcdbdb642006-04-09 01:32:52 +0000478 break;
479 default:
Peter Maydell6d5433e2012-10-18 14:11:40 +0100480 qemu_log_mask(LOG_GUEST_ERROR,
Peter Maydell76b09fa2019-02-21 18:17:46 +0000481 "pl011_write: Bad offset 0x%x\n", (int)offset);
pbrookcdbdb642006-04-09 01:32:52 +0000482 }
483}
484
thsaa1f17c2007-07-11 22:48:58 +0000485static int pl011_can_receive(void *opaque)
pbrookcdbdb642006-04-09 01:32:52 +0000486{
Andreas Färberab640bf2013-07-24 23:13:57 +0200487 PL011State *s = (PL011State *)opaque;
Peter Maydell041ac052016-10-12 18:54:36 +0100488 int r;
pbrookcdbdb642006-04-09 01:32:52 +0000489
Evgeny Iakovlev9d889352023-01-23 17:23:00 +0100490 r = s->read_count < pl011_get_fifo_depth(s);
Peter Maydell041ac052016-10-12 18:54:36 +0100491 trace_pl011_can_receive(s->lcr, s->read_count, r);
492 return r;
pbrookcdbdb642006-04-09 01:32:52 +0000493}
494
aurel32cc9c9ff2008-04-08 19:51:43 +0000495static void pl011_receive(void *opaque, const uint8_t *buf, int size)
496{
Tong Hof576e072024-02-26 21:48:55 -0800497 /*
498 * In loopback mode, the RX input signal is internally disconnected
499 * from the entire receiving logics; thus, all inputs are ignored,
500 * and BREAK detection on RX input signal is also not performed.
501 */
502 if (pl011_loopback_enabled(opaque)) {
503 return;
504 }
505
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200506 pl011_fifo_rx_put(opaque, *buf);
aurel32cc9c9ff2008-04-08 19:51:43 +0000507}
508
Philippe Mathieu-Daudé083b2662019-12-18 18:20:09 +0100509static void pl011_event(void *opaque, QEMUChrEvent event)
pbrookcdbdb642006-04-09 01:32:52 +0000510{
Tong Hof576e072024-02-26 21:48:55 -0800511 if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) {
Philippe Mathieu-Daudé40871ca2024-07-18 17:31:30 +0200512 pl011_fifo_rx_put(opaque, DR_BE);
Philippe Mathieu-Daudé07738852023-05-22 10:37:30 +0200513 }
pbrookcdbdb642006-04-09 01:32:52 +0000514}
515
Peter Maydell5ee0abe2021-02-19 14:45:34 +0000516static void pl011_clock_update(void *opaque, ClockEvent event)
Luc Michelaac63e02020-10-10 15:57:58 +0200517{
518 PL011State *s = PL011(opaque);
519
520 pl011_trace_baudrate_change(s);
521}
522
Avi Kivity48484752011-10-10 17:08:49 +0200523static const MemoryRegionOps pl011_ops = {
524 .read = pl011_read,
525 .write = pl011_write,
526 .endianness = DEVICE_NATIVE_ENDIAN,
Philippe Mathieu-Daudé7e66d522023-07-10 17:55:56 +0200527 .impl.min_access_size = 4,
528 .impl.max_access_size = 4,
pbrookcdbdb642006-04-09 01:32:52 +0000529};
530
Gavin Shane6fa9782021-03-18 10:38:01 +0800531static bool pl011_clock_needed(void *opaque)
532{
533 PL011State *s = PL011(opaque);
534
535 return s->migrate_clk;
536}
537
Luc Michelaac63e02020-10-10 15:57:58 +0200538static const VMStateDescription vmstate_pl011_clock = {
539 .name = "pl011/clock",
540 .version_id = 1,
541 .minimum_version_id = 1,
Gavin Shane6fa9782021-03-18 10:38:01 +0800542 .needed = pl011_clock_needed,
Richard Henderson2f6cab02023-12-21 14:16:06 +1100543 .fields = (const VMStateField[]) {
Luc Michelaac63e02020-10-10 15:57:58 +0200544 VMSTATE_CLOCK(clk, PL011State),
545 VMSTATE_END_OF_LIST()
546 }
547};
548
Evgeny Iakovlev13ea96f2023-01-23 17:23:01 +0100549static int pl011_post_load(void *opaque, int version_id)
550{
551 PL011State* s = opaque;
552
553 /* Sanity-check input state */
554 if (s->read_pos >= ARRAY_SIZE(s->read_fifo) ||
555 s->read_count > ARRAY_SIZE(s->read_fifo)) {
556 return -1;
557 }
558
559 if (!pl011_is_fifo_enabled(s) && s->read_count > 0 && s->read_pos > 0) {
560 /*
561 * Older versions of PL011 didn't ensure that the single
562 * character in the FIFO in FIFO-disabled mode is in
563 * element 0 of the array; convert to follow the current
564 * code's assumptions.
565 */
566 s->read_fifo[0] = s->read_fifo[s->read_pos];
567 s->read_pos = 0;
568 }
569
Zheyu Mab88cfee2024-07-02 17:57:52 +0200570 s->ibrd &= IBRD_MASK;
571 s->fbrd &= FBRD_MASK;
572
Evgeny Iakovlev13ea96f2023-01-23 17:23:01 +0100573 return 0;
574}
575
Juan Quintela02b68752010-12-02 01:50:33 +0100576static const VMStateDescription vmstate_pl011 = {
577 .name = "pl011",
Rob Herringce8f0902014-03-18 13:18:40 -0500578 .version_id = 2,
579 .minimum_version_id = 2,
Evgeny Iakovlev13ea96f2023-01-23 17:23:01 +0100580 .post_load = pl011_post_load,
Richard Henderson2f6cab02023-12-21 14:16:06 +1100581 .fields = (const VMStateField[]) {
Philippe Mathieu-Daudé41df6c82023-11-10 07:21:20 +0100582 VMSTATE_UNUSED(sizeof(uint32_t)),
Andreas Färberab640bf2013-07-24 23:13:57 +0200583 VMSTATE_UINT32(flags, PL011State),
584 VMSTATE_UINT32(lcr, PL011State),
Rob Herringce8f0902014-03-18 13:18:40 -0500585 VMSTATE_UINT32(rsr, PL011State),
Andreas Färberab640bf2013-07-24 23:13:57 +0200586 VMSTATE_UINT32(cr, PL011State),
587 VMSTATE_UINT32(dmacr, PL011State),
588 VMSTATE_UINT32(int_enabled, PL011State),
589 VMSTATE_UINT32(int_level, PL011State),
Evgeny Iakovlev9d889352023-01-23 17:23:00 +0100590 VMSTATE_UINT32_ARRAY(read_fifo, PL011State, PL011_FIFO_DEPTH),
Andreas Färberab640bf2013-07-24 23:13:57 +0200591 VMSTATE_UINT32(ilpr, PL011State),
592 VMSTATE_UINT32(ibrd, PL011State),
593 VMSTATE_UINT32(fbrd, PL011State),
594 VMSTATE_UINT32(ifl, PL011State),
595 VMSTATE_INT32(read_pos, PL011State),
596 VMSTATE_INT32(read_count, PL011State),
597 VMSTATE_INT32(read_trigger, PL011State),
Juan Quintela02b68752010-12-02 01:50:33 +0100598 VMSTATE_END_OF_LIST()
Luc Michelaac63e02020-10-10 15:57:58 +0200599 },
Richard Henderson2f6cab02023-12-21 14:16:06 +1100600 .subsections = (const VMStateDescription * const []) {
Luc Michelaac63e02020-10-10 15:57:58 +0200601 &vmstate_pl011_clock,
602 NULL
Juan Quintela02b68752010-12-02 01:50:33 +0100603 }
604};
pbrook23e39292008-07-02 16:48:32 +0000605
xiaoqiang zhaof0d1d2c2016-06-06 16:59:31 +0100606static Property pl011_properties[] = {
607 DEFINE_PROP_CHR("chardev", PL011State, chr),
Gavin Shane6fa9782021-03-18 10:38:01 +0800608 DEFINE_PROP_BOOL("migrate-clk", PL011State, migrate_clk, true),
xiaoqiang zhaof0d1d2c2016-06-06 16:59:31 +0100609 DEFINE_PROP_END_OF_LIST(),
610};
611
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200612static void pl011_init(Object *obj)
pbrookcdbdb642006-04-09 01:32:52 +0000613{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200614 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
615 PL011State *s = PL011(obj);
Peter Maydella3c1ca52019-02-21 18:17:46 +0000616 int i;
pbrookcdbdb642006-04-09 01:32:52 +0000617
Paolo Bonzini300b1fc2013-06-06 21:25:08 -0400618 memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000);
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200619 sysbus_init_mmio(sbd, &s->iomem);
Peter Maydella3c1ca52019-02-21 18:17:46 +0000620 for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
621 sysbus_init_irq(sbd, &s->irq[i]);
622 }
Paul Brooka7d518a2009-05-14 22:35:07 +0100623
Peter Maydell5ee0abe2021-02-19 14:45:34 +0000624 s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
625 ClockUpdate);
Luc Michelaac63e02020-10-10 15:57:58 +0200626
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200627 s->id = pl011_id_arm;
628}
629
630static void pl011_realize(DeviceState *dev, Error **errp)
631{
632 PL011State *s = PL011(dev);
633
Marc-André Lureaufa394ed2016-10-22 12:52:59 +0300634 qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive,
Anton Nefedov81517ba2017-07-06 15:08:49 +0300635 pl011_event, NULL, s, NULL, true);
pbrookcdbdb642006-04-09 01:32:52 +0000636}
Paul Brooka7d518a2009-05-14 22:35:07 +0100637
Evgeny Iakovlev3b7a1652023-01-23 17:23:02 +0100638static void pl011_reset(DeviceState *dev)
639{
640 PL011State *s = PL011(dev);
641
642 s->lcr = 0;
643 s->rsr = 0;
644 s->dmacr = 0;
645 s->int_enabled = 0;
646 s->int_level = 0;
647 s->ilpr = 0;
648 s->ibrd = 0;
649 s->fbrd = 0;
Evgeny Iakovlev3b7a1652023-01-23 17:23:02 +0100650 s->read_trigger = 1;
651 s->ifl = 0x12;
652 s->cr = 0x300;
Evgeny Iakovlev23dcbfc2023-01-23 17:23:03 +0100653 s->flags = 0;
Philippe Mathieu-Daudé02b1f7f2023-05-22 15:52:06 +0200654 pl011_reset_rx_fifo(s);
655 pl011_reset_tx_fifo(s);
Evgeny Iakovlev3b7a1652023-01-23 17:23:02 +0100656}
657
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200658static void pl011_class_init(ObjectClass *oc, void *data)
Paul Brooka7d518a2009-05-14 22:35:07 +0100659{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200660 DeviceClass *dc = DEVICE_CLASS(oc);
Paul Brooka7d518a2009-05-14 22:35:07 +0100661
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200662 dc->realize = pl011_realize;
Peter Maydelle3d08142024-09-13 15:31:44 +0100663 device_class_set_legacy_reset(dc, pl011_reset);
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200664 dc->vmsd = &vmstate_pl011;
Marc-André Lureau4f67d302020-01-10 19:30:32 +0400665 device_class_set_props(dc, pl011_properties);
Anthony Liguori999e12b2012-01-24 13:12:29 -0600666}
667
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100668static const TypeInfo pl011_arm_info = {
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200669 .name = TYPE_PL011,
Anthony Liguori39bffca2011-12-07 21:34:16 -0600670 .parent = TYPE_SYS_BUS_DEVICE,
Andreas Färberab640bf2013-07-24 23:13:57 +0200671 .instance_size = sizeof(PL011State),
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200672 .instance_init = pl011_init,
673 .class_init = pl011_class_init,
Anthony Liguori999e12b2012-01-24 13:12:29 -0600674};
675
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200676static void pl011_luminary_init(Object *obj)
Anthony Liguori999e12b2012-01-24 13:12:29 -0600677{
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200678 PL011State *s = PL011(obj);
Anthony Liguori999e12b2012-01-24 13:12:29 -0600679
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200680 s->id = pl011_id_luminary;
Anthony Liguori999e12b2012-01-24 13:12:29 -0600681}
682
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100683static const TypeInfo pl011_luminary_info = {
Peter Maydell694cf202019-02-21 18:17:46 +0000684 .name = TYPE_PL011_LUMINARY,
Andreas Färber71ffe1a2013-07-24 23:29:17 +0200685 .parent = TYPE_PL011,
686 .instance_init = pl011_luminary_init,
Anthony Liguori999e12b2012-01-24 13:12:29 -0600687};
688
Andreas Färber83f7d432012-02-09 15:20:55 +0100689static void pl011_register_types(void)
Paul Brooka7d518a2009-05-14 22:35:07 +0100690{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600691 type_register_static(&pl011_arm_info);
692 type_register_static(&pl011_luminary_info);
Paul Brooka7d518a2009-05-14 22:35:07 +0100693}
694
Andreas Färber83f7d432012-02-09 15:20:55 +0100695type_init(pl011_register_types)