U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] virtio: Add a virtio RTC driver
@ 2026-10-14 19:06 Hung-Chun Tseng
  2026-10-14 19:06 ` [PATCH v2 1/3] virtio: rtc: Add " Hung-Chun Tseng
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Hung-Chun Tseng @ 2026-10-14 19:06 UTC (permalink / raw)
  To: Bin Meng, Tom Rini
  Cc: Kuan-Wei Chiu, Simon Glass, Heinrich Schuchardt, u-boot

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: it finds the first UTC clock on the device and reads it through
the RTC uclass, so "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.

Tested on QEMU v11.1.2 with qemu-riscv64_smode_defconfig:

- With -device virtio-rtc-pci and with -device virtio-rtc-device, the
  driver probes. As "date" uses the first RTC, which is the goldfish
  RTC on this machine, I also built with RTC_GOLDFISH disabled: "date"
  then read the host time from the virtio RTC, advancing between reads.
- Setting the time fails with -ENOSYS, as the clocks are read-only.
- With RTC_GOLDFISH disabled and no virtio RTC device, "date" fails
  cleanly with err=-19.

qemu-riscv32_defconfig also builds the driver without libgcc helpers
for the 64-bit division. On sandbox, where VIRTIO_RTC now defaults to y,
"ut dm rtc*" and "ut dm virtio*" pass.

The alarm feature is not supported. I have not added a pytest because
the CI Docker image builds QEMU v10.1.2, which does not have the
device yet.

Changes in v2:
- Patch 1: look for the UTC clock on the first read instead of in
  probe(), as the virtqueue must not be used before DRIVER_OK (Bin)
- Patches 2 and 3: add Bin's Reviewed-by

v1: https://lore.kernel.org/u-boot/20261014140716.1181882-1-alan.tseng.cs@gmail.com/

Hung-Chun Tseng (3):
  virtio: rtc: Add virtio RTC driver
  doc: virtio: Mention the RTC device
  MAINTAINERS: Add entry for virtio RTC

 MAINTAINERS                         |   5 +
 doc/develop/driver-model/virtio.rst |   4 +-
 drivers/virtio/Kconfig              |   9 ++
 drivers/virtio/Makefile             |   1 +
 drivers/virtio/virtio-uclass.c      |   1 +
 drivers/virtio/virtio_rtc.c         | 213 ++++++++++++++++++++++++++++
 include/virtio.h                    |   4 +-
 7 files changed, 235 insertions(+), 2 deletions(-)
 create mode 100644 drivers/virtio/virtio_rtc.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/3] virtio: rtc: Add virtio RTC driver
  2026-10-14 19:06 [PATCH v2 0/3] virtio: Add a virtio RTC driver Hung-Chun Tseng
@ 2026-10-14 19:06 ` Hung-Chun Tseng
  2026-10-14 19:46   ` Kuan-Wei Chiu
  2026-10-14 19:06 ` [PATCH v2 2/3] doc: virtio: Mention the RTC device Hung-Chun Tseng
  2026-10-14 19:06 ` [PATCH v2 3/3] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
  2 siblings, 1 reply; 6+ messages in thread
From: Hung-Chun Tseng @ 2026-10-14 19:06 UTC (permalink / raw)
  To: Bin Meng, Tom Rini
  Cc: Kuan-Wei Chiu, Simon Glass, Heinrich Schuchardt, u-boot

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 picks the
first one whose VIRTIO_RTC_REQ_CLOCK_CAP type is UTC, smeared UTC or
maybe-smeared UTC. Reading the time sends a VIRTIO_RTC_REQ_READ for
that clock and converts the nanoseconds since the epoch with
rtc_to_tm(). The clocks are read-only, so there is no set operation,
and the optional alarm feature is not negotiated.

VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
VIRTIO_RNG does for DM_RNG.

Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
---

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         |   9 ++
 drivers/virtio/Makefile        |   1 +
 drivers/virtio/virtio-uclass.c |   1 +
 drivers/virtio/virtio_rtc.c    | 213 +++++++++++++++++++++++++++++++++
 include/virtio.h               |   4 +-
 5 files changed, 227 insertions(+), 1 deletion(-)
 create mode 100644 drivers/virtio/virtio_rtc.c

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 55b444280..c19138ef2 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -84,4 +84,13 @@ 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 is the virtual RTC driver. It reads the UTC clock of a
+	  virtio RTC device. It can be used with QEMU based targets.
 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..f5d4a694b
--- /dev/null
+++ b/drivers/virtio/virtio_rtc.c
@@ -0,0 +1,213 @@
+// 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 the first UTC clock and reports it through the RTC uclass.
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <log.h>
+#include <rtc.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <linux/types.h>
+
+#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];
+} __packed;
+
+struct virtio_rtc_resp_head {
+	u8 status;
+	u8 reserved[7];
+} __packed;
+
+/* 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];
+} __packed;
+
+struct virtio_rtc_resp_read {
+	struct virtio_rtc_resp_head head;
+	__le64 clock_reading;
+} __packed;
+
+struct virtio_rtc_resp_cfg {
+	struct virtio_rtc_resp_head head;
+	__le16 num_clocks;
+	u8 reserved[6];
+} __packed;
+
+struct virtio_rtc_resp_clock_cap {
+	struct virtio_rtc_resp_head head;
+	u8 type;
+	u8 leap_second_smearing;
+	u8 flags;
+	u8 reserved[5];
+} __packed;
+
+struct virtio_rtc_priv {
+	struct virtqueue *vq;
+	u16 clock_id;
+	bool clock_found;
+};
+
+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;
+	int ret;
+
+	ret = virtqueue_add(priv->vq, sgs, 1, 1);
+	if (ret)
+		return ret;
+
+	virtqueue_kick(priv->vq);
+
+	while (!virtqueue_get_buf(priv->vq, &len))
+		;
+
+	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;
+	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 = {};
+
+		ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
+		if (ret)
+			continue;
+
+		if (virtio_rtc_is_utc(cap.type)) {
+			priv->clock_id = id;
+			priv->clock_found = true;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+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->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] 6+ messages in thread

* [PATCH v2 2/3] doc: virtio: Mention the RTC device
  2026-10-14 19:06 [PATCH v2 0/3] virtio: Add a virtio RTC driver Hung-Chun Tseng
  2026-10-14 19:06 ` [PATCH v2 1/3] virtio: rtc: Add " Hung-Chun Tseng
@ 2026-10-14 19:06 ` Hung-Chun Tseng
  2026-10-14 19:30   ` Kuan-Wei Chiu
  2026-10-14 19:06 ` [PATCH v2 3/3] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
  2 siblings, 1 reply; 6+ messages in thread
From: Hung-Chun Tseng @ 2026-10-14 19:06 UTC (permalink / raw)
  To: Bin Meng, Tom Rini
  Cc: Kuan-Wei Chiu, Simon Glass, Heinrich Schuchardt, u-boot

The list of supported device types still only named network and block
devices. Add the entropy, 9P and RTC devices, and 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>
---
 doc/develop/driver-model/virtio.rst | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/doc/develop/driver-model/virtio.rst b/doc/develop/driver-model/virtio.rst
index e5da9f430..211304706 100644
--- a/doc/develop/driver-model/virtio.rst
+++ b/doc/develop/driver-model/virtio.rst
@@ -28,7 +28,7 @@ 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.
+network, block, entropy (rng), 9P transport and RTC devices are supported.
 
 The following QEMU targets are supported.
 
@@ -72,6 +72,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] 6+ messages in thread

* [PATCH v2 3/3] MAINTAINERS: Add entry for virtio RTC
  2026-10-14 19:06 [PATCH v2 0/3] virtio: Add a virtio RTC driver Hung-Chun Tseng
  2026-10-14 19:06 ` [PATCH v2 1/3] virtio: rtc: Add " Hung-Chun Tseng
  2026-10-14 19:06 ` [PATCH v2 2/3] doc: virtio: Mention the RTC device Hung-Chun Tseng
@ 2026-10-14 19:06 ` Hung-Chun Tseng
  2 siblings, 0 replies; 6+ messages in thread
From: Hung-Chun Tseng @ 2026-10-14 19:06 UTC (permalink / raw)
  To: Bin Meng, Tom Rini
  Cc: Kuan-Wei Chiu, Simon Glass, Heinrich Schuchardt, u-boot

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>
---
 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] 6+ messages in thread

* Re: [PATCH v2 2/3] doc: virtio: Mention the RTC device
  2026-10-14 19:06 ` [PATCH v2 2/3] doc: virtio: Mention the RTC device Hung-Chun Tseng
@ 2026-10-14 19:30   ` Kuan-Wei Chiu
  0 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-10-14 19:30 UTC (permalink / raw)
  To: Hung-Chun Tseng
  Cc: Bin Meng, Tom Rini, Simon Glass, Heinrich Schuchardt, u-boot

Hi Hung-Chun,

On Thu, Oct 15, 2026 at 03:06:30AM +0800, Hung-Chun Tseng wrote:
> The list of supported device types still only named network and block
> devices. Add the entropy, 9P and RTC devices, and 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>
> ---
>  doc/develop/driver-model/virtio.rst | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 

Nit: Since this patch also mentions other virtio devices, the subject
title should be updated to reflect that rather than only mentioning the
RTC.

> diff --git a/doc/develop/driver-model/virtio.rst b/doc/develop/driver-model/virtio.rst
> index e5da9f430..211304706 100644
> --- a/doc/develop/driver-model/virtio.rst
> +++ b/doc/develop/driver-model/virtio.rst
> @@ -28,7 +28,7 @@ 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.
> +network, block, entropy (rng), 9P transport and RTC devices are supported.

Since u-boot supports more virtio drivers now and likely even more in
the future, I'm wondering if we should present them as a bulleted list
for better clarity, rather than listing them inline in a sentence.

Regards,
Kuan-Wei

>  
>  The following QEMU targets are supported.
>  
> @@ -72,6 +72,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	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 1/3] virtio: rtc: Add virtio RTC driver
  2026-10-14 19:06 ` [PATCH v2 1/3] virtio: rtc: Add " Hung-Chun Tseng
@ 2026-10-14 19:46   ` Kuan-Wei Chiu
  0 siblings, 0 replies; 6+ messages in thread
From: Kuan-Wei Chiu @ 2026-10-14 19:46 UTC (permalink / raw)
  To: Hung-Chun Tseng
  Cc: Bin Meng, Tom Rini, Simon Glass, Heinrich Schuchardt, u-boot

Hi Hung-Chun,

On Thu, Oct 15, 2026 at 03:06:29AM +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 picks the
> first one whose VIRTIO_RTC_REQ_CLOCK_CAP type is UTC, smeared UTC or
> maybe-smeared UTC. Reading the time sends a VIRTIO_RTC_REQ_READ for
> that clock and converts the nanoseconds since the epoch with
> rtc_to_tm(). The clocks are read-only, so there is no set operation,
> and the optional alarm feature is not negotiated.
> 
> VIRTIO_RTC defaults to y when VIRTIO and DM_RTC are enabled, like
> VIRTIO_RNG does for DM_RNG.
> 
> Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>

Thanks for the patch.

Actually, when I sent out the qemu virtio-rtc code, I also had a local
dirty version of the virtio-rtc u-boot driver, but it has been sitting
on my TODO list. I might take a closer look at this patch in a day or
two. This also reminds me that I still have some other virtio drivers
locally that I would like to try upstreaming. I might try to send them
out within the next week or two.

> ---
> 
> 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)

I would suggest leaving more time between sending out new patch
versions to give people a chance to review and reply, which helps avoid
spamming the list with rapid revisions. We have folks in various
timezones, and many of them are quite busy.

> 
>  drivers/virtio/Kconfig         |   9 ++
>  drivers/virtio/Makefile        |   1 +
>  drivers/virtio/virtio-uclass.c |   1 +
>  drivers/virtio/virtio_rtc.c    | 213 +++++++++++++++++++++++++++++++++
>  include/virtio.h               |   4 +-
>  5 files changed, 227 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/virtio/virtio_rtc.c
> 
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index 55b444280..c19138ef2 100644
> --- a/drivers/virtio/Kconfig
> +++ b/drivers/virtio/Kconfig
> @@ -84,4 +84,13 @@ 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 is the virtual RTC driver. It reads the UTC clock of a
> +	  virtio RTC device. It can be used with QEMU based targets.
>  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..f5d4a694b
> --- /dev/null
> +++ b/drivers/virtio/virtio_rtc.c
> @@ -0,0 +1,213 @@
> +// 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 the first UTC clock and reports it through the RTC uclass.
> + */
> +
> +#include <div64.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <rtc.h>
> +#include <virtio_types.h>
> +#include <virtio.h>
> +#include <virtio_ring.h>
> +#include <linux/types.h>
> +
> +#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];
> +} __packed;
> +
> +struct virtio_rtc_resp_head {
> +	u8 status;
> +	u8 reserved[7];
> +} __packed;
> +
> +/* 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];
> +} __packed;
> +
> +struct virtio_rtc_resp_read {
> +	struct virtio_rtc_resp_head head;
> +	__le64 clock_reading;
> +} __packed;
> +
> +struct virtio_rtc_resp_cfg {
> +	struct virtio_rtc_resp_head head;
> +	__le16 num_clocks;
> +	u8 reserved[6];
> +} __packed;
> +
> +struct virtio_rtc_resp_clock_cap {
> +	struct virtio_rtc_resp_head head;
> +	u8 type;
> +	u8 leap_second_smearing;
> +	u8 flags;
> +	u8 reserved[5];
> +} __packed;
> +
> +struct virtio_rtc_priv {
> +	struct virtqueue *vq;
> +	u16 clock_id;
> +	bool clock_found;
> +};
> +
> +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;
> +	int ret;
> +
> +	ret = virtqueue_add(priv->vq, sgs, 1, 1);
> +	if (ret)
> +		return ret;
> +
> +	virtqueue_kick(priv->vq);
> +
> +	while (!virtqueue_get_buf(priv->vq, &len))

IIRC, when I sent out the virtio-9p driver for review, Simon made the
same suggestion to add a timeout mechanism to the loop here to prevent
potential hangs. While it is true that many other virtio drivers are
currently written this way, it is on my TODO list to clean them up.

Regards,
Kuan-Wei

> +		;
> +
> +	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;
> +	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 = {};
> +
> +		ret = virtio_rtc_xfer(dev, &req, sizeof(req), &cap, sizeof(cap));
> +		if (ret)
> +			continue;
> +
> +		if (virtio_rtc_is_utc(cap.type)) {
> +			priv->clock_id = id;
> +			priv->clock_found = true;
> +			return 0;
> +		}
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +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->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] 6+ messages in thread

end of thread, other threads:[~2026-10-14 19:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 19:06 [PATCH v2 0/3] virtio: Add a virtio RTC driver Hung-Chun Tseng
2026-10-14 19:06 ` [PATCH v2 1/3] virtio: rtc: Add " Hung-Chun Tseng
2026-10-14 19:46   ` Kuan-Wei Chiu
2026-10-14 19:06 ` [PATCH v2 2/3] doc: virtio: Mention the RTC device Hung-Chun Tseng
2026-10-14 19:30   ` Kuan-Wei Chiu
2026-10-14 19:06 ` [PATCH v2 3/3] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox