From: Kuan-Wei Chiu <visitorckw@gmail.com>
To: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
Cc: Bin Meng <bmeng.cn@gmail.com>, Tom Rini <trini@konsulko.com>,
Simon Glass <sjg@chromium.org>,
Heinrich Schuchardt <xypron.glpk@gmx.de>,
u-boot@lists.u-boot-project.org
Subject: Re: [PATCH v2 1/3] virtio: rtc: Add virtio RTC driver
Date: Sun, 18 Oct 2026 02:28:17 +0800 [thread overview]
Message-ID: <atO-QSSxMboOtqym@google.com> (raw)
In-Reply-To: <20261014190631.2232208-2-alan.tseng.cs@gmail.com>
Hi Hung-Chun,
Sorry for getting back on this late.
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>
> ---
>
> 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;
IIUC, reserved[] is already intended to ensure natural alignment for
all members, so I'm wondering if adding __packed here is actually
counterintuitive?
> +
> +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;
Linux only permits VIRTIO_RTC_CLOCK_UTC_SMEARED to be registered as an
RTC class device and deliberately skips VIRTIO_RTC_CLOCK_UTC and
VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED. Of course, since U-Boot does not
have a PTP subsystem, we probably shouldn't follow Linux strictly here.
However, as an example, if a device exposes both VIRTIO_RTC_CLOCK_UTC
(e.g., clock 0 for PTP) and VIRTIO_RTC_CLOCK_UTC_SMEARED (e.g., clock 1
for RTC), the Linux RTC class binds to clock 1, whereas this U-Boot
driver simply picks the first UTC-like clock it encounters
unconditionally (clock 0). I'm not sure if this is what we want, or
whether you have considered this scenario? I feel like this is at least
worth mentioning in the commit message or in an code comment.
> +}
> +
> +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;
The Linux virtio-rtc driver stops and returns an error code when
viortc_clocks_init() fails. I'm not sure if we have any particular
reason to proceed here?
Regards,
Kuan-Wei
> +
> + 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
>
next prev parent reply other threads:[~2026-10-17 18:28 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
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-16 16:41 ` Hung-Chun Tseng
2026-10-16 21:47 ` Simon Glass
2026-10-17 18:28 ` Kuan-Wei Chiu [this message]
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-16 21:47 ` Simon Glass
2026-10-14 19:06 ` [PATCH v2 3/3] MAINTAINERS: Add entry for virtio RTC Hung-Chun Tseng
2026-10-16 21:47 ` Simon Glass
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=atO-QSSxMboOtqym@google.com \
--to=visitorckw@gmail.com \
--cc=alan.tseng.cs@gmail.com \
--cc=bmeng.cn@gmail.com \
--cc=sjg@chromium.org \
--cc=trini@konsulko.com \
--cc=u-boot@lists.u-boot-project.org \
--cc=xypron.glpk@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox