Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.0/patches/2221190/?format=api
{ "id": 2221190, "url": "http://patchwork.ozlabs.org/api/1.0/patches/2221190/?format=api", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/1.0/projects/14/?format=api", "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=api", "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=api", "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" ] }