From: Lex Bailey <lex.bailey@lowrisc.org>
The register definitions for the UART device need to be updated to match
the documentation at https://opentitan.org/book/hw/ip/uart/doc/registers.html
This commit does that, and also switches to using Fifo8 for managing the
transmit and receive buffers.
Signed-off-by: Lex Bailey <lex.bailey@lowrisc.org>
---
hw/char/ot_uart.c | 332 ++++++++++++++++++++++----------------
include/hw/char/ot_uart.h | 18 +--
2 files changed, 196 insertions(+), 154 deletions(-)
diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
index 2cf0d73cf5..3bf3295b1b 100644
--- a/hw/char/ot_uart.c
+++ b/hw/char/ot_uart.c
@@ -27,6 +27,7 @@
#include "qemu/osdep.h"
#include "hw/char/ot_uart.h"
+#include "qemu/fifo8.h"
#include "hw/core/irq.h"
#include "hw/core/qdev-clock.h"
#include "hw/core/qdev-properties.h"
@@ -36,17 +37,24 @@
#include "qemu/log.h"
#include "qemu/module.h"
+/* clang-format off */
REG32(INTR_STATE, 0x00)
- FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
- FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
- FIELD(INTR_STATE, TX_EMPTY, 2, 1)
- FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
+ SHARED_FIELD(INTR_TX_WATERMARK, 0, 1)
+ SHARED_FIELD(INTR_RX_WATERMARK, 1, 1)
+ SHARED_FIELD(INTR_TX_DONE, 2, 1)
+ SHARED_FIELD(INTR_RX_OVERFLOW, 3, 1)
+ SHARED_FIELD(INTR_RX_FRAME_ERR, 4, 1)
+ SHARED_FIELD(INTR_RX_BREAK_ERR, 5, 1)
+ SHARED_FIELD(INTR_RX_TIMEOUT, 6, 1)
+ SHARED_FIELD(INTR_RX_PARITY_ERR, 7, 1)
+ SHARED_FIELD(INTR_TX_EMPTY, 8, 1)
REG32(INTR_ENABLE, 0x04)
REG32(INTR_TEST, 0x08)
REG32(ALERT_TEST, 0x0C)
+ FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
REG32(CTRL, 0x10)
- FIELD(CTRL, TX_ENABLE, 0, 1)
- FIELD(CTRL, RX_ENABLE, 1, 1)
+ FIELD(CTRL, TX, 0, 1)
+ FIELD(CTRL, RX, 1, 1)
FIELD(CTRL, NF, 2, 1)
FIELD(CTRL, SLPBK, 4, 1)
FIELD(CTRL, LLPBK, 5, 1)
@@ -58,43 +66,78 @@ REG32(STATUS, 0x14)
FIELD(STATUS, TXFULL, 0, 1)
FIELD(STATUS, RXFULL, 1, 1)
FIELD(STATUS, TXEMPTY, 2, 1)
+ FIELD(STATUS, TXIDLE, 3, 1)
FIELD(STATUS, RXIDLE, 4, 1)
FIELD(STATUS, RXEMPTY, 5, 1)
REG32(RDATA, 0x18)
+ FIELD(RDATA, RDATA, 0, 8)
REG32(WDATA, 0x1C)
+ FIELD(WDATA, WDATA, 0, 8)
REG32(FIFO_CTRL, 0x20)
FIELD(FIFO_CTRL, RXRST, 0, 1)
FIELD(FIFO_CTRL, TXRST, 1, 1)
FIELD(FIFO_CTRL, RXILVL, 2, 3)
- FIELD(FIFO_CTRL, TXILVL, 5, 2)
+ FIELD(FIFO_CTRL, TXILVL, 5, 3)
REG32(FIFO_STATUS, 0x24)
- FIELD(FIFO_STATUS, TXLVL, 0, 5)
- FIELD(FIFO_STATUS, RXLVL, 16, 5)
+ FIELD(FIFO_STATUS, TXLVL, 0, 8)
+ FIELD(FIFO_STATUS, RXLVL, 16, 8)
REG32(OVRD, 0x28)
+ FIELD(OVRD, TXEN, 0, 1)
+ FIELD(OVRD, TXVAL, 1, 1)
REG32(VAL, 0x2C)
+ FIELD(VAL, RX, 0, 16)
REG32(TIMEOUT_CTRL, 0x30)
+ FIELD(TIMEOUT_CTRL, VAL, 0, 24)
+ FIELD(TIMEOUT_CTRL, EN, 31, 1)
+/* clang-format on */
+
+#define INTR_MASK \
+ (INTR_TX_WATERMARK_MASK | INTR_RX_WATERMARK_MASK | INTR_TX_DONE_MASK | \
+ INTR_RX_OVERFLOW_MASK | INTR_RX_FRAME_ERR_MASK | INTR_RX_BREAK_ERR_MASK | \
+ INTR_RX_TIMEOUT_MASK | INTR_RX_PARITY_ERR_MASK | INTR_TX_EMPTY_MASK)
+
+#define CTRL_MASK \
+ (R_CTRL_TX_MASK | R_CTRL_RX_MASK | R_CTRL_NF_MASK | R_CTRL_SLPBK_MASK | \
+ R_CTRL_LLPBK_MASK | R_CTRL_PARITY_EN_MASK | R_CTRL_PARITY_ODD_MASK | \
+ R_CTRL_RXBLVL_MASK | R_CTRL_NCO_MASK)
+
+#define CTRL_SUP_MASK \
+ (R_CTRL_RX_MASK | R_CTRL_TX_MASK | R_CTRL_SLPBK_MASK | R_CTRL_NCO_MASK)
+
+#define OT_UART_NCO_BITS 16
+#define OT_UART_TX_FIFO_SIZE 128
+#define OT_UART_RX_FIFO_SIZE 128
+
+#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t))
+
+#define R_LAST_REG (R_TIMEOUT_CTRL)
+#define REGS_COUNT (R_LAST_REG + 1u)
+#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t))
static void ot_uart_update_irqs(OtUARTState *s)
{
- if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
+ if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+ & INTR_TX_WATERMARK_MASK) {
qemu_set_irq(s->tx_watermark, 1);
} else {
qemu_set_irq(s->tx_watermark, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
+ if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+ & INTR_RX_WATERMARK_MASK) {
qemu_set_irq(s->rx_watermark, 1);
} else {
qemu_set_irq(s->rx_watermark, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
+ if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE] & INTR_TX_EMPTY_MASK) {
qemu_set_irq(s->tx_empty, 1);
} else {
qemu_set_irq(s->tx_empty, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
+ if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
+ & INTR_RX_OVERFLOW_MASK) {
qemu_set_irq(s->rx_overflow, 1);
} else {
qemu_set_irq(s->rx_overflow, 0);
@@ -105,126 +148,133 @@ static int ot_uart_can_receive(void *opaque)
{
OtUARTState *s = opaque;
- if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
- && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
- return 1;
+ if (s->regs[R_CTRL] & R_CTRL_RX_MASK) {
+ return (int)fifo8_num_free(&s->rx_fifo);
}
return 0;
}
+static uint32_t ot_uart_get_rx_watermark_level(const OtUARTState *s)
+{
+ uint32_t rx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, RXILVL);
+
+ return rx_ilvl < 7 ? (1 << rx_ilvl) : 126;
+}
+
static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
{
OtUARTState *s = opaque;
- uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
- >> R_FIFO_CTRL_RXILVL_SHIFT;
-
- s->uart_rdata = *buf;
+ uint32_t rx_watermark_level;
+ size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);
- s->uart_status &= ~R_STATUS_RXIDLE_MASK;
- s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
- /* The RXFULL is set after receiving a single byte
- * as the FIFO buffers are not yet implemented.
- */
- s->uart_status |= R_STATUS_RXFULL_MASK;
- s->rx_level += 1;
+ for (int index = 0; index < size; index++) {
+ fifo8_push(&s->rx_fifo, buf[index]);
+ }
- if (size > rx_fifo_level) {
- s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
+ /* update INTR_STATE */
+ if (count != size) {
+ s->regs[R_INTR_STATE] |= INTR_RX_OVERFLOW_MASK;
+ }
+ rx_watermark_level = ot_uart_get_rx_watermark_level(s);
+ if (rx_watermark_level && size >= rx_watermark_level) {
+ s->regs[R_INTR_STATE] |= INTR_RX_WATERMARK_MASK;
}
ot_uart_update_irqs(s);
}
-static gboolean ot_uart_xmit(void *do_not_use, GIOCondition cond,
- void *opaque)
+static void ot_uart_reset_tx_fifo(OtUARTState *s)
{
- OtUARTState *s = opaque;
- uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
- >> R_FIFO_CTRL_TXILVL_SHIFT;
- int ret;
-
- /* instant drain the fifo when there's no back-end */
- if (!qemu_chr_fe_backend_connected(&s->chr)) {
- s->tx_level = 0;
- return G_SOURCE_REMOVE;
+ fifo8_reset(&s->tx_fifo);
+ s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
+ s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
+ if (s->tx_watermark_level) {
+ s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
+ s->tx_watermark_level = 0;
}
+}
- if (!s->tx_level) {
- s->uart_status &= ~R_STATUS_TXFULL_MASK;
- s->uart_status |= R_STATUS_TXEMPTY_MASK;
- s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
- s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
- ot_uart_update_irqs(s);
- return G_SOURCE_REMOVE;
+static void ot_uart_reset_rx_fifo(OtUARTState *s)
+{
+ fifo8_reset(&s->rx_fifo);
+ s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;
+ s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;
+ if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {
+ qemu_chr_fe_accept_input(&s->chr);
}
+}
- ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
+static uint32_t ot_uart_get_tx_watermark_level(const OtUARTState *s)
+{
+ uint32_t tx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, TXILVL);
- if (ret >= 0) {
- s->tx_level -= ret;
- memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
- }
+ return tx_ilvl < 7 ? (1 << tx_ilvl) : 64;
+}
- if (s->tx_level) {
- guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
- ot_uart_xmit, s);
- if (!r) {
- s->tx_level = 0;
- return G_SOURCE_REMOVE;
- }
+static void ot_uart_xmit(OtUARTState *s)
+{
+ const uint8_t *buf;
+ uint32_t size;
+ int ret;
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ return;
}
- /* Clear the TX Full bit */
- if (s->tx_level != OT_UART_TX_FIFO_SIZE) {
- s->uart_status &= ~R_STATUS_TXFULL_MASK;
+ /* instant drain the fifo when there's no back-end */
+ if (!qemu_chr_fe_backend_connected(&s->chr)) {
+ ot_uart_reset_tx_fifo(s);
+ ot_uart_update_irqs(s);
+ return;
}
- /* Disable the TX_WATERMARK IRQ */
- if (s->tx_level < tx_fifo_level) {
- s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
+ /* get a continuous buffer from the FIFO */
+ buf =
+ fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);
+ /* send as much as possible */
+ ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
+ /* if some characters where sent, remove them from the FIFO */
+ if (ret >= 0) {
+ fifo8_drop(&s->tx_fifo, ret);
}
- /* Set TX empty */
- if (s->tx_level == 0) {
- s->uart_status |= R_STATUS_TXEMPTY_MASK;
- s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
+ /* update INTR_STATE */
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
+ s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
+ }
+ if (s->tx_watermark_level &&
+ fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
+ s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
+ s->tx_watermark_level = 0;
}
ot_uart_update_irqs(s);
- return G_SOURCE_REMOVE;
}
-static void uart_write_tx_fifo(OtUARTState *s, const uint8_t *buf,
- int size)
+static void uart_write_tx_fifo(OtUARTState *s, uint8_t val)
{
- uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
- >> R_FIFO_CTRL_TXILVL_SHIFT;
-
- if (size > OT_UART_TX_FIFO_SIZE - s->tx_level) {
- size = OT_UART_TX_FIFO_SIZE - s->tx_level;
+ if (fifo8_is_full(&s->tx_fifo)) {
qemu_log_mask(LOG_GUEST_ERROR, "ot_uart: TX FIFO overflow");
+ return;
}
- memcpy(s->tx_fifo + s->tx_level, buf, size);
- s->tx_level += size;
-
- if (s->tx_level > 0) {
- s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
- }
+ fifo8_push(&s->tx_fifo, val);
- if (s->tx_level >= tx_fifo_level) {
- s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
- ot_uart_update_irqs(s);
+ s->tx_watermark_level = ot_uart_get_tx_watermark_level(s);
+ if (fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
+ /*
+ * TX watermark interrupt is raised when FIFO depth goes from above
+ * watermark to below. If we haven't reached watermark, reset cached
+ * watermark level
+ */
+ s->tx_watermark_level = 0;
}
- if (s->tx_level == OT_UART_TX_FIFO_SIZE) {
- s->uart_status |= R_STATUS_TXFULL_MASK;
+ if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {
+ ot_uart_xmit(s);
}
-
- timer_mod(s->fifo_trigger_handle, current_time +
- (s->char_tx_time * 4));
}
static void ot_uart_reset_enter(Object *obj, ResetType type)
@@ -236,17 +286,13 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)
c->parent_phases.enter(obj, type);
}
- s->uart_intr_state = 0x00000000;
- s->uart_intr_state = 0x00000000;
- s->uart_intr_enable = 0x00000000;
- s->uart_ctrl = 0x00000000;
- s->uart_status = 0x0000003c;
- s->uart_rdata = 0x00000000;
- s->uart_fifo_ctrl = 0x00000000;
- s->uart_fifo_status = 0x00000000;
- s->uart_ovrd = 0x00000000;
- s->uart_val = 0x00000000;
- s->uart_timeout_ctrl = 0x00000000;
+ memset(&s->regs[0], 0, sizeof(s->regs));
+
+ s->regs[R_STATUS] = 0x0000003c;
+
+ s->tx_watermark_level = 0;
+ ot_uart_reset_tx_fifo(s);
+ ot_uart_reset_rx_fifo(s);
s->tx_level = 0;
s->rx_level = 0;
@@ -260,7 +306,7 @@ static uint64_t ot_uart_get_baud(OtUARTState *s)
{
uint64_t baud;
- baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
+ baud = ((s->regs[R_CTRL] & R_CTRL_NCO_MASK) >> 16);
baud *= clock_get_hz(s->f_clk);
baud >>= 20;
@@ -274,10 +320,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
switch (addr >> 2) {
case R_INTR_STATE:
- retvalue = s->uart_intr_state;
+ retvalue = s->regs[R_INTR_STATE];
break;
case R_INTR_ENABLE:
- retvalue = s->uart_intr_enable;
+ retvalue = s->regs[R_INTR_ENABLE];
break;
case R_INTR_TEST:
qemu_log_mask(LOG_GUEST_ERROR,
@@ -285,22 +331,22 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
break;
case R_CTRL:
- retvalue = s->uart_ctrl;
+ retvalue = s->regs[R_CTRL];
break;
case R_STATUS:
- retvalue = s->uart_status;
+ retvalue = s->regs[R_STATUS];
break;
case R_RDATA:
- retvalue = s->uart_rdata;
- if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
+ retvalue = s->regs[R_RDATA];
+ if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {
qemu_chr_fe_accept_input(&s->chr);
s->rx_level -= 1;
- s->uart_status &= ~R_STATUS_RXFULL_MASK;
+ s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;
if (s->rx_level == 0) {
- s->uart_status |= R_STATUS_RXIDLE_MASK;
- s->uart_status |= R_STATUS_RXEMPTY_MASK;
+ s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;
+ s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;
}
}
break;
@@ -310,10 +356,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
break;
case R_FIFO_CTRL:
- retvalue = s->uart_fifo_ctrl;
+ retvalue = s->regs[R_FIFO_CTRL];
break;
case R_FIFO_STATUS:
- retvalue = s->uart_fifo_status;
+ retvalue = s->regs[R_FIFO_STATUS];
retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
@@ -323,17 +369,17 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
break;
case R_OVRD:
- retvalue = s->uart_ovrd;
+ retvalue = s->regs[R_OVRD];
qemu_log_mask(LOG_UNIMP,
"%s: ovrd is not supported\n", __func__);
break;
case R_VAL:
- retvalue = s->uart_val;
+ retvalue = s->regs[R_VAL];
qemu_log_mask(LOG_UNIMP,
"%s: val is not supported\n", __func__);
break;
case R_TIMEOUT_CTRL:
- retvalue = s->uart_timeout_ctrl;
+ retvalue = s->regs[R_TIMEOUT_CTRL];
qemu_log_mask(LOG_UNIMP,
"%s: timeout_ctrl is not supported\n", __func__);
break;
@@ -355,20 +401,20 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
switch (addr >> 2) {
case R_INTR_STATE:
/* Write 1 clear */
- s->uart_intr_state &= ~value;
+ s->regs[R_INTR_STATE] &= ~value;
ot_uart_update_irqs(s);
break;
case R_INTR_ENABLE:
- s->uart_intr_enable = value;
+ s->regs[R_INTR_ENABLE] = value;
ot_uart_update_irqs(s);
break;
case R_INTR_TEST:
- s->uart_intr_state |= value;
+ s->regs[R_INTR_STATE] |= value;
ot_uart_update_irqs(s);
break;
case R_CTRL:
- s->uart_ctrl = value;
+ s->regs[R_CTRL] = value;
if (value & R_CTRL_NF_MASK) {
qemu_log_mask(LOG_UNIMP,
@@ -412,11 +458,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
"%s: rdata is read only\n", __func__);
break;
case R_WDATA:
- uart_write_tx_fifo(s, (uint8_t *) &value, 1);
+ uart_write_tx_fifo(s, value);
break;
case R_FIFO_CTRL:
- s->uart_fifo_ctrl = value;
+ s->regs[R_FIFO_CTRL] = value;
if (value & R_FIFO_CTRL_RXRST_MASK) {
s->rx_level = 0;
@@ -433,7 +479,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
break;
case R_OVRD:
- s->uart_ovrd = value;
+ s->regs[R_OVRD] = value;
qemu_log_mask(LOG_UNIMP,
"%s: ovrd is not supported\n", __func__);
break;
@@ -442,7 +488,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
"%s: val is read only\n", __func__);
break;
case R_TIMEOUT_CTRL:
- s->uart_timeout_ctrl = value;
+ s->regs[R_TIMEOUT_CTRL] = value;
qemu_log_mask(LOG_UNIMP,
"%s: timeout_ctrl is not supported\n", __func__);
break;
@@ -466,8 +512,8 @@ static void fifo_trigger_update(void *opaque)
{
OtUARTState *s = opaque;
- if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
- ot_uart_xmit(NULL, G_IO_OUT, s);
+ if (s->regs[R_CTRL] & R_CTRL_TX_MASK) {
+ ot_uart_xmit(s);
}
}
@@ -489,25 +535,17 @@ static int ot_uart_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_ot_uart = {
.name = TYPE_OT_UART,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.post_load = ot_uart_post_load,
.fields = (const VMStateField[]) {
- VMSTATE_UINT8_ARRAY(tx_fifo, OtUARTState,
- OT_UART_TX_FIFO_SIZE),
+ VMSTATE_STRUCT(tx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
+ VMSTATE_STRUCT(rx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
VMSTATE_UINT32(tx_level, OtUARTState),
VMSTATE_UINT64(char_tx_time, OtUARTState),
VMSTATE_TIMER_PTR(fifo_trigger_handle, OtUARTState),
- VMSTATE_UINT32(uart_intr_state, OtUARTState),
- VMSTATE_UINT32(uart_intr_enable, OtUARTState),
- VMSTATE_UINT32(uart_ctrl, OtUARTState),
- VMSTATE_UINT32(uart_status, OtUARTState),
- VMSTATE_UINT32(uart_rdata, OtUARTState),
- VMSTATE_UINT32(uart_fifo_ctrl, OtUARTState),
- VMSTATE_UINT32(uart_fifo_status, OtUARTState),
- VMSTATE_UINT32(uart_ovrd, OtUARTState),
- VMSTATE_UINT32(uart_val, OtUARTState),
- VMSTATE_UINT32(uart_timeout_ctrl, OtUARTState),
+ VMSTATE_ARRAY(regs, OtUARTState, REGS_COUNT, 1, vmstate_info_uint32,
+ uint32_t),
VMSTATE_END_OF_LIST()
}
};
@@ -532,6 +570,13 @@ static void ot_uart_init(Object *obj)
memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s,
TYPE_OT_UART, 0x400);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+ /*
+ * This array has a fixed size in the header. This assertion is used to
+ * check that it is consistent with the definition in this file. This is
+ * ostensibly a runtime check, but may be optimised away by the compiler.
+ */
+ assert(REGS_SIZE == sizeof(s->regs));
}
static void ot_uart_realize(DeviceState *dev, Error **errp)
@@ -541,6 +586,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
fifo_trigger_update, s);
+ fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
+ fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);
+
qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive,
ot_uart_receive, NULL, NULL,
s, NULL, true);
diff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h
index fee2128f90..f489612700 100644
--- a/include/hw/char/ot_uart.h
+++ b/include/hw/char/ot_uart.h
@@ -26,11 +26,11 @@
#define HW_OT_UART_H
#include "hw/core/sysbus.h"
+#include "qemu/fifo8.h"
#include "chardev/char-fe.h"
#include "qemu/timer.h"
#include "qom/object.h"
-#define OT_UART_TX_FIFO_SIZE 16
#define OT_UART_CLOCK 50000000 /* 50MHz clock */
#define TYPE_OT_UART "ot-uart"
@@ -43,7 +43,6 @@ struct OtUARTState {
/* <public> */
MemoryRegion mmio;
- uint8_t tx_fifo[OT_UART_TX_FIFO_SIZE];
uint32_t tx_level;
uint32_t rx_level;
@@ -51,16 +50,11 @@ struct OtUARTState {
QEMUTimer *fifo_trigger_handle;
uint64_t char_tx_time;
- uint32_t uart_intr_state;
- uint32_t uart_intr_enable;
- uint32_t uart_ctrl;
- uint32_t uart_status;
- uint32_t uart_rdata;
- uint32_t uart_fifo_ctrl;
- uint32_t uart_fifo_status;
- uint32_t uart_ovrd;
- uint32_t uart_val;
- uint32_t uart_timeout_ctrl;
+ uint32_t regs[13]; /* Length must be updated if regs added or removed */
+
+ Fifo8 tx_fifo;
+ Fifo8 rx_fifo;
+ uint32_t tx_watermark_level;
Clock *f_clk;
--
2.49.1
On Thu, Apr 9, 2026 at 5:52 AM ~lexbaileylowrisc
<lexbaileylowrisc@git.sr.ht> wrote:
>
> From: Lex Bailey <lex.bailey@lowrisc.org>
>
> The register definitions for the UART device need to be updated to match
> the documentation at https://opentitan.org/book/hw/ip/uart/doc/registers.html
>
> This commit does that, and also switches to using Fifo8 for managing the
> transmit and receive buffers.
>
> Signed-off-by: Lex Bailey <lex.bailey@lowrisc.org>
> ---
> hw/char/ot_uart.c | 332 ++++++++++++++++++++++----------------
> include/hw/char/ot_uart.h | 18 +--
> 2 files changed, 196 insertions(+), 154 deletions(-)
>
> diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c
> index 2cf0d73cf5..3bf3295b1b 100644
> --- a/hw/char/ot_uart.c
> +++ b/hw/char/ot_uart.c
> @@ -27,6 +27,7 @@
>
> #include "qemu/osdep.h"
> #include "hw/char/ot_uart.h"
> +#include "qemu/fifo8.h"
> #include "hw/core/irq.h"
> #include "hw/core/qdev-clock.h"
> #include "hw/core/qdev-properties.h"
> @@ -36,17 +37,24 @@
> #include "qemu/log.h"
> #include "qemu/module.h"
>
> +/* clang-format off */
Let's remove these
> REG32(INTR_STATE, 0x00)
> - FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
> - FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
> - FIELD(INTR_STATE, TX_EMPTY, 2, 1)
> - FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
> + SHARED_FIELD(INTR_TX_WATERMARK, 0, 1)
> + SHARED_FIELD(INTR_RX_WATERMARK, 1, 1)
> + SHARED_FIELD(INTR_TX_DONE, 2, 1)
> + SHARED_FIELD(INTR_RX_OVERFLOW, 3, 1)
> + SHARED_FIELD(INTR_RX_FRAME_ERR, 4, 1)
> + SHARED_FIELD(INTR_RX_BREAK_ERR, 5, 1)
> + SHARED_FIELD(INTR_RX_TIMEOUT, 6, 1)
> + SHARED_FIELD(INTR_RX_PARITY_ERR, 7, 1)
> + SHARED_FIELD(INTR_TX_EMPTY, 8, 1)
> REG32(INTR_ENABLE, 0x04)
> REG32(INTR_TEST, 0x08)
> REG32(ALERT_TEST, 0x0C)
> + FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
> REG32(CTRL, 0x10)
> - FIELD(CTRL, TX_ENABLE, 0, 1)
> - FIELD(CTRL, RX_ENABLE, 1, 1)
> + FIELD(CTRL, TX, 0, 1)
> + FIELD(CTRL, RX, 1, 1)
> FIELD(CTRL, NF, 2, 1)
> FIELD(CTRL, SLPBK, 4, 1)
> FIELD(CTRL, LLPBK, 5, 1)
> @@ -58,43 +66,78 @@ REG32(STATUS, 0x14)
> FIELD(STATUS, TXFULL, 0, 1)
> FIELD(STATUS, RXFULL, 1, 1)
> FIELD(STATUS, TXEMPTY, 2, 1)
> + FIELD(STATUS, TXIDLE, 3, 1)
> FIELD(STATUS, RXIDLE, 4, 1)
> FIELD(STATUS, RXEMPTY, 5, 1)
> REG32(RDATA, 0x18)
> + FIELD(RDATA, RDATA, 0, 8)
> REG32(WDATA, 0x1C)
> + FIELD(WDATA, WDATA, 0, 8)
> REG32(FIFO_CTRL, 0x20)
> FIELD(FIFO_CTRL, RXRST, 0, 1)
> FIELD(FIFO_CTRL, TXRST, 1, 1)
> FIELD(FIFO_CTRL, RXILVL, 2, 3)
> - FIELD(FIFO_CTRL, TXILVL, 5, 2)
> + FIELD(FIFO_CTRL, TXILVL, 5, 3)
> REG32(FIFO_STATUS, 0x24)
> - FIELD(FIFO_STATUS, TXLVL, 0, 5)
> - FIELD(FIFO_STATUS, RXLVL, 16, 5)
> + FIELD(FIFO_STATUS, TXLVL, 0, 8)
> + FIELD(FIFO_STATUS, RXLVL, 16, 8)
> REG32(OVRD, 0x28)
> + FIELD(OVRD, TXEN, 0, 1)
> + FIELD(OVRD, TXVAL, 1, 1)
> REG32(VAL, 0x2C)
> + FIELD(VAL, RX, 0, 16)
> REG32(TIMEOUT_CTRL, 0x30)
> + FIELD(TIMEOUT_CTRL, VAL, 0, 24)
> + FIELD(TIMEOUT_CTRL, EN, 31, 1)
> +/* clang-format on */
> +
> +#define INTR_MASK \
> + (INTR_TX_WATERMARK_MASK | INTR_RX_WATERMARK_MASK | INTR_TX_DONE_MASK | \
> + INTR_RX_OVERFLOW_MASK | INTR_RX_FRAME_ERR_MASK | INTR_RX_BREAK_ERR_MASK | \
> + INTR_RX_TIMEOUT_MASK | INTR_RX_PARITY_ERR_MASK | INTR_TX_EMPTY_MASK)
> +
> +#define CTRL_MASK \
> + (R_CTRL_TX_MASK | R_CTRL_RX_MASK | R_CTRL_NF_MASK | R_CTRL_SLPBK_MASK | \
> + R_CTRL_LLPBK_MASK | R_CTRL_PARITY_EN_MASK | R_CTRL_PARITY_ODD_MASK | \
> + R_CTRL_RXBLVL_MASK | R_CTRL_NCO_MASK)
> +
> +#define CTRL_SUP_MASK \
> + (R_CTRL_RX_MASK | R_CTRL_TX_MASK | R_CTRL_SLPBK_MASK | R_CTRL_NCO_MASK)
> +
> +#define OT_UART_NCO_BITS 16
> +#define OT_UART_TX_FIFO_SIZE 128
> +#define OT_UART_RX_FIFO_SIZE 128
> +
> +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t))
> +
> +#define R_LAST_REG (R_TIMEOUT_CTRL)
> +#define REGS_COUNT (R_LAST_REG + 1u)
> +#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t))
>
> static void ot_uart_update_irqs(OtUARTState *s)
> {
> - if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
> + if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> + & INTR_TX_WATERMARK_MASK) {
> qemu_set_irq(s->tx_watermark, 1);
> } else {
> qemu_set_irq(s->tx_watermark, 0);
> }
>
> - if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
> + if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> + & INTR_RX_WATERMARK_MASK) {
> qemu_set_irq(s->rx_watermark, 1);
> } else {
> qemu_set_irq(s->rx_watermark, 0);
> }
>
> - if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
> + if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE] & INTR_TX_EMPTY_MASK) {
> qemu_set_irq(s->tx_empty, 1);
> } else {
> qemu_set_irq(s->tx_empty, 0);
> }
>
> - if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
> + if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]
> + & INTR_RX_OVERFLOW_MASK) {
> qemu_set_irq(s->rx_overflow, 1);
> } else {
> qemu_set_irq(s->rx_overflow, 0);
> @@ -105,126 +148,133 @@ static int ot_uart_can_receive(void *opaque)
> {
> OtUARTState *s = opaque;
>
> - if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
> - && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
> - return 1;
> + if (s->regs[R_CTRL] & R_CTRL_RX_MASK) {
> + return (int)fifo8_num_free(&s->rx_fifo);
> }
>
> return 0;
> }
>
> +static uint32_t ot_uart_get_rx_watermark_level(const OtUARTState *s)
> +{
> + uint32_t rx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, RXILVL);
> +
> + return rx_ilvl < 7 ? (1 << rx_ilvl) : 126;
> +}
> +
> static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)
> {
> OtUARTState *s = opaque;
> - uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
> - >> R_FIFO_CTRL_RXILVL_SHIFT;
> -
> - s->uart_rdata = *buf;
> + uint32_t rx_watermark_level;
> + size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);
>
> - s->uart_status &= ~R_STATUS_RXIDLE_MASK;
> - s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
> - /* The RXFULL is set after receiving a single byte
> - * as the FIFO buffers are not yet implemented.
> - */
> - s->uart_status |= R_STATUS_RXFULL_MASK;
> - s->rx_level += 1;
> + for (int index = 0; index < size; index++) {
> + fifo8_push(&s->rx_fifo, buf[index]);
> + }
>
> - if (size > rx_fifo_level) {
> - s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
> + /* update INTR_STATE */
> + if (count != size) {
> + s->regs[R_INTR_STATE] |= INTR_RX_OVERFLOW_MASK;
> + }
> + rx_watermark_level = ot_uart_get_rx_watermark_level(s);
> + if (rx_watermark_level && size >= rx_watermark_level) {
> + s->regs[R_INTR_STATE] |= INTR_RX_WATERMARK_MASK;
> }
>
> ot_uart_update_irqs(s);
> }
>
> -static gboolean ot_uart_xmit(void *do_not_use, GIOCondition cond,
> - void *opaque)
> +static void ot_uart_reset_tx_fifo(OtUARTState *s)
> {
> - OtUARTState *s = opaque;
> - uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
> - >> R_FIFO_CTRL_TXILVL_SHIFT;
> - int ret;
> -
> - /* instant drain the fifo when there's no back-end */
> - if (!qemu_chr_fe_backend_connected(&s->chr)) {
> - s->tx_level = 0;
> - return G_SOURCE_REMOVE;
> + fifo8_reset(&s->tx_fifo);
> + s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
> + s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
> + if (s->tx_watermark_level) {
> + s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
> + s->tx_watermark_level = 0;
> }
> +}
>
> - if (!s->tx_level) {
> - s->uart_status &= ~R_STATUS_TXFULL_MASK;
> - s->uart_status |= R_STATUS_TXEMPTY_MASK;
> - s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
> - s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
> - ot_uart_update_irqs(s);
> - return G_SOURCE_REMOVE;
> +static void ot_uart_reset_rx_fifo(OtUARTState *s)
> +{
> + fifo8_reset(&s->rx_fifo);
> + s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;
> + s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;
> + if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {
> + qemu_chr_fe_accept_input(&s->chr);
> }
> +}
>
> - ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
> +static uint32_t ot_uart_get_tx_watermark_level(const OtUARTState *s)
> +{
> + uint32_t tx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, TXILVL);
>
> - if (ret >= 0) {
> - s->tx_level -= ret;
> - memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
> - }
> + return tx_ilvl < 7 ? (1 << tx_ilvl) : 64;
> +}
>
> - if (s->tx_level) {
> - guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
> - ot_uart_xmit, s);
> - if (!r) {
> - s->tx_level = 0;
> - return G_SOURCE_REMOVE;
> - }
> +static void ot_uart_xmit(OtUARTState *s)
> +{
> + const uint8_t *buf;
> + uint32_t size;
> + int ret;
> +
> + if (fifo8_is_empty(&s->tx_fifo)) {
> + return;
> }
>
> - /* Clear the TX Full bit */
> - if (s->tx_level != OT_UART_TX_FIFO_SIZE) {
> - s->uart_status &= ~R_STATUS_TXFULL_MASK;
> + /* instant drain the fifo when there's no back-end */
> + if (!qemu_chr_fe_backend_connected(&s->chr)) {
> + ot_uart_reset_tx_fifo(s);
> + ot_uart_update_irqs(s);
> + return;
> }
>
> - /* Disable the TX_WATERMARK IRQ */
> - if (s->tx_level < tx_fifo_level) {
> - s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
> + /* get a continuous buffer from the FIFO */
> + buf =
> + fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);
> + /* send as much as possible */
> + ret = qemu_chr_fe_write(&s->chr, buf, (int)size);
> + /* if some characters where sent, remove them from the FIFO */
> + if (ret >= 0) {
> + fifo8_drop(&s->tx_fifo, ret);
> }
>
> - /* Set TX empty */
> - if (s->tx_level == 0) {
> - s->uart_status |= R_STATUS_TXEMPTY_MASK;
> - s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
> + /* update INTR_STATE */
> + if (fifo8_is_empty(&s->tx_fifo)) {
> + s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;
> + s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;
> + }
> + if (s->tx_watermark_level &&
> + fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
> + s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;
> + s->tx_watermark_level = 0;
> }
>
> ot_uart_update_irqs(s);
> - return G_SOURCE_REMOVE;
> }
>
> -static void uart_write_tx_fifo(OtUARTState *s, const uint8_t *buf,
> - int size)
> +static void uart_write_tx_fifo(OtUARTState *s, uint8_t val)
> {
> - uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> - uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
> - >> R_FIFO_CTRL_TXILVL_SHIFT;
> -
> - if (size > OT_UART_TX_FIFO_SIZE - s->tx_level) {
> - size = OT_UART_TX_FIFO_SIZE - s->tx_level;
> + if (fifo8_is_full(&s->tx_fifo)) {
> qemu_log_mask(LOG_GUEST_ERROR, "ot_uart: TX FIFO overflow");
> + return;
> }
>
> - memcpy(s->tx_fifo + s->tx_level, buf, size);
> - s->tx_level += size;
> -
> - if (s->tx_level > 0) {
> - s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
> - }
> + fifo8_push(&s->tx_fifo, val);
>
> - if (s->tx_level >= tx_fifo_level) {
> - s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
> - ot_uart_update_irqs(s);
> + s->tx_watermark_level = ot_uart_get_tx_watermark_level(s);
> + if (fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {
> + /*
> + * TX watermark interrupt is raised when FIFO depth goes from above
> + * watermark to below. If we haven't reached watermark, reset cached
> + * watermark level
> + */
> + s->tx_watermark_level = 0;
> }
>
> - if (s->tx_level == OT_UART_TX_FIFO_SIZE) {
> - s->uart_status |= R_STATUS_TXFULL_MASK;
> + if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {
> + ot_uart_xmit(s);
> }
> -
> - timer_mod(s->fifo_trigger_handle, current_time +
> - (s->char_tx_time * 4));
What about the timer?
Alistair
> }
>
> static void ot_uart_reset_enter(Object *obj, ResetType type)
> @@ -236,17 +286,13 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)
> c->parent_phases.enter(obj, type);
> }
>
> - s->uart_intr_state = 0x00000000;
> - s->uart_intr_state = 0x00000000;
> - s->uart_intr_enable = 0x00000000;
> - s->uart_ctrl = 0x00000000;
> - s->uart_status = 0x0000003c;
> - s->uart_rdata = 0x00000000;
> - s->uart_fifo_ctrl = 0x00000000;
> - s->uart_fifo_status = 0x00000000;
> - s->uart_ovrd = 0x00000000;
> - s->uart_val = 0x00000000;
> - s->uart_timeout_ctrl = 0x00000000;
> + memset(&s->regs[0], 0, sizeof(s->regs));
> +
> + s->regs[R_STATUS] = 0x0000003c;
> +
> + s->tx_watermark_level = 0;
> + ot_uart_reset_tx_fifo(s);
> + ot_uart_reset_rx_fifo(s);
>
> s->tx_level = 0;
> s->rx_level = 0;
> @@ -260,7 +306,7 @@ static uint64_t ot_uart_get_baud(OtUARTState *s)
> {
> uint64_t baud;
>
> - baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
> + baud = ((s->regs[R_CTRL] & R_CTRL_NCO_MASK) >> 16);
> baud *= clock_get_hz(s->f_clk);
> baud >>= 20;
>
> @@ -274,10 +320,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
>
> switch (addr >> 2) {
> case R_INTR_STATE:
> - retvalue = s->uart_intr_state;
> + retvalue = s->regs[R_INTR_STATE];
> break;
> case R_INTR_ENABLE:
> - retvalue = s->uart_intr_enable;
> + retvalue = s->regs[R_INTR_ENABLE];
> break;
> case R_INTR_TEST:
> qemu_log_mask(LOG_GUEST_ERROR,
> @@ -285,22 +331,22 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
> break;
>
> case R_CTRL:
> - retvalue = s->uart_ctrl;
> + retvalue = s->regs[R_CTRL];
> break;
> case R_STATUS:
> - retvalue = s->uart_status;
> + retvalue = s->regs[R_STATUS];
> break;
>
> case R_RDATA:
> - retvalue = s->uart_rdata;
> - if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
> + retvalue = s->regs[R_RDATA];
> + if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {
> qemu_chr_fe_accept_input(&s->chr);
>
> s->rx_level -= 1;
> - s->uart_status &= ~R_STATUS_RXFULL_MASK;
> + s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;
> if (s->rx_level == 0) {
> - s->uart_status |= R_STATUS_RXIDLE_MASK;
> - s->uart_status |= R_STATUS_RXEMPTY_MASK;
> + s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;
> + s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;
> }
> }
> break;
> @@ -310,10 +356,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
> break;
>
> case R_FIFO_CTRL:
> - retvalue = s->uart_fifo_ctrl;
> + retvalue = s->regs[R_FIFO_CTRL];
> break;
> case R_FIFO_STATUS:
> - retvalue = s->uart_fifo_status;
> + retvalue = s->regs[R_FIFO_STATUS];
>
> retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
> retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
> @@ -323,17 +369,17 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)
> break;
>
> case R_OVRD:
> - retvalue = s->uart_ovrd;
> + retvalue = s->regs[R_OVRD];
> qemu_log_mask(LOG_UNIMP,
> "%s: ovrd is not supported\n", __func__);
> break;
> case R_VAL:
> - retvalue = s->uart_val;
> + retvalue = s->regs[R_VAL];
> qemu_log_mask(LOG_UNIMP,
> "%s: val is not supported\n", __func__);
> break;
> case R_TIMEOUT_CTRL:
> - retvalue = s->uart_timeout_ctrl;
> + retvalue = s->regs[R_TIMEOUT_CTRL];
> qemu_log_mask(LOG_UNIMP,
> "%s: timeout_ctrl is not supported\n", __func__);
> break;
> @@ -355,20 +401,20 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
> switch (addr >> 2) {
> case R_INTR_STATE:
> /* Write 1 clear */
> - s->uart_intr_state &= ~value;
> + s->regs[R_INTR_STATE] &= ~value;
> ot_uart_update_irqs(s);
> break;
> case R_INTR_ENABLE:
> - s->uart_intr_enable = value;
> + s->regs[R_INTR_ENABLE] = value;
> ot_uart_update_irqs(s);
> break;
> case R_INTR_TEST:
> - s->uart_intr_state |= value;
> + s->regs[R_INTR_STATE] |= value;
> ot_uart_update_irqs(s);
> break;
>
> case R_CTRL:
> - s->uart_ctrl = value;
> + s->regs[R_CTRL] = value;
>
> if (value & R_CTRL_NF_MASK) {
> qemu_log_mask(LOG_UNIMP,
> @@ -412,11 +458,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
> "%s: rdata is read only\n", __func__);
> break;
> case R_WDATA:
> - uart_write_tx_fifo(s, (uint8_t *) &value, 1);
> + uart_write_tx_fifo(s, value);
> break;
>
> case R_FIFO_CTRL:
> - s->uart_fifo_ctrl = value;
> + s->regs[R_FIFO_CTRL] = value;
>
> if (value & R_FIFO_CTRL_RXRST_MASK) {
> s->rx_level = 0;
> @@ -433,7 +479,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
> break;
>
> case R_OVRD:
> - s->uart_ovrd = value;
> + s->regs[R_OVRD] = value;
> qemu_log_mask(LOG_UNIMP,
> "%s: ovrd is not supported\n", __func__);
> break;
> @@ -442,7 +488,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,
> "%s: val is read only\n", __func__);
> break;
> case R_TIMEOUT_CTRL:
> - s->uart_timeout_ctrl = value;
> + s->regs[R_TIMEOUT_CTRL] = value;
> qemu_log_mask(LOG_UNIMP,
> "%s: timeout_ctrl is not supported\n", __func__);
> break;
> @@ -466,8 +512,8 @@ static void fifo_trigger_update(void *opaque)
> {
> OtUARTState *s = opaque;
>
> - if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
> - ot_uart_xmit(NULL, G_IO_OUT, s);
> + if (s->regs[R_CTRL] & R_CTRL_TX_MASK) {
> + ot_uart_xmit(s);
> }
> }
>
> @@ -489,25 +535,17 @@ static int ot_uart_post_load(void *opaque, int version_id)
>
> static const VMStateDescription vmstate_ot_uart = {
> .name = TYPE_OT_UART,
> - .version_id = 1,
> - .minimum_version_id = 1,
> + .version_id = 2,
> + .minimum_version_id = 2,
> .post_load = ot_uart_post_load,
> .fields = (const VMStateField[]) {
> - VMSTATE_UINT8_ARRAY(tx_fifo, OtUARTState,
> - OT_UART_TX_FIFO_SIZE),
> + VMSTATE_STRUCT(tx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
> + VMSTATE_STRUCT(rx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),
> VMSTATE_UINT32(tx_level, OtUARTState),
> VMSTATE_UINT64(char_tx_time, OtUARTState),
> VMSTATE_TIMER_PTR(fifo_trigger_handle, OtUARTState),
> - VMSTATE_UINT32(uart_intr_state, OtUARTState),
> - VMSTATE_UINT32(uart_intr_enable, OtUARTState),
> - VMSTATE_UINT32(uart_ctrl, OtUARTState),
> - VMSTATE_UINT32(uart_status, OtUARTState),
> - VMSTATE_UINT32(uart_rdata, OtUARTState),
> - VMSTATE_UINT32(uart_fifo_ctrl, OtUARTState),
> - VMSTATE_UINT32(uart_fifo_status, OtUARTState),
> - VMSTATE_UINT32(uart_ovrd, OtUARTState),
> - VMSTATE_UINT32(uart_val, OtUARTState),
> - VMSTATE_UINT32(uart_timeout_ctrl, OtUARTState),
> + VMSTATE_ARRAY(regs, OtUARTState, REGS_COUNT, 1, vmstate_info_uint32,
> + uint32_t),
> VMSTATE_END_OF_LIST()
> }
> };
> @@ -532,6 +570,13 @@ static void ot_uart_init(Object *obj)
> memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s,
> TYPE_OT_UART, 0x400);
> sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +
> + /*
> + * This array has a fixed size in the header. This assertion is used to
> + * check that it is consistent with the definition in this file. This is
> + * ostensibly a runtime check, but may be optimised away by the compiler.
> + */
> + assert(REGS_SIZE == sizeof(s->regs));
> }
>
> static void ot_uart_realize(DeviceState *dev, Error **errp)
> @@ -541,6 +586,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)
> s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> fifo_trigger_update, s);
>
> + fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);
> + fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);
> +
> qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive,
> ot_uart_receive, NULL, NULL,
> s, NULL, true);
> diff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h
> index fee2128f90..f489612700 100644
> --- a/include/hw/char/ot_uart.h
> +++ b/include/hw/char/ot_uart.h
> @@ -26,11 +26,11 @@
> #define HW_OT_UART_H
>
> #include "hw/core/sysbus.h"
> +#include "qemu/fifo8.h"
> #include "chardev/char-fe.h"
> #include "qemu/timer.h"
> #include "qom/object.h"
>
> -#define OT_UART_TX_FIFO_SIZE 16
> #define OT_UART_CLOCK 50000000 /* 50MHz clock */
>
> #define TYPE_OT_UART "ot-uart"
> @@ -43,7 +43,6 @@ struct OtUARTState {
> /* <public> */
> MemoryRegion mmio;
>
> - uint8_t tx_fifo[OT_UART_TX_FIFO_SIZE];
> uint32_t tx_level;
>
> uint32_t rx_level;
> @@ -51,16 +50,11 @@ struct OtUARTState {
> QEMUTimer *fifo_trigger_handle;
> uint64_t char_tx_time;
>
> - uint32_t uart_intr_state;
> - uint32_t uart_intr_enable;
> - uint32_t uart_ctrl;
> - uint32_t uart_status;
> - uint32_t uart_rdata;
> - uint32_t uart_fifo_ctrl;
> - uint32_t uart_fifo_status;
> - uint32_t uart_ovrd;
> - uint32_t uart_val;
> - uint32_t uart_timeout_ctrl;
> + uint32_t regs[13]; /* Length must be updated if regs added or removed */
> +
> + Fifo8 tx_fifo;
> + Fifo8 rx_fifo;
> + uint32_t tx_watermark_level;
>
> Clock *f_clk;
>
> --
> 2.49.1
>
>
© 2016 - 2026 Red Hat, Inc.