{"id":2221190,"url":"http://patchwork.ozlabs.org/api/1.0/patches/2221190/?format=json","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.0/projects/14/?format=json","name":"QEMU Development","link_name":"qemu-devel","list_id":"qemu-devel.nongnu.org","list_email":"qemu-devel@nongnu.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<177564643888.23414.7922925369077631439-3@git.sr.ht>","date":"2026-04-07T12:44:36","name":"[qemu,v2,3/7] ot_uart: update register defs, switch to Fifo8 for tx/rx buffers","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"0dfc4c5cb5a6f6ad9f1445800bdc43a34a353005","submitter":{"id":92675,"url":"http://patchwork.ozlabs.org/api/1.0/people/92675/?format=json","name":"~lexbaileylowrisc","email":"lexbaileylowrisc@git.sr.ht"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/177564643888.23414.7922925369077631439-3@git.sr.ht/mbox/","series":[{"id":499197,"url":"http://patchwork.ozlabs.org/api/1.0/series/499197/?format=json","date":"2026-04-07T14:11:43","name":"Update opentitan uart (part of supporting opentitan version 1)","version":2,"mbox":"http://patchwork.ozlabs.org/series/499197/mbox/"}],"check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2221190/checks/","tags":{},"headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"key not found in DNS\" header.d=git.sr.ht\n header.i=@git.sr.ht header.a=rsa-sha256 header.s=20240113 header.b=ooQ3QTUV;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from lists.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4frYMB0j6xz1xy1\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 09 Apr 2026 05:40:38 +1000 (AEST)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wAYcA-00059Y-88; Wed, 08 Apr 2026 15:31:02 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <outgoing@sr.ht>)\n id 1wAYA3-0000k9-JV; Wed, 08 Apr 2026 15:01:59 -0400","from mail-a.sr.ht ([46.23.81.152])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <outgoing@sr.ht>)\n id 1wAQmh-00035u-V0; Wed, 08 Apr 2026 07:09:28 -0400","from git.sr.ht (unknown [46.23.81.155])\n by mail-a.sr.ht (Postfix) with ESMTPSA id A91EE207F7;\n Wed, 08 Apr 2026 11:07:20 +0000 (UTC)"],"DKIM-Signature":"a=rsa-sha256; bh=yewQvIAGXDimQYHAips91HCzdpavP12MIwQGGEfWhaU=;\n c=simple/simple; d=git.sr.ht;\n h=From:Date:Subject:Reply-to:In-Reply-To:To:Cc; q=dns/txt; s=20240113;\n t=1775646440; v=1;\n b=ooQ3QTUV+gYZm3WEyGbmOGxIoypYWicFrwDuJ5Xyj5F6BqTMvsYD2wrqsnvpxvl2Ot4NhYHm\n Hxv7zlAoPE5CT7bK5iJvqbouThFQwmDUkrPaBXvkFVBrRKyRCuvNIHfIg4OFk//Tdb/0R1Vky0Z\n h+Ni5k0o9YzhRO2RhviM92u26dFBZheA7qcOkI7hcRMiL+ryAeIRgwATwkb+3pSQFcNJAgv7jWb\n bRXtWixLArVUTJdNagMJop8SzfHxkqAptWZXpEEZ9Ug2nmQCM9cJMywLlitGWCScA/G9YRm+ssJ\n nmHzeXmShES76/6fIWAyYI+tAMvlkdQbXPeOgSvOJiPoQ==","From":"~lexbaileylowrisc <lexbaileylowrisc@git.sr.ht>","Date":"Tue, 07 Apr 2026 13:44:36 +0100","Subject":"[PATCH qemu v2 3/7] ot_uart: update register defs, switch to Fifo8\n for tx/rx buffers","Message-ID":"<177564643888.23414.7922925369077631439-3@git.sr.ht>","X-Mailer":"git.sr.ht","In-Reply-To":"<177564643888.23414.7922925369077631439-0@git.sr.ht>","To":"qemu-riscv@nongnu.org, Alistair Francis <Alistair.Francis@wdc.com>","Cc":"Paolo Bonzini <pbonzini@redhat.com>,\n =?utf-8?q?Marc-Andr=C3=A9?= Lureau <marcandre.lureau@redhat.com>,\n Palmer Dabbelt <palmer@dabbelt.com>, Weiwei Li <liwei1518@gmail.com>,\n Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>,\n Liu Zhiwei <zhiwei_liu@linux.alibaba.com>,\n Chao Liu <chao.liu.zevorn@gmail.com>, qemu-devel@nongnu.org,\n Amit Kumar-Hermosillo <amitkh@google.com>, nabihestefan@google.com","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"quoted-printable","MIME-Version":"1.0","Received-SPF":"pass client-ip=46.23.81.152; envelope-from=outgoing@sr.ht;\n helo=mail-a.sr.ht","X-Spam_score_int":"-5","X-Spam_score":"-0.6","X-Spam_bar":"/","X-Spam_report":"(-0.6 / 5.0 requ) BAYES_00=-1.9, DATE_IN_PAST_12_24=1.049,\n DKIM_INVALID=0.1, DKIM_SIGNED=0.1, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001,\n RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=no autolearn_force=no","X-Spam_action":"no action","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"qemu development <qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<https://lists.nongnu.org/archive/html/qemu-devel>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Reply-To":"~lexbaileylowrisc <lex.bailey@lowrisc.org>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"},"content":"From: Lex Bailey <lex.bailey@lowrisc.org>\n\nThe register definitions for the UART device need to be updated to match\nthe documentation at https://opentitan.org/book/hw/ip/uart/doc/registers.html\n\nThis commit does that, and also switches to using Fifo8 for managing the\ntransmit and receive buffers.\n\nSigned-off-by: Lex Bailey <lex.bailey@lowrisc.org>\n---\n hw/char/ot_uart.c         | 332 ++++++++++++++++++++++----------------\n include/hw/char/ot_uart.h |  18 +--\n 2 files changed, 196 insertions(+), 154 deletions(-)","diff":"diff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c\nindex 2cf0d73cf5..3bf3295b1b 100644\n--- a/hw/char/ot_uart.c\n+++ b/hw/char/ot_uart.c\n@@ -27,6 +27,7 @@\n \n #include \"qemu/osdep.h\"\n #include \"hw/char/ot_uart.h\"\n+#include \"qemu/fifo8.h\"\n #include \"hw/core/irq.h\"\n #include \"hw/core/qdev-clock.h\"\n #include \"hw/core/qdev-properties.h\"\n@@ -36,17 +37,24 @@\n #include \"qemu/log.h\"\n #include \"qemu/module.h\"\n \n+/* clang-format off */\n REG32(INTR_STATE, 0x00)\n-    FIELD(INTR_STATE, TX_WATERMARK, 0, 1)\n-    FIELD(INTR_STATE, RX_WATERMARK, 1, 1)\n-    FIELD(INTR_STATE, TX_EMPTY, 2, 1)\n-    FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)\n+    SHARED_FIELD(INTR_TX_WATERMARK, 0, 1)\n+    SHARED_FIELD(INTR_RX_WATERMARK, 1, 1)\n+    SHARED_FIELD(INTR_TX_DONE, 2, 1)\n+    SHARED_FIELD(INTR_RX_OVERFLOW, 3, 1)\n+    SHARED_FIELD(INTR_RX_FRAME_ERR, 4, 1)\n+    SHARED_FIELD(INTR_RX_BREAK_ERR, 5, 1)\n+    SHARED_FIELD(INTR_RX_TIMEOUT, 6, 1)\n+    SHARED_FIELD(INTR_RX_PARITY_ERR, 7, 1)\n+    SHARED_FIELD(INTR_TX_EMPTY, 8, 1)\n REG32(INTR_ENABLE, 0x04)\n REG32(INTR_TEST, 0x08)\n REG32(ALERT_TEST, 0x0C)\n+    FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)\n REG32(CTRL, 0x10)\n-    FIELD(CTRL, TX_ENABLE, 0, 1)\n-    FIELD(CTRL, RX_ENABLE, 1, 1)\n+    FIELD(CTRL, TX, 0, 1)\n+    FIELD(CTRL, RX, 1, 1)\n     FIELD(CTRL, NF, 2, 1)\n     FIELD(CTRL, SLPBK, 4, 1)\n     FIELD(CTRL, LLPBK, 5, 1)\n@@ -58,43 +66,78 @@ REG32(STATUS, 0x14)\n     FIELD(STATUS, TXFULL, 0, 1)\n     FIELD(STATUS, RXFULL, 1, 1)\n     FIELD(STATUS, TXEMPTY, 2, 1)\n+    FIELD(STATUS, TXIDLE, 3, 1)\n     FIELD(STATUS, RXIDLE, 4, 1)\n     FIELD(STATUS, RXEMPTY, 5, 1)\n REG32(RDATA, 0x18)\n+    FIELD(RDATA, RDATA, 0, 8)\n REG32(WDATA, 0x1C)\n+    FIELD(WDATA, WDATA, 0, 8)\n REG32(FIFO_CTRL, 0x20)\n     FIELD(FIFO_CTRL, RXRST, 0, 1)\n     FIELD(FIFO_CTRL, TXRST, 1, 1)\n     FIELD(FIFO_CTRL, RXILVL, 2, 3)\n-    FIELD(FIFO_CTRL, TXILVL, 5, 2)\n+    FIELD(FIFO_CTRL, TXILVL, 5, 3)\n REG32(FIFO_STATUS, 0x24)\n-    FIELD(FIFO_STATUS, TXLVL, 0, 5)\n-    FIELD(FIFO_STATUS, RXLVL, 16, 5)\n+    FIELD(FIFO_STATUS, TXLVL, 0, 8)\n+    FIELD(FIFO_STATUS, RXLVL, 16, 8)\n REG32(OVRD, 0x28)\n+    FIELD(OVRD, TXEN, 0, 1)\n+    FIELD(OVRD, TXVAL, 1, 1)\n REG32(VAL, 0x2C)\n+    FIELD(VAL, RX, 0, 16)\n REG32(TIMEOUT_CTRL, 0x30)\n+    FIELD(TIMEOUT_CTRL, VAL, 0, 24)\n+    FIELD(TIMEOUT_CTRL, EN, 31, 1)\n+/* clang-format on */\n+\n+#define INTR_MASK \\\n+    (INTR_TX_WATERMARK_MASK | INTR_RX_WATERMARK_MASK | INTR_TX_DONE_MASK | \\\n+     INTR_RX_OVERFLOW_MASK | INTR_RX_FRAME_ERR_MASK | INTR_RX_BREAK_ERR_MASK | \\\n+     INTR_RX_TIMEOUT_MASK | INTR_RX_PARITY_ERR_MASK | INTR_TX_EMPTY_MASK)\n+\n+#define CTRL_MASK \\\n+    (R_CTRL_TX_MASK | R_CTRL_RX_MASK | R_CTRL_NF_MASK | R_CTRL_SLPBK_MASK | \\\n+     R_CTRL_LLPBK_MASK | R_CTRL_PARITY_EN_MASK | R_CTRL_PARITY_ODD_MASK | \\\n+     R_CTRL_RXBLVL_MASK | R_CTRL_NCO_MASK)\n+\n+#define CTRL_SUP_MASK \\\n+    (R_CTRL_RX_MASK | R_CTRL_TX_MASK | R_CTRL_SLPBK_MASK | R_CTRL_NCO_MASK)\n+\n+#define OT_UART_NCO_BITS     16\n+#define OT_UART_TX_FIFO_SIZE 128\n+#define OT_UART_RX_FIFO_SIZE 128\n+\n+#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t))\n+\n+#define R_LAST_REG (R_TIMEOUT_CTRL)\n+#define REGS_COUNT (R_LAST_REG + 1u)\n+#define REGS_SIZE  (REGS_COUNT * sizeof(uint32_t))\n \n static void ot_uart_update_irqs(OtUARTState *s)\n {\n-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {\n+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]\n+        & INTR_TX_WATERMARK_MASK) {\n         qemu_set_irq(s->tx_watermark, 1);\n     } else {\n         qemu_set_irq(s->tx_watermark, 0);\n     }\n \n-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {\n+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]\n+        & INTR_RX_WATERMARK_MASK) {\n         qemu_set_irq(s->rx_watermark, 1);\n     } else {\n         qemu_set_irq(s->rx_watermark, 0);\n     }\n \n-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {\n+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE] & INTR_TX_EMPTY_MASK) {\n         qemu_set_irq(s->tx_empty, 1);\n     } else {\n         qemu_set_irq(s->tx_empty, 0);\n     }\n \n-    if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {\n+    if (s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]\n+        & INTR_RX_OVERFLOW_MASK) {\n         qemu_set_irq(s->rx_overflow, 1);\n     } else {\n         qemu_set_irq(s->rx_overflow, 0);\n@@ -105,126 +148,133 @@ static int ot_uart_can_receive(void *opaque)\n {\n     OtUARTState *s = opaque;\n \n-    if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)\n-           && !(s->uart_status & R_STATUS_RXFULL_MASK)) {\n-        return 1;\n+    if (s->regs[R_CTRL] & R_CTRL_RX_MASK) {\n+        return (int)fifo8_num_free(&s->rx_fifo);\n     }\n \n     return 0;\n }\n \n+static uint32_t ot_uart_get_rx_watermark_level(const OtUARTState *s)\n+{\n+    uint32_t rx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, RXILVL);\n+\n+    return rx_ilvl < 7 ? (1 << rx_ilvl) : 126;\n+}\n+\n static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)\n {\n     OtUARTState *s = opaque;\n-    uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)\n-                            >> R_FIFO_CTRL_RXILVL_SHIFT;\n-\n-    s->uart_rdata = *buf;\n+    uint32_t rx_watermark_level;\n+    size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);\n \n-    s->uart_status &= ~R_STATUS_RXIDLE_MASK;\n-    s->uart_status &= ~R_STATUS_RXEMPTY_MASK;\n-    /* The RXFULL is set after receiving a single byte\n-     * as the FIFO buffers are not yet implemented.\n-     */\n-    s->uart_status |= R_STATUS_RXFULL_MASK;\n-    s->rx_level += 1;\n+    for (int index = 0; index < size; index++) {\n+        fifo8_push(&s->rx_fifo, buf[index]);\n+    }\n \n-    if (size > rx_fifo_level) {\n-        s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;\n+    /* update INTR_STATE */\n+    if (count != size) {\n+        s->regs[R_INTR_STATE] |= INTR_RX_OVERFLOW_MASK;\n+    }\n+    rx_watermark_level = ot_uart_get_rx_watermark_level(s);\n+    if (rx_watermark_level && size >= rx_watermark_level) {\n+        s->regs[R_INTR_STATE] |= INTR_RX_WATERMARK_MASK;\n     }\n \n     ot_uart_update_irqs(s);\n }\n \n-static gboolean ot_uart_xmit(void *do_not_use, GIOCondition cond,\n-                             void *opaque)\n+static void ot_uart_reset_tx_fifo(OtUARTState *s)\n {\n-    OtUARTState *s = opaque;\n-    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)\n-                            >> R_FIFO_CTRL_TXILVL_SHIFT;\n-    int ret;\n-\n-    /* instant drain the fifo when there's no back-end */\n-    if (!qemu_chr_fe_backend_connected(&s->chr)) {\n-        s->tx_level = 0;\n-        return G_SOURCE_REMOVE;\n+    fifo8_reset(&s->tx_fifo);\n+    s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;\n+    s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;\n+    if (s->tx_watermark_level) {\n+        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;\n+        s->tx_watermark_level = 0;\n     }\n+}\n \n-    if (!s->tx_level) {\n-        s->uart_status &= ~R_STATUS_TXFULL_MASK;\n-        s->uart_status |= R_STATUS_TXEMPTY_MASK;\n-        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;\n-        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;\n-        ot_uart_update_irqs(s);\n-        return G_SOURCE_REMOVE;\n+static void ot_uart_reset_rx_fifo(OtUARTState *s)\n+{\n+    fifo8_reset(&s->rx_fifo);\n+    s->regs[R_INTR_STATE] &= ~INTR_RX_WATERMARK_MASK;\n+    s->regs[R_INTR_STATE] &= ~INTR_RX_OVERFLOW_MASK;\n+    if (FIELD_EX32(s->regs[R_CTRL], CTRL, RX)) {\n+        qemu_chr_fe_accept_input(&s->chr);\n     }\n+}\n \n-    ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);\n+static uint32_t ot_uart_get_tx_watermark_level(const OtUARTState *s)\n+{\n+    uint32_t tx_ilvl = FIELD_EX32(s->regs[R_FIFO_CTRL], FIFO_CTRL, TXILVL);\n \n-    if (ret >= 0) {\n-        s->tx_level -= ret;\n-        memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);\n-    }\n+    return tx_ilvl < 7 ? (1 << tx_ilvl) : 64;\n+}\n \n-    if (s->tx_level) {\n-        guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,\n-                                        ot_uart_xmit, s);\n-        if (!r) {\n-            s->tx_level = 0;\n-            return G_SOURCE_REMOVE;\n-        }\n+static void ot_uart_xmit(OtUARTState *s)\n+{\n+    const uint8_t *buf;\n+    uint32_t size;\n+    int ret;\n+\n+    if (fifo8_is_empty(&s->tx_fifo)) {\n+        return;\n     }\n \n-    /* Clear the TX Full bit */\n-    if (s->tx_level != OT_UART_TX_FIFO_SIZE) {\n-        s->uart_status &= ~R_STATUS_TXFULL_MASK;\n+    /* instant drain the fifo when there's no back-end */\n+    if (!qemu_chr_fe_backend_connected(&s->chr)) {\n+        ot_uart_reset_tx_fifo(s);\n+        ot_uart_update_irqs(s);\n+        return;\n     }\n \n-    /* Disable the TX_WATERMARK IRQ */\n-    if (s->tx_level < tx_fifo_level) {\n-        s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;\n+    /* get a continuous buffer from the FIFO */\n+    buf =\n+        fifo8_peek_bufptr(&s->tx_fifo, fifo8_num_used(&s->tx_fifo), &size);\n+    /* send as much as possible */\n+    ret = qemu_chr_fe_write(&s->chr, buf, (int)size);\n+    /* if some characters where sent, remove them from the FIFO */\n+    if (ret >= 0) {\n+        fifo8_drop(&s->tx_fifo, ret);\n     }\n \n-    /* Set TX empty */\n-    if (s->tx_level == 0) {\n-        s->uart_status |= R_STATUS_TXEMPTY_MASK;\n-        s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;\n+    /* update INTR_STATE */\n+    if (fifo8_is_empty(&s->tx_fifo)) {\n+        s->regs[R_INTR_STATE] |= INTR_TX_EMPTY_MASK;\n+        s->regs[R_INTR_STATE] |= INTR_TX_DONE_MASK;\n+    }\n+    if (s->tx_watermark_level &&\n+        fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {\n+        s->regs[R_INTR_STATE] |= INTR_TX_WATERMARK_MASK;\n+        s->tx_watermark_level = 0;\n     }\n \n     ot_uart_update_irqs(s);\n-    return G_SOURCE_REMOVE;\n }\n \n-static void uart_write_tx_fifo(OtUARTState *s, const uint8_t *buf,\n-                               int size)\n+static void uart_write_tx_fifo(OtUARTState *s, uint8_t val)\n {\n-    uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n-    uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)\n-                            >> R_FIFO_CTRL_TXILVL_SHIFT;\n-\n-    if (size > OT_UART_TX_FIFO_SIZE - s->tx_level) {\n-        size = OT_UART_TX_FIFO_SIZE - s->tx_level;\n+    if (fifo8_is_full(&s->tx_fifo)) {\n         qemu_log_mask(LOG_GUEST_ERROR, \"ot_uart: TX FIFO overflow\");\n+        return;\n     }\n \n-    memcpy(s->tx_fifo + s->tx_level, buf, size);\n-    s->tx_level += size;\n-\n-    if (s->tx_level > 0) {\n-        s->uart_status &= ~R_STATUS_TXEMPTY_MASK;\n-    }\n+    fifo8_push(&s->tx_fifo, val);\n \n-    if (s->tx_level >= tx_fifo_level) {\n-        s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;\n-        ot_uart_update_irqs(s);\n+    s->tx_watermark_level = ot_uart_get_tx_watermark_level(s);\n+    if (fifo8_num_used(&s->tx_fifo) < s->tx_watermark_level) {\n+        /*\n+         * TX watermark interrupt is raised when FIFO depth goes from above\n+         * watermark to below. If we haven't reached watermark, reset cached\n+         * watermark level\n+         */\n+        s->tx_watermark_level = 0;\n     }\n \n-    if (s->tx_level == OT_UART_TX_FIFO_SIZE) {\n-        s->uart_status |= R_STATUS_TXFULL_MASK;\n+    if (FIELD_EX32(s->regs[R_CTRL], CTRL, TX)) {\n+        ot_uart_xmit(s);\n     }\n-\n-    timer_mod(s->fifo_trigger_handle, current_time +\n-              (s->char_tx_time * 4));\n }\n \n static void ot_uart_reset_enter(Object *obj, ResetType type)\n@@ -236,17 +286,13 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)\n         c->parent_phases.enter(obj, type);\n     }\n \n-    s->uart_intr_state = 0x00000000;\n-    s->uart_intr_state = 0x00000000;\n-    s->uart_intr_enable = 0x00000000;\n-    s->uart_ctrl = 0x00000000;\n-    s->uart_status = 0x0000003c;\n-    s->uart_rdata = 0x00000000;\n-    s->uart_fifo_ctrl = 0x00000000;\n-    s->uart_fifo_status = 0x00000000;\n-    s->uart_ovrd = 0x00000000;\n-    s->uart_val = 0x00000000;\n-    s->uart_timeout_ctrl = 0x00000000;\n+    memset(&s->regs[0], 0, sizeof(s->regs));\n+\n+    s->regs[R_STATUS] = 0x0000003c;\n+\n+    s->tx_watermark_level = 0;\n+    ot_uart_reset_tx_fifo(s);\n+    ot_uart_reset_rx_fifo(s);\n \n     s->tx_level = 0;\n     s->rx_level = 0;\n@@ -260,7 +306,7 @@ static uint64_t ot_uart_get_baud(OtUARTState *s)\n {\n     uint64_t baud;\n \n-    baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);\n+    baud = ((s->regs[R_CTRL] & R_CTRL_NCO_MASK) >> 16);\n     baud *= clock_get_hz(s->f_clk);\n     baud >>= 20;\n \n@@ -274,10 +320,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)\n \n     switch (addr >> 2) {\n     case R_INTR_STATE:\n-        retvalue = s->uart_intr_state;\n+        retvalue = s->regs[R_INTR_STATE];\n         break;\n     case R_INTR_ENABLE:\n-        retvalue = s->uart_intr_enable;\n+        retvalue = s->regs[R_INTR_ENABLE];\n         break;\n     case R_INTR_TEST:\n         qemu_log_mask(LOG_GUEST_ERROR,\n@@ -285,22 +331,22 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)\n         break;\n \n     case R_CTRL:\n-        retvalue = s->uart_ctrl;\n+        retvalue = s->regs[R_CTRL];\n         break;\n     case R_STATUS:\n-        retvalue = s->uart_status;\n+        retvalue = s->regs[R_STATUS];\n         break;\n \n     case R_RDATA:\n-        retvalue = s->uart_rdata;\n-        if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {\n+        retvalue = s->regs[R_RDATA];\n+        if ((s->regs[R_CTRL] & R_CTRL_RX_MASK) && (s->rx_level > 0)) {\n             qemu_chr_fe_accept_input(&s->chr);\n \n             s->rx_level -= 1;\n-            s->uart_status &= ~R_STATUS_RXFULL_MASK;\n+            s->regs[R_STATUS] &= ~R_STATUS_RXFULL_MASK;\n             if (s->rx_level == 0) {\n-                s->uart_status |= R_STATUS_RXIDLE_MASK;\n-                s->uart_status |= R_STATUS_RXEMPTY_MASK;\n+                s->regs[R_STATUS] |= R_STATUS_RXIDLE_MASK;\n+                s->regs[R_STATUS] |= R_STATUS_RXEMPTY_MASK;\n             }\n         }\n         break;\n@@ -310,10 +356,10 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)\n         break;\n \n     case R_FIFO_CTRL:\n-        retvalue = s->uart_fifo_ctrl;\n+        retvalue = s->regs[R_FIFO_CTRL];\n         break;\n     case R_FIFO_STATUS:\n-        retvalue = s->uart_fifo_status;\n+        retvalue = s->regs[R_FIFO_STATUS];\n \n         retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;\n         retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;\n@@ -323,17 +369,17 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)\n         break;\n \n     case R_OVRD:\n-        retvalue = s->uart_ovrd;\n+        retvalue = s->regs[R_OVRD];\n         qemu_log_mask(LOG_UNIMP,\n                       \"%s: ovrd is not supported\\n\", __func__);\n         break;\n     case R_VAL:\n-        retvalue = s->uart_val;\n+        retvalue = s->regs[R_VAL];\n         qemu_log_mask(LOG_UNIMP,\n                       \"%s: val is not supported\\n\", __func__);\n         break;\n     case R_TIMEOUT_CTRL:\n-        retvalue = s->uart_timeout_ctrl;\n+        retvalue = s->regs[R_TIMEOUT_CTRL];\n         qemu_log_mask(LOG_UNIMP,\n                       \"%s: timeout_ctrl is not supported\\n\", __func__);\n         break;\n@@ -355,20 +401,20 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,\n     switch (addr >> 2) {\n     case R_INTR_STATE:\n         /* Write 1 clear */\n-        s->uart_intr_state &= ~value;\n+        s->regs[R_INTR_STATE] &= ~value;\n         ot_uart_update_irqs(s);\n         break;\n     case R_INTR_ENABLE:\n-        s->uart_intr_enable = value;\n+        s->regs[R_INTR_ENABLE] = value;\n         ot_uart_update_irqs(s);\n         break;\n     case R_INTR_TEST:\n-        s->uart_intr_state |= value;\n+        s->regs[R_INTR_STATE] |= value;\n         ot_uart_update_irqs(s);\n         break;\n \n     case R_CTRL:\n-        s->uart_ctrl = value;\n+        s->regs[R_CTRL] = value;\n \n         if (value & R_CTRL_NF_MASK) {\n             qemu_log_mask(LOG_UNIMP,\n@@ -412,11 +458,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,\n                       \"%s: rdata is read only\\n\", __func__);\n         break;\n     case R_WDATA:\n-        uart_write_tx_fifo(s, (uint8_t *) &value, 1);\n+        uart_write_tx_fifo(s, value);\n         break;\n \n     case R_FIFO_CTRL:\n-        s->uart_fifo_ctrl = value;\n+        s->regs[R_FIFO_CTRL] = value;\n \n         if (value & R_FIFO_CTRL_RXRST_MASK) {\n             s->rx_level = 0;\n@@ -433,7 +479,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,\n         break;\n \n     case R_OVRD:\n-        s->uart_ovrd = value;\n+        s->regs[R_OVRD] = value;\n         qemu_log_mask(LOG_UNIMP,\n                       \"%s: ovrd is not supported\\n\", __func__);\n         break;\n@@ -442,7 +488,7 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64,\n                       \"%s: val is read only\\n\", __func__);\n         break;\n     case R_TIMEOUT_CTRL:\n-        s->uart_timeout_ctrl = value;\n+        s->regs[R_TIMEOUT_CTRL] = value;\n         qemu_log_mask(LOG_UNIMP,\n                       \"%s: timeout_ctrl is not supported\\n\", __func__);\n         break;\n@@ -466,8 +512,8 @@ static void fifo_trigger_update(void *opaque)\n {\n     OtUARTState *s = opaque;\n \n-    if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {\n-        ot_uart_xmit(NULL, G_IO_OUT, s);\n+    if (s->regs[R_CTRL] & R_CTRL_TX_MASK) {\n+        ot_uart_xmit(s);\n     }\n }\n \n@@ -489,25 +535,17 @@ static int ot_uart_post_load(void *opaque, int version_id)\n \n static const VMStateDescription vmstate_ot_uart = {\n     .name = TYPE_OT_UART,\n-    .version_id = 1,\n-    .minimum_version_id = 1,\n+    .version_id = 2,\n+    .minimum_version_id = 2,\n     .post_load = ot_uart_post_load,\n     .fields = (const VMStateField[]) {\n-        VMSTATE_UINT8_ARRAY(tx_fifo, OtUARTState,\n-                            OT_UART_TX_FIFO_SIZE),\n+        VMSTATE_STRUCT(tx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),\n+        VMSTATE_STRUCT(rx_fifo, OtUARTState, 1, vmstate_fifo8, Fifo8),\n         VMSTATE_UINT32(tx_level, OtUARTState),\n         VMSTATE_UINT64(char_tx_time, OtUARTState),\n         VMSTATE_TIMER_PTR(fifo_trigger_handle, OtUARTState),\n-        VMSTATE_UINT32(uart_intr_state, OtUARTState),\n-        VMSTATE_UINT32(uart_intr_enable, OtUARTState),\n-        VMSTATE_UINT32(uart_ctrl, OtUARTState),\n-        VMSTATE_UINT32(uart_status, OtUARTState),\n-        VMSTATE_UINT32(uart_rdata, OtUARTState),\n-        VMSTATE_UINT32(uart_fifo_ctrl, OtUARTState),\n-        VMSTATE_UINT32(uart_fifo_status, OtUARTState),\n-        VMSTATE_UINT32(uart_ovrd, OtUARTState),\n-        VMSTATE_UINT32(uart_val, OtUARTState),\n-        VMSTATE_UINT32(uart_timeout_ctrl, OtUARTState),\n+        VMSTATE_ARRAY(regs, OtUARTState, REGS_COUNT, 1, vmstate_info_uint32,\n+                      uint32_t),\n         VMSTATE_END_OF_LIST()\n     }\n };\n@@ -532,6 +570,13 @@ static void ot_uart_init(Object *obj)\n     memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s,\n                           TYPE_OT_UART, 0x400);\n     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);\n+\n+    /*\n+     * This array has a fixed size in the header. This assertion is used to\n+     * check that it is consistent with the definition in this file. This is\n+     * ostensibly a runtime check, but may be optimised away by the compiler.\n+     */\n+    assert(REGS_SIZE == sizeof(s->regs));\n }\n \n static void ot_uart_realize(DeviceState *dev, Error **errp)\n@@ -541,6 +586,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)\n     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,\n                                           fifo_trigger_update, s);\n \n+    fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE);\n+    fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE);\n+\n     qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive,\n                              ot_uart_receive, NULL, NULL,\n                              s, NULL, true);\ndiff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h\nindex fee2128f90..f489612700 100644\n--- a/include/hw/char/ot_uart.h\n+++ b/include/hw/char/ot_uart.h\n@@ -26,11 +26,11 @@\n #define HW_OT_UART_H\n \n #include \"hw/core/sysbus.h\"\n+#include \"qemu/fifo8.h\"\n #include \"chardev/char-fe.h\"\n #include \"qemu/timer.h\"\n #include \"qom/object.h\"\n \n-#define OT_UART_TX_FIFO_SIZE 16\n #define OT_UART_CLOCK 50000000 /* 50MHz clock */\n \n #define TYPE_OT_UART \"ot-uart\"\n@@ -43,7 +43,6 @@ struct OtUARTState {\n     /* <public> */\n     MemoryRegion mmio;\n \n-    uint8_t tx_fifo[OT_UART_TX_FIFO_SIZE];\n     uint32_t tx_level;\n \n     uint32_t rx_level;\n@@ -51,16 +50,11 @@ struct OtUARTState {\n     QEMUTimer *fifo_trigger_handle;\n     uint64_t char_tx_time;\n \n-    uint32_t uart_intr_state;\n-    uint32_t uart_intr_enable;\n-    uint32_t uart_ctrl;\n-    uint32_t uart_status;\n-    uint32_t uart_rdata;\n-    uint32_t uart_fifo_ctrl;\n-    uint32_t uart_fifo_status;\n-    uint32_t uart_ovrd;\n-    uint32_t uart_val;\n-    uint32_t uart_timeout_ctrl;\n+    uint32_t regs[13]; /* Length must be updated if regs added or removed */\n+\n+    Fifo8 tx_fifo;\n+    Fifo8 rx_fifo;\n+    uint32_t tx_watermark_level;\n \n     Clock *f_clk;\n \n","prefixes":["qemu","v2","3/7"]}