Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2231331/?format=api
{ "id": 2231331, "url": "http://patchwork.ozlabs.org/api/patches/2231331/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/177756498815.8917.12449488721849651971-7@git.sr.ht/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/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": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<177756498815.8917.12449488721849651971-7@git.sr.ht>", "list_archive_url": null, "date": "2026-04-14T12:45:41", "name": "[qemu,v4,7/9] ot_uart: handle break condition", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "a759dcef5869c998dbc29d4c498e4c3db59d3349", "submitter": { "id": 92675, "url": "http://patchwork.ozlabs.org/api/people/92675/?format=api", "name": "~lexbaileylowrisc", "email": "lexbaileylowrisc@git.sr.ht" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/177756498815.8917.12449488721849651971-7@git.sr.ht/mbox/", "series": [ { "id": 502334, "url": "http://patchwork.ozlabs.org/api/series/502334/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=502334", "date": "2026-04-14T12:45:41", "name": "Update opentitan uart (part of supporting opentitan version 1)", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/502334/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2231331/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2231331/checks/", "tags": {}, "related": [], "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=mzAFVgt/;\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=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists1p.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 4g5zVx51qzz1yGq\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 02:03:53 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wITrS-0007qB-R6; Thu, 30 Apr 2026 12:03:35 -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 1wITrK-0007cA-TJ; Thu, 30 Apr 2026 12:03:30 -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 1wITrG-00079Y-7Q; Thu, 30 Apr 2026 12:03:26 -0400", "from git.sr.ht (unknown [46.23.81.155])\n by mail-a.sr.ht (Postfix) with ESMTPSA id 83EF020C81;\n Thu, 30 Apr 2026 16:03:13 +0000 (UTC)" ], "DKIM-Signature": "a=rsa-sha256; bh=5CJSSplXBWzwOKftMniqF9csQcjNBZIZr2WVOB8vGes=;\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=1777564993; v=1;\n b=mzAFVgt/+mLoRP9qs4hGPiYrWfzmWTn3uw3rBa0O9M7xxXAqJiTAyCxjFuvskJTlMP4j6PGs\n qe6ZUBHobGeKIc01aSVTY8JnxXaIWXbtLjjbDidaOLIjhGqy1AWhdxuYP2WW1DNigE8/5JRC+fB\n uOqLfddhXvrqmzMFuYYbb+8J7XTPQin5kZiljGiYAs/lbBNmvzB0oEJdFapTtSI6Eg/0xbZdWXN\n EdUMGGsyFDWEADdfhdW9CKLscN8/4tZ4C1LXP9lMZHLX8+pxlhRrOELZULTO3fRQuoIEXF2KHyz\n zMlI9lz3uVMiDJ70sjMLjCK7PAUMsNAEqiZKRnogRJ0dg==", "From": "~lexbaileylowrisc <lexbaileylowrisc@git.sr.ht>", "Date": "Tue, 14 Apr 2026 13:45:41 +0100", "Subject": "[PATCH qemu v4 7/9] ot_uart: handle break condition", "Message-ID": "<177756498815.8917.12449488721849651971-7@git.sr.ht>", "X-Mailer": "git.sr.ht", "In-Reply-To": "<177756498815.8917.12449488721849651971-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>,\n nabihestefan <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": "17", "X-Spam_score": "1.7", "X-Spam_bar": "+", "X-Spam_report": "(1.7 / 5.0 requ) BAYES_00=-1.9, DATE_IN_PAST_96_XX=3.405,\n DKIM_INVALID=0.1, DKIM_SIGNED=0.1, 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\nWhat is a break event:\nNormally, a UART wire is held high by the transmitting device any time the\ncommunication channel is idle (nothing to transmit). If a receiver does not see\nthat its receive line is high, this means one of two things:\n 1. there is a transmission happening now\n 2. there is no transmitter connected, and the line is floating or pulled\n low, or the transmitting device is in a bad state or reset state and so\n it has not yet asserted its TX output.\n\nIn case 1, the line will go high again very soon.\nIn case 2, it is sometimes useful to detect that this is the case and signal to\nthe software that this has happened (normally by means of an interrupt)\n\nThe ot_uart device does exactly this in case 2. It detects that this is\nhappening by detecting when the RX pin stays low for an extended period. This\nperiod can be configured to be 2, 4, 8, or 16 times a character period. The\ncharacter period itself depends on the configuration of the device (baud rate,\nand parity bit)\n\nfor more details about how this works, there is documentation about the UART\nbreak detection on this page:\nhttps://opentitan.org/book/hw/ip/uart/doc/theory_of_operation.html#rx_break_err\n\nThis commit adds a handler for the UART break event, and logic to emulate a\nbreak when oversampling is enabled and there is a break event.\n\nSigned-off-by: Lex Bailey <lex.bailey@lowrisc.org>\n---\n docs/system/riscv/opentitan-uart.rst | 43 +++++++++++++\n hw/char/ot_uart.c | 92 +++++++++++++++++++++++++++-\n include/hw/char/ot_uart.h | 4 ++\n 3 files changed, 136 insertions(+), 3 deletions(-)\n create mode 100644 docs/system/riscv/opentitan-uart.rst", "diff": "diff --git a/docs/system/riscv/opentitan-uart.rst b/docs/system/riscv/opentitan-uart.rst\nnew file mode 100644\nindex 0000000000..7ca596712d\n--- /dev/null\n+++ b/docs/system/riscv/opentitan-uart.rst\n@@ -0,0 +1,43 @@\n+# OpenTitan UART Support\n+\n+## Connecting to the UART\n+\n+* `-serial mon:stdio`, used as the first `-serial` option, redirects the virtual UART0 to the\n+ current console/shell.\n+\n+* `-chardev socket,id=serial1,host=localhost,port=8001,server=on,wait=off` and\n+ `-serial chardev:serial1` can be used to redirect UART1 (in this example) to a TCP socket. These\n+ options are not specific to OpenTitan emulation, but are useful to communicate over a UART.\n+ Note that QEMU offers many `chardev` backends, please check QEMU documentation for details.\n+\n+## Sending Break Conditions\n+\n+Break conditions can be sent to the UART on select supported CharDev backends (telnet, mux)\n+or by sending the `chardev-send-break` command with the CharDev ID via the QEMU Monitor.\n+Break conditions are treated as transient events and the length of time of a break condition\n+is not considered.\n+\n+## Oversampling\n+\n+OpenTitan's UART has a `VAL` register which oversamples the RX pin 16 times per bit.\n+This cannot be emulated by QEMU which uses a CharDev backend and does not have a notion of\n+accurate sampling times.\n+\n+If software wishes to poll the `VAL` register to determine break conditions, there are\n+some properties available to help with emulating this use case:\n+\n+* `-global ot-uart.oversample-break=true` is used to enable UART break oversampling.\n+ This will attempt to display 16 samples of the last bit received in the `VAL` register,\n+ which will be 16 high bits after any UART frame is transmitted (as these end with a stop\n+ bit, which is high), or 16 low bits if the UART previously received a break condition\n+ and has not received any frames since. That is, enabling this property assumes that\n+ transmitted break conditions are \"held\" until the next UART transfer in terms of what\n+ is being shown in the oversampled `VAL` register.\n+\n+* `-global ot-uart.toggle-break=true` is used to provide more control over \"holding\"\n+ the UART RX break condition like a GPIO strap, and changes the behavior of a UART\n+ such that received break condition events now *toggle* the break condition state\n+ rather than keeping it asserted until the next transfer. This allows any device talking\n+ to OpenTitan via UART to have more precise control over when the UART VAL register\n+ displays idle and when it displays a break condition, as it can precisely toggle the\n+ break condition on or off like a GPIO strapping being held down.\ndiff --git a/hw/char/ot_uart.c b/hw/char/ot_uart.c\nindex 3511a42fbe..cdf02da62b 100644\n--- a/hw/char/ot_uart.c\n+++ b/hw/char/ot_uart.c\n@@ -179,6 +179,11 @@ static void ot_uart_receive(void *opaque, const uint8_t *buf, int size)\n uint32_t rx_watermark_level;\n size_t count = MIN(fifo8_num_free(&s->rx_fifo), (size_t)size);\n \n+ if (size && !s->toggle_break) {\n+ /* no longer breaking, so emulate idle in oversampled VAL register */\n+ s->in_break = false;\n+ }\n+\n for (int index = 0; index < size; index++) {\n fifo8_push(&s->rx_fifo, buf[index]);\n }\n@@ -331,9 +336,40 @@ static void ot_uart_reset_enter(Object *obj, ResetType type)\n \n s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10;\n \n+ /*\n+ * do not reset `s->in_break`, as that tracks whether we are currently\n+ * receiving a break condition over UART RX from some device talking\n+ * to OpenTitan, which should survive resets. The QEMU CharDev only\n+ * supports transient break events and not the notion of holding the\n+ * UART in break, so remembering breaks like this is required to\n+ * support mocking of break conditions in the oversampled `VAL` reg.\n+ */\n+ if (s->in_break) {\n+ /* ignore CTRL.RXBLVL as we have no notion of break \"time\" */\n+ s->regs[R_INTR_STATE] |= INTR_RX_BREAK_ERR_MASK;\n+ }\n+\n ot_uart_update_irqs(s);\n }\n \n+static void ot_uart_event_handler(void *opaque, QEMUChrEvent event)\n+{\n+ OtUARTState *s = opaque;\n+\n+ if (event == CHR_EVENT_BREAK) {\n+ if (!s->in_break || !s->oversample_break) {\n+ /* ignore CTRL.RXBLVL as we have no notion of break \"time\" */\n+ s->regs[R_INTR_STATE] |= INTR_RX_BREAK_ERR_MASK;\n+ ot_uart_update_irqs(s);\n+ /* emulate break in the oversampled VAL register */\n+ s->in_break = true;\n+ } else if (s->toggle_break) {\n+ /* emulate toggling break off in the oversampled VAL register */\n+ s->in_break = false;\n+ }\n+ }\n+}\n+\n static uint8_t ot_uart_read_rx_fifo(OtUARTState *s)\n {\n uint8_t val;\n@@ -355,6 +391,17 @@ static uint8_t ot_uart_read_rx_fifo(OtUARTState *s)\n return val;\n }\n \n+static gboolean ot_uart_watch_cb(void *do_not_use, GIOCondition cond,\n+ void *opaque)\n+{\n+ OtUARTState *s = opaque;\n+\n+ s->watch_tag = 0;\n+ ot_uart_xmit(s);\n+\n+ return FALSE;\n+}\n+\n static uint64_t ot_uart_get_baud(OtUARTState *s)\n {\n uint64_t baud;\n@@ -424,6 +471,26 @@ static uint64_t ot_uart_read(void *opaque, hwaddr addr, unsigned int size)\n break;\n \n case R_VAL:\n+ /*\n+ * This is not trivially implemented due to the QEMU UART\n+ * interface. There is no way to reliably sample or oversample\n+ * given our emulated interface, but some software might poll the\n+ * value of this register to determine break conditions.\n+ *\n+ * As such, default to reporting 16 of the last sample received\n+ * instead. This defaults to 16 idle high samples (as a stop bit is\n+ * always the last received), except for when the `oversample-break`\n+ * property is set and a break condition is received over UART RX,\n+ * where we then show 16 low samples until the next valid UART\n+ * transmission is received (or break is toggled off with the\n+ * `toggle-break` property enabled). This will not be accurate, but\n+ * should be sufficient to support basic software flows that\n+ * essentially use UART break as a strapping mechanism.\n+ */\n+ retvalue = (s->in_break && s->oversample_break) ? 0u : UINT16_MAX;\n+ qemu_log_mask(LOG_UNIMP, \"%s: VAL only shows idle%s\\n\", __func__,\n+ (s->oversample_break ? \"/break\" : \"\"));\n+ break;\n case R_OVRD:\n case R_TIMEOUT_CTRL:\n retvalue = s->regs[reg];\n@@ -607,8 +674,27 @@ static const VMStateDescription vmstate_ot_uart = {\n \n static const Property ot_uart_properties[] = {\n DEFINE_PROP_CHR(\"chardev\", OtUARTState, chr),\n+ DEFINE_PROP_BOOL(\"oversample-break\", OtUARTState, oversample_break, false),\n+ DEFINE_PROP_BOOL(\"toggle-break\", OtUARTState, toggle_break, false),\n };\n \n+static int ot_uart_fe_change(void *opaque)\n+{\n+ OtUARTState *s = opaque;\n+\n+ qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive, ot_uart_receive,\n+ ot_uart_event_handler, ot_uart_fe_change, s, NULL,\n+ true);\n+\n+ if (s->watch_tag > 0) {\n+ g_source_remove(s->watch_tag);\n+ s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,\n+ ot_uart_watch_cb, s);\n+ }\n+\n+ return 0;\n+}\n+\n static void ot_uart_init(Object *obj)\n {\n OtUARTState *s = OT_UART(obj);\n@@ -644,9 +730,9 @@ static void ot_uart_realize(DeviceState *dev, Error **errp)\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);\n+ qemu_chr_fe_set_handlers(&s->chr, ot_uart_can_receive, ot_uart_receive,\n+ ot_uart_event_handler, ot_uart_fe_change, s, NULL,\n+ true);\n }\n \n static void ot_uart_class_init(ObjectClass *klass, const void *data)\ndiff --git a/include/hw/char/ot_uart.h b/include/hw/char/ot_uart.h\nindex a2c5ff8b33..997bf2f367 100644\n--- a/include/hw/char/ot_uart.h\n+++ b/include/hw/char/ot_uart.h\n@@ -56,10 +56,14 @@ struct OtUARTState {\n Fifo8 tx_fifo;\n Fifo8 rx_fifo;\n uint32_t tx_watermark_level;\n+ bool in_break;\n+ guint watch_tag;\n \n Clock *f_clk;\n \n CharFrontend chr;\n+ bool oversample_break; /* Should mock break in the oversampled VAL reg? */\n+ bool toggle_break; /* Are incoming breaks temporary or toggled? */\n };\n \n struct OtUARTClass {\n", "prefixes": [ "qemu", "v4", "7/9" ] }