aboutsummaryrefslogtreecommitdiff
path: root/hw/char/pl011.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char/pl011.c')
-rw-r--r--hw/char/pl011.c209
1 files changed, 120 insertions, 89 deletions
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index 615fe3a567..15df7c1e1c 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -85,7 +85,9 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
#define CR_OUT1 (1 << 12)
#define CR_RTS (1 << 11)
#define CR_DTR (1 << 10)
+#define CR_TXE (1 << 8)
#define CR_LBE (1 << 7)
+#define CR_UARTEN (1 << 0)
/* Integer Baud Rate Divider, UARTIBRD */
#define IBRD_MASK 0x3f
@@ -138,6 +140,11 @@ static void pl011_update(PL011State *s)
}
}
+static bool pl011_loopback_enabled(PL011State *s)
+{
+ return !!(s->cr & CR_LBE);
+}
+
static bool pl011_is_fifo_enabled(PL011State *s)
{
return (s->lcr & LCR_FEN) != 0;
@@ -149,41 +156,126 @@ static inline unsigned pl011_get_fifo_depth(PL011State *s)
return pl011_is_fifo_enabled(s) ? PL011_FIFO_DEPTH : 1;
}
-static inline void pl011_reset_fifo(PL011State *s)
+static inline void pl011_reset_rx_fifo(PL011State *s)
{
s->read_count = 0;
s->read_pos = 0;
/* Reset FIFO flags */
- s->flags &= ~(PL011_FLAG_RXFF | PL011_FLAG_TXFF);
- s->flags |= PL011_FLAG_RXFE | PL011_FLAG_TXFE;
+ s->flags &= ~PL011_FLAG_RXFF;
+ s->flags |= PL011_FLAG_RXFE;
+}
+
+static inline void pl011_reset_tx_fifo(PL011State *s)
+{
+ /* Reset FIFO flags */
+ s->flags &= ~PL011_FLAG_TXFF;
+ s->flags |= PL011_FLAG_TXFE;
+}
+
+static void pl011_fifo_rx_put(void *opaque, uint32_t value)
+{
+ PL011State *s = (PL011State *)opaque;
+ int slot;
+ unsigned pipe_depth;
+
+ pipe_depth = pl011_get_fifo_depth(s);
+ slot = (s->read_pos + s->read_count) & (pipe_depth - 1);
+ s->read_fifo[slot] = value;
+ s->read_count++;
+ s->flags &= ~PL011_FLAG_RXFE;
+ trace_pl011_fifo_rx_put(value, s->read_count);
+ if (s->read_count == pipe_depth) {
+ trace_pl011_fifo_rx_full();
+ s->flags |= PL011_FLAG_RXFF;
+ }
+ if (s->read_count == s->read_trigger) {
+ s->int_level |= INT_RX;
+ pl011_update(s);
+ }
+}
+
+static void pl011_loopback_tx(PL011State *s, uint32_t value)
+{
+ if (!pl011_loopback_enabled(s)) {
+ return;
+ }
+
+ /*
+ * Caveat:
+ *
+ * In real hardware, TX loopback happens at the serial-bit level
+ * and then reassembled by the RX logics back into bytes and placed
+ * into the RX fifo. That is, loopback happens after TX fifo.
+ *
+ * Because the real hardware TX fifo is time-drained at the frame
+ * rate governed by the configured serial format, some loopback
+ * bytes in TX fifo may still be able to get into the RX fifo
+ * that could be full at times while being drained at software
+ * pace.
+ *
+ * In such scenario, the RX draining pace is the major factor
+ * deciding which loopback bytes get into the RX fifo, unless
+ * hardware flow-control is enabled.
+ *
+ * For simplicity, the above described is not emulated.
+ */
+ pl011_fifo_rx_put(s, value);
+}
+
+static void pl011_write_txdata(PL011State *s, uint8_t data)
+{
+ if (!(s->cr & CR_UARTEN)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "PL011 data written to disabled UART\n");
+ }
+ if (!(s->cr & CR_TXE)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "PL011 data written to disabled TX UART\n");
+ }
+
+ /*
+ * XXX this blocks entire thread. Rewrite to use
+ * qemu_chr_fe_write and background I/O callbacks
+ */
+ qemu_chr_fe_write_all(&s->chr, &data, 1);
+ pl011_loopback_tx(s, data);
+ s->int_level |= INT_TX;
+ pl011_update(s);
+}
+
+static uint32_t pl011_read_rxdata(PL011State *s)
+{
+ uint32_t c;
+
+ s->flags &= ~PL011_FLAG_RXFF;
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1);
+ }
+ if (s->read_count == 0) {
+ s->flags |= PL011_FLAG_RXFE;
+ }
+ if (s->read_count == s->read_trigger - 1) {
+ s->int_level &= ~INT_RX;
+ }
+ trace_pl011_read_fifo(s->read_count);
+ s->rsr = c >> 8;
+ pl011_update(s);
+ qemu_chr_fe_accept_input(&s->chr);
+ return c;
}
static uint64_t pl011_read(void *opaque, hwaddr offset,
unsigned size)
{
PL011State *s = (PL011State *)opaque;
- uint32_t c;
uint64_t r;
switch (offset >> 2) {
case 0: /* UARTDR */
- s->flags &= ~PL011_FLAG_RXFF;
- c = s->read_fifo[s->read_pos];
- if (s->read_count > 0) {
- s->read_count--;
- s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1);
- }
- if (s->read_count == 0) {
- s->flags |= PL011_FLAG_RXFE;
- }
- if (s->read_count == s->read_trigger - 1)
- s->int_level &= ~ INT_RX;
- trace_pl011_read_fifo(s->read_count);
- s->rsr = c >> 8;
- pl011_update(s);
- qemu_chr_fe_accept_input(&s->chr);
- r = c;
+ r = pl011_read_rxdata(s);
break;
case 1: /* UARTRSR */
r = s->rsr;
@@ -268,11 +360,6 @@ static void pl011_trace_baudrate_change(const PL011State *s)
s->ibrd, s->fbrd);
}
-static bool pl011_loopback_enabled(PL011State *s)
-{
- return !!(s->cr & CR_LBE);
-}
-
static void pl011_loopback_mdmctrl(PL011State *s)
{
uint32_t cr, fr, il;
@@ -314,36 +401,6 @@ static void pl011_loopback_mdmctrl(PL011State *s)
pl011_update(s);
}
-static void pl011_put_fifo(void *opaque, uint32_t value);
-
-static void pl011_loopback_tx(PL011State *s, uint32_t value)
-{
- if (!pl011_loopback_enabled(s)) {
- return;
- }
-
- /*
- * Caveat:
- *
- * In real hardware, TX loopback happens at the serial-bit level
- * and then reassembled by the RX logics back into bytes and placed
- * into the RX fifo. That is, loopback happens after TX fifo.
- *
- * Because the real hardware TX fifo is time-drained at the frame
- * rate governed by the configured serial format, some loopback
- * bytes in TX fifo may still be able to get into the RX fifo
- * that could be full at times while being drained at software
- * pace.
- *
- * In such scenario, the RX draining pace is the major factor
- * deciding which loopback bytes get into the RX fifo, unless
- * hardware flow-control is enabled.
- *
- * For simplicity, the above described is not emulated.
- */
- pl011_put_fifo(s, value);
-}
-
static void pl011_loopback_break(PL011State *s, int brk_enable)
{
if (brk_enable) {
@@ -361,14 +418,8 @@ static void pl011_write(void *opaque, hwaddr offset,
switch (offset >> 2) {
case 0: /* UARTDR */
- /* ??? Check if transmitter is enabled. */
ch = value;
- /* XXX this blocks entire thread. Rewrite to use
- * qemu_chr_fe_write and background I/O callbacks */
- qemu_chr_fe_write_all(&s->chr, &ch, 1);
- pl011_loopback_tx(s, ch);
- s->int_level |= INT_TX;
- pl011_update(s);
+ pl011_write_txdata(s, ch);
break;
case 1: /* UARTRSR/UARTECR */
s->rsr = 0;
@@ -390,7 +441,8 @@ static void pl011_write(void *opaque, hwaddr offset,
case 11: /* UARTLCR_H */
/* Reset the FIFO state on FIFO enable or disable */
if ((s->lcr ^ value) & LCR_FEN) {
- pl011_reset_fifo(s);
+ pl011_reset_rx_fifo(s);
+ pl011_reset_tx_fifo(s);
}
if ((s->lcr ^ value) & LCR_BRK) {
int break_enable = value & LCR_BRK;
@@ -440,28 +492,6 @@ static int pl011_can_receive(void *opaque)
return r;
}
-static void pl011_put_fifo(void *opaque, uint32_t value)
-{
- PL011State *s = (PL011State *)opaque;
- int slot;
- unsigned pipe_depth;
-
- pipe_depth = pl011_get_fifo_depth(s);
- slot = (s->read_pos + s->read_count) & (pipe_depth - 1);
- s->read_fifo[slot] = value;
- s->read_count++;
- s->flags &= ~PL011_FLAG_RXFE;
- trace_pl011_put_fifo(value, s->read_count);
- if (s->read_count == pipe_depth) {
- trace_pl011_put_fifo_full();
- s->flags |= PL011_FLAG_RXFF;
- }
- if (s->read_count == s->read_trigger) {
- s->int_level |= INT_RX;
- pl011_update(s);
- }
-}
-
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
{
/*
@@ -473,13 +503,13 @@ static void pl011_receive(void *opaque, const uint8_t *buf, int size)
return;
}
- pl011_put_fifo(opaque, *buf);
+ pl011_fifo_rx_put(opaque, *buf);
}
static void pl011_event(void *opaque, QEMUChrEvent event)
{
if (event == CHR_EVENT_BREAK && !pl011_loopback_enabled(opaque)) {
- pl011_put_fifo(opaque, DR_BE);
+ pl011_fifo_rx_put(opaque, DR_BE);
}
}
@@ -549,7 +579,7 @@ static const VMStateDescription vmstate_pl011 = {
.minimum_version_id = 2,
.post_load = pl011_post_load,
.fields = (const VMStateField[]) {
- VMSTATE_UINT32(readbuff, PL011State),
+ VMSTATE_UNUSED(sizeof(uint32_t)),
VMSTATE_UINT32(flags, PL011State),
VMSTATE_UINT32(lcr, PL011State),
VMSTATE_UINT32(rsr, PL011State),
@@ -621,7 +651,8 @@ static void pl011_reset(DeviceState *dev)
s->ifl = 0x12;
s->cr = 0x300;
s->flags = 0;
- pl011_reset_fifo(s);
+ pl011_reset_rx_fifo(s);
+ pl011_reset_tx_fifo(s);
}
static void pl011_class_init(ObjectClass *oc, void *data)