* [PATCH v3 0/5] virtio: Add a virtio RTC driver
@ 2026-10-17 18:46 Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
QEMU v11.1.0 added a virtio RTC device (virtio-rtc-pci and
virtio-rtc-device), but U-Boot has no driver for it. This series adds
one, so that "date" and anything else using dm_rtc_get() work on any
virtio-capable QEMU target, not only on machines that also have a
goldfish or PL031 RTC.
Patch 1 adds the driver, patch 2 updates the virtio documentation and
patch 3 adds a MAINTAINERS entry. Patch 4 lets sandbox tests answer
virtqueue requests as the device would, which patch 5 uses to test the
driver.
The main change in v3 is how the clock is chosen. Following Kuan-Wei's
review, the driver now prefers a clock which smears leap seconds, as
the Linux RTC class does, but falls back to the first other UTC clock,
since QEMU only provides a VIRTIO_RTC_CLOCK_UTC clock. Patch 1
explains this in more detail.
Tested on QEMU v11.1.2 with qemu-riscv64_smode_defconfig and
RTC_GOLDFISH disabled, so that the virtio RTC is the only RTC:
- With -device virtio-rtc-pci and with -device virtio-rtc-device,
"date" reads the host time and advances between reads.
- Setting the time fails with -ENOSYS, as the clocks are read-only.
- With no virtio RTC device, "date" fails cleanly with err=-19.
On sandbox, "ut dm virtio_rtc*" passes.
Hung-Chun Tseng (5):
virtio: rtc: Add virtio RTC driver
doc: virtio: List the supported device types
MAINTAINERS: Add entry for virtio RTC
virtio: sandbox: Let tests emulate the device side of a virtqueue
test: dm: Add tests for the virtio RTC driver
MAINTAINERS | 6 +
arch/sandbox/dts/test.dts | 5 +
arch/sandbox/include/asm/test.h | 25 +++
doc/develop/driver-model/virtio.rst | 10 +-
drivers/virtio/Kconfig | 11 +
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio-uclass.c | 1 +
drivers/virtio/virtio_rtc.c | 249 ++++++++++++++++++++++
drivers/virtio/virtio_sandbox.c | 15 ++
include/virtio.h | 4 +-
test/dm/Makefile | 1 +
test/dm/virtio_rtc.c | 306 ++++++++++++++++++++++++++++
12 files changed, 632 insertions(+), 2 deletions(-)
create mode 100644 drivers/virtio/virtio_rtc.c
create mode 100644 test/dm/virtio_rtc.c
base-commit: 32ebe922e574a733d01c3f106fa77d9176bfecad
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
@ 2026-10-17 18:46 ` Hung-Chun Tseng
2026-10-17 19:06 ` Kuan-Wei Chiu
2026-10-17 19:13 ` Kuan-Wei Chiu
2026-10-17 18:46 ` [PATCH v3 2/5] doc: virtio: List the supported device types Hung-Chun Tseng
` (3 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
Add a driver for the virtio RTC device (virtio device ID 17), which
QEMU provides as virtio-rtc-pci and virtio-rtc-device since v11.1.0.
The device exposes its clocks through a request virtqueue. As the
virtqueue must not be used before the virtio uclass sets DRIVER_OK,
which happens after probe(), the first read looks for the clock: it
asks for the number of clocks with VIRTIO_RTC_REQ_CFG and queries the
type of each with VIRTIO_RTC_REQ_CLOCK_CAP. Reading the time sends a
VIRTIO_RTC_REQ_READ for the chosen clock and converts the nanoseconds
since the epoch with rtc_to_tm(). Each request gives up with
-ETIMEDOUT if the device does not answer within a second, as virtio_9p
does. The clocks are read-only, so there is no set operation, and the
optional alarm feature is not negotiated.
Like Linux, the driver prefers a VIRTIO_RTC_CLOCK_UTC_SMEARED clock.
Linux registers an RTC class device only for such a clock and skips
VIRTIO_RTC_CLOCK_UTC and VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED, which may
step on leap seconds. QEMU, however, exposes a single clock of type
VIRTIO_RTC_CLOCK_UTC, so following Linux strictly would leave U-Boot
without an RTC there. If no clock smears leap seconds, the driver
therefore falls back to the first UTC or maybe-smeared UTC clock. A
leap second step matters little to U-Boot, which only reads the time.
Like Linux, the driver stops looking for a clock as soon as a request
fails. After a timeout the request is still on the virtqueue, so its
late response could be taken for that of a later request.
VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
VIRTIO_RNG does for DM_RNG.
If none of the clocks is UTC, the driver remembers it and fails later
reads with -ENODEV at once, as the set of clocks never changes.
Tested with QEMU v11.1.2 on qemu-riscv64 over both PCI and MMIO.
Sandbox tests, which emulate the device, follow later in this series.
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
---
Changes in v3:
- Give up on a request after one second instead of polling forever
(Kuan-Wei)
- Remember that no clock is UTC, so that later reads fail at once
(Simon)
- Expand the VIRTIO_RTC help text (Simon)
- Drop __packed from the message structs, which are naturally aligned
(Kuan-Wei)
- Prefer a VIRTIO_RTC_CLOCK_UTC_SMEARED clock as Linux does, fall back
to the first other UTC clock, and explain why in the commit message
(Kuan-Wei)
- Stop looking for a clock as soon as a request fails, as Linux does
(Kuan-Wei)
- Mention in the commit message that sandbox tests follow (Simon)
Changes in v2:
- Look for the UTC clock on the first virtio_rtc_get() instead of in
probe(), as the virtqueue must not be used before DRIVER_OK (Bin)
drivers/virtio/Kconfig | 11 ++
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio-uclass.c | 1 +
drivers/virtio/virtio_rtc.c | 249 +++++++++++++++++++++++++++++++++
include/virtio.h | 4 +-
5 files changed, 265 insertions(+), 1 deletion(-)
create mode 100644 drivers/virtio/virtio_rtc.c
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 55b444280..48ab669f3 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -84,4 +84,15 @@ config VIRTIO_9P
help
This enables the 9P transport driver over virtio.
It can be used with QEMU based targets.
+
+config VIRTIO_RTC
+ bool "virtio RTC driver"
+ depends on DM_RTC
+ depends on VIRTIO
+ default y
+ help
+ This enables the virtio RTC driver, which reads the UTC clock
+ exposed by a virtio RTC device. QEMU v11.1.0 or later provides
+ it as virtio-rtc-pci and virtio-rtc-device. The clocks are
+ read-only, so the time cannot be set.
endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6e7bcfc8e..ddb5ce4eb 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
obj-$(CONFIG_VIRTIO_9P) += virtio_9p.o
+obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index c163b2fd7..c6cf55922 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
[VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
[VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
[VIRTIO_ID_9P] = VIRTIO_9P_DRV_NAME,
+ [VIRTIO_ID_CLOCK] = VIRTIO_RTC_DRV_NAME,
};
int virtio_get_config(struct udevice *vdev, unsigned int offset,
diff --git a/drivers/virtio/virtio_rtc.c b/drivers/virtio/virtio_rtc.c
new file mode 100644
index 000000000..806abe237
--- /dev/null
+++ b/drivers/virtio/virtio_rtc.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026, Hung-Chun Tseng <alan.tseng.cs@gmail.com>
+ *
+ * Driver for the virtio RTC device (virtio device ID 17). The device
+ * exposes one or more read-only clocks through a request virtqueue; this
+ * driver reads a UTC clock, preferring one which smears leap seconds, and
+ * reports it through the RTC uclass.
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <log.h>
+#include <rtc.h>
+#include <time.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <linux/types.h>
+
+#define VIRTIO_RTC_TIMEOUT_MS 1000
+
+#define VIRTIO_RTC_REQ_READ 0x0001
+#define VIRTIO_RTC_REQ_CFG 0x1000
+#define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001
+
+#define VIRTIO_RTC_S_OK 0
+
+#define VIRTIO_RTC_CLOCK_UTC 0
+#define VIRTIO_RTC_CLOCK_UTC_SMEARED 3
+#define VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED 4
+
+struct virtio_rtc_req_head {
+ __le16 msg_type;
+ u8 reserved[6];
+};
+
+struct virtio_rtc_resp_head {
+ u8 status;
+ u8 reserved[7];
+};
+
+/* Request for VIRTIO_RTC_REQ_READ and VIRTIO_RTC_REQ_CLOCK_CAP */
+struct virtio_rtc_req_clock {
+ struct virtio_rtc_req_head head;
+ __le16 clock_id;
+ u8 reserved[6];
+};
+
+struct virtio_rtc_resp_read {
+ struct virtio_rtc_resp_head head;
+ __le64 clock_reading;
+};
+
+struct virtio_rtc_resp_cfg {
+ struct virtio_rtc_resp_head head;
+ __le16 num_clocks;
+ u8 reserved[6];
+};
+
+struct virtio_rtc_resp_clock_cap {
+ struct virtio_rtc_resp_head head;
+ u8 type;
+ u8 leap_second_smearing;
+ u8 flags;
+ u8 reserved[5];
+};
+
+struct virtio_rtc_priv {
+ struct virtqueue *vq;
+ u16 clock_id;
+ bool clock_found;
+ bool no_utc_clock;
+};
+
+static int virtio_rtc_xfer(struct udevice *dev, void *req, size_t req_len,
+ void *resp, size_t resp_len)
+{
+ struct virtio_rtc_priv *priv = dev_get_priv(dev);
+ struct virtio_rtc_resp_head *head = resp;
+ struct virtio_sg req_sg = { req, req_len };
+ struct virtio_sg resp_sg = { resp, resp_len };
+ struct virtio_sg *sgs[] = { &req_sg, &resp_sg };
+ unsigned int len;
+ ulong start;
+ int ret;
+
+ ret = virtqueue_add(priv->vq, sgs, 1, 1);
+ if (ret)
+ return ret;
+
+ virtqueue_kick(priv->vq);
+
+ start = get_timer(0);
+ while (!virtqueue_get_buf(priv->vq, &len)) {
+ if (get_timer(start) > VIRTIO_RTC_TIMEOUT_MS)
+ return -ETIMEDOUT;
+ }
+
+ if (len < sizeof(*head) || head->status != VIRTIO_RTC_S_OK)
+ return -EIO;
+
+ if (len < resp_len)
+ return -EPROTO;
+
+ return 0;
+}
+
+static bool virtio_rtc_is_utc(u8 type)
+{
+ return type == VIRTIO_RTC_CLOCK_UTC ||
+ type == VIRTIO_RTC_CLOCK_UTC_SMEARED ||
+ type == VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED;
+}
+
+static int virtio_rtc_find_utc_clock(struct udevice *dev)
+{
+ struct virtio_rtc_priv *priv = dev_get_priv(dev);
+ struct virtio_rtc_req_head cfg_req = {
+ .msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CFG),
+ };
+ struct virtio_rtc_resp_cfg cfg = {};
+ u16 num_clocks, id, utc_id = 0;
+ bool utc_found = false;
+ int ret;
+
+ ret = virtio_rtc_xfer(dev, &cfg_req, sizeof(cfg_req), &cfg, sizeof(cfg));
+ if (ret)
+ return ret;
+
+ num_clocks = le16_to_cpu(cfg.num_clocks);
+ for (id = 0; id < num_clocks; id++) {
+ struct virtio_rtc_req_clock req = {
+ .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CLOCK_CAP),
+ .clock_id = cpu_to_le16(id),
+ };
+ struct virtio_rtc_resp_clock_cap cap = {};
+
+ /*
+ * Give up on any error: after a timeout the request is still
+ * on the virtqueue, so a later response could be mismatched.
+ */
+ ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
+ if (ret)
+ return ret;
+
+ /*
+ * Like Linux, prefer a clock which smears leap seconds. Unlike
+ * Linux, fall back to the first other UTC clock, since that is
+ * the only kind of clock that QEMU provides.
+ */
+ if (cap.type == VIRTIO_RTC_CLOCK_UTC_SMEARED) {
+ utc_id = id;
+ utc_found = true;
+ break;
+ }
+
+ if (!utc_found && virtio_rtc_is_utc(cap.type)) {
+ utc_id = id;
+ utc_found = true;
+ }
+ }
+
+ /* The set of clocks is fixed, so remember that none of them is UTC */
+ if (!utc_found) {
+ priv->no_utc_clock = true;
+ return -ENODEV;
+ }
+
+ priv->clock_id = utc_id;
+ priv->clock_found = true;
+
+ return 0;
+}
+
+static int virtio_rtc_get(struct udevice *dev, struct rtc_time *time)
+{
+ struct virtio_rtc_priv *priv = dev_get_priv(dev);
+ struct virtio_rtc_req_clock req = {
+ .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_READ),
+ };
+ struct virtio_rtc_resp_read resp = {};
+ u64 now;
+ int ret;
+
+ /*
+ * The virtqueue must not be used before DRIVER_OK, which the virtio
+ * uclass sets only after probe(), so look for the clock on first use.
+ */
+ if (priv->no_utc_clock)
+ return -ENODEV;
+
+ if (!priv->clock_found) {
+ ret = virtio_rtc_find_utc_clock(dev);
+ if (ret)
+ return ret;
+ }
+
+ req.clock_id = cpu_to_le16(priv->clock_id);
+
+ ret = virtio_rtc_xfer(dev, &req, sizeof(req), &resp, sizeof(resp));
+ if (ret)
+ return ret;
+
+ now = le64_to_cpu(resp.clock_reading);
+ do_div(now, 1000000000U);
+ rtc_to_tm(now, time);
+
+ return 0;
+}
+
+static int virtio_rtc_bind(struct udevice *dev)
+{
+ struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
+
+ /* The alarm feature is not supported */
+ virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
+
+ return 0;
+}
+
+static int virtio_rtc_probe(struct udevice *dev)
+{
+ struct virtio_rtc_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = virtio_find_vqs(dev, 1, &priv->vq);
+ if (ret < 0) {
+ debug("%s: virtio_find_vqs failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rtc_ops virtio_rtc_ops = {
+ .get = virtio_rtc_get,
+};
+
+U_BOOT_DRIVER(virtio_rtc) = {
+ .name = VIRTIO_RTC_DRV_NAME,
+ .id = UCLASS_RTC,
+ .bind = virtio_rtc_bind,
+ .probe = virtio_rtc_probe,
+ .remove = virtio_reset,
+ .ops = &virtio_rtc_ops,
+ .priv_auto = sizeof(struct virtio_rtc_priv),
+ .flags = DM_FLAG_ACTIVE_DMA,
+};
diff --git a/include/virtio.h b/include/virtio.h
index b981eead5..95069a2de 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -32,12 +32,14 @@
#define VIRTIO_ID_BLOCK 2 /* virtio block */
#define VIRTIO_ID_RNG 4 /* virtio rng */
#define VIRTIO_ID_9P 9 /* virtio 9p */
-#define VIRTIO_ID_MAX_NUM 10
+#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer (rtc) */
+#define VIRTIO_ID_MAX_NUM 18
#define VIRTIO_NET_DRV_NAME "virtio-net"
#define VIRTIO_BLK_DRV_NAME "virtio-blk"
#define VIRTIO_RNG_DRV_NAME "virtio-rng"
#define VIRTIO_9P_DRV_NAME "virtio-9p"
+#define VIRTIO_RTC_DRV_NAME "virtio-rtc"
/* Feature bits for virtio 9P */
#define VIRTIO_9P_MOUNT_TAG 0
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 2/5] doc: virtio: List the supported device types
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
@ 2026-10-17 18:46 ` Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 3/5] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
` (2 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
The list of supported device types still only named network and block
devices. Turn it into a bulleted list, which is easier to extend, and
add the entropy, 9P and RTC devices. Also note that the virtio RTC
device needs QEMU v11.1.0 or later.
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
Reviewed-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Retitle from "doc: virtio: Mention the RTC device", as the patch
lists other devices too (Kuan-Wei)
- Turn the list of devices into a bulleted list (Kuan-Wei)
- Keep the Reviewed-by tags of Bin and Simon, as only the layout changed
doc/develop/driver-model/virtio.rst | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/doc/develop/driver-model/virtio.rst b/doc/develop/driver-model/virtio.rst
index e5da9f430..7b2f31b3d 100644
--- a/doc/develop/driver-model/virtio.rst
+++ b/doc/develop/driver-model/virtio.rst
@@ -28,7 +28,13 @@ queues configuration and buffer transfers are nearly identical. Both MMIO
and PCI transport options are supported in U-Boot.
The VirtIO spec defines a lots of VirtIO device types, however at present only
-network and block device, the most two commonly used devices, are supported.
+the following devices are supported.
+
+ - network
+ - block
+ - entropy (rng)
+ - 9P transport
+ - RTC
The following QEMU targets are supported.
@@ -72,6 +78,8 @@ Requirements
------------
It is required that QEMU v2.5.0+ should be used to test U-Boot VirtIO support
on QEMU ARM and x86, and v2.12.0+ on QEMU RISC-V.
+The VirtIO RTC device (``-device virtio-rtc-pci`` or
+``-device virtio-rtc-device``) requires QEMU v11.1.0+.
Testing
-------
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 3/5] MAINTAINERS: Add entry for virtio RTC
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 2/5] doc: virtio: List the supported device types Hung-Chun Tseng
@ 2026-10-17 18:46 ` Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 4/5] virtio: sandbox: Let tests emulate the device side of a virtqueue Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 5/5] test: dm: Add tests for the virtio RTC driver Hung-Chun Tseng
4 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
Add myself as the maintainer of the virtio RTC driver.
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
Reviewed-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Add test/dm/virtio_rtc.c, which this version adds
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 28d090d3c..2ce94d7f5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1872,6 +1872,11 @@ F: include/virtio*.h
F: test/dm/virtio.c
F: doc/develop/driver-model/virtio.rst
+VIRTIO RTC
+M: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
+S: Maintained
+F: drivers/virtio/virtio_rtc.c
+
WATCHDOG
M: Stefan Roese <stefan.roese@mailbox.org>
S: Maintained
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 4/5] virtio: sandbox: Let tests emulate the device side of a virtqueue
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
` (2 preceding siblings ...)
2026-10-17 18:46 ` [PATCH v3 3/5] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
@ 2026-10-17 18:46 ` Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 5/5] test: dm: Add tests for the virtio RTC driver Hung-Chun Tseng
4 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
The sandbox virtio transport ignores notifications, so a driver that
waits for the device to answer a request cannot be tested beyond its
probe. The virtio-rng test works around this by writing to the used
ring of the driver's virtqueue before reading.
Add sandbox_virtio_set_notify() so that a test can install a handler
which is called when the driver kicks a virtqueue. The handler can then
process the available ring and fill in the used ring, as a real device
would. Without a handler, notifications are still ignored.
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
---
Changes in v3:
- New patch, needed by the next one
arch/sandbox/include/asm/test.h | 25 +++++++++++++++++++++++++
drivers/virtio/virtio_sandbox.c | 15 +++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index 043879002..d95de1095 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -11,6 +11,8 @@
#include <pci_ids.h>
struct unit_test_state;
+struct udevice;
+struct virtqueue;
/* The sandbox driver always permits an I2C device with this address */
#define SANDBOX_I2C_TEST_ADDR 0x59
@@ -369,4 +371,27 @@ bool sandbox_sf_bootdev_enabled(void);
*/
void sandbox_sf_set_enable_bootdevs(bool enable);
+/**
+ * typedef sandbox_virtio_notify_t - Emulate the device side of a virtqueue
+ *
+ * @dev: sandbox virtio transport device
+ * @vq: virtqueue that was kicked
+ * Returns: 0 on success, or -ve error code
+ */
+typedef int (*sandbox_virtio_notify_t)(struct udevice *dev,
+ struct virtqueue *vq);
+
+/**
+ * sandbox_virtio_set_notify() - Set a handler for virtqueue notifications
+ *
+ * The sandbox virtio transport ignores notifications by default. A test can
+ * set a handler here to process the buffers the driver adds, like a real
+ * device would.
+ *
+ * @dev: sandbox virtio transport device
+ * @notify: handler to call, or NULL to ignore notifications again
+ */
+void sandbox_virtio_set_notify(struct udevice *dev,
+ sandbox_virtio_notify_t notify);
+
#endif
diff --git a/drivers/virtio/virtio_sandbox.c b/drivers/virtio/virtio_sandbox.c
index 0f1ebef22..3b8a4d5a6 100644
--- a/drivers/virtio/virtio_sandbox.c
+++ b/drivers/virtio/virtio_sandbox.c
@@ -13,6 +13,7 @@
#include <linux/compat.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <asm/test.h>
struct virtio_sandbox_priv {
u8 id;
@@ -22,6 +23,7 @@ struct virtio_sandbox_priv {
ulong queue_desc;
ulong queue_available;
ulong queue_used;
+ sandbox_virtio_notify_t notify;
};
static int virtio_sandbox_get_config(struct udevice *udev, unsigned int offset,
@@ -150,9 +152,22 @@ static int virtio_sandbox_find_vqs(struct udevice *udev, unsigned int nvqs,
static int virtio_sandbox_notify(struct udevice *udev, struct virtqueue *vq)
{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ if (priv->notify)
+ return priv->notify(udev, vq);
+
return 0;
}
+void sandbox_virtio_set_notify(struct udevice *udev,
+ sandbox_virtio_notify_t notify)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ priv->notify = notify;
+}
+
static int virtio_sandbox_probe(struct udevice *udev)
{
struct virtio_sandbox_priv *priv = dev_get_priv(udev);
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v3 5/5] test: dm: Add tests for the virtio RTC driver
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
` (3 preceding siblings ...)
2026-10-17 18:46 ` [PATCH v3 4/5] virtio: sandbox: Let tests emulate the device side of a virtqueue Hung-Chun Tseng
@ 2026-10-17 18:46 ` Hung-Chun Tseng
4 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-17 18:46 UTC (permalink / raw)
To: u-boot; +Cc: bmeng.cn, trini, visitorckw, sjg, xypron.glpk
Add a virtio RTC device to the sandbox test device tree and emulate it
through the sandbox virtio transport. The emulated device answers the
VIRTIO_RTC_REQ_CFG, VIRTIO_RTC_REQ_CLOCK_CAP and VIRTIO_RTC_REQ_READ
requests from a configurable list of clocks.
The tests check that the driver:
- does not use the virtqueue in probe()
- reads the time from the first UTC clock and looks it up only once
- remembers that there is no UTC clock and does not ask again
- reports a failed request and looks for the clock again next time
- gives up with -ETIMEDOUT when the device does not answer
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
---
Changes in v3:
- New patch (Simon)
MAINTAINERS | 1 +
arch/sandbox/dts/test.dts | 5 +
test/dm/Makefile | 1 +
test/dm/virtio_rtc.c | 306 ++++++++++++++++++++++++++++++++++++++
4 files changed, 313 insertions(+)
create mode 100644 test/dm/virtio_rtc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 2ce94d7f5..486bfea29 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1876,6 +1876,7 @@ VIRTIO RTC
M: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
S: Maintained
F: drivers/virtio/virtio_rtc.c
+F: test/dm/virtio_rtc.c
WATCHDOG
M: Stefan Roese <stefan.roese@mailbox.org>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 25c99ef81..758b04a1b 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1974,6 +1974,11 @@
virtio-type = <2>; /* block */
};
+ sandbox-virtio-rtc {
+ compatible = "sandbox,virtio1";
+ virtio-type = <17>; /* clock */
+ };
+
sandbox_scmi {
compatible = "sandbox,scmi-devices";
power-domains = <&pwrdom_scmi 2>;
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 5dd6b57bb..975adce55 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -142,6 +142,7 @@ ifeq ($(CONFIG_VIRTIO_SANDBOX),y)
obj-y += virtio.o
obj-$(CONFIG_VIRTIO_RNG) += virtio_device.o
obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
+obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
endif
ifeq ($(CONFIG_WDT_GPIO)$(CONFIG_WDT_SANDBOX),yy)
obj-y += wdt.o
diff --git a/test/dm/virtio_rtc.c b/test/dm/virtio_rtc.c
new file mode 100644
index 000000000..23f7934dd
--- /dev/null
+++ b/test/dm/virtio_rtc.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026, Hung-Chun Tseng <alan.tseng.cs@gmail.com>
+ *
+ * Tests for the virtio RTC driver, with the device emulated through the
+ * sandbox virtio transport
+ */
+
+#include <dm.h>
+#include <rtc.h>
+#include <time.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Message types and clock types from the virtio specification */
+#define REQ_READ 0x0001
+#define REQ_CFG 0x1000
+#define REQ_CLOCK_CAP 0x1001
+
+#define S_OK 0
+#define S_EIO 4
+
+#define CLOCK_UTC 0
+#define CLOCK_TAI 1
+#define CLOCK_MONOTONIC 2
+#define CLOCK_UTC_SMEARED 3
+#define CLOCK_UTC_MAYBE_SMEARED 4
+
+/* Request: le16 msg_type, 6 reserved bytes, then le16 clock_id */
+struct emul_req {
+ __le16 msg_type;
+ u8 reserved[6];
+ __le16 clock_id;
+};
+
+/* Response: u8 status, 7 reserved bytes, then the message payload */
+struct emul_resp {
+ u8 status;
+ u8 reserved[7];
+ union {
+ __le16 num_clocks;
+ u8 type;
+ __le64 clock_reading;
+ };
+};
+
+/* 2023-11-14 22:13:20 UTC, plus some nanoseconds */
+#define EMUL_TIME_S 1700000000ULL
+#define EMUL_TIME_NS (EMUL_TIME_S * 1000000000ULL + 123456789)
+
+#define EMUL_MAX_CLOCKS 4
+
+/**
+ * struct virtio_rtc_emul - State of the emulated device
+ *
+ * @num_clocks: number of clocks the device reports
+ * @types: type of each clock
+ * @cap_status: status to return for VIRTIO_RTC_REQ_CLOCK_CAP
+ * @no_reply: do not answer requests, but move time past the timeout
+ * @cfg_count: number of VIRTIO_RTC_REQ_CFG requests seen
+ * @cap_count: number of VIRTIO_RTC_REQ_CLOCK_CAP requests seen
+ * @read_count: number of VIRTIO_RTC_REQ_READ requests seen
+ * @read_clock: clock ID of the last VIRTIO_RTC_REQ_READ
+ * @last_avail: next available ring entry to process
+ */
+struct virtio_rtc_emul {
+ u16 num_clocks;
+ u8 types[EMUL_MAX_CLOCKS];
+ u8 cap_status;
+ bool no_reply;
+ int cfg_count;
+ int cap_count;
+ int read_count;
+ u16 read_clock;
+ u16 last_avail;
+};
+
+static struct virtio_rtc_emul emul;
+
+static u32 emul_handle(const struct emul_req *req, struct emul_resp *resp)
+{
+ u16 clock_id = le16_to_cpu(req->clock_id);
+
+ memset(resp, '\0', sizeof(*resp));
+ resp->status = S_OK;
+
+ switch (le16_to_cpu(req->msg_type)) {
+ case REQ_CFG:
+ emul.cfg_count++;
+ resp->num_clocks = cpu_to_le16(emul.num_clocks);
+ return 16;
+ case REQ_CLOCK_CAP:
+ emul.cap_count++;
+ resp->status = emul.cap_status;
+ if (clock_id >= emul.num_clocks)
+ resp->status = S_EIO;
+ else
+ resp->type = emul.types[clock_id];
+ return 16;
+ case REQ_READ:
+ emul.read_count++;
+ emul.read_clock = clock_id;
+ resp->clock_reading = cpu_to_le64(EMUL_TIME_NS);
+ return 16;
+ default:
+ resp->status = S_EIO;
+ return 8;
+ }
+}
+
+static int emul_notify(struct udevice *dev, struct virtqueue *vq)
+{
+ struct udevice *vdev = vq->vdev;
+ struct vring *vr = &vq->vring;
+
+ if (emul.no_reply) {
+ timer_test_add_offset(2000);
+ return 0;
+ }
+
+ while (emul.last_avail != virtio16_to_cpu(vdev, vr->avail->idx)) {
+ u16 head = virtio16_to_cpu(vdev, vr->avail->ring[emul.last_avail %
+ vr->num]);
+ struct vring_desc *out = &vr->desc[head];
+ struct vring_desc *in = &vr->desc[virtio16_to_cpu(vdev,
+ out->next)];
+ void *req = (void *)(uintptr_t)virtio64_to_cpu(vdev, out->addr);
+ void *resp = (void *)(uintptr_t)virtio64_to_cpu(vdev, in->addr);
+ u16 used = virtio16_to_cpu(vdev, vr->used->idx);
+ u32 len;
+
+ len = emul_handle(req, resp);
+ vr->used->ring[used % vr->num].id = cpu_to_virtio32(vdev, head);
+ vr->used->ring[used % vr->num].len = cpu_to_virtio32(vdev, len);
+ vr->used->idx = cpu_to_virtio16(vdev, used + 1);
+ emul.last_avail++;
+ }
+
+ return 0;
+}
+
+/* Set up the emulated device and return the virtio RTC device */
+static int setup(struct unit_test_state *uts, u16 num_clocks, const u8 *types,
+ struct udevice **devp)
+{
+ struct udevice *bus;
+
+ memset(&emul, '\0', sizeof(emul));
+ emul.num_clocks = num_clocks;
+ memcpy(emul.types, types, num_clocks);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_VIRTIO,
+ "sandbox-virtio-rtc", &bus));
+ sandbox_virtio_set_notify(bus, emul_notify);
+ ut_assertok(device_find_first_child(bus, devp));
+ ut_asserteq_str(VIRTIO_RTC_DRV_NAME, (*devp)->driver->name);
+ ut_assertok(device_probe(*devp));
+
+ return 0;
+}
+
+/* Test reading the time from the only UTC clock */
+static int dm_test_virtio_rtc_get(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_MONOTONIC, CLOCK_UTC, CLOCK_TAI };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ /* probe() must not use the virtqueue */
+ ut_asserteq(0, emul.cfg_count);
+
+ ut_assertok(dm_rtc_get(dev, &tm));
+ ut_asserteq(1, emul.cfg_count);
+ ut_asserteq(3, emul.cap_count);
+ ut_asserteq(1, emul.read_count);
+ ut_asserteq(1, emul.read_clock);
+
+ ut_asserteq(2023, tm.tm_year);
+ ut_asserteq(11, tm.tm_mon);
+ ut_asserteq(14, tm.tm_mday);
+ ut_asserteq(22, tm.tm_hour);
+ ut_asserteq(13, tm.tm_min);
+ ut_asserteq(20, tm.tm_sec);
+ ut_asserteq(2, tm.tm_wday);
+
+ /* The clock is looked up only once */
+ ut_assertok(dm_rtc_get(dev, &tm));
+ ut_asserteq(1, emul.cfg_count);
+ ut_asserteq(3, emul.cap_count);
+ ut_asserteq(2, emul.read_count);
+
+ /* The clocks are read-only */
+ ut_asserteq(-ENOSYS, dm_rtc_set(dev, &tm));
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_get, UTF_SCAN_FDT);
+
+/* Test that a clock which smears leap seconds is preferred */
+static int dm_test_virtio_rtc_smeared(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_UTC, CLOCK_UTC_MAYBE_SMEARED,
+ CLOCK_UTC_SMEARED, CLOCK_TAI };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ /* The search stops at the smeared clock */
+ ut_assertok(dm_rtc_get(dev, &tm));
+ ut_asserteq(3, emul.cap_count);
+ ut_asserteq(2, emul.read_clock);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_smeared, UTF_SCAN_FDT);
+
+/* Test that the first UTC clock is used if none smears leap seconds */
+static int dm_test_virtio_rtc_fallback(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_TAI, CLOCK_UTC_MAYBE_SMEARED,
+ CLOCK_UTC };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ ut_assertok(dm_rtc_get(dev, &tm));
+ ut_asserteq(3, emul.cap_count);
+ ut_asserteq(1, emul.read_clock);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_fallback, UTF_SCAN_FDT);
+
+/* Test that a device without a UTC clock is only queried once */
+static int dm_test_virtio_rtc_no_utc(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_TAI, CLOCK_MONOTONIC };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ ut_asserteq(-ENODEV, dm_rtc_get(dev, &tm));
+ ut_asserteq(1, emul.cfg_count);
+ ut_asserteq(2, emul.cap_count);
+
+ ut_asserteq(-ENODEV, dm_rtc_get(dev, &tm));
+ ut_asserteq(1, emul.cfg_count);
+ ut_asserteq(2, emul.cap_count);
+ ut_asserteq(0, emul.read_count);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_no_utc, UTF_SCAN_FDT);
+
+/* Test that a failed request stops the search and is not remembered */
+static int dm_test_virtio_rtc_error(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_TAI, CLOCK_UTC };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ emul.cap_status = S_EIO;
+ ut_asserteq(-EIO, dm_rtc_get(dev, &tm));
+ ut_asserteq(1, emul.cap_count);
+
+ /* The next read looks for the clock again */
+ emul.cap_status = S_OK;
+ ut_assertok(dm_rtc_get(dev, &tm));
+ ut_asserteq(2, emul.cfg_count);
+ ut_asserteq(3, emul.cap_count);
+ ut_asserteq(1, emul.read_count);
+ ut_asserteq(1, emul.read_clock);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_error, UTF_SCAN_FDT);
+
+/* Test that the driver gives up if the device does not answer */
+static int dm_test_virtio_rtc_timeout(struct unit_test_state *uts)
+{
+ static const u8 types[] = { CLOCK_UTC };
+ struct udevice *dev;
+ struct rtc_time tm;
+
+ ut_assertok(setup(uts, ARRAY_SIZE(types), types, &dev));
+
+ emul.no_reply = true;
+ ut_asserteq(-ETIMEDOUT, dm_rtc_get(dev, &tm));
+ ut_asserteq(0, emul.cfg_count);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rtc_timeout, UTF_SCAN_FDT);
--
2.43.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
@ 2026-10-17 19:06 ` Kuan-Wei Chiu
2026-10-18 1:05 ` Hung-Chun Tseng
2026-10-17 19:13 ` Kuan-Wei Chiu
1 sibling, 1 reply; 12+ messages in thread
From: Kuan-Wei Chiu @ 2026-10-17 19:06 UTC (permalink / raw)
To: Hung-Chun Tseng; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 02:46:18AM +0800, Hung-Chun Tseng wrote:
> Add a driver for the virtio RTC device (virtio device ID 17), which
> QEMU provides as virtio-rtc-pci and virtio-rtc-device since v11.1.0.
>
> The device exposes its clocks through a request virtqueue. As the
> virtqueue must not be used before the virtio uclass sets DRIVER_OK,
> which happens after probe(), the first read looks for the clock: it
> asks for the number of clocks with VIRTIO_RTC_REQ_CFG and queries the
> type of each with VIRTIO_RTC_REQ_CLOCK_CAP. Reading the time sends a
> VIRTIO_RTC_REQ_READ for the chosen clock and converts the nanoseconds
> since the epoch with rtc_to_tm(). Each request gives up with
> -ETIMEDOUT if the device does not answer within a second, as virtio_9p
> does. The clocks are read-only, so there is no set operation, and the
> optional alarm feature is not negotiated.
>
> Like Linux, the driver prefers a VIRTIO_RTC_CLOCK_UTC_SMEARED clock.
> Linux registers an RTC class device only for such a clock and skips
> VIRTIO_RTC_CLOCK_UTC and VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED, which may
> step on leap seconds. QEMU, however, exposes a single clock of type
> VIRTIO_RTC_CLOCK_UTC, so following Linux strictly would leave U-Boot
This could confuse people who are looking at the current upstream qemu
code, since my patch [1] has already been merged into the qemu main
branch.
[1]: https://lore.kernel.org/qemu-devel/20260831190612.369235-1-visitorckw@gmail.com/
Regards,
Kuan-Wei
> without an RTC there. If no clock smears leap seconds, the driver
> therefore falls back to the first UTC or maybe-smeared UTC clock. A
> leap second step matters little to U-Boot, which only reads the time.
>
> Like Linux, the driver stops looking for a clock as soon as a request
> fails. After a timeout the request is still on the virtqueue, so its
> late response could be taken for that of a later request.
>
> VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
> VIRTIO_RNG does for DM_RNG.
>
> If none of the clocks is UTC, the driver remembers it and fails later
> reads with -ENODEV at once, as the set of clocks never changes.
>
> Tested with QEMU v11.1.2 on qemu-riscv64 over both PCI and MMIO.
> Sandbox tests, which emulate the device, follow later in this series.
>
> Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> ---
> Changes in v3:
> - Give up on a request after one second instead of polling forever
> (Kuan-Wei)
> - Remember that no clock is UTC, so that later reads fail at once
> (Simon)
> - Expand the VIRTIO_RTC help text (Simon)
> - Drop __packed from the message structs, which are naturally aligned
> (Kuan-Wei)
> - Prefer a VIRTIO_RTC_CLOCK_UTC_SMEARED clock as Linux does, fall back
> to the first other UTC clock, and explain why in the commit message
> (Kuan-Wei)
> - Stop looking for a clock as soon as a request fails, as Linux does
> (Kuan-Wei)
> - Mention in the commit message that sandbox tests follow (Simon)
>
> Changes in v2:
> - Look for the UTC clock on the first virtio_rtc_get() instead of in
> probe(), as the virtqueue must not be used before DRIVER_OK (Bin)
>
> drivers/virtio/Kconfig | 11 ++
> drivers/virtio/Makefile | 1 +
> drivers/virtio/virtio-uclass.c | 1 +
> drivers/virtio/virtio_rtc.c | 249 +++++++++++++++++++++++++++++++++
> include/virtio.h | 4 +-
> 5 files changed, 265 insertions(+), 1 deletion(-)
> create mode 100644 drivers/virtio/virtio_rtc.c
>
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index 55b444280..48ab669f3 100644
> --- a/drivers/virtio/Kconfig
> +++ b/drivers/virtio/Kconfig
> @@ -84,4 +84,15 @@ config VIRTIO_9P
> help
> This enables the 9P transport driver over virtio.
> It can be used with QEMU based targets.
> +
> +config VIRTIO_RTC
> + bool "virtio RTC driver"
> + depends on DM_RTC
> + depends on VIRTIO
> + default y
> + help
> + This enables the virtio RTC driver, which reads the UTC clock
> + exposed by a virtio RTC device. QEMU v11.1.0 or later provides
> + it as virtio-rtc-pci and virtio-rtc-device. The clocks are
> + read-only, so the time cannot be set.
> endmenu
> diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> index 6e7bcfc8e..ddb5ce4eb 100644
> --- a/drivers/virtio/Makefile
> +++ b/drivers/virtio/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
> obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
> obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
> obj-$(CONFIG_VIRTIO_9P) += virtio_9p.o
> +obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
> diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
> index c163b2fd7..c6cf55922 100644
> --- a/drivers/virtio/virtio-uclass.c
> +++ b/drivers/virtio/virtio-uclass.c
> @@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
> [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
> [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
> [VIRTIO_ID_9P] = VIRTIO_9P_DRV_NAME,
> + [VIRTIO_ID_CLOCK] = VIRTIO_RTC_DRV_NAME,
> };
>
> int virtio_get_config(struct udevice *vdev, unsigned int offset,
> diff --git a/drivers/virtio/virtio_rtc.c b/drivers/virtio/virtio_rtc.c
> new file mode 100644
> index 000000000..806abe237
> --- /dev/null
> +++ b/drivers/virtio/virtio_rtc.c
> @@ -0,0 +1,249 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026, Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> + *
> + * Driver for the virtio RTC device (virtio device ID 17). The device
> + * exposes one or more read-only clocks through a request virtqueue; this
> + * driver reads a UTC clock, preferring one which smears leap seconds, and
> + * reports it through the RTC uclass.
> + */
> +
> +#include <div64.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <rtc.h>
> +#include <time.h>
> +#include <virtio_types.h>
> +#include <virtio.h>
> +#include <virtio_ring.h>
> +#include <linux/types.h>
> +
> +#define VIRTIO_RTC_TIMEOUT_MS 1000
> +
> +#define VIRTIO_RTC_REQ_READ 0x0001
> +#define VIRTIO_RTC_REQ_CFG 0x1000
> +#define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001
> +
> +#define VIRTIO_RTC_S_OK 0
> +
> +#define VIRTIO_RTC_CLOCK_UTC 0
> +#define VIRTIO_RTC_CLOCK_UTC_SMEARED 3
> +#define VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED 4
> +
> +struct virtio_rtc_req_head {
> + __le16 msg_type;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_head {
> + u8 status;
> + u8 reserved[7];
> +};
> +
> +/* Request for VIRTIO_RTC_REQ_READ and VIRTIO_RTC_REQ_CLOCK_CAP */
> +struct virtio_rtc_req_clock {
> + struct virtio_rtc_req_head head;
> + __le16 clock_id;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_read {
> + struct virtio_rtc_resp_head head;
> + __le64 clock_reading;
> +};
> +
> +struct virtio_rtc_resp_cfg {
> + struct virtio_rtc_resp_head head;
> + __le16 num_clocks;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_clock_cap {
> + struct virtio_rtc_resp_head head;
> + u8 type;
> + u8 leap_second_smearing;
> + u8 flags;
> + u8 reserved[5];
> +};
> +
> +struct virtio_rtc_priv {
> + struct virtqueue *vq;
> + u16 clock_id;
> + bool clock_found;
> + bool no_utc_clock;
> +};
> +
> +static int virtio_rtc_xfer(struct udevice *dev, void *req, size_t req_len,
> + void *resp, size_t resp_len)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_resp_head *head = resp;
> + struct virtio_sg req_sg = { req, req_len };
> + struct virtio_sg resp_sg = { resp, resp_len };
> + struct virtio_sg *sgs[] = { &req_sg, &resp_sg };
> + unsigned int len;
> + ulong start;
> + int ret;
> +
> + ret = virtqueue_add(priv->vq, sgs, 1, 1);
> + if (ret)
> + return ret;
> +
> + virtqueue_kick(priv->vq);
> +
> + start = get_timer(0);
> + while (!virtqueue_get_buf(priv->vq, &len)) {
> + if (get_timer(start) > VIRTIO_RTC_TIMEOUT_MS)
> + return -ETIMEDOUT;
> + }
> +
> + if (len < sizeof(*head) || head->status != VIRTIO_RTC_S_OK)
> + return -EIO;
> +
> + if (len < resp_len)
> + return -EPROTO;
> +
> + return 0;
> +}
> +
> +static bool virtio_rtc_is_utc(u8 type)
> +{
> + return type == VIRTIO_RTC_CLOCK_UTC ||
> + type == VIRTIO_RTC_CLOCK_UTC_SMEARED ||
> + type == VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED;
> +}
> +
> +static int virtio_rtc_find_utc_clock(struct udevice *dev)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_req_head cfg_req = {
> + .msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CFG),
> + };
> + struct virtio_rtc_resp_cfg cfg = {};
> + u16 num_clocks, id, utc_id = 0;
> + bool utc_found = false;
> + int ret;
> +
> + ret = virtio_rtc_xfer(dev, &cfg_req, sizeof(cfg_req), &cfg, sizeof(cfg));
> + if (ret)
> + return ret;
> +
> + num_clocks = le16_to_cpu(cfg.num_clocks);
> + for (id = 0; id < num_clocks; id++) {
> + struct virtio_rtc_req_clock req = {
> + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CLOCK_CAP),
> + .clock_id = cpu_to_le16(id),
> + };
> + struct virtio_rtc_resp_clock_cap cap = {};
> +
> + /*
> + * Give up on any error: after a timeout the request is still
> + * on the virtqueue, so a later response could be mismatched.
> + */
> + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
> + if (ret)
> + return ret;
> +
> + /*
> + * Like Linux, prefer a clock which smears leap seconds. Unlike
> + * Linux, fall back to the first other UTC clock, since that is
> + * the only kind of clock that QEMU provides.
> + */
> + if (cap.type == VIRTIO_RTC_CLOCK_UTC_SMEARED) {
> + utc_id = id;
> + utc_found = true;
> + break;
> + }
> +
> + if (!utc_found && virtio_rtc_is_utc(cap.type)) {
> + utc_id = id;
> + utc_found = true;
> + }
> + }
> +
> + /* The set of clocks is fixed, so remember that none of them is UTC */
> + if (!utc_found) {
> + priv->no_utc_clock = true;
> + return -ENODEV;
> + }
> +
> + priv->clock_id = utc_id;
> + priv->clock_found = true;
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_get(struct udevice *dev, struct rtc_time *time)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_req_clock req = {
> + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_READ),
> + };
> + struct virtio_rtc_resp_read resp = {};
> + u64 now;
> + int ret;
> +
> + /*
> + * The virtqueue must not be used before DRIVER_OK, which the virtio
> + * uclass sets only after probe(), so look for the clock on first use.
> + */
> + if (priv->no_utc_clock)
> + return -ENODEV;
> +
> + if (!priv->clock_found) {
> + ret = virtio_rtc_find_utc_clock(dev);
> + if (ret)
> + return ret;
> + }
> +
> + req.clock_id = cpu_to_le16(priv->clock_id);
> +
> + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &resp, sizeof(resp));
> + if (ret)
> + return ret;
> +
> + now = le64_to_cpu(resp.clock_reading);
> + do_div(now, 1000000000U);
> + rtc_to_tm(now, time);
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_bind(struct udevice *dev)
> +{
> + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
> +
> + /* The alarm feature is not supported */
> + virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_probe(struct udevice *dev)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + ret = virtio_find_vqs(dev, 1, &priv->vq);
> + if (ret < 0) {
> + debug("%s: virtio_find_vqs failed\n", __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct rtc_ops virtio_rtc_ops = {
> + .get = virtio_rtc_get,
> +};
> +
> +U_BOOT_DRIVER(virtio_rtc) = {
> + .name = VIRTIO_RTC_DRV_NAME,
> + .id = UCLASS_RTC,
> + .bind = virtio_rtc_bind,
> + .probe = virtio_rtc_probe,
> + .remove = virtio_reset,
> + .ops = &virtio_rtc_ops,
> + .priv_auto = sizeof(struct virtio_rtc_priv),
> + .flags = DM_FLAG_ACTIVE_DMA,
> +};
> diff --git a/include/virtio.h b/include/virtio.h
> index b981eead5..95069a2de 100644
> --- a/include/virtio.h
> +++ b/include/virtio.h
> @@ -32,12 +32,14 @@
> #define VIRTIO_ID_BLOCK 2 /* virtio block */
> #define VIRTIO_ID_RNG 4 /* virtio rng */
> #define VIRTIO_ID_9P 9 /* virtio 9p */
> -#define VIRTIO_ID_MAX_NUM 10
> +#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer (rtc) */
> +#define VIRTIO_ID_MAX_NUM 18
>
> #define VIRTIO_NET_DRV_NAME "virtio-net"
> #define VIRTIO_BLK_DRV_NAME "virtio-blk"
> #define VIRTIO_RNG_DRV_NAME "virtio-rng"
> #define VIRTIO_9P_DRV_NAME "virtio-9p"
> +#define VIRTIO_RTC_DRV_NAME "virtio-rtc"
>
> /* Feature bits for virtio 9P */
> #define VIRTIO_9P_MOUNT_TAG 0
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
2026-10-17 19:06 ` Kuan-Wei Chiu
@ 2026-10-17 19:13 ` Kuan-Wei Chiu
2026-10-17 19:32 ` Kuan-Wei Chiu
2026-10-18 1:05 ` Hung-Chun Tseng
1 sibling, 2 replies; 12+ messages in thread
From: Kuan-Wei Chiu @ 2026-10-17 19:13 UTC (permalink / raw)
To: Hung-Chun Tseng; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 02:46:18AM +0800, Hung-Chun Tseng wrote:
> Add a driver for the virtio RTC device (virtio device ID 17), which
> QEMU provides as virtio-rtc-pci and virtio-rtc-device since v11.1.0.
>
> The device exposes its clocks through a request virtqueue. As the
> virtqueue must not be used before the virtio uclass sets DRIVER_OK,
> which happens after probe(), the first read looks for the clock: it
> asks for the number of clocks with VIRTIO_RTC_REQ_CFG and queries the
> type of each with VIRTIO_RTC_REQ_CLOCK_CAP. Reading the time sends a
> VIRTIO_RTC_REQ_READ for the chosen clock and converts the nanoseconds
> since the epoch with rtc_to_tm(). Each request gives up with
> -ETIMEDOUT if the device does not answer within a second, as virtio_9p
> does. The clocks are read-only, so there is no set operation, and the
> optional alarm feature is not negotiated.
This is a bit subjective, but my feeling is that this has become
somewhat too verbose. Ideally, a commit message should explain the
"why" rather than just stating what was done. What was done can
basically be seen directly from the code changes themselves.
Regards,
Kuan-Wei
>
> Like Linux, the driver prefers a VIRTIO_RTC_CLOCK_UTC_SMEARED clock.
> Linux registers an RTC class device only for such a clock and skips
> VIRTIO_RTC_CLOCK_UTC and VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED, which may
> step on leap seconds. QEMU, however, exposes a single clock of type
> VIRTIO_RTC_CLOCK_UTC, so following Linux strictly would leave U-Boot
> without an RTC there. If no clock smears leap seconds, the driver
> therefore falls back to the first UTC or maybe-smeared UTC clock. A
> leap second step matters little to U-Boot, which only reads the time.
>
> Like Linux, the driver stops looking for a clock as soon as a request
> fails. After a timeout the request is still on the virtqueue, so its
> late response could be taken for that of a later request.
>
> VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
> VIRTIO_RNG does for DM_RNG.
>
> If none of the clocks is UTC, the driver remembers it and fails later
> reads with -ENODEV at once, as the set of clocks never changes.
>
> Tested with QEMU v11.1.2 on qemu-riscv64 over both PCI and MMIO.
> Sandbox tests, which emulate the device, follow later in this series.
>
> Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> ---
> Changes in v3:
> - Give up on a request after one second instead of polling forever
> (Kuan-Wei)
> - Remember that no clock is UTC, so that later reads fail at once
> (Simon)
> - Expand the VIRTIO_RTC help text (Simon)
> - Drop __packed from the message structs, which are naturally aligned
> (Kuan-Wei)
> - Prefer a VIRTIO_RTC_CLOCK_UTC_SMEARED clock as Linux does, fall back
> to the first other UTC clock, and explain why in the commit message
> (Kuan-Wei)
> - Stop looking for a clock as soon as a request fails, as Linux does
> (Kuan-Wei)
> - Mention in the commit message that sandbox tests follow (Simon)
>
> Changes in v2:
> - Look for the UTC clock on the first virtio_rtc_get() instead of in
> probe(), as the virtqueue must not be used before DRIVER_OK (Bin)
>
> drivers/virtio/Kconfig | 11 ++
> drivers/virtio/Makefile | 1 +
> drivers/virtio/virtio-uclass.c | 1 +
> drivers/virtio/virtio_rtc.c | 249 +++++++++++++++++++++++++++++++++
> include/virtio.h | 4 +-
> 5 files changed, 265 insertions(+), 1 deletion(-)
> create mode 100644 drivers/virtio/virtio_rtc.c
>
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index 55b444280..48ab669f3 100644
> --- a/drivers/virtio/Kconfig
> +++ b/drivers/virtio/Kconfig
> @@ -84,4 +84,15 @@ config VIRTIO_9P
> help
> This enables the 9P transport driver over virtio.
> It can be used with QEMU based targets.
> +
> +config VIRTIO_RTC
> + bool "virtio RTC driver"
> + depends on DM_RTC
> + depends on VIRTIO
> + default y
> + help
> + This enables the virtio RTC driver, which reads the UTC clock
> + exposed by a virtio RTC device. QEMU v11.1.0 or later provides
> + it as virtio-rtc-pci and virtio-rtc-device. The clocks are
> + read-only, so the time cannot be set.
> endmenu
> diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> index 6e7bcfc8e..ddb5ce4eb 100644
> --- a/drivers/virtio/Makefile
> +++ b/drivers/virtio/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
> obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
> obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
> obj-$(CONFIG_VIRTIO_9P) += virtio_9p.o
> +obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
> diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
> index c163b2fd7..c6cf55922 100644
> --- a/drivers/virtio/virtio-uclass.c
> +++ b/drivers/virtio/virtio-uclass.c
> @@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
> [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
> [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
> [VIRTIO_ID_9P] = VIRTIO_9P_DRV_NAME,
> + [VIRTIO_ID_CLOCK] = VIRTIO_RTC_DRV_NAME,
> };
>
> int virtio_get_config(struct udevice *vdev, unsigned int offset,
> diff --git a/drivers/virtio/virtio_rtc.c b/drivers/virtio/virtio_rtc.c
> new file mode 100644
> index 000000000..806abe237
> --- /dev/null
> +++ b/drivers/virtio/virtio_rtc.c
> @@ -0,0 +1,249 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026, Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> + *
> + * Driver for the virtio RTC device (virtio device ID 17). The device
> + * exposes one or more read-only clocks through a request virtqueue; this
> + * driver reads a UTC clock, preferring one which smears leap seconds, and
> + * reports it through the RTC uclass.
> + */
> +
> +#include <div64.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <rtc.h>
> +#include <time.h>
> +#include <virtio_types.h>
> +#include <virtio.h>
> +#include <virtio_ring.h>
> +#include <linux/types.h>
> +
> +#define VIRTIO_RTC_TIMEOUT_MS 1000
> +
> +#define VIRTIO_RTC_REQ_READ 0x0001
> +#define VIRTIO_RTC_REQ_CFG 0x1000
> +#define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001
> +
> +#define VIRTIO_RTC_S_OK 0
> +
> +#define VIRTIO_RTC_CLOCK_UTC 0
> +#define VIRTIO_RTC_CLOCK_UTC_SMEARED 3
> +#define VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED 4
> +
> +struct virtio_rtc_req_head {
> + __le16 msg_type;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_head {
> + u8 status;
> + u8 reserved[7];
> +};
> +
> +/* Request for VIRTIO_RTC_REQ_READ and VIRTIO_RTC_REQ_CLOCK_CAP */
> +struct virtio_rtc_req_clock {
> + struct virtio_rtc_req_head head;
> + __le16 clock_id;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_read {
> + struct virtio_rtc_resp_head head;
> + __le64 clock_reading;
> +};
> +
> +struct virtio_rtc_resp_cfg {
> + struct virtio_rtc_resp_head head;
> + __le16 num_clocks;
> + u8 reserved[6];
> +};
> +
> +struct virtio_rtc_resp_clock_cap {
> + struct virtio_rtc_resp_head head;
> + u8 type;
> + u8 leap_second_smearing;
> + u8 flags;
> + u8 reserved[5];
> +};
> +
> +struct virtio_rtc_priv {
> + struct virtqueue *vq;
> + u16 clock_id;
> + bool clock_found;
> + bool no_utc_clock;
> +};
> +
> +static int virtio_rtc_xfer(struct udevice *dev, void *req, size_t req_len,
> + void *resp, size_t resp_len)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_resp_head *head = resp;
> + struct virtio_sg req_sg = { req, req_len };
> + struct virtio_sg resp_sg = { resp, resp_len };
> + struct virtio_sg *sgs[] = { &req_sg, &resp_sg };
> + unsigned int len;
> + ulong start;
> + int ret;
> +
> + ret = virtqueue_add(priv->vq, sgs, 1, 1);
> + if (ret)
> + return ret;
> +
> + virtqueue_kick(priv->vq);
> +
> + start = get_timer(0);
> + while (!virtqueue_get_buf(priv->vq, &len)) {
> + if (get_timer(start) > VIRTIO_RTC_TIMEOUT_MS)
> + return -ETIMEDOUT;
> + }
> +
> + if (len < sizeof(*head) || head->status != VIRTIO_RTC_S_OK)
> + return -EIO;
> +
> + if (len < resp_len)
> + return -EPROTO;
> +
> + return 0;
> +}
> +
> +static bool virtio_rtc_is_utc(u8 type)
> +{
> + return type == VIRTIO_RTC_CLOCK_UTC ||
> + type == VIRTIO_RTC_CLOCK_UTC_SMEARED ||
> + type == VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED;
> +}
> +
> +static int virtio_rtc_find_utc_clock(struct udevice *dev)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_req_head cfg_req = {
> + .msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CFG),
> + };
> + struct virtio_rtc_resp_cfg cfg = {};
> + u16 num_clocks, id, utc_id = 0;
> + bool utc_found = false;
> + int ret;
> +
> + ret = virtio_rtc_xfer(dev, &cfg_req, sizeof(cfg_req), &cfg, sizeof(cfg));
> + if (ret)
> + return ret;
> +
> + num_clocks = le16_to_cpu(cfg.num_clocks);
> + for (id = 0; id < num_clocks; id++) {
> + struct virtio_rtc_req_clock req = {
> + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CLOCK_CAP),
> + .clock_id = cpu_to_le16(id),
> + };
> + struct virtio_rtc_resp_clock_cap cap = {};
> +
> + /*
> + * Give up on any error: after a timeout the request is still
> + * on the virtqueue, so a later response could be mismatched.
> + */
> + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
> + if (ret)
> + return ret;
> +
> + /*
> + * Like Linux, prefer a clock which smears leap seconds. Unlike
> + * Linux, fall back to the first other UTC clock, since that is
> + * the only kind of clock that QEMU provides.
> + */
> + if (cap.type == VIRTIO_RTC_CLOCK_UTC_SMEARED) {
> + utc_id = id;
> + utc_found = true;
> + break;
> + }
> +
> + if (!utc_found && virtio_rtc_is_utc(cap.type)) {
> + utc_id = id;
> + utc_found = true;
> + }
> + }
> +
> + /* The set of clocks is fixed, so remember that none of them is UTC */
> + if (!utc_found) {
> + priv->no_utc_clock = true;
> + return -ENODEV;
> + }
> +
> + priv->clock_id = utc_id;
> + priv->clock_found = true;
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_get(struct udevice *dev, struct rtc_time *time)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + struct virtio_rtc_req_clock req = {
> + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_READ),
> + };
> + struct virtio_rtc_resp_read resp = {};
> + u64 now;
> + int ret;
> +
> + /*
> + * The virtqueue must not be used before DRIVER_OK, which the virtio
> + * uclass sets only after probe(), so look for the clock on first use.
> + */
> + if (priv->no_utc_clock)
> + return -ENODEV;
> +
> + if (!priv->clock_found) {
> + ret = virtio_rtc_find_utc_clock(dev);
> + if (ret)
> + return ret;
> + }
> +
> + req.clock_id = cpu_to_le16(priv->clock_id);
> +
> + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &resp, sizeof(resp));
> + if (ret)
> + return ret;
> +
> + now = le64_to_cpu(resp.clock_reading);
> + do_div(now, 1000000000U);
> + rtc_to_tm(now, time);
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_bind(struct udevice *dev)
> +{
> + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
> +
> + /* The alarm feature is not supported */
> + virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
> +
> + return 0;
> +}
> +
> +static int virtio_rtc_probe(struct udevice *dev)
> +{
> + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + ret = virtio_find_vqs(dev, 1, &priv->vq);
> + if (ret < 0) {
> + debug("%s: virtio_find_vqs failed\n", __func__);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct rtc_ops virtio_rtc_ops = {
> + .get = virtio_rtc_get,
> +};
> +
> +U_BOOT_DRIVER(virtio_rtc) = {
> + .name = VIRTIO_RTC_DRV_NAME,
> + .id = UCLASS_RTC,
> + .bind = virtio_rtc_bind,
> + .probe = virtio_rtc_probe,
> + .remove = virtio_reset,
> + .ops = &virtio_rtc_ops,
> + .priv_auto = sizeof(struct virtio_rtc_priv),
> + .flags = DM_FLAG_ACTIVE_DMA,
> +};
> diff --git a/include/virtio.h b/include/virtio.h
> index b981eead5..95069a2de 100644
> --- a/include/virtio.h
> +++ b/include/virtio.h
> @@ -32,12 +32,14 @@
> #define VIRTIO_ID_BLOCK 2 /* virtio block */
> #define VIRTIO_ID_RNG 4 /* virtio rng */
> #define VIRTIO_ID_9P 9 /* virtio 9p */
> -#define VIRTIO_ID_MAX_NUM 10
> +#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer (rtc) */
> +#define VIRTIO_ID_MAX_NUM 18
>
> #define VIRTIO_NET_DRV_NAME "virtio-net"
> #define VIRTIO_BLK_DRV_NAME "virtio-blk"
> #define VIRTIO_RNG_DRV_NAME "virtio-rng"
> #define VIRTIO_9P_DRV_NAME "virtio-9p"
> +#define VIRTIO_RTC_DRV_NAME "virtio-rtc"
>
> /* Feature bits for virtio 9P */
> #define VIRTIO_9P_MOUNT_TAG 0
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 19:13 ` Kuan-Wei Chiu
@ 2026-10-17 19:32 ` Kuan-Wei Chiu
2026-10-18 1:05 ` Hung-Chun Tseng
2026-10-18 1:05 ` Hung-Chun Tseng
1 sibling, 1 reply; 12+ messages in thread
From: Kuan-Wei Chiu @ 2026-10-17 19:32 UTC (permalink / raw)
To: Hung-Chun Tseng; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 03:13:46AM +0800, Kuan-Wei Chiu wrote:
> On Sun, Oct 18, 2026 at 02:46:18AM +0800, Hung-Chun Tseng wrote:
> > Add a driver for the virtio RTC device (virtio device ID 17), which
> > QEMU provides as virtio-rtc-pci and virtio-rtc-device since v11.1.0.
> >
> > The device exposes its clocks through a request virtqueue. As the
> > virtqueue must not be used before the virtio uclass sets DRIVER_OK,
> > which happens after probe(), the first read looks for the clock: it
> > asks for the number of clocks with VIRTIO_RTC_REQ_CFG and queries the
> > type of each with VIRTIO_RTC_REQ_CLOCK_CAP. Reading the time sends a
> > VIRTIO_RTC_REQ_READ for the chosen clock and converts the nanoseconds
> > since the epoch with rtc_to_tm(). Each request gives up with
> > -ETIMEDOUT if the device does not answer within a second, as virtio_9p
> > does. The clocks are read-only, so there is no set operation, and the
> > optional alarm feature is not negotiated.
>
> This is a bit subjective, but my feeling is that this has become
> somewhat too verbose. Ideally, a commit message should explain the
> "why" rather than just stating what was done. What was done can
> basically be seen directly from the code changes themselves.
>
> Regards,
> Kuan-Wei
>
> >
> > Like Linux, the driver prefers a VIRTIO_RTC_CLOCK_UTC_SMEARED clock.
> > Linux registers an RTC class device only for such a clock and skips
> > VIRTIO_RTC_CLOCK_UTC and VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED, which may
> > step on leap seconds. QEMU, however, exposes a single clock of type
> > VIRTIO_RTC_CLOCK_UTC, so following Linux strictly would leave U-Boot
> > without an RTC there. If no clock smears leap seconds, the driver
> > therefore falls back to the first UTC or maybe-smeared UTC clock. A
> > leap second step matters little to U-Boot, which only reads the time.
> >
> > Like Linux, the driver stops looking for a clock as soon as a request
> > fails. After a timeout the request is still on the virtqueue, so its
> > late response could be taken for that of a later request.
> >
> > VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
> > VIRTIO_RNG does for DM_RNG.
> >
> > If none of the clocks is UTC, the driver remembers it and fails later
> > reads with -ENODEV at once, as the set of clocks never changes.
> >
> > Tested with QEMU v11.1.2 on qemu-riscv64 over both PCI and MMIO.
> > Sandbox tests, which emulate the device, follow later in this series.
> >
> > Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> > ---
> > Changes in v3:
> > - Give up on a request after one second instead of polling forever
> > (Kuan-Wei)
> > - Remember that no clock is UTC, so that later reads fail at once
> > (Simon)
> > - Expand the VIRTIO_RTC help text (Simon)
> > - Drop __packed from the message structs, which are naturally aligned
> > (Kuan-Wei)
> > - Prefer a VIRTIO_RTC_CLOCK_UTC_SMEARED clock as Linux does, fall back
> > to the first other UTC clock, and explain why in the commit message
> > (Kuan-Wei)
> > - Stop looking for a clock as soon as a request fails, as Linux does
> > (Kuan-Wei)
> > - Mention in the commit message that sandbox tests follow (Simon)
> >
> > Changes in v2:
> > - Look for the UTC clock on the first virtio_rtc_get() instead of in
> > probe(), as the virtqueue must not be used before DRIVER_OK (Bin)
> >
> > drivers/virtio/Kconfig | 11 ++
> > drivers/virtio/Makefile | 1 +
> > drivers/virtio/virtio-uclass.c | 1 +
> > drivers/virtio/virtio_rtc.c | 249 +++++++++++++++++++++++++++++++++
> > include/virtio.h | 4 +-
> > 5 files changed, 265 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/virtio/virtio_rtc.c
> >
> > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> > index 55b444280..48ab669f3 100644
> > --- a/drivers/virtio/Kconfig
> > +++ b/drivers/virtio/Kconfig
> > @@ -84,4 +84,15 @@ config VIRTIO_9P
> > help
> > This enables the 9P transport driver over virtio.
> > It can be used with QEMU based targets.
> > +
> > +config VIRTIO_RTC
> > + bool "virtio RTC driver"
> > + depends on DM_RTC
> > + depends on VIRTIO
> > + default y
> > + help
> > + This enables the virtio RTC driver, which reads the UTC clock
> > + exposed by a virtio RTC device. QEMU v11.1.0 or later provides
> > + it as virtio-rtc-pci and virtio-rtc-device. The clocks are
> > + read-only, so the time cannot be set.
> > endmenu
> > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> > index 6e7bcfc8e..ddb5ce4eb 100644
> > --- a/drivers/virtio/Makefile
> > +++ b/drivers/virtio/Makefile
> > @@ -12,3 +12,4 @@ obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
> > obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
> > obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
> > obj-$(CONFIG_VIRTIO_9P) += virtio_9p.o
> > +obj-$(CONFIG_VIRTIO_RTC) += virtio_rtc.o
> > diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
> > index c163b2fd7..c6cf55922 100644
> > --- a/drivers/virtio/virtio-uclass.c
> > +++ b/drivers/virtio/virtio-uclass.c
> > @@ -31,6 +31,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
> > [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
> > [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
> > [VIRTIO_ID_9P] = VIRTIO_9P_DRV_NAME,
> > + [VIRTIO_ID_CLOCK] = VIRTIO_RTC_DRV_NAME,
> > };
> >
> > int virtio_get_config(struct udevice *vdev, unsigned int offset,
> > diff --git a/drivers/virtio/virtio_rtc.c b/drivers/virtio/virtio_rtc.c
> > new file mode 100644
> > index 000000000..806abe237
> > --- /dev/null
> > +++ b/drivers/virtio/virtio_rtc.c
> > @@ -0,0 +1,249 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2026, Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> > + *
> > + * Driver for the virtio RTC device (virtio device ID 17). The device
> > + * exposes one or more read-only clocks through a request virtqueue; this
> > + * driver reads a UTC clock, preferring one which smears leap seconds, and
> > + * reports it through the RTC uclass.
> > + */
> > +
> > +#include <div64.h>
> > +#include <dm.h>
> > +#include <log.h>
> > +#include <rtc.h>
> > +#include <time.h>
> > +#include <virtio_types.h>
> > +#include <virtio.h>
> > +#include <virtio_ring.h>
> > +#include <linux/types.h>
> > +
> > +#define VIRTIO_RTC_TIMEOUT_MS 1000
> > +
> > +#define VIRTIO_RTC_REQ_READ 0x0001
> > +#define VIRTIO_RTC_REQ_CFG 0x1000
> > +#define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001
> > +
> > +#define VIRTIO_RTC_S_OK 0
> > +
> > +#define VIRTIO_RTC_CLOCK_UTC 0
> > +#define VIRTIO_RTC_CLOCK_UTC_SMEARED 3
> > +#define VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED 4
> > +
> > +struct virtio_rtc_req_head {
> > + __le16 msg_type;
> > + u8 reserved[6];
> > +};
> > +
> > +struct virtio_rtc_resp_head {
> > + u8 status;
> > + u8 reserved[7];
> > +};
> > +
> > +/* Request for VIRTIO_RTC_REQ_READ and VIRTIO_RTC_REQ_CLOCK_CAP */
> > +struct virtio_rtc_req_clock {
> > + struct virtio_rtc_req_head head;
> > + __le16 clock_id;
> > + u8 reserved[6];
> > +};
> > +
> > +struct virtio_rtc_resp_read {
> > + struct virtio_rtc_resp_head head;
> > + __le64 clock_reading;
> > +};
> > +
> > +struct virtio_rtc_resp_cfg {
> > + struct virtio_rtc_resp_head head;
> > + __le16 num_clocks;
> > + u8 reserved[6];
> > +};
> > +
> > +struct virtio_rtc_resp_clock_cap {
> > + struct virtio_rtc_resp_head head;
> > + u8 type;
> > + u8 leap_second_smearing;
> > + u8 flags;
> > + u8 reserved[5];
> > +};
> > +
> > +struct virtio_rtc_priv {
> > + struct virtqueue *vq;
> > + u16 clock_id;
> > + bool clock_found;
> > + bool no_utc_clock;
> > +};
> > +
> > +static int virtio_rtc_xfer(struct udevice *dev, void *req, size_t req_len,
> > + void *resp, size_t resp_len)
> > +{
> > + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> > + struct virtio_rtc_resp_head *head = resp;
> > + struct virtio_sg req_sg = { req, req_len };
> > + struct virtio_sg resp_sg = { resp, resp_len };
> > + struct virtio_sg *sgs[] = { &req_sg, &resp_sg };
> > + unsigned int len;
> > + ulong start;
> > + int ret;
> > +
> > + ret = virtqueue_add(priv->vq, sgs, 1, 1);
> > + if (ret)
> > + return ret;
> > +
> > + virtqueue_kick(priv->vq);
> > +
> > + start = get_timer(0);
> > + while (!virtqueue_get_buf(priv->vq, &len)) {
> > + if (get_timer(start) > VIRTIO_RTC_TIMEOUT_MS)
> > + return -ETIMEDOUT;
> > + }
> > +
> > + if (len < sizeof(*head) || head->status != VIRTIO_RTC_S_OK)
> > + return -EIO;
> > +
> > + if (len < resp_len)
> > + return -EPROTO;
> > +
> > + return 0;
> > +}
> > +
> > +static bool virtio_rtc_is_utc(u8 type)
> > +{
> > + return type == VIRTIO_RTC_CLOCK_UTC ||
> > + type == VIRTIO_RTC_CLOCK_UTC_SMEARED ||
> > + type == VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED;
> > +}
> > +
> > +static int virtio_rtc_find_utc_clock(struct udevice *dev)
> > +{
> > + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> > + struct virtio_rtc_req_head cfg_req = {
> > + .msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CFG),
> > + };
> > + struct virtio_rtc_resp_cfg cfg = {};
> > + u16 num_clocks, id, utc_id = 0;
> > + bool utc_found = false;
> > + int ret;
> > +
> > + ret = virtio_rtc_xfer(dev, &cfg_req, sizeof(cfg_req), &cfg, sizeof(cfg));
> > + if (ret)
> > + return ret;
> > +
> > + num_clocks = le16_to_cpu(cfg.num_clocks);
> > + for (id = 0; id < num_clocks; id++) {
> > + struct virtio_rtc_req_clock req = {
> > + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_CLOCK_CAP),
> > + .clock_id = cpu_to_le16(id),
> > + };
> > + struct virtio_rtc_resp_clock_cap cap = {};
> > +
> > + /*
> > + * Give up on any error: after a timeout the request is still
> > + * on the virtqueue, so a later response could be mismatched.
> > + */
I guess your LLM added this comment while fixing my review comment?
I don't really see why we need a comment here. Even if we ignore the
virtqueue timeout mismatch issue, we already abort immediately on any
other error anyway. It looks like a standard and normal error handling
to me, so why is it special to need a comment here?
Regards,
Kuan-Wei
> > + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * Like Linux, prefer a clock which smears leap seconds. Unlike
> > + * Linux, fall back to the first other UTC clock, since that is
> > + * the only kind of clock that QEMU provides.
> > + */
> > + if (cap.type == VIRTIO_RTC_CLOCK_UTC_SMEARED) {
> > + utc_id = id;
> > + utc_found = true;
> > + break;
> > + }
> > +
> > + if (!utc_found && virtio_rtc_is_utc(cap.type)) {
> > + utc_id = id;
> > + utc_found = true;
> > + }
> > + }
> > +
> > + /* The set of clocks is fixed, so remember that none of them is UTC */
> > + if (!utc_found) {
> > + priv->no_utc_clock = true;
> > + return -ENODEV;
> > + }
> > +
> > + priv->clock_id = utc_id;
> > + priv->clock_found = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int virtio_rtc_get(struct udevice *dev, struct rtc_time *time)
> > +{
> > + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> > + struct virtio_rtc_req_clock req = {
> > + .head.msg_type = cpu_to_le16(VIRTIO_RTC_REQ_READ),
> > + };
> > + struct virtio_rtc_resp_read resp = {};
> > + u64 now;
> > + int ret;
> > +
> > + /*
> > + * The virtqueue must not be used before DRIVER_OK, which the virtio
> > + * uclass sets only after probe(), so look for the clock on first use.
> > + */
> > + if (priv->no_utc_clock)
> > + return -ENODEV;
> > +
> > + if (!priv->clock_found) {
> > + ret = virtio_rtc_find_utc_clock(dev);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + req.clock_id = cpu_to_le16(priv->clock_id);
> > +
> > + ret = virtio_rtc_xfer(dev, &req, sizeof(req), &resp, sizeof(resp));
> > + if (ret)
> > + return ret;
> > +
> > + now = le64_to_cpu(resp.clock_reading);
> > + do_div(now, 1000000000U);
> > + rtc_to_tm(now, time);
> > +
> > + return 0;
> > +}
> > +
> > +static int virtio_rtc_bind(struct udevice *dev)
> > +{
> > + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
> > +
> > + /* The alarm feature is not supported */
> > + virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int virtio_rtc_probe(struct udevice *dev)
> > +{
> > + struct virtio_rtc_priv *priv = dev_get_priv(dev);
> > + int ret;
> > +
> > + ret = virtio_find_vqs(dev, 1, &priv->vq);
> > + if (ret < 0) {
> > + debug("%s: virtio_find_vqs failed\n", __func__);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static const struct rtc_ops virtio_rtc_ops = {
> > + .get = virtio_rtc_get,
> > +};
> > +
> > +U_BOOT_DRIVER(virtio_rtc) = {
> > + .name = VIRTIO_RTC_DRV_NAME,
> > + .id = UCLASS_RTC,
> > + .bind = virtio_rtc_bind,
> > + .probe = virtio_rtc_probe,
> > + .remove = virtio_reset,
> > + .ops = &virtio_rtc_ops,
> > + .priv_auto = sizeof(struct virtio_rtc_priv),
> > + .flags = DM_FLAG_ACTIVE_DMA,
> > +};
> > diff --git a/include/virtio.h b/include/virtio.h
> > index b981eead5..95069a2de 100644
> > --- a/include/virtio.h
> > +++ b/include/virtio.h
> > @@ -32,12 +32,14 @@
> > #define VIRTIO_ID_BLOCK 2 /* virtio block */
> > #define VIRTIO_ID_RNG 4 /* virtio rng */
> > #define VIRTIO_ID_9P 9 /* virtio 9p */
> > -#define VIRTIO_ID_MAX_NUM 10
> > +#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer (rtc) */
> > +#define VIRTIO_ID_MAX_NUM 18
> >
> > #define VIRTIO_NET_DRV_NAME "virtio-net"
> > #define VIRTIO_BLK_DRV_NAME "virtio-blk"
> > #define VIRTIO_RNG_DRV_NAME "virtio-rng"
> > #define VIRTIO_9P_DRV_NAME "virtio-9p"
> > +#define VIRTIO_RTC_DRV_NAME "virtio-rtc"
> >
> > /* Feature bits for virtio 9P */
> > #define VIRTIO_9P_MOUNT_TAG 0
> > --
> > 2.43.0
> >
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 19:06 ` Kuan-Wei Chiu
@ 2026-10-18 1:05 ` Hung-Chun Tseng
0 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-18 1:05 UTC (permalink / raw)
To: Kuan-Wei Chiu; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 03:06:49AM +0800, Kuan-Wei Chiu wrote:
> On Sun, Oct 18, 2026 at 02:46:18AM +0800, Hung-Chun Tseng wrote:
> > Linux registers an RTC class device only for such a clock and skips
> > VIRTIO_RTC_CLOCK_UTC and VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED, which may
> > step on leap seconds. QEMU, however, exposes a single clock of type
> > VIRTIO_RTC_CLOCK_UTC, so following Linux strictly would leave U-Boot
>
> This could confuse people who are looking at the current upstream qemu
> code, since my patch [1] has already been merged into the qemu main
> branch.
>
> [1]: https://lore.kernel.org/qemu-devel/20260831190612.369235-1-visitorckw@gmail.com/
Thanks, I missed that your patch is already in QEMU master, and it
matches the UTC plus smeared UTC case you pointed out on v2. v11.1.2,
which I tested with, still reports VIRTIO_RTC_CLOCK_UTC, so I'd like to
keep the fallback for released versions, but I'll reword the commit
message to say that newer QEMU reports a smeared clock and the driver
picks it directly.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 19:13 ` Kuan-Wei Chiu
2026-10-17 19:32 ` Kuan-Wei Chiu
@ 2026-10-18 1:05 ` Hung-Chun Tseng
1 sibling, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-18 1:05 UTC (permalink / raw)
To: Kuan-Wei Chiu; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 03:13:41AM +0800, Kuan-Wei Chiu wrote:
> This is a bit subjective, but my feeling is that this has become
> somewhat too verbose. Ideally, a commit message should explain the
> "why" rather than just stating what was done. What was done can
> basically be seen directly from the code changes themselves.
Agreed. I'll cut it down to the reasons: why the clock lookup happens on
first read, why a smeared clock is preferred with a fallback, and why
the "no UTC clock" result is remembered.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v3 1/5] virtio: rtc: Add virtio RTC driver
2026-10-17 19:32 ` Kuan-Wei Chiu
@ 2026-10-18 1:05 ` Hung-Chun Tseng
0 siblings, 0 replies; 12+ messages in thread
From: Hung-Chun Tseng @ 2026-10-18 1:05 UTC (permalink / raw)
To: Kuan-Wei Chiu; +Cc: u-boot, bmeng.cn, trini, sjg, xypron.glpk
On Sun, Oct 18, 2026 at 03:32:57AM +0800, Kuan-Wei Chiu wrote:
> On Sun, Oct 18, 2026 at 03:13:46AM +0800, Kuan-Wei Chiu wrote:
> > > + /*
> > > + * Give up on any error: after a timeout the request is still
> > > + * on the virtqueue, so a later response could be mismatched.
> > > + */
>
> I guess your LLM added this comment while fixing my review comment?
> I don't really see why we need a comment here. Even if we ignore the
> virtqueue timeout mismatch issue, we already abort immediately on any
> other error anyway. It looks like a standard and normal error handling
> to me, so why is it special to need a comment here?
Yes. I used AI tools to assist with this work. I reviewed every line and
tested it on QEMU and sandbox. You're right that this is just normal
error handling, so I'll drop the comment in v4. I'll remove overly long
comments and descriptions in the future.
Thanks for the careful review.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-18 1:05 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 18:46 [PATCH v3 0/5] virtio: Add a virtio RTC driver Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 1/5] virtio: rtc: Add " Hung-Chun Tseng
2026-10-17 19:06 ` Kuan-Wei Chiu
2026-10-18 1:05 ` Hung-Chun Tseng
2026-10-17 19:13 ` Kuan-Wei Chiu
2026-10-17 19:32 ` Kuan-Wei Chiu
2026-10-18 1:05 ` Hung-Chun Tseng
2026-10-18 1:05 ` Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 2/5] doc: virtio: List the supported device types Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 3/5] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 4/5] virtio: sandbox: Let tests emulate the device side of a virtqueue Hung-Chun Tseng
2026-10-17 18:46 ` [PATCH v3 5/5] test: dm: Add tests for the virtio RTC driver Hung-Chun Tseng
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.