From nobody Wed Dec 17 08:56:15 2025 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CCDD42356A9; Fri, 9 May 2025 16:08:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806922; cv=none; b=QYv7wGyH7m8ACgQ/YDq0iuOa4JGkbNverwnIJ39+WtFN6ShEA6K3vffPKQ6A+5IJBcmyeCF7OcOAbMzZZeoe2RRLtCdxzBwgPDh/gRAVIKsgmCahw6P8jUTMHeibsKzuongRjsitWZqEQmxbhjoEGe0glfWjY4apHq9Y9wkSc1Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806922; c=relaxed/simple; bh=Y/pwIYQ7Gvkoj4IE27qFKAazwu34nAkHNTKwiqwd7iI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=NMjH5/Mr8IC4XQEsrXXbmNX/cuL4ui76tS5RvLRRkvc7KV7QnSc3w2wHfn2BwDIE6Y7nL9X9Ypp/yLRwh/9HX0HqZkcrKn+bVikKdYndW/yCNmGjjw08JxqtlqGN5tBt1/YEjCs9By1QcTq2/WZg7vbhFrn9MLcyIPQOqatqOE0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=KBrXl4MH; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="KBrXl4MH" Received: from pps.filterd (m0279873.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 549Brwcm008543; Fri, 9 May 2025 16:08:06 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= RgVhBK4rL51OIXUQVnrhKnCX4dAK5HxRcD9ls+aPKuw=; b=KBrXl4MHG1A7qMSH DAoViBNFmm6BniZOAkawtHkjT62vGqcn3i5t0fDaYMosMEETyyx+PZ45lS7PewDX YazudzdGIn+6K0OxY4Q2XuqSY6wsCUJyr0Tfdam9Chs6d+7E4MR0+deNtIULEPnF IyU+cod8kDswrPwMiuJSxBHan4SnzNWLzjZV0avanH63ZnpS4zudkOK+xgzLz2KM uQtnK/07I9huKYtuzsz/HkhJ/v92pV+LNpMfOmX40Bi1LvhesOKbqQpbm46oa4WG jUcou0+rjJZDDKPxXTeFfZFKLS/P7mFbfb0J5OI7rWSpAfyrL44RLA9mbHzFD3s3 5GTUPQ== Received: from nalasppmta01.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 46gnp5d776-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 09 May 2025 16:08:05 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA01.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 549G84i8028927 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 9 May 2025 16:08:04 GMT Received: from PHILBER.na.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Fri, 9 May 2025 09:07:58 -0700 From: Peter Hilber To: "Michael S. Tsirkin" , Jason Wang CC: Trilok Soni , Peter Hilber , Xuan Zhuo , =?UTF-8?q?Eugenio=20P=C3=A9rez?= , Richard Cochran , , , , David Woodhouse , "Ridoux, Julien" , Marc Zyngier , Mark Rutland , Daniel Lezcano , Alexandre Belloni , Parav Pandit , "Matias Ezequiel Vara Larsen" , Cornelia Huck , Simon Horman , , , Subject: [PATCH v7 1/4] virtio_rtc: Add module and driver core Date: Fri, 9 May 2025 18:07:22 +0200 Message-ID: <20250509160734.1772-2-quic_philber@quicinc.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250509160734.1772-1-quic_philber@quicinc.com> References: <20250509160734.1772-1-quic_philber@quicinc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Authority-Analysis: v=2.4 cv=Lu2Symdc c=1 sm=1 tr=0 ts=681e2865 cx=c_pps a=ouPCqIW2jiPt+lZRy3xVPw==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=GEpy-HfZoHoA:10 a=dt9VzEwgFbYA:10 a=COk6AnOGAAAA:8 a=bs-e4Z8cAAAA:8 a=20KFwNOVAAAA:8 a=dsUN5IrYLHpEE-TGldIA:9 a=TjNXssC_j7lpFel5tvFf:22 a=YFpLQiK6rFjqGb8m0bXF:22 X-Proofpoint-GUID: Huc3-JNtTAj3Ud8BCNcL8skMeYSjn31N X-Proofpoint-ORIG-GUID: Huc3-JNtTAj3Ud8BCNcL8skMeYSjn31N X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNTA5MDE1OSBTYWx0ZWRfX7sdfoH6QV8r9 8bUcxjWQ+ExGL3HBkw65YxDWiBZGyBu4/6Wp+qKRLgBjqPjRV37V8cgf+RjFwqM642CkzaMI9/G pcrAfvLZNlcNNSWHYc58zHNhVv8ENK4O3dj0sNJdu1HZbvwqwP/SMEDiHUKNjCg6DCDvSqApLMz /LSlt4mfLDSMdywjRyn7OI036TJ4Wh570soOmIYQMo1Q507DFh98qJRGYaVhmQksZnADDQKMhSk jJR6iUsog436Sqyu7/CrJn/RQ34HUYf+Xo9VCwN8AbgGUuzpF5SvYX/oslrX2YHbJTACcwi4llS Grl5FuOosCYZ1YelN2cwBt4t2CnQ5vmotfaTmKTdvqvVt14AE0ZpKx9D2mrjmlsOmsJQACt4UTw BTjC/mLPgaBez+cwvqgkbzFPPMnnobn6aC0Qwrxyn8dq3s76UHZluv9aMTiMkeKL27YkJym+ X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-05-09_06,2025-05-09_01,2025-02-21_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 lowpriorityscore=0 bulkscore=0 mlxlogscore=999 priorityscore=1501 suspectscore=0 clxscore=1011 adultscore=0 malwarescore=0 spamscore=0 impostorscore=0 mlxscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2504070000 definitions=main-2505090159 Content-Type: text/plain; charset="utf-8" Add the virtio_rtc module and driver core. The virtio_rtc module implements a driver compatible with the proposed Virtio RTC device specification. The Virtio RTC (Real Time Clock) device provides information about current time. The device can provide different clocks, e.g. for the UTC or TAI time standards, or for physical time elapsed since some past epoch. The driver can read the clocks with simple or more accurate methods. Implement the core, which interacts with the Virtio RTC device. Apart from this, the core does not expose functionality outside of the virtio_rtc module. Follow-up patches will expose PTP clocks and an RTC Class device. Provide synchronous messaging, which is enough for the expected time synchronization use cases through PTP clocks (similar to ptp_kvm) or RTC Class device. Signed-off-by: Peter Hilber --- Notes: v6: =20 - Fix sparse warning about endianness mismatch (Simon Horman). =20 - Improve error status computation readability. =20 v5: =20 - Remove definitions dropped in virtio-rtc spec v7. =20 - Fix style issues. =20 - Add freeze/restore ops already in this patch. =20 - Drop unnecessary memory barrier pair. =20 - Return error status from device, whenever available. =20 - Use secs_to_jiffies() macro. =20 v4: =20 - Update Virtio interface to spec v6. =20 - Distinguish UTC-like clocks by handling of leap seconds (spec v6). =20 - Remove unnecessary memory barriers. =20 - Cosmetic improvements. =20 v3: =20 - merge readq and controlq into a single requestq (spec v3) =20 - don't guard cross-timestamping with feature bit (spec v3) =20 - pad message headers to 64 bits (spec v3) =20 - reduce clock id to 16 bits (spec v3) =20 - change Virtio status codes (spec v3) =20 - use 'VIRTIO_RTC_REQ_' prefix for request messages (spec v3) MAINTAINERS | 7 + drivers/virtio/Kconfig | 13 + drivers/virtio/Makefile | 2 + drivers/virtio/virtio_rtc_driver.c | 786 +++++++++++++++++++++++++++ drivers/virtio/virtio_rtc_internal.h | 24 + include/uapi/linux/virtio_rtc.h | 151 +++++ 6 files changed, 983 insertions(+) create mode 100644 drivers/virtio/virtio_rtc_driver.c create mode 100644 drivers/virtio/virtio_rtc_internal.h create mode 100644 include/uapi/linux/virtio_rtc.h diff --git a/MAINTAINERS b/MAINTAINERS index c59316109e3f..f5b06737c5cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25658,6 +25658,13 @@ S: Maintained F: drivers/nvdimm/nd_virtio.c F: drivers/nvdimm/virtio_pmem.c =20 +VIRTIO RTC DRIVER +M: Peter Hilber +L: virtualization@lists.linux.dev +S: Maintained +F: drivers/virtio/virtio_rtc_* +F: include/uapi/linux/virtio_rtc.h + VIRTIO SOUND DRIVER M: Anton Yakovlev M: "Michael S. Tsirkin" diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 2eb747311bfd..83bcb06acb6c 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -188,4 +188,17 @@ config VIRTIO_DEBUG =20 If unsure, say N. =20 +config VIRTIO_RTC + tristate "Virtio RTC driver" + depends on VIRTIO + depends on PTP_1588_CLOCK_OPTIONAL + help + This driver provides current time from a Virtio RTC device. The driver + provides the time through one or more clocks. + + To compile this code as a module, choose M here: the module will be + called virtio_rtc. + + If unsure, say M. + endif # VIRTIO_MENU diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 58b2b0489fc9..c41c4c0f9264 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -14,3 +14,5 @@ obj-$(CONFIG_VIRTIO_VDPA) +=3D virtio_vdpa.o obj-$(CONFIG_VIRTIO_MEM) +=3D virtio_mem.o obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) +=3D virtio_dma_buf.o obj-$(CONFIG_VIRTIO_DEBUG) +=3D virtio_debug.o +obj-$(CONFIG_VIRTIO_RTC) +=3D virtio_rtc.o +virtio_rtc-y :=3D virtio_rtc_driver.o diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc= _driver.c new file mode 100644 index 000000000000..c87ee4e63424 --- /dev/null +++ b/drivers/virtio/virtio_rtc_driver.c @@ -0,0 +1,786 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * virtio_rtc driver core + * + * Copyright (C) 2022-2024 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "virtio_rtc_internal.h" + +/* virtqueue order */ +enum { + VIORTC_REQUESTQ, + VIORTC_MAX_NR_QUEUES, +}; + +/** + * struct viortc_vq - virtqueue abstraction + * @vq: virtqueue + * @lock: protects access to vq + */ +struct viortc_vq { + struct virtqueue *vq; + spinlock_t lock; +}; + +/** + * struct viortc_dev - virtio_rtc device data + * @vdev: virtio device + * @vqs: virtqueues + * @num_clocks: # of virtio_rtc clocks + */ +struct viortc_dev { + struct virtio_device *vdev; + struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES]; + u16 num_clocks; +}; + +/** + * struct viortc_msg - Message requested by driver, responded by device. + * @viortc: device data + * @req: request buffer + * @resp: response buffer + * @responded: vqueue callback signals response reception + * @refcnt: Message reference count, message and buffers will be deallocat= ed + * once 0. refcnt is decremented in the vqueue callback and in the + * thread waiting on the responded completion. + * If a message response wait function times out, the message wil= l be + * freed upon late reception (refcnt will reach 0 in the callback= ), or + * device removal. + * @req_size: size of request in bytes + * @resp_cap: maximum size of response in bytes + * @resp_actual_size: actual size of response + */ +struct viortc_msg { + struct viortc_dev *viortc; + void *req; + void *resp; + struct completion responded; + refcount_t refcnt; + unsigned int req_size; + unsigned int resp_cap; + unsigned int resp_actual_size; +}; + +/** + * viortc_msg_init() - Allocate and initialize requestq message. + * @viortc: device data + * @msg_type: virtio_rtc message type + * @req_size: size of request buffer to be allocated + * @resp_cap: size of response buffer to be allocated + * + * Initializes the message refcnt to 2. The refcnt will be decremented onc= e in + * the virtqueue callback, and once in the thread waiting on the message (= on + * completion or timeout). + * + * Context: Process context. + * Return: non-NULL on success. + */ +static struct viortc_msg *viortc_msg_init(struct viortc_dev *viortc, + u16 msg_type, unsigned int req_size, + unsigned int resp_cap) +{ + struct device *dev =3D &viortc->vdev->dev; + struct virtio_rtc_req_head *req_head; + struct viortc_msg *msg; + + msg =3D devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL); + if (!msg) + return NULL; + + init_completion(&msg->responded); + + msg->req =3D devm_kzalloc(dev, req_size, GFP_KERNEL); + if (!msg->req) + goto err_free_msg; + + req_head =3D msg->req; + + msg->resp =3D devm_kzalloc(dev, resp_cap, GFP_KERNEL); + if (!msg->resp) + goto err_free_msg_req; + + msg->viortc =3D viortc; + msg->req_size =3D req_size; + msg->resp_cap =3D resp_cap; + + refcount_set(&msg->refcnt, 2); + + req_head->msg_type =3D virtio_cpu_to_le(msg_type, req_head->msg_type); + + return msg; + +err_free_msg_req: + devm_kfree(dev, msg->req); + +err_free_msg: + devm_kfree(dev, msg); + + return NULL; +} + +/** + * viortc_msg_release() - Decrement message refcnt, potentially free messa= ge. + * @msg: message requested by driver + * + * Context: Any context. + */ +static void viortc_msg_release(struct viortc_msg *msg) +{ + struct device *dev; + + if (refcount_dec_and_test(&msg->refcnt)) { + dev =3D &msg->viortc->vdev->dev; + + devm_kfree(dev, msg->req); + devm_kfree(dev, msg->resp); + devm_kfree(dev, msg); + } +} + +/** + * viortc_do_cb() - generic virtqueue callback logic + * @vq: virtqueue + * @handle_buf: function to process a used buffer + * + * Context: virtqueue callback, typically interrupt. Takes and releases vq= lock. + */ +static void viortc_do_cb(struct virtqueue *vq, + void (*handle_buf)(void *token, unsigned int len, + struct virtqueue *vq, + struct viortc_vq *viortc_vq, + struct viortc_dev *viortc)) +{ + struct viortc_dev *viortc =3D vq->vdev->priv; + struct viortc_vq *viortc_vq; + bool cb_enabled =3D true; + unsigned long flags; + unsigned int len; + void *token; + + viortc_vq =3D &viortc->vqs[vq->index]; + + for (;;) { + spin_lock_irqsave(&viortc_vq->lock, flags); + + if (cb_enabled) { + virtqueue_disable_cb(vq); + cb_enabled =3D false; + } + + token =3D virtqueue_get_buf(vq, &len); + if (!token) { + if (virtqueue_enable_cb(vq)) { + spin_unlock_irqrestore(&viortc_vq->lock, flags); + return; + } + cb_enabled =3D true; + } + + spin_unlock_irqrestore(&viortc_vq->lock, flags); + + if (token) + handle_buf(token, len, vq, viortc_vq, viortc); + } +} + +/** + * viortc_requestq_hdlr() - process a requestq used buffer + * @token: token identifying the buffer + * @len: bytes written by device + * @vq: virtqueue + * @viortc_vq: device specific data for virtqueue + * @viortc: device data + * + * Signals completion for each received message. + * + * Context: virtqueue callback + */ +static void viortc_requestq_hdlr(void *token, unsigned int len, + struct virtqueue *vq, + struct viortc_vq *viortc_vq, + struct viortc_dev *viortc) +{ + struct viortc_msg *msg =3D token; + + msg->resp_actual_size =3D len; + + complete(&msg->responded); + viortc_msg_release(msg); +} + +/** + * viortc_cb_requestq() - callback for requestq + * @vq: virtqueue + * + * Context: virtqueue callback + */ +static void viortc_cb_requestq(struct virtqueue *vq) +{ + viortc_do_cb(vq, viortc_requestq_hdlr); +} + +/** + * viortc_get_resp_errno() - converts virtio_rtc errnos to system errnos + * @resp_head: message response header + * + * Return: negative system errno, or 0 + */ +static int viortc_get_resp_errno(struct virtio_rtc_resp_head *resp_head) +{ + switch (virtio_le_to_cpu(resp_head->status)) { + case VIRTIO_RTC_S_OK: + return 0; + case VIRTIO_RTC_S_EOPNOTSUPP: + return -EOPNOTSUPP; + case VIRTIO_RTC_S_EINVAL: + return -EINVAL; + case VIRTIO_RTC_S_ENODEV: + return -ENODEV; + case VIRTIO_RTC_S_EIO: + default: + return -EIO; + } +} + +/** + * viortc_msg_xfer() - send message request, wait until message response + * @vq: virtqueue + * @msg: message with driver request + * @timeout_jiffies: message response timeout, 0 for no timeout + * + * Context: Process context. Takes and releases vq.lock. May sleep. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg, + unsigned long timeout_jiffies) +{ + struct scatterlist out_sg[1]; + struct scatterlist in_sg[1]; + struct scatterlist *sgs[2]; + unsigned long flags; + long timeout_ret; + bool notify; + int ret; + + sgs[0] =3D out_sg; + sgs[1] =3D in_sg; + + sg_init_one(out_sg, msg->req, msg->req_size); + sg_init_one(in_sg, msg->resp, msg->resp_cap); + + spin_lock_irqsave(&vq->lock, flags); + + ret =3D virtqueue_add_sgs(vq->vq, sgs, 1, 1, msg, GFP_ATOMIC); + if (ret) { + spin_unlock_irqrestore(&vq->lock, flags); + /* + * Release in place of the response callback, which will never + * come. + */ + viortc_msg_release(msg); + return ret; + } + + notify =3D virtqueue_kick_prepare(vq->vq); + + spin_unlock_irqrestore(&vq->lock, flags); + + if (notify) + virtqueue_notify(vq->vq); + + if (timeout_jiffies) { + timeout_ret =3D wait_for_completion_interruptible_timeout( + &msg->responded, timeout_jiffies); + + if (!timeout_ret) + return -ETIMEDOUT; + else if (timeout_ret < 0) + return (int)timeout_ret; + } else { + ret =3D wait_for_completion_interruptible(&msg->responded); + if (ret) + return ret; + } + + if (msg->resp_actual_size < sizeof(struct virtio_rtc_resp_head)) + return -EINVAL; + + ret =3D viortc_get_resp_errno(msg->resp); + if (ret) + return ret; + + /* + * There is not yet a case where returning a short message would make + * sense, so consider any deviation an error. + */ + if (msg->resp_actual_size !=3D msg->resp_cap) + return -EINVAL; + + return 0; +} + +/* + * common message handle macros for messages of different types + */ + +/** + * VIORTC_DECLARE_MSG_HDL_ONSTACK() - declare message handle on stack + * @hdl: message handle name + * @msg_id: message type id + * @msg_req: message request type + * @msg_resp: message response type + */ +#define VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, msg_id, msg_req, msg_resp) = \ + struct { \ + struct viortc_msg *msg; \ + msg_req *req; \ + msg_resp *resp; \ + unsigned int req_size; \ + unsigned int resp_cap; \ + u16 msg_type; \ + } hdl =3D { \ + NULL, NULL, NULL, sizeof(msg_req), sizeof(msg_resp), (msg_id), \ + } + +/** + * VIORTC_MSG() - extract message from message handle + * @hdl: message handle + * + * Return: struct viortc_msg + */ +#define VIORTC_MSG(hdl) ((hdl).msg) + +/** + * VIORTC_MSG_INIT() - initialize message handle + * @hdl: message handle + * @viortc: device data (struct viortc_dev *) + * + * Context: Process context. + * Return: 0 on success, -ENOMEM otherwise. + */ +#define VIORTC_MSG_INIT(hdl, viortc) = \ + ({ \ + typeof(hdl) *_hdl =3D &(hdl); \ + \ + _hdl->msg =3D viortc_msg_init((viortc), _hdl->msg_type, \ + _hdl->req_size, _hdl->resp_cap); \ + if (_hdl->msg) { \ + _hdl->req =3D _hdl->msg->req; \ + _hdl->resp =3D _hdl->msg->resp; \ + } \ + _hdl->msg ? 0 : -ENOMEM; \ + }) + +/** + * VIORTC_MSG_WRITE() - write a request message field + * @hdl: message handle + * @dest_member: request message field name + * @src_ptr: pointer to data of compatible type + * + * Writes the field in little-endian format. + */ +#define VIORTC_MSG_WRITE(hdl, dest_member, src_ptr) = \ + do { \ + typeof(hdl) _hdl =3D (hdl); \ + typeof(src_ptr) _src_ptr =3D (src_ptr); \ + \ + /* Sanity check: must match the member's type */ \ + typecheck(typeof(virtio_le_to_cpu(_hdl.req->dest_member)), \ + *_src_ptr); \ + \ + _hdl.req->dest_member =3D \ + virtio_cpu_to_le(*_src_ptr, _hdl.req->dest_member); \ + } while (0) + +/** + * VIORTC_MSG_READ() - read from a response message field + * @hdl: message handle + * @src_member: response message field name + * @dest_ptr: pointer to data of compatible type + * + * Converts from little-endian format and writes to dest_ptr. + */ +#define VIORTC_MSG_READ(hdl, src_member, dest_ptr) = \ + do { \ + typeof(dest_ptr) _dest_ptr =3D (dest_ptr); \ + \ + /* Sanity check: must match the member's type */ \ + typecheck(typeof(virtio_le_to_cpu((hdl).resp->src_member)), \ + *_dest_ptr); \ + \ + *_dest_ptr =3D virtio_le_to_cpu((hdl).resp->src_member); \ + } while (0) + +/* + * read requests + */ + +/** timeout for clock readings, where timeouts are considered non-fatal */ +#define VIORTC_MSG_READ_TIMEOUT secs_to_jiffies(60) + +/** + * viortc_read() - VIRTIO_RTC_REQ_READ wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @reading: clock reading [ns] + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_read(struct viortc_dev *viortc, u16 vio_clk_id, u64 *reading) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_READ, + struct virtio_rtc_req_read, + struct virtio_rtc_resp_read); + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + VIORTC_MSG_READ_TIMEOUT); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, clock_reading, reading); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/** + * viortc_read_cross() - VIRTIO_RTC_REQ_READ_CROSS wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @hw_counter: virtio_rtc HW counter type + * @reading: clock reading [ns] + * @cycles: HW counter cycles during clock reading + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_read_cross(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_cou= nter, + u64 *reading, u64 *cycles) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_READ_CROSS, + struct virtio_rtc_req_read_cross, + struct virtio_rtc_resp_read_cross); + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + VIORTC_MSG_WRITE(hdl, hw_counter, &hw_counter); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + VIORTC_MSG_READ_TIMEOUT); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, clock_reading, reading); + VIORTC_MSG_READ(hdl, counter_cycles, cycles); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/* + * control requests + */ + +/** + * viortc_cfg() - VIRTIO_RTC_REQ_CFG wrapper + * @viortc: device data + * @num_clocks: # of virtio_rtc clocks + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_cfg(struct viortc_dev *viortc, u16 *num_clocks) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_CFG, + struct virtio_rtc_req_cfg, + struct virtio_rtc_resp_cfg); + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, num_clocks, num_clocks); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/** + * viortc_clock_cap() - VIRTIO_RTC_REQ_CLOCK_CAP wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @type: virtio_rtc clock type + * @leap_second_smearing: virtio_rtc smearing variant + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_clock_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 = *type, + u8 *leap_second_smearing) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_CLOCK_CAP, + struct virtio_rtc_req_clock_cap, + struct virtio_rtc_resp_clock_cap); + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, type, type); + VIORTC_MSG_READ(hdl, leap_second_smearing, leap_second_smearing); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/** + * viortc_cross_cap() - VIRTIO_RTC_REQ_CROSS_CAP wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @hw_counter: virtio_rtc HW counter type + * @supported: xtstamping is supported for the vio_clk_id/hw_counter pair + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_coun= ter, + bool *supported) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_CROSS_CAP, + struct virtio_rtc_req_cross_cap, + struct virtio_rtc_resp_cross_cap); + u8 flags; + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + VIORTC_MSG_WRITE(hdl, hw_counter, &hw_counter); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, flags, &flags); + *supported =3D !!(flags & VIRTIO_RTC_FLAG_CROSS_CAP); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/* + * init, deinit + */ + +/** + * viortc_clocks_init() - init local representations of virtio_rtc clocks + * @viortc: device data + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_clocks_init(struct viortc_dev *viortc) +{ + u16 num_clocks; + int ret; + + ret =3D viortc_cfg(viortc, &num_clocks); + if (ret) + return ret; + + if (num_clocks < 1) { + dev_err(&viortc->vdev->dev, "device reported 0 clocks\n"); + return -ENODEV; + } + + viortc->num_clocks =3D num_clocks; + + /* In the future, PTP clocks will be initialized here. */ + (void)viortc_clock_cap; + + return ret; +} + +/** + * viortc_init_vqs() - init virtqueues + * @viortc: device data + * + * Inits virtqueues and associated data. + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_init_vqs(struct viortc_dev *viortc) +{ + struct virtqueue *vqs[VIORTC_MAX_NR_QUEUES]; + struct virtqueue_info vqs_info[] =3D { + { "requestq", viortc_cb_requestq }, + }; + struct virtio_device *vdev =3D viortc->vdev; + int nr_queues, ret; + + nr_queues =3D VIORTC_REQUESTQ + 1; + + ret =3D virtio_find_vqs(vdev, nr_queues, vqs, vqs_info, NULL); + if (ret) + return ret; + + viortc->vqs[VIORTC_REQUESTQ].vq =3D vqs[VIORTC_REQUESTQ]; + spin_lock_init(&viortc->vqs[VIORTC_REQUESTQ].lock); + + return 0; +} + +/** + * viortc_probe() - probe a virtio_rtc virtio device + * @vdev: virtio device + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_probe(struct virtio_device *vdev) +{ + struct viortc_dev *viortc; + int ret; + + viortc =3D devm_kzalloc(&vdev->dev, sizeof(*viortc), GFP_KERNEL); + if (!viortc) + return -ENOMEM; + + vdev->priv =3D viortc; + viortc->vdev =3D vdev; + + ret =3D viortc_init_vqs(viortc); + if (ret) + return ret; + + virtio_device_ready(vdev); + + ret =3D viortc_clocks_init(viortc); + if (ret) + goto err_reset_vdev; + + return 0; + +err_reset_vdev: + virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); + + return ret; +} + +/** + * viortc_remove() - remove a virtio_rtc virtio device + * @vdev: virtio device + */ +static void viortc_remove(struct virtio_device *vdev) +{ + /* In the future, PTP clocks will be deinitialized here. */ + + virtio_reset_device(vdev); + vdev->config->del_vqs(vdev); +} + +static int viortc_freeze(struct virtio_device *dev) +{ + /* + * Do not reset the device, so that the device may still wake up the + * system through an alarmq notification. + */ + + return 0; +} + +static int viortc_restore(struct virtio_device *dev) +{ + struct viortc_dev *viortc =3D dev->priv; + + return viortc_init_vqs(viortc); +} + +static struct virtio_device_id id_table[] =3D { + { VIRTIO_ID_CLOCK, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; +MODULE_DEVICE_TABLE(virtio, id_table); + +static struct virtio_driver virtio_rtc_drv =3D { + .driver.name =3D KBUILD_MODNAME, + .id_table =3D id_table, + .probe =3D viortc_probe, + .remove =3D viortc_remove, + .freeze =3D pm_sleep_ptr(viortc_freeze), + .restore =3D pm_sleep_ptr(viortc_restore), +}; + +module_virtio_driver(virtio_rtc_drv); + +MODULE_DESCRIPTION("Virtio RTC driver"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/virtio/virtio_rtc_internal.h b/drivers/virtio/virtio_r= tc_internal.h new file mode 100644 index 000000000000..9c249c15b68f --- /dev/null +++ b/drivers/virtio/virtio_rtc_internal.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * virtio_rtc internal interfaces + * + * Copyright (C) 2022-2023 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _VIRTIO_RTC_INTERNAL_H_ +#define _VIRTIO_RTC_INTERNAL_H_ + +#include + +/* driver core IFs */ + +struct viortc_dev; + +int viortc_read(struct viortc_dev *viortc, u16 vio_clk_id, u64 *reading); +int viortc_read_cross(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_cou= nter, + u64 *reading, u64 *cycles); +int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_coun= ter, + bool *supported); + +#endif /* _VIRTIO_RTC_INTERNAL_H_ */ diff --git a/include/uapi/linux/virtio_rtc.h b/include/uapi/linux/virtio_rt= c.h new file mode 100644 index 000000000000..6b3af4e9bbfb --- /dev/null +++ b/include/uapi/linux/virtio_rtc.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-C= lause) */ +/* + * Copyright (C) 2022-2024 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _LINUX_VIRTIO_RTC_H +#define _LINUX_VIRTIO_RTC_H + +#include + +/* read request message types */ + +#define VIRTIO_RTC_REQ_READ 0x0001 +#define VIRTIO_RTC_REQ_READ_CROSS 0x0002 + +/* control request message types */ + +#define VIRTIO_RTC_REQ_CFG 0x1000 +#define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001 +#define VIRTIO_RTC_REQ_CROSS_CAP 0x1002 + +/* Message headers */ + +/** common request header */ +struct virtio_rtc_req_head { + __le16 msg_type; + __u8 reserved[6]; +}; + +/** common response header */ +struct virtio_rtc_resp_head { +#define VIRTIO_RTC_S_OK 0 +#define VIRTIO_RTC_S_EOPNOTSUPP 2 +#define VIRTIO_RTC_S_ENODEV 3 +#define VIRTIO_RTC_S_EINVAL 4 +#define VIRTIO_RTC_S_EIO 5 + __u8 status; + __u8 reserved[7]; +}; + +/* read requests */ + +/* VIRTIO_RTC_REQ_READ message */ + +struct virtio_rtc_req_read { + 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; +}; + +/* VIRTIO_RTC_REQ_READ_CROSS message */ + +struct virtio_rtc_req_read_cross { + struct virtio_rtc_req_head head; + __le16 clock_id; +/* Arm Generic Timer Counter-timer Virtual Count Register (CNTVCT_EL0) */ +#define VIRTIO_RTC_COUNTER_ARM_VCT 0 +/* x86 Time-Stamp Counter */ +#define VIRTIO_RTC_COUNTER_X86_TSC 1 +/* Invalid */ +#define VIRTIO_RTC_COUNTER_INVALID 0xFF + __u8 hw_counter; + __u8 reserved[5]; +}; + +struct virtio_rtc_resp_read_cross { + struct virtio_rtc_resp_head head; + __le64 clock_reading; + __le64 counter_cycles; +}; + +/* control requests */ + +/* VIRTIO_RTC_REQ_CFG message */ + +struct virtio_rtc_req_cfg { + struct virtio_rtc_req_head head; + /* no request params */ +}; + +struct virtio_rtc_resp_cfg { + struct virtio_rtc_resp_head head; + /** # of clocks -> clock ids < num_clocks are valid */ + __le16 num_clocks; + __u8 reserved[6]; +}; + +/* VIRTIO_RTC_REQ_CLOCK_CAP message */ + +struct virtio_rtc_req_clock_cap { + struct virtio_rtc_req_head head; + __le16 clock_id; + __u8 reserved[6]; +}; + +struct virtio_rtc_resp_clock_cap { + struct virtio_rtc_resp_head head; +#define VIRTIO_RTC_CLOCK_UTC 0 +#define VIRTIO_RTC_CLOCK_TAI 1 +#define VIRTIO_RTC_CLOCK_MONOTONIC 2 +#define VIRTIO_RTC_CLOCK_UTC_SMEARED 3 +#define VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED 4 + __u8 type; +#define VIRTIO_RTC_SMEAR_UNSPECIFIED 0 +#define VIRTIO_RTC_SMEAR_NOON_LINEAR 1 +#define VIRTIO_RTC_SMEAR_UTC_SLS 2 + __u8 leap_second_smearing; + __u8 reserved[6]; +}; + +/* VIRTIO_RTC_REQ_CROSS_CAP message */ + +struct virtio_rtc_req_cross_cap { + struct virtio_rtc_req_head head; + __le16 clock_id; + __u8 hw_counter; + __u8 reserved[5]; +}; + +struct virtio_rtc_resp_cross_cap { + struct virtio_rtc_resp_head head; +#define VIRTIO_RTC_FLAG_CROSS_CAP (1 << 0) + __u8 flags; + __u8 reserved[7]; +}; + +/** Union of request types for requestq */ +union virtio_rtc_req_requestq { + struct virtio_rtc_req_read read; + struct virtio_rtc_req_read_cross read_cross; + struct virtio_rtc_req_cfg cfg; + struct virtio_rtc_req_clock_cap clock_cap; + struct virtio_rtc_req_cross_cap cross_cap; +}; + +/** Union of response types for requestq */ +union virtio_rtc_resp_requestq { + struct virtio_rtc_resp_read read; + struct virtio_rtc_resp_read_cross read_cross; + struct virtio_rtc_resp_cfg cfg; + struct virtio_rtc_resp_clock_cap clock_cap; + struct virtio_rtc_resp_cross_cap cross_cap; +}; + +#endif /* _LINUX_VIRTIO_RTC_H */ --=20 2.43.0 From nobody Wed Dec 17 08:56:15 2025 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DBA72235045; Fri, 9 May 2025 16:08:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806923; cv=none; b=tk/qCOVBfxOmgcI9nJd+ijIpcremSLc4LiXUuKhynMtYXu2yld9uPnbFpf8gBpfFhtK/OZAd/NrLmQNRSssF+eupeIwLsrXNuKaMmNwK9iayqXePJKuRS0MefneWblVH0vSvzakiwZZ802rhYg+ua5ulDRVA6WG1KM0dT+K1Upw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806923; c=relaxed/simple; bh=+YmS+XO10LE2RWZoerrvmhIROZ5i+zr7drXRI+JCM8Q=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OgmnzNARMwPQX4gXvaXgPemIdq05zgr/mSiYCbH7K5YKlrPJToW4YNPmf4rbJeYy+DNiN4PKMwYcOHOHv2ONk0qNffl9O18Dlg2LectiRRJidyzOdMYvWuufK7LawUdvNevyQqYQPQpY3q8YLFG1+lC91Ub/hO7Trdw0Ib/NWH4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=VPt0ilw+; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="VPt0ilw+" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 549DVrnO016373; Fri, 9 May 2025 16:08:12 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= VCxERcKy6WGw1b/FGOKeUm8vQBZKq1ALhgHvbZGHfds=; b=VPt0ilw+vAD9/gpL oXhz7d0rrC1NfS6So8HamzK+NIcqRmgZYdJGH8fJpfj7GWX16JulZDOqYSelBPKK 13vIGQyHubHPgIoj2z93p3UOvonA1BAtm+CCUl+Wp33moVKa4Tpb04MSdOHcbb/z nqm5n4EExpZsKtiJ19HNN90BEPYuOeGccSQ26cR0uGvtoKanBpeNZFcMGfQP3WX0 Wm1rSRMZMHPkMQjiMrURJ0Zm1DOaRKXigrJnXhhTmc3d119BoQ1xVeE2RoecEtK4 aow0DjiiEyriSdysxEnxpxHj/fEaSXkav3yY3odRX32F+vCSQeiwcvPSd1XRZXza c2AE3Q== Received: from nalasppmta02.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 46gnp8w574-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 09 May 2025 16:08:12 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA02.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 549G8B2x028285 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 9 May 2025 16:08:11 GMT Received: from PHILBER.na.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Fri, 9 May 2025 09:08:05 -0700 From: Peter Hilber To: "Michael S. Tsirkin" , Jason Wang , Richard Cochran CC: Trilok Soni , Peter Hilber , Xuan Zhuo , =?UTF-8?q?Eugenio=20P=C3=A9rez?= , , , , David Woodhouse , "Ridoux, Julien" , Marc Zyngier , Mark Rutland , Daniel Lezcano , "Alexandre Belloni" , Parav Pandit , Matias Ezequiel Vara Larsen , Cornelia Huck , Simon Horman , , Subject: [PATCH v7 2/4] virtio_rtc: Add PTP clocks Date: Fri, 9 May 2025 18:07:23 +0200 Message-ID: <20250509160734.1772-3-quic_philber@quicinc.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250509160734.1772-1-quic_philber@quicinc.com> References: <20250509160734.1772-1-quic_philber@quicinc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: WgumjOGdyjLmMQOgSR1Zvk_3p4Nf4sSp X-Proofpoint-ORIG-GUID: WgumjOGdyjLmMQOgSR1Zvk_3p4Nf4sSp X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNTA5MDE1OSBTYWx0ZWRfX0isE/mqxkkHL j3jm0FAf4F/TzBV7yKjO7STEdrlNo/+E9rH14lg0oJy0lheij8lf8ezT8X7GtDuFx55fRoAfSOt 9syWSaPc45eV/ZCZlHGEwzzl0JQs2DuePtzLHsj8Y7EBiGsvPOQ/zw6CZJwFtkKTxRYlemegLUk LuAmbqxdE6oHxxLupzep5vx+NXzOiSz0ggvElGaCIlmzqUzEMYf+2HOqFVLUAqi+YgHHNhjidFb DYSBnLHvJGu6iwVV+XI0FH6J/mhVNs7g8AhrcuqvnuRdyWKcQWrs5KKiQQNRT+9w3o1cBpgOhyO 0wnT/Ofqh0vxiZdgGoCv2mjoa/zmA8thJULNFmnMJvBmN8+goFb/X0HoiOTf79LEO4JK8YqsxC5 ym7UuHHJZUgez3KkRuJWy8p75PBb0r2NLo0A1L1FSfN/6ZBKigp1VEDJCT7aCbE/gLOndcRR X-Authority-Analysis: v=2.4 cv=e/4GSbp/ c=1 sm=1 tr=0 ts=681e286c cx=c_pps a=ouPCqIW2jiPt+lZRy3xVPw==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=GEpy-HfZoHoA:10 a=dt9VzEwgFbYA:10 a=COk6AnOGAAAA:8 a=OJK5QsYNW5bFeTl00T4A:9 a=TjNXssC_j7lpFel5tvFf:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-05-09_06,2025-05-09_01,2025-02-21_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 impostorscore=0 adultscore=0 phishscore=0 clxscore=1015 mlxlogscore=999 spamscore=0 bulkscore=0 suspectscore=0 lowpriorityscore=0 mlxscore=0 malwarescore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2504070000 definitions=main-2505090159 Content-Type: text/plain; charset="utf-8" Expose the virtio_rtc clocks as PTP clocks to userspace, similar to ptp_kvm. virtio_rtc can expose multiple clocks, e.g. a UTC clock and a monotonic clock. Userspace should distinguish different clocks through the name assigned by the driver. In particular, UTC-like clocks can also be distinguished by if and how leap seconds are smeared. udev rules such as the following can be used to get different symlinks for different clock types: SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 0/variant 0",= SYMLINK +=3D "ptp_virtio" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 1/variant 0",= SYMLINK +=3D "ptp_virtio_tai" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 2/variant 0",= SYMLINK +=3D "ptp_virtio_monotonic" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 3/variant 0",= SYMLINK +=3D "ptp_virtio_smear_unspecified" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 3/variant 1",= SYMLINK +=3D "ptp_virtio_smear_noon_linear" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 3/variant 2",= SYMLINK +=3D "ptp_virtio_smear_sls" SUBSYSTEM=3D=3D"ptp", ATTR{clock_name}=3D=3D"Virtio PTP type 4/variant 0",= SYMLINK +=3D "ptp_virtio_maybe_smeared" The preferred PTP clock reading method is ioctl PTP_SYS_OFFSET_PRECISE2, through the ptp_clock_info.getcrosststamp() op. For now, PTP_SYS_OFFSET_PRECISE2 will return -EOPNOTSUPP through a weak function. PTP_SYS_OFFSET_PRECISE2 requires cross-timestamping support for specific clocksources, which will be added in the following. If the clocksource specific code is enabled, check that the Virtio RTC device supports the respective HW counter before obtaining an actual cross-timestamp from the Virtio device. The Virtio RTC device response time may be higher than the timekeeper seqcount increment interval. Therefore, obtain the cross-timestamp before calling get_device_system_crosststamp(). As a fallback, support the ioctl PTP_SYS_OFFSET_EXTENDED2 for all platforms. Assume that concurrency issues during PTP clock removal are avoided by the posix_clock framework. Kconfig recursive dependencies prevent virtio_rtc from implicitly enabling PTP_1588_CLOCK, therefore just warn the user if PTP_1588_CLOCK is not available. Since virtio_rtc should in the future also expose clocks as RTC class devices, do not depend VIRTIO_RTC on PTP_1588_CLOCK. Signed-off-by: Peter Hilber --- Notes: v7: =20 - Use char length printf modifier to avoid truncation warning (kernel t= est robot). =20 v6: =20 - Shorten PTP clock names to always fit into 32 bytes. =20 - Do not mark comments missing parameter documentation as kernel doc (S= imon Horman). =20 v5: =20 - Fix style issues. =20 v4: =20 - Distinguish UTC-like clocks by handling of leap seconds (spec v6). =20 - For PTP clock name, always use numeric clock type, and numeric varian= t. =20 - Update types to spec v6. =20 - Cosmetic improvements. =20 v3: =20 - don't guard cross-timestamping with feature bit (spec v3) =20 - reduce clock id to 16 bits (spec v3) =20 v2: =20 - Depend on prerequisite patch series "treewide: Use clocksource id for get_device_system_crosststamp()". =20 - Check clocksource id before sending crosststamp message to device. =20 - Do not support multiple hardware counters at runtime any more, since distinction of Arm physical and virtual counter appears unneeded after discussion with Marc Zyngier. drivers/virtio/Kconfig | 24 +- drivers/virtio/Makefile | 1 + drivers/virtio/virtio_rtc_driver.c | 121 +++++++++- drivers/virtio/virtio_rtc_internal.h | 46 ++++ drivers/virtio/virtio_rtc_ptp.c | 347 +++++++++++++++++++++++++++ 5 files changed, 535 insertions(+), 4 deletions(-) create mode 100644 drivers/virtio/virtio_rtc_ptp.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 83bcb06acb6c..a14a2b77e142 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -194,11 +194,33 @@ config VIRTIO_RTC depends on PTP_1588_CLOCK_OPTIONAL help This driver provides current time from a Virtio RTC device. The driver - provides the time through one or more clocks. + provides the time through one or more clocks. The Virtio RTC PTP + clocks must be enabled to expose the clocks to userspace. =20 To compile this code as a module, choose M here: the module will be called virtio_rtc. =20 If unsure, say M. =20 +if VIRTIO_RTC + +comment "WARNING: Consider enabling VIRTIO_RTC_PTP." + depends on !VIRTIO_RTC_PTP + +comment "Enable PTP_1588_CLOCK in order to enable VIRTIO_RTC_PTP." + depends on PTP_1588_CLOCK=3Dn + +config VIRTIO_RTC_PTP + bool "Virtio RTC PTP clocks" + default y + depends on PTP_1588_CLOCK + help + This exposes any Virtio RTC clocks as PTP Hardware Clocks (PHCs) to + userspace. The PHC sysfs attribute "clock_name" describes the clock + type. + + If unsure, say Y. + +endif # VIRTIO_RTC + endif # VIRTIO_MENU diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index c41c4c0f9264..88d6fb8d4731 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) +=3D virtio_dma_bu= f.o obj-$(CONFIG_VIRTIO_DEBUG) +=3D virtio_debug.o obj-$(CONFIG_VIRTIO_RTC) +=3D virtio_rtc.o virtio_rtc-y :=3D virtio_rtc_driver.o +virtio_rtc-$(CONFIG_VIRTIO_RTC_PTP) +=3D virtio_rtc_ptp.o diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc= _driver.c index c87ee4e63424..7499908ee34c 100644 --- a/drivers/virtio/virtio_rtc_driver.c +++ b/drivers/virtio/virtio_rtc_driver.c @@ -38,11 +38,16 @@ struct viortc_vq { * struct viortc_dev - virtio_rtc device data * @vdev: virtio device * @vqs: virtqueues + * @clocks_to_unregister: Clock references, which are only used during dev= ice + * removal. + * For other uses, there would be a race between device + * creation and setting the pointers here. * @num_clocks: # of virtio_rtc clocks */ struct viortc_dev { struct virtio_device *vdev; struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES]; + struct viortc_ptp_clock **clocks_to_unregister; u16 num_clocks; }; =20 @@ -638,6 +643,99 @@ int viortc_cross_cap(struct viortc_dev *viortc, u16 vi= o_clk_id, u8 hw_counter, * init, deinit */ =20 +/** + * viortc_init_ptp_clock() - init and register PTP clock + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @clock_type: virtio_rtc clock type + * @leap_second_smearing: virtio_rtc leap second smearing + * + * Context: Process context. + * Return: Positive if registered, zero if not supported by configuration, + * negative error code otherwise. + */ +static int viortc_init_ptp_clock(struct viortc_dev *viortc, u16 vio_clk_id, + u8 clock_type, u8 leap_second_smearing) +{ + struct device *dev =3D &viortc->vdev->dev; + char ptp_clock_name[PTP_CLOCK_NAME_LEN]; + struct viortc_ptp_clock *vio_ptp; + + snprintf(ptp_clock_name, PTP_CLOCK_NAME_LEN, + "Virtio PTP type %hhu/variant %hhu", clock_type, + leap_second_smearing); + + vio_ptp =3D viortc_ptp_register(viortc, dev, vio_clk_id, ptp_clock_name); + if (IS_ERR(vio_ptp)) { + dev_err(dev, "failed to register PTP clock '%s'\n", + ptp_clock_name); + return PTR_ERR(vio_ptp); + } + + viortc->clocks_to_unregister[vio_clk_id] =3D vio_ptp; + + return !!vio_ptp; +} + +/** + * viortc_init_clock() - init local representation of virtio_rtc clock + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * + * Initializes PHC to represent virtio_rtc clock. + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_init_clock(struct viortc_dev *viortc, u16 vio_clk_id) +{ + u8 clock_type, leap_second_smearing; + bool is_exposed =3D false; + int ret; + + ret =3D viortc_clock_cap(viortc, vio_clk_id, &clock_type, + &leap_second_smearing); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_VIRTIO_RTC_PTP)) { + ret =3D viortc_init_ptp_clock(viortc, vio_clk_id, clock_type, + leap_second_smearing); + if (ret < 0) + return ret; + if (ret > 0) + is_exposed =3D true; + } + + if (!is_exposed) + dev_warn(&viortc->vdev->dev, + "cannot expose clock %d (type %d, variant %d) to userspace\n", + vio_clk_id, clock_type, leap_second_smearing); + + return 0; +} + +/** + * viortc_clocks_deinit() - unregister PHCs + * @viortc: device data + */ +static void viortc_clocks_deinit(struct viortc_dev *viortc) +{ + struct viortc_ptp_clock *vio_ptp; + unsigned int i; + + for (i =3D 0; i < viortc->num_clocks; i++) { + vio_ptp =3D viortc->clocks_to_unregister[i]; + + if (!vio_ptp) + continue; + + viortc->clocks_to_unregister[i] =3D NULL; + + WARN_ON(viortc_ptp_unregister(vio_ptp, &viortc->vdev->dev)); + } +} + /** * viortc_clocks_init() - init local representations of virtio_rtc clocks * @viortc: device data @@ -648,6 +746,7 @@ int viortc_cross_cap(struct viortc_dev *viortc, u16 vio= _clk_id, u8 hw_counter, static int viortc_clocks_init(struct viortc_dev *viortc) { u16 num_clocks; + unsigned int i; int ret; =20 ret =3D viortc_cfg(viortc, &num_clocks); @@ -661,8 +760,22 @@ static int viortc_clocks_init(struct viortc_dev *viort= c) =20 viortc->num_clocks =3D num_clocks; =20 - /* In the future, PTP clocks will be initialized here. */ - (void)viortc_clock_cap; + viortc->clocks_to_unregister =3D + devm_kcalloc(&viortc->vdev->dev, num_clocks, + sizeof(*viortc->clocks_to_unregister), GFP_KERNEL); + if (!viortc->clocks_to_unregister) + return -ENOMEM; + + for (i =3D 0; i < num_clocks; i++) { + ret =3D viortc_init_clock(viortc, i); + if (ret) + goto err_deinit_clocks; + } + + return 0; + +err_deinit_clocks: + viortc_clocks_deinit(viortc); =20 return ret; } @@ -741,7 +854,9 @@ static int viortc_probe(struct virtio_device *vdev) */ static void viortc_remove(struct virtio_device *vdev) { - /* In the future, PTP clocks will be deinitialized here. */ + struct viortc_dev *viortc =3D vdev->priv; + + viortc_clocks_deinit(viortc); =20 virtio_reset_device(vdev); vdev->config->del_vqs(vdev); diff --git a/drivers/virtio/virtio_rtc_internal.h b/drivers/virtio/virtio_r= tc_internal.h index 9c249c15b68f..2e589903d04f 100644 --- a/drivers/virtio/virtio_rtc_internal.h +++ b/drivers/virtio/virtio_rtc_internal.h @@ -9,6 +9,7 @@ #ifndef _VIRTIO_RTC_INTERNAL_H_ #define _VIRTIO_RTC_INTERNAL_H_ =20 +#include #include =20 /* driver core IFs */ @@ -21,4 +22,49 @@ int viortc_read_cross(struct viortc_dev *viortc, u16 vio= _clk_id, u8 hw_counter, int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_coun= ter, bool *supported); =20 +/* PTP IFs */ + +struct viortc_ptp_clock; + +#if IS_ENABLED(CONFIG_VIRTIO_RTC_PTP) + +struct viortc_ptp_clock *viortc_ptp_register(struct viortc_dev *viortc, + struct device *parent_dev, + u16 vio_clk_id, + const char *ptp_clock_name); +int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp, + struct device *parent_dev); + +#else + +static inline struct viortc_ptp_clock * +viortc_ptp_register(struct viortc_dev *viortc, struct device *parent_dev, + u16 vio_clk_id, const char *ptp_clock_name) +{ + return NULL; +} + +static inline int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp, + struct device *parent_dev) +{ + return -ENODEV; +} + +#endif + +/* HW counter IFs */ + +/** + * viortc_hw_xtstamp_params() - get HW-specific xtstamp params + * @hw_counter: virtio_rtc HW counter type + * @cs_id: clocksource id corresponding to hw_counter + * + * Gets the HW-specific xtstamp params. Returns an error if the driver can= not + * support xtstamp. + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id); + #endif /* _VIRTIO_RTC_INTERNAL_H_ */ diff --git a/drivers/virtio/virtio_rtc_ptp.c b/drivers/virtio/virtio_rtc_pt= p.c new file mode 100644 index 000000000000..f84599950cd4 --- /dev/null +++ b/drivers/virtio/virtio_rtc_ptp.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Expose virtio_rtc clocks as PTP clocks. + * + * Copyright (C) 2022-2023 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * + * Derived from ptp_kvm_common.c, virtual PTP 1588 clock for use with KVM + * guests. + * + * Copyright (C) 2017 Red Hat Inc. + */ + +#include +#include +#include + +#include + +#include "virtio_rtc_internal.h" + +/** + * struct viortc_ptp_clock - PTP clock abstraction + * @ptp_clock: PTP clock handle for unregistering + * @viortc: virtio_rtc device data + * @ptp_info: PTP clock description + * @vio_clk_id: virtio_rtc clock id + * @have_cross: device supports crosststamp with available HW counter + */ +struct viortc_ptp_clock { + struct ptp_clock *ptp_clock; + struct viortc_dev *viortc; + struct ptp_clock_info ptp_info; + u16 vio_clk_id; + bool have_cross; +}; + +/** + * struct viortc_ptp_cross_ctx - context for get_device_system_crosststamp= () + * @device_time: device clock reading + * @system_counterval: HW counter value at device_time + * + * Provides the already obtained crosststamp to get_device_system_crosstst= amp(). + */ +struct viortc_ptp_cross_ctx { + ktime_t device_time; + struct system_counterval_t system_counterval; +}; + +/* Weak function in case get_device_system_crosststamp() is not supported = */ +int __weak viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *= cs_id) +{ + return -EOPNOTSUPP; +} + +/** + * viortc_ptp_get_time_fn() - callback for get_device_system_crosststamp() + * @device_time: device clock reading + * @system_counterval: HW counter value at device_time + * @ctx: context with already obtained crosststamp + * + * Return: zero (success). + */ +static int viortc_ptp_get_time_fn(ktime_t *device_time, + struct system_counterval_t *system_counterval, + void *ctx) +{ + struct viortc_ptp_cross_ctx *vio_ctx =3D ctx; + + *device_time =3D vio_ctx->device_time; + *system_counterval =3D vio_ctx->system_counterval; + + return 0; +} + +/** + * viortc_ptp_do_xtstamp() - get crosststamp from device + * @vio_ptp: virtio_rtc PTP clock + * @hw_counter: virtio_rtc HW counter type + * @cs_id: clocksource id corresponding to hw_counter + * @ctx: context for get_device_system_crosststamp() + * + * Reads HW-specific crosststamp from device. + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_ptp_do_xtstamp(struct viortc_ptp_clock *vio_ptp, + u8 hw_counter, enum clocksource_ids cs_id, + struct viortc_ptp_cross_ctx *ctx) +{ + u64 max_ns, ns; + int ret; + + ctx->system_counterval.cs_id =3D cs_id; + + ret =3D viortc_read_cross(vio_ptp->viortc, vio_ptp->vio_clk_id, + hw_counter, &ns, + &ctx->system_counterval.cycles); + if (ret) + return ret; + + max_ns =3D (u64)ktime_to_ns(KTIME_MAX); + if (ns > max_ns) + return -EINVAL; + + ctx->device_time =3D ns_to_ktime(ns); + + return 0; +} + +/* + * PTP clock operations + */ + +/** + * viortc_ptp_getcrosststamp() - PTP clock getcrosststamp op + * @ptp: PTP clock info + * @xtstamp: crosststamp + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_ptp_getcrosststamp(struct ptp_clock_info *ptp, + struct system_device_crosststamp *xtstamp) +{ + struct viortc_ptp_clock *vio_ptp =3D + container_of(ptp, struct viortc_ptp_clock, ptp_info); + struct system_time_snapshot history_begin; + struct viortc_ptp_cross_ctx ctx; + enum clocksource_ids cs_id; + u8 hw_counter; + int ret; + + if (!vio_ptp->have_cross) + return -EOPNOTSUPP; + + ret =3D viortc_hw_xtstamp_params(&hw_counter, &cs_id); + if (ret) + return ret; + + ktime_get_snapshot(&history_begin); + if (history_begin.cs_id !=3D cs_id) + return -EOPNOTSUPP; + + /* + * Getting the timestamp can take many milliseconds with a slow Virtio + * device. This is too long for viortc_ptp_get_time_fn() passed to + * get_device_system_crosststamp(), which has to usually return before + * the timekeeper seqcount increases (every tick or so). + * + * So, get the actual cross-timestamp first. + */ + ret =3D viortc_ptp_do_xtstamp(vio_ptp, hw_counter, cs_id, &ctx); + if (ret) + return ret; + + ret =3D get_device_system_crosststamp(viortc_ptp_get_time_fn, &ctx, + &history_begin, xtstamp); + if (ret) + pr_debug("%s: get_device_system_crosststamp() returned %d\n", + __func__, ret); + + return ret; +} + +/* viortc_ptp_adjfine() - unsupported PTP clock adjfine op */ +static int viortc_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + return -EOPNOTSUPP; +} + +/* viortc_ptp_adjtime() - unsupported PTP clock adjtime op */ +static int viortc_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + return -EOPNOTSUPP; +} + +/* viortc_ptp_settime64() - unsupported PTP clock settime64 op */ +static int viortc_ptp_settime64(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + return -EOPNOTSUPP; +} + +/* + * viortc_ptp_gettimex64() - PTP clock gettimex64 op + * + * Context: Process context. + */ +static int viortc_ptp_gettimex64(struct ptp_clock_info *ptp, + struct timespec64 *ts, + struct ptp_system_timestamp *sts) +{ + struct viortc_ptp_clock *vio_ptp =3D + container_of(ptp, struct viortc_ptp_clock, ptp_info); + int ret; + u64 ns; + + ptp_read_system_prets(sts); + ret =3D viortc_read(vio_ptp->viortc, vio_ptp->vio_clk_id, &ns); + ptp_read_system_postts(sts); + + if (ret) + return ret; + + if (ns > (u64)S64_MAX) + return -EINVAL; + + *ts =3D ns_to_timespec64((s64)ns); + + return 0; +} + +/* viortc_ptp_enable() - unsupported PTP clock enable op */ +static int viortc_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +/* + * viortc_ptp_info_template - ptp_clock_info template + * + * The .name member will be set for individual virtio_rtc PTP clocks. + * + * The .getcrosststamp member will be cleared for PTP clocks not supporting + * crosststamp. + */ +static const struct ptp_clock_info viortc_ptp_info_template =3D { + .owner =3D THIS_MODULE, + /* .name is set according to clock type */ + .adjfine =3D viortc_ptp_adjfine, + .adjtime =3D viortc_ptp_adjtime, + .gettimex64 =3D viortc_ptp_gettimex64, + .settime64 =3D viortc_ptp_settime64, + .enable =3D viortc_ptp_enable, + .getcrosststamp =3D viortc_ptp_getcrosststamp, +}; + +/** + * viortc_ptp_unregister() - PTP clock unregistering wrapper + * @vio_ptp: virtio_rtc PTP clock + * @parent_dev: parent device of PTP clock + * + * Return: Zero on success, negative error code otherwise. + */ +int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp, + struct device *parent_dev) +{ + int ret =3D ptp_clock_unregister(vio_ptp->ptp_clock); + + if (!ret) + devm_kfree(parent_dev, vio_ptp); + + return ret; +} + +/** + * viortc_ptp_get_cross_cap() - get xtstamp support info from device + * @viortc: virtio_rtc device data + * @vio_ptp: virtio_rtc PTP clock abstraction + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_ptp_get_cross_cap(struct viortc_dev *viortc, + struct viortc_ptp_clock *vio_ptp) +{ + enum clocksource_ids cs_id; + bool xtstamp_supported; + u8 hw_counter; + int ret; + + ret =3D viortc_hw_xtstamp_params(&hw_counter, &cs_id); + if (ret) { + vio_ptp->have_cross =3D false; + return 0; + } + + ret =3D viortc_cross_cap(viortc, vio_ptp->vio_clk_id, hw_counter, + &xtstamp_supported); + if (ret) + return ret; + + vio_ptp->have_cross =3D xtstamp_supported; + + return 0; +} + +/** + * viortc_ptp_register() - prepare and register PTP clock + * @viortc: virtio_rtc device data + * @parent_dev: parent device for PTP clock + * @vio_clk_id: id of virtio_rtc clock which backs PTP clock + * @ptp_clock_name: PTP clock name + * + * Context: Process context. + * Return: Pointer on success, ERR_PTR() otherwise; NULL if PTP clock supp= ort + * not available. + */ +struct viortc_ptp_clock *viortc_ptp_register(struct viortc_dev *viortc, + struct device *parent_dev, + u16 vio_clk_id, + const char *ptp_clock_name) +{ + struct viortc_ptp_clock *vio_ptp; + struct ptp_clock *ptp_clock; + ssize_t len; + int ret; + + vio_ptp =3D devm_kzalloc(parent_dev, sizeof(*vio_ptp), GFP_KERNEL); + if (!vio_ptp) + return ERR_PTR(-ENOMEM); + + vio_ptp->viortc =3D viortc; + vio_ptp->vio_clk_id =3D vio_clk_id; + vio_ptp->ptp_info =3D viortc_ptp_info_template; + len =3D strscpy(vio_ptp->ptp_info.name, ptp_clock_name, + sizeof(vio_ptp->ptp_info.name)); + if (len < 0) { + ret =3D len; + goto err_free_dev; + } + + ret =3D viortc_ptp_get_cross_cap(viortc, vio_ptp); + if (ret) + goto err_free_dev; + + if (!vio_ptp->have_cross) + vio_ptp->ptp_info.getcrosststamp =3D NULL; + + ptp_clock =3D ptp_clock_register(&vio_ptp->ptp_info, parent_dev); + if (IS_ERR(ptp_clock)) + goto err_on_register; + + vio_ptp->ptp_clock =3D ptp_clock; + + return vio_ptp; + +err_on_register: + ret =3D PTR_ERR(ptp_clock); + +err_free_dev: + devm_kfree(parent_dev, vio_ptp); + return ERR_PTR(ret); +} --=20 2.43.0 From nobody Wed Dec 17 08:56:15 2025 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59AE5235073; Fri, 9 May 2025 16:08:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806923; cv=none; b=B8nUh+cMeNKXq14biWRiyATtsETMcP44A7qhSXrXjRe6Vr8qxyxG1mDZklaD2F2ZT6L4BKVlZQcBpifT5hXNSBauzQW+8dRCAnndJrrX9PfDWCqmLPWbTRezLBcyKxeoqPAOW7nR4S0SppTLryaRDKFBuWw9v0dKdfBjDsv9p+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806923; c=relaxed/simple; bh=RtOPI/DsxM1LZcAWF42dPpfdNPGAFh8TELrLyWLyS1o=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MtgJOGiFZZ2RKOqJN670llg0U6yb7XraLQp+XwGoFwFWL43KtGb1u8/QK4Ja9pJdf+MrudH7dTCaIdKfsVCb/ATXMdeRFc5O8BtOibnOw9yKh+Cp/SiQkUKO+sWQreWSJeeHdwcfnP4ZmdGwTXGHqQJUK1nF2H8eKp7BcmsPV54= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=F0UnNh9u; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="F0UnNh9u" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 549Bpi3T002461; Fri, 9 May 2025 16:08:18 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 5s1FY5CT9goxhPTr851JPrtvAQnIM6QWFPyD9XtgGJ0=; b=F0UnNh9uSSgTpLcd eWDdR2Sus+u4Yc5Zwt/QMjjo8qpbOtINWI8vCRQD0Xd7F/vZJ76c4UTuqZxoecB1 AwK55cAeKsE89/ah2CNSbmbYf4tLbslLGh6jbQifpR9RaFClhvKATovONxb2QDFW 2Fwq4jR4fahRi0ZpyD57xAqDeQlKjAUOORqNd1/mpKKotj3NsjkOQlIWqJFgCcRq jntozNpHU2pRTVdzKqjnl9IogaT/XcEA56uqZcA0Zcg5Bzy9O70UtUYBjdoA4pOQ sUXTZOZboxIt3ovQh20sTV+cCTyenctThvQQLhBdJFSAmgng8qT79HUF04M1uf2I i/wa9Q== Received: from nalasppmta01.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 46gnp5d5vu-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 09 May 2025 16:08:17 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA01.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 549G8GF4029185 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 9 May 2025 16:08:16 GMT Received: from PHILBER.na.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Fri, 9 May 2025 09:08:11 -0700 From: Peter Hilber To: "Michael S. Tsirkin" , Jason Wang , Marc Zyngier , Mark Rutland CC: Trilok Soni , Peter Hilber , Xuan Zhuo , =?UTF-8?q?Eugenio=20P=C3=A9rez?= , , , "David Woodhouse" , "Ridoux, Julien" , Daniel Lezcano , Alexandre Belloni , Parav Pandit , "Matias Ezequiel Vara Larsen" , Cornelia Huck , Simon Horman , , , "Richard Cochran" , Subject: [PATCH v7 3/4] virtio_rtc: Add Arm Generic Timer cross-timestamping Date: Fri, 9 May 2025 18:07:24 +0200 Message-ID: <20250509160734.1772-4-quic_philber@quicinc.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250509160734.1772-1-quic_philber@quicinc.com> References: <20250509160734.1772-1-quic_philber@quicinc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Authority-Analysis: v=2.4 cv=XL0wSRhE c=1 sm=1 tr=0 ts=681e2871 cx=c_pps a=ouPCqIW2jiPt+lZRy3xVPw==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=GEpy-HfZoHoA:10 a=dt9VzEwgFbYA:10 a=COk6AnOGAAAA:8 a=7oUh35WbpK7Jc3m_RqkA:9 a=TjNXssC_j7lpFel5tvFf:22 X-Proofpoint-GUID: X0TyMq46uLtEqPJB9-Bf0oQrryMyX-6d X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNTA5MDE1OSBTYWx0ZWRfX1dyLxOnPtdJl ZX3NmqHSswSZ/yAkm5kV5X3GQKs0l7VQF6r1d+5Hnn2Fe80JxKcMWWat8XjuaQ10jsqUbyI1p45 EMIsJBXKkBfz7PgqFx7+AQV4nDc73MLqOHKbt4cjyYNQimvOmdWyZUKZW08Pph4VcdUTL6/DQUy HXs8igIncSxOAQq4kiDVrnmyifOrYOhMeirkXsR4ApLR1QeG9hakTRWJEGvAgc2f3dPVySFSddQ 6UdjSG144Xgm73mvY2s5LJoz0ykqC1u+9fh7Wi8i/rYVRXCJE7Zyo6v/IujsaBZDcn8kqu9dvbc g3U3A1orDF9NUZNz0QDeCs5nlsyp7FIaC4oBdkFqVGHyhtIdJN4BGbldM0JYhfPHi/F63L3ro0J xCKcb/Rm8Mr2BIxFymM0R5Lca+VcGJH/sa411VpyY8wPRVEEG3U1Dfk+fTvdJMUrxzu6K56B X-Proofpoint-ORIG-GUID: X0TyMq46uLtEqPJB9-Bf0oQrryMyX-6d X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-05-09_06,2025-05-09_01,2025-02-21_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 mlxscore=0 clxscore=1015 lowpriorityscore=0 suspectscore=0 mlxlogscore=999 malwarescore=0 adultscore=0 priorityscore=1501 bulkscore=0 spamscore=0 phishscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2504070000 definitions=main-2505090159 Content-Type: text/plain; charset="utf-8" For platforms using the Arm Generic Timer, add precise cross-timestamping support to virtio_rtc. Always report the CP15 virtual counter as the HW counter in use by arm_arch_timer, since the Linux kernel's usage of the Arm Generic Timer should always be compatible with this. Signed-off-by: Peter Hilber --- Notes: v4: =20 - Update names and types to spec v6. =20 v2: =20 - Depend on prerequisite patch series "treewide: Use clocksource id for get_device_system_crosststamp()". =20 - Return clocksource id instead of calling dropped arm_arch_timer helpe= rs. =20 - Always report the CP15 virtual counter to be in use by arm_arch_timer, since distinction of Arm physical and virtual counter appears unneeded after discussion with Marc Zyngier. drivers/virtio/Kconfig | 13 +++++++++++++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio_rtc_arm.c | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 drivers/virtio/virtio_rtc_arm.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index a14a2b77e142..3d8b366c0625 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -221,6 +221,19 @@ config VIRTIO_RTC_PTP =20 If unsure, say Y. =20 +config VIRTIO_RTC_ARM + bool "Virtio RTC cross-timestamping using Arm Generic Timer" + default y + depends on VIRTIO_RTC_PTP && ARM_ARCH_TIMER + help + This enables Virtio RTC cross-timestamping using the Arm Generic Timer. + It only has an effect if the Virtio RTC device also supports this. The + cross-timestamp is available through the PTP clock driver precise + cross-timestamp ioctl (PTP_SYS_OFFSET_PRECISE2 aka + PTP_SYS_OFFSET_PRECISE). + + If unsure, say Y. + endif # VIRTIO_RTC =20 endif # VIRTIO_MENU diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 88d6fb8d4731..dbd77f124ba9 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_VIRTIO_DEBUG) +=3D virtio_debug.o obj-$(CONFIG_VIRTIO_RTC) +=3D virtio_rtc.o virtio_rtc-y :=3D virtio_rtc_driver.o virtio_rtc-$(CONFIG_VIRTIO_RTC_PTP) +=3D virtio_rtc_ptp.o +virtio_rtc-$(CONFIG_VIRTIO_RTC_ARM) +=3D virtio_rtc_arm.o diff --git a/drivers/virtio/virtio_rtc_arm.c b/drivers/virtio/virtio_rtc_ar= m.c new file mode 100644 index 000000000000..211299d72870 --- /dev/null +++ b/drivers/virtio/virtio_rtc_arm.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Provides cross-timestamp params for Arm. + * + * Copyright (C) 2022-2023 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#include + +#include "virtio_rtc_internal.h" + +/* see header for doc */ + +int viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id) +{ + *hw_counter =3D VIRTIO_RTC_COUNTER_ARM_VCT; + *cs_id =3D CSID_ARM_ARCH_COUNTER; + + return 0; +} --=20 2.43.0 From nobody Wed Dec 17 08:56:15 2025 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B159235045; Fri, 9 May 2025 16:08:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806938; cv=none; b=Uoe9srq88iwHjADl4X7x8TkZuxwq6zFRhjsJ2uwTamOzOzNq8HOkNpxFJCujFwTxMgAiGb6BRFWeZTQ4tyu2yhMIlElNp0prsgW240QHRTKKNE4I7uTzdzfCsIE4pGM/OZ0ZObov8sBDFOlsz9ur+JXUhLc0xHtJJ1v0RwEBy2U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746806938; c=relaxed/simple; bh=5KLYkgjj9A5qrw2pYLf85Vpi7KQicOXMoxiJpA7JdjA=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=epL09vn4L8MYViOyHdYWbcT/CuFv53QeFJlXFZWIDtR6ps8nphc/SM92xw3kDCgG59Jyc46f5A29z6D5ZOK2tYh816IQ45H36QFaZs6IT8aKkjZ+wXtE1kCvHiFz5tcGbWVWRaAGtTqrTfFV5uj4mbmGPMYAFEgRJpMeDpCEy8E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=EbVfoGP3; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="EbVfoGP3" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 549BR3In031435; Fri, 9 May 2025 16:08:24 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= B4ASX7jJ2LUASYUSF5gNLf6ceV4P7ZeXA14LvNuktsw=; b=EbVfoGP3g533okLj h+bBQNSPWeBejMEU0Lx6ej0lTZYYWnwdNpNkUXJ0+eGY0A1+pUaOulpVatEWmIx9 TUH0i2yGmQtHN91K0k0LOd1wQG3NK9LH5jkCNzExl/Kz7iku2B63kC5dsZQpcUCY a8g6K+o+DnKGFev62QgAnO5LcQ2v9hlVsdrrKmrMCqqBU8qEyKIJO+ytrUH5SVH0 vZ5N20ixax6RaRfK7P1bgCd6tUJ5Ziopd6kr1m+iNzKy44aJBncui4IvtIK+R2ZH WtpQmHsnsMUXCsONNmIqQIkX7Bfjpgymfg1/IoW+9+5y/BuKW6nNlJL10sbga39w t6CGBw== Received: from nalasppmta01.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 46gnpgn4xd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 09 May 2025 16:08:24 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA01.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 549G8NHw029333 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 9 May 2025 16:08:23 GMT Received: from PHILBER.na.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Fri, 9 May 2025 09:08:17 -0700 From: Peter Hilber To: "Michael S. Tsirkin" , Jason Wang CC: Trilok Soni , Peter Hilber , Alexandre Belloni , Xuan Zhuo , =?UTF-8?q?Eugenio=20P=C3=A9rez?= , , , "David Woodhouse" , "Ridoux, Julien" , "Marc Zyngier" , Mark Rutland , Daniel Lezcano , Parav Pandit , "Matias Ezequiel Vara Larsen" , Cornelia Huck , "Simon Horman" , , Subject: [PATCH v7 4/4] virtio_rtc: Add RTC class driver Date: Fri, 9 May 2025 18:07:25 +0200 Message-ID: <20250509160734.1772-5-quic_philber@quicinc.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250509160734.1772-1-quic_philber@quicinc.com> References: <20250509160734.1772-1-quic_philber@quicinc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Authority-Analysis: v=2.4 cv=Ao/u3P9P c=1 sm=1 tr=0 ts=681e2878 cx=c_pps a=ouPCqIW2jiPt+lZRy3xVPw==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=GEpy-HfZoHoA:10 a=dt9VzEwgFbYA:10 a=COk6AnOGAAAA:8 a=P-IC7800AAAA:8 a=qxVlfQJH33CYPoNJzBcA:9 a=TjNXssC_j7lpFel5tvFf:22 a=d3PnA9EDa4IxuAV0gXij:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNTA5MDE1OSBTYWx0ZWRfX8FdnB/Yv1ZgO 3cAYUsKVx1xhEBoup7JgvrvLrAFJr9VjLDVRD/OXkA83Lh8fJ6FHRlHcI9zbEOJhU7CCXruIALa 1QBwOIrPWVs2E4PAVZPLL8Oonqpe8lH6C13mJIXd4y2u9gx1qaxJ9CjBzG5SZ1i3wL/aaR1AhBM LKDDXl1m9JlVSIJ0pORtb+EjvOm7Tb0ILHz2OMMoRFXSGMB1tUqa8D3kd33P4U6LUaEJjTveSha AOlgaqDTQqZnO6MILl1vxzY7vnFaXYqEAcLAkGTCT7DaOFqS76yIG9sccX9kz3A7AeeaXokXsj1 DJokEtpkogERKjSzmvcuK6GePEmYLDNCRMfy888CWnNVmnDWkJSUaqYJK4IDgfwHhhFR0ZLdHNn 6lqqCNE9h/Gi07baXeyNREDiGKgzgoBF/cgYzov4l599dRjZw28f3V1O3HrtVbQN+G+jfGXV X-Proofpoint-GUID: JauzQwCpBr4GrKcQPkXr5c1swxjTCWyx X-Proofpoint-ORIG-GUID: JauzQwCpBr4GrKcQPkXr5c1swxjTCWyx X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-05-09_06,2025-05-09_01,2025-02-21_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 mlxscore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 spamscore=0 impostorscore=0 phishscore=0 malwarescore=0 lowpriorityscore=0 clxscore=1015 adultscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2504070000 definitions=main-2505090159 Content-Type: text/plain; charset="utf-8" Expose the virtio-rtc UTC-like clock as an RTC clock to userspace - if it is present, and if it does not step on leap seconds. The RTC class enables the virtio-rtc device to resume the system from sleep states on RTC alarm. Support RTC alarm if the virtio-rtc alarm feature is present. The virtio-rtc device signals an alarm by marking an alarmq buffer as used. Peculiarities Acked-by: Alexandre Belloni ------------- A virtio-rtc clock is a bit special for an RTC clock in that - the clock may step (also backwards) autonomously at any time and - the device, and its notification mechanism, will be reset during boot or resume from sleep. The virtio-rtc device avoids that the driver might miss an alarm. The device signals an alarm whenever the clock has reached or passed the alarm time, and also when the device is reset (on boot or resume from sleep), if the alarm time is in the past. Open Issue ---------- The CLOCK_BOOTTIME_ALARM will use the RTC clock to wake up from sleep, and implicitly assumes that no RTC clock steps will occur during sleep. The RTC class driver does not know whether the current alarm is a real-time alarm or a boot-time alarm. Perhaps this might be handled by the driver also setting a virtio-rtc monotonic alarm (which uses a clock similar to CLOCK_BOOTTIME_ALARM). The virtio-rtc monotonic alarm would just be used to wake up in case it was a CLOCK_BOOTTIME_ALARM alarm. Otherwise, the behavior should not differ from other RTC class drivers. Signed-off-by: Peter Hilber Acked-by: Alexandre Belloni --- Notes: v6: =20 - Avoid conditional locking within a block. =20 v5: =20 - This version retains the Acked-By from Alexandre Belloni, since the changes essentially only affect the Virtio interface, not the RTC cla= ss interface. =20 - Actually initialize Virtio clock id for RTC class device. =20 - Fix race conditions after readying device during probe. =20 - Deinit clocks on late probe errors. =20 - Notify alarmq on .restore(). =20 - Use new devm_device_init_wakeup() function. =20 - Fix style issues. =20 - Improve logging types and clarity. =20 - Change ALARM_CAP flag value according to spec v7. =20 v4: =20 - Do not create RTC class device for clocks which may step on leap seco= nds (Alexandre Belloni). =20 - Clear RTC class feature bit instead of defining reduced ops (Alexandre Belloni). =20 - Use macros for 64-bit divisions. =20 - Remove unnecessary memory barrier. =20 - Cosmetic changes. =20 v3: =20 Added. drivers/virtio/Kconfig | 22 +- drivers/virtio/Makefile | 1 + drivers/virtio/virtio_rtc_class.c | 262 ++++++++++++++ drivers/virtio/virtio_rtc_driver.c | 520 ++++++++++++++++++++++++++- drivers/virtio/virtio_rtc_internal.h | 52 +++ include/uapi/linux/virtio_rtc.h | 88 ++++- 6 files changed, 934 insertions(+), 11 deletions(-) create mode 100644 drivers/virtio/virtio_rtc_class.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 3d8b366c0625..6db5235a7693 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -195,7 +195,8 @@ config VIRTIO_RTC help This driver provides current time from a Virtio RTC device. The driver provides the time through one or more clocks. The Virtio RTC PTP - clocks must be enabled to expose the clocks to userspace. + clocks and/or the Real Time Clock driver for Virtio RTC must be + enabled to expose the clocks to userspace. =20 To compile this code as a module, choose M here: the module will be called virtio_rtc. @@ -204,8 +205,8 @@ config VIRTIO_RTC =20 if VIRTIO_RTC =20 -comment "WARNING: Consider enabling VIRTIO_RTC_PTP." - depends on !VIRTIO_RTC_PTP +comment "WARNING: Consider enabling VIRTIO_RTC_PTP and/or VIRTIO_RTC_CLASS= ." + depends on !VIRTIO_RTC_PTP && !VIRTIO_RTC_CLASS =20 comment "Enable PTP_1588_CLOCK in order to enable VIRTIO_RTC_PTP." depends on PTP_1588_CLOCK=3Dn @@ -234,6 +235,21 @@ config VIRTIO_RTC_ARM =20 If unsure, say Y. =20 +comment "Enable RTC_CLASS in order to enable VIRTIO_RTC_CLASS." + depends on RTC_CLASS=3Dn + +config VIRTIO_RTC_CLASS + bool "Real Time Clock driver for Virtio RTC" + default y + depends on RTC_CLASS + help + This exposes the Virtio RTC UTC-like clock as a Linux Real Time Clock. + It only has an effect if the Virtio RTC device has a UTC-like clock + which smears leap seconds to avoid steps. The Real Time Clock is + read-only, and may support setting an alarm. + + If unsure, say Y. + endif # VIRTIO_RTC =20 endif # VIRTIO_MENU diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index dbd77f124ba9..eefcfe90d6b8 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_VIRTIO_RTC) +=3D virtio_rtc.o virtio_rtc-y :=3D virtio_rtc_driver.o virtio_rtc-$(CONFIG_VIRTIO_RTC_PTP) +=3D virtio_rtc_ptp.o virtio_rtc-$(CONFIG_VIRTIO_RTC_ARM) +=3D virtio_rtc_arm.o +virtio_rtc-$(CONFIG_VIRTIO_RTC_CLASS) +=3D virtio_rtc_class.o diff --git a/drivers/virtio/virtio_rtc_class.c b/drivers/virtio/virtio_rtc_= class.c new file mode 100644 index 000000000000..05d6d28255cf --- /dev/null +++ b/drivers/virtio/virtio_rtc_class.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * virtio_rtc RTC class driver + * + * Copyright (C) 2023 OpenSynergy GmbH + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include + +#include + +#include "virtio_rtc_internal.h" + +/** + * struct viortc_class - RTC class wrapper + * @viortc: virtio_rtc device data + * @rtc: RTC device + * @vio_clk_id: virtio_rtc clock id + * @stopped: Whether RTC ops are disallowed. Access protected by rtc_lock(= ). + */ +struct viortc_class { + struct viortc_dev *viortc; + struct rtc_device *rtc; + u16 vio_clk_id; + bool stopped; +}; + +/** + * viortc_class_get_locked() - get RTC class wrapper, if ops allowed + * @dev: virtio device + * + * Gets the RTC class wrapper from the virtio device, if it is available a= nd + * ops are allowed. + * + * Context: Caller must hold rtc_lock(). + * Return: RTC class wrapper if available and ops allowed, ERR_PTR otherwi= se. + */ +static struct viortc_class *viortc_class_get_locked(struct device *dev) +{ + struct viortc_class *viortc_class; + + viortc_class =3D viortc_class_from_dev(dev); + if (IS_ERR(viortc_class)) + return viortc_class; + + if (viortc_class->stopped) + return ERR_PTR(-EBUSY); + + return viortc_class; +} + +/** + * viortc_class_read_time() - RTC class op read_time + * @dev: virtio device + * @tm: read time + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_class_read_time(struct device *dev, struct rtc_time *tm) +{ + struct viortc_class *viortc_class; + time64_t sec; + int ret; + u64 ns; + + viortc_class =3D viortc_class_get_locked(dev); + if (IS_ERR(viortc_class)) + return PTR_ERR(viortc_class); + + ret =3D viortc_read(viortc_class->viortc, viortc_class->vio_clk_id, &ns); + if (ret) + return ret; + + sec =3D div_u64(ns, NSEC_PER_SEC); + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +/** + * viortc_class_read_alarm() - RTC class op read_alarm + * @dev: virtio device + * @alrm: alarm read out + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_class_read_alarm(struct device *dev, struct rtc_wkalrm *= alrm) +{ + struct viortc_class *viortc_class; + time64_t alarm_time_sec; + u64 alarm_time_ns; + bool enabled; + int ret; + + viortc_class =3D viortc_class_get_locked(dev); + if (IS_ERR(viortc_class)) + return PTR_ERR(viortc_class); + + ret =3D viortc_read_alarm(viortc_class->viortc, viortc_class->vio_clk_id, + &alarm_time_ns, &enabled); + if (ret) + return ret; + + alarm_time_sec =3D div_u64(alarm_time_ns, NSEC_PER_SEC); + rtc_time64_to_tm(alarm_time_sec, &alrm->time); + + alrm->enabled =3D enabled; + + return 0; +} + +/** + * viortc_class_set_alarm() - RTC class op set_alarm + * @dev: virtio device + * @alrm: alarm to set + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_class_set_alarm(struct device *dev, struct rtc_wkalrm *a= lrm) +{ + struct viortc_class *viortc_class; + time64_t alarm_time_sec; + u64 alarm_time_ns; + + viortc_class =3D viortc_class_get_locked(dev); + if (IS_ERR(viortc_class)) + return PTR_ERR(viortc_class); + + alarm_time_sec =3D rtc_tm_to_time64(&alrm->time); + + if (alarm_time_sec < 0) + return -EINVAL; + + if (check_mul_overflow((u64)alarm_time_sec, (u64)NSEC_PER_SEC, + &alarm_time_ns)) + return -EINVAL; + + return viortc_set_alarm(viortc_class->viortc, viortc_class->vio_clk_id, + alarm_time_ns, alrm->enabled); +} + +/** + * viortc_class_alarm_irq_enable() - RTC class op alarm_irq_enable + * @dev: virtio device + * @enabled: enable or disable alarm IRQ + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_class_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct viortc_class *viortc_class; + + viortc_class =3D viortc_class_get_locked(dev); + if (IS_ERR(viortc_class)) + return PTR_ERR(viortc_class); + + return viortc_set_alarm_enabled(viortc_class->viortc, + viortc_class->vio_clk_id, enabled); +} + +static const struct rtc_class_ops viortc_class_ops =3D { + .read_time =3D viortc_class_read_time, + .read_alarm =3D viortc_class_read_alarm, + .set_alarm =3D viortc_class_set_alarm, + .alarm_irq_enable =3D viortc_class_alarm_irq_enable, +}; + +/** + * viortc_class_alarm() - propagate alarm notification as alarm interrupt + * @viortc_class: RTC class wrapper + * @vio_clk_id: virtio_rtc clock id + * + * Context: Any context. + */ +void viortc_class_alarm(struct viortc_class *viortc_class, u16 vio_clk_id) +{ + if (vio_clk_id !=3D viortc_class->vio_clk_id) { + dev_warn_ratelimited(&viortc_class->rtc->dev, + "ignoring alarm for clock id %d, expected id %d\n", + vio_clk_id, viortc_class->vio_clk_id); + return; + } + + rtc_update_irq(viortc_class->rtc, 1, RTC_AF | RTC_IRQF); +} + +/** + * viortc_class_stop() - disallow RTC class ops + * @viortc_class: RTC class wrapper + * + * Context: Process context. Caller must NOT hold rtc_lock(). + */ +void viortc_class_stop(struct viortc_class *viortc_class) +{ + rtc_lock(viortc_class->rtc); + + viortc_class->stopped =3D true; + + rtc_unlock(viortc_class->rtc); +} + +/** + * viortc_class_register() - register RTC class device + * @viortc_class: RTC class wrapper + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_class_register(struct viortc_class *viortc_class) +{ + return devm_rtc_register_device(viortc_class->rtc); +} + +/** + * viortc_class_init() - init RTC class wrapper and device + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @have_alarm: have alarm feature + * @parent_dev: virtio device + * + * Context: Process context. + * Return: RTC class wrapper on success, ERR_PTR otherwise. + */ +struct viortc_class *viortc_class_init(struct viortc_dev *viortc, + u16 vio_clk_id, bool have_alarm, + struct device *parent_dev) +{ + struct viortc_class *viortc_class; + struct rtc_device *rtc; + + viortc_class =3D + devm_kzalloc(parent_dev, sizeof(*viortc_class), GFP_KERNEL); + if (!viortc_class) + return ERR_PTR(-ENOMEM); + + rtc =3D devm_rtc_allocate_device(parent_dev); + if (IS_ERR(rtc)) + return ERR_CAST(rtc); + + viortc_class->viortc =3D viortc; + viortc_class->rtc =3D rtc; + viortc_class->vio_clk_id =3D vio_clk_id; + + if (!have_alarm) + clear_bit(RTC_FEATURE_ALARM, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + rtc->ops =3D &viortc_class_ops; + rtc->range_max =3D div_u64(U64_MAX, NSEC_PER_SEC); + + return viortc_class; +} diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc= _driver.c index 7499908ee34c..a57d5e06e19d 100644 --- a/drivers/virtio/virtio_rtc_driver.c +++ b/drivers/virtio/virtio_rtc_driver.c @@ -18,9 +18,12 @@ =20 #include "virtio_rtc_internal.h" =20 +#define VIORTC_ALARMQ_BUF_CAP sizeof(union virtio_rtc_notif_alarmq) + /* virtqueue order */ enum { VIORTC_REQUESTQ, + VIORTC_ALARMQ, VIORTC_MAX_NR_QUEUES, }; =20 @@ -37,17 +40,23 @@ struct viortc_vq { /** * struct viortc_dev - virtio_rtc device data * @vdev: virtio device + * @viortc_class: RTC class wrapper for UTC-like clock, NULL if not availa= ble * @vqs: virtqueues * @clocks_to_unregister: Clock references, which are only used during dev= ice * removal. * For other uses, there would be a race between device * creation and setting the pointers here. + * @alarmq_bufs: alarmq buffers list + * @num_alarmq_bufs: # of alarmq buffers * @num_clocks: # of virtio_rtc clocks */ struct viortc_dev { struct virtio_device *vdev; + struct viortc_class *viortc_class; struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES]; struct viortc_ptp_clock **clocks_to_unregister; + void **alarmq_bufs; + unsigned int num_alarmq_bufs; u16 num_clocks; }; =20 @@ -78,6 +87,60 @@ struct viortc_msg { unsigned int resp_actual_size; }; =20 +/** + * viortc_class_from_dev() - Get RTC class object from virtio device. + * @dev: virtio device + * + * Context: Any context. + * Return: RTC class object if available, ERR_PTR otherwise. + */ +struct viortc_class *viortc_class_from_dev(struct device *dev) +{ + struct virtio_device *vdev; + struct viortc_dev *viortc; + + vdev =3D container_of(dev, typeof(*vdev), dev); + viortc =3D vdev->priv; + + return viortc->viortc_class ?: ERR_PTR(-ENODEV); +} + +/** + * viortc_alarms_supported() - Whether device and driver support alarms. + * @vdev: virtio device + * + * NB: Device and driver may not support alarms for the same clocks. + * + * Context: Any context. + * Return: True if both device and driver can support alarms. + */ +static bool viortc_alarms_supported(struct virtio_device *vdev) +{ + return IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) && + virtio_has_feature(vdev, VIRTIO_RTC_F_ALARM); +} + +/** + * viortc_feed_vq() - Make a device write-only buffer available. + * @viortc: device data + * @vq: notification virtqueue + * @buf: buffer + * @buf_len: buffer capacity in bytes + * @data: token, identifying buffer + * + * Context: Caller must prevent concurrent access to vq. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_feed_vq(struct viortc_dev *viortc, struct virtqueue *vq, + void *buf, unsigned int buf_len, void *data) +{ + struct scatterlist sg; + + sg_init_one(&sg, buf, buf_len); + + return virtqueue_add_inbuf(vq, &sg, 1, data, GFP_ATOMIC); +} + /** * viortc_msg_init() - Allocate and initialize requestq message. * @viortc: device data @@ -236,6 +299,85 @@ static void viortc_cb_requestq(struct virtqueue *vq) viortc_do_cb(vq, viortc_requestq_hdlr); } =20 +/** + * viortc_alarmq_hdlr() - process an alarmq used buffer + * @token: token identifying the buffer + * @len: bytes written by device + * @vq: virtqueue + * @viortc_vq: device specific data for virtqueue + * @viortc: device data + * + * Processes a VIRTIO_RTC_NOTIF_ALARM notification by calling the RTC class + * driver. Makes the buffer available again. + * + * Context: virtqueue callback + */ +static void viortc_alarmq_hdlr(void *token, unsigned int len, + struct virtqueue *vq, + struct viortc_vq *viortc_vq, + struct viortc_dev *viortc) +{ + struct virtio_rtc_notif_alarm *notif =3D token; + struct virtio_rtc_notif_head *head =3D token; + unsigned long flags; + u16 clock_id; + bool notify; + + if (len < sizeof(*head)) { + dev_err_ratelimited(&viortc->vdev->dev, + "%s: ignoring notification with short header\n", + __func__); + goto feed_vq; + } + + if (virtio_le_to_cpu(head->msg_type) !=3D VIRTIO_RTC_NOTIF_ALARM) { + dev_err_ratelimited(&viortc->vdev->dev, + "%s: ignoring unknown notification type 0x%x\n", + __func__, virtio_le_to_cpu(head->msg_type)); + goto feed_vq; + } + + if (len < sizeof(*notif)) { + dev_err_ratelimited(&viortc->vdev->dev, + "%s: ignoring too small alarm notification\n", + __func__); + goto feed_vq; + } + + clock_id =3D virtio_le_to_cpu(notif->clock_id); + + if (!viortc->viortc_class) + dev_warn_ratelimited(&viortc->vdev->dev, + "ignoring alarm, no RTC class device available\n"); + else + viortc_class_alarm(viortc->viortc_class, clock_id); + +feed_vq: + spin_lock_irqsave(&viortc_vq->lock, flags); + + if (viortc_feed_vq(viortc, vq, notif, VIORTC_ALARMQ_BUF_CAP, token)) + dev_warn(&viortc->vdev->dev, + "%s: failed to re-expose input buffer\n", __func__); + + notify =3D virtqueue_kick_prepare(vq); + + spin_unlock_irqrestore(&viortc_vq->lock, flags); + + if (notify) + virtqueue_notify(vq); +} + +/** + * viortc_cb_alarmq() - callback for alarmq + * @vq: virtqueue + * + * Context: virtqueue callback + */ +static void viortc_cb_alarmq(struct virtqueue *vq) +{ + viortc_do_cb(vq, viortc_alarmq_hdlr); +} + /** * viortc_get_resp_errno() - converts virtio_rtc errnos to system errnos * @resp_head: message response header @@ -561,12 +703,13 @@ static int viortc_cfg(struct viortc_dev *viortc, u16 = *num_clocks) * @vio_clk_id: virtio_rtc clock id * @type: virtio_rtc clock type * @leap_second_smearing: virtio_rtc smearing variant + * @flags: struct virtio_rtc_resp_clock_cap.flags * * Context: Process context. * Return: Zero on success, negative error code otherwise. */ static int viortc_clock_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 = *type, - u8 *leap_second_smearing) + u8 *leap_second_smearing, u8 *flags) { VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_CLOCK_CAP, struct virtio_rtc_req_clock_cap, @@ -589,6 +732,7 @@ static int viortc_clock_cap(struct viortc_dev *viortc, = u16 vio_clk_id, u8 *type, =20 VIORTC_MSG_READ(hdl, type, type); VIORTC_MSG_READ(hdl, leap_second_smearing, leap_second_smearing); + VIORTC_MSG_READ(hdl, flags, flags); =20 out_release: viortc_msg_release(VIORTC_MSG(hdl)); @@ -639,10 +783,189 @@ int viortc_cross_cap(struct viortc_dev *viortc, u16 = vio_clk_id, u8 hw_counter, return ret; } =20 +/** + * viortc_read_alarm() - VIRTIO_RTC_REQ_READ_ALARM wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @alarm_time: alarm time in ns + * @enabled: whether alarm is enabled + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_read_alarm(struct viortc_dev *viortc, u16 vio_clk_id, + u64 *alarm_time, bool *enabled) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_READ_ALARM, + struct virtio_rtc_req_read_alarm, + struct virtio_rtc_resp_read_alarm); + u8 flags; + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + + VIORTC_MSG_READ(hdl, alarm_time, alarm_time); + VIORTC_MSG_READ(hdl, flags, &flags); + + *enabled =3D !!(flags & VIRTIO_RTC_FLAG_ALARM_ENABLED); + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/** + * viortc_set_alarm() - VIRTIO_RTC_REQ_SET_ALARM wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @alarm_time: alarm time in ns + * @alarm_enable: enable or disable alarm + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_set_alarm(struct viortc_dev *viortc, u16 vio_clk_id, u64 alarm_= time, + bool alarm_enable) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_SET_ALARM, + struct virtio_rtc_req_set_alarm, + struct virtio_rtc_resp_set_alarm); + u8 flags =3D 0; + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + if (alarm_enable) + flags |=3D VIRTIO_RTC_FLAG_ALARM_ENABLED; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + VIORTC_MSG_WRITE(hdl, alarm_time, &alarm_time); + VIORTC_MSG_WRITE(hdl, flags, &flags); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + +/** + * viortc_set_alarm_enabled() - VIRTIO_RTC_REQ_SET_ALARM_ENABLED wrapper + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @alarm_enable: enable or disable alarm + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +int viortc_set_alarm_enabled(struct viortc_dev *viortc, u16 vio_clk_id, + bool alarm_enable) +{ + VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_SET_ALARM_ENABLED, + struct virtio_rtc_req_set_alarm_enabled, + struct virtio_rtc_resp_set_alarm_enabled); + u8 flags =3D 0; + int ret; + + ret =3D VIORTC_MSG_INIT(hdl, viortc); + if (ret) + return ret; + + if (alarm_enable) + flags |=3D VIRTIO_RTC_FLAG_ALARM_ENABLED; + + VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id); + VIORTC_MSG_WRITE(hdl, flags, &flags); + + ret =3D viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl), + 0); + if (ret) { + dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__, + ret); + goto out_release; + } + +out_release: + viortc_msg_release(VIORTC_MSG(hdl)); + + return ret; +} + /* * init, deinit */ =20 +/** + * viortc_init_rtc_class_clock() - init and register a RTC class device + * @viortc: device data + * @vio_clk_id: virtio_rtc clock id + * @clock_type: virtio_rtc clock type + * @flags: struct virtio_rtc_resp_clock_cap.flags + * + * The clock must be a UTC-like clock. + * + * Context: Process context. + * Return: Positive if registered, zero if not supported by configuration, + * negative error code otherwise. + */ +static int viortc_init_rtc_class_clock(struct viortc_dev *viortc, + u16 vio_clk_id, u8 clock_type, u8 flags) +{ + struct virtio_device *vdev =3D viortc->vdev; + struct viortc_class *viortc_class; + struct device *dev =3D &vdev->dev; + bool have_alarm; + + if (clock_type !=3D VIRTIO_RTC_CLOCK_UTC_SMEARED) { + dev_info(dev, + "not creating RTC class device for clock %d, which may step on leap se= conds\n", + vio_clk_id); + return 0; + } + + if (viortc->viortc_class) { + dev_warn_once(dev, + "multiple UTC-like clocks are present, but creating only one RTC = class device\n"); + return 0; + } + + have_alarm =3D viortc_alarms_supported(vdev) && + !!(flags & VIRTIO_RTC_FLAG_ALARM_CAP); + + viortc_class =3D viortc_class_init(viortc, vio_clk_id, have_alarm, dev); + if (IS_ERR(viortc_class)) + return PTR_ERR(viortc_class); + + viortc->viortc_class =3D viortc_class; + + if (have_alarm) + devm_device_init_wakeup(dev); + + return viortc_class_register(viortc_class) ?: 1; +} + /** * viortc_init_ptp_clock() - init and register PTP clock * @viortc: device data @@ -682,22 +1005,34 @@ static int viortc_init_ptp_clock(struct viortc_dev *= viortc, u16 vio_clk_id, * @viortc: device data * @vio_clk_id: virtio_rtc clock id * - * Initializes PHC to represent virtio_rtc clock. + * Initializes PHC and/or RTC class device to represent virtio_rtc clock. * * Context: Process context. * Return: Zero on success, negative error code otherwise. */ static int viortc_init_clock(struct viortc_dev *viortc, u16 vio_clk_id) { - u8 clock_type, leap_second_smearing; + u8 clock_type, leap_second_smearing, flags; bool is_exposed =3D false; int ret; =20 ret =3D viortc_clock_cap(viortc, vio_clk_id, &clock_type, - &leap_second_smearing); + &leap_second_smearing, &flags); if (ret) return ret; =20 + if (IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) && + (clock_type =3D=3D VIRTIO_RTC_CLOCK_UTC || + clock_type =3D=3D VIRTIO_RTC_CLOCK_UTC_SMEARED || + clock_type =3D=3D VIRTIO_RTC_CLOCK_UTC_MAYBE_SMEARED)) { + ret =3D viortc_init_rtc_class_clock(viortc, vio_clk_id, + clock_type, flags); + if (ret < 0) + return ret; + if (ret > 0) + is_exposed =3D true; + } + if (IS_ENABLED(CONFIG_VIRTIO_RTC_PTP)) { ret =3D viortc_init_ptp_clock(viortc, vio_clk_id, clock_type, leap_second_smearing); @@ -716,7 +1051,7 @@ static int viortc_init_clock(struct viortc_dev *viortc= , u16 vio_clk_id) } =20 /** - * viortc_clocks_deinit() - unregister PHCs + * viortc_clocks_deinit() - unregister PHCs, stop RTC ops * @viortc: device data */ static void viortc_clocks_deinit(struct viortc_dev *viortc) @@ -734,6 +1069,9 @@ static void viortc_clocks_deinit(struct viortc_dev *vi= ortc) =20 WARN_ON(viortc_ptp_unregister(vio_ptp, &viortc->vdev->dev)); } + + if (viortc->viortc_class) + viortc_class_stop(viortc->viortc_class); } =20 /** @@ -780,6 +1118,88 @@ static int viortc_clocks_init(struct viortc_dev *vior= tc) return ret; } =20 +/** + * viortc_populate_vq() - populate alarmq with device-writable buffers + * @viortc: device data + * @viortc_vq: device specific data for virtqueue + * @buf_cap: device-writable buffer size in bytes + * @lock: lock queue during accesses + * + * Populates the alarmq with pre-allocated buffers. + * + * The caller is responsible for kicking the device. + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_populate_vq(struct viortc_dev *viortc, + struct viortc_vq *viortc_vq, u32 buf_cap, + bool lock) +{ + unsigned int num_elems, i; + struct virtqueue *vq; + unsigned long flags; + void *buf; + int ret; + + num_elems =3D viortc->num_alarmq_bufs; + vq =3D viortc_vq->vq; + + for (i =3D 0; i < num_elems; i++) { + buf =3D viortc->alarmq_bufs[i]; + + if (lock) { + spin_lock_irqsave(&viortc_vq->lock, flags); + + ret =3D viortc_feed_vq(viortc, vq, buf, buf_cap, buf); + + spin_unlock_irqrestore(&viortc_vq->lock, flags); + } else { + ret =3D viortc_feed_vq(viortc, vq, buf, buf_cap, buf); + } + + if (ret) + return ret; + } + + return 0; +} + +/** + * viortc_alloc_vq_bufs() - allocate alarmq buffers + * @viortc: device data + * @num_elems: # of buffers + * @buf_cap: per-buffer device-writable bytes + * + * Context: Process context. + * Return: Zero on success, negative error code otherwise. + */ +static int viortc_alloc_vq_bufs(struct viortc_dev *viortc, + unsigned int num_elems, u32 buf_cap) +{ + struct device *dev =3D &viortc->vdev->dev; + void **buf_list; + unsigned int i; + void *buf; + + buf_list =3D devm_kcalloc(dev, num_elems, sizeof(*buf_list), GFP_KERNEL); + if (!buf_list) + return -ENOMEM; + + viortc->alarmq_bufs =3D buf_list; + viortc->num_alarmq_bufs =3D num_elems; + + for (i =3D 0; i < num_elems; i++) { + buf =3D devm_kzalloc(dev, buf_cap, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf_list[i] =3D buf; + } + + return 0; +} + /** * viortc_init_vqs() - init virtqueues * @viortc: device data @@ -794,11 +1214,19 @@ static int viortc_init_vqs(struct viortc_dev *viortc) struct virtqueue *vqs[VIORTC_MAX_NR_QUEUES]; struct virtqueue_info vqs_info[] =3D { { "requestq", viortc_cb_requestq }, + { "alarmq", viortc_cb_alarmq }, }; struct virtio_device *vdev =3D viortc->vdev; + unsigned int num_elems; int nr_queues, ret; + bool have_alarms; =20 - nr_queues =3D VIORTC_REQUESTQ + 1; + have_alarms =3D viortc_alarms_supported(vdev); + + if (have_alarms) + nr_queues =3D VIORTC_ALARMQ + 1; + else + nr_queues =3D VIORTC_REQUESTQ + 1; =20 ret =3D virtio_find_vqs(vdev, nr_queues, vqs, vqs_info, NULL); if (ret) @@ -807,6 +1235,25 @@ static int viortc_init_vqs(struct viortc_dev *viortc) viortc->vqs[VIORTC_REQUESTQ].vq =3D vqs[VIORTC_REQUESTQ]; spin_lock_init(&viortc->vqs[VIORTC_REQUESTQ].lock); =20 + if (have_alarms) { + viortc->vqs[VIORTC_ALARMQ].vq =3D vqs[VIORTC_ALARMQ]; + spin_lock_init(&viortc->vqs[VIORTC_ALARMQ].lock); + + num_elems =3D virtqueue_get_vring_size(vqs[VIORTC_ALARMQ]); + if (num_elems =3D=3D 0) + return -ENOSPC; + + if (!viortc->alarmq_bufs) { + ret =3D viortc_alloc_vq_bufs(viortc, num_elems, + VIORTC_ALARMQ_BUF_CAP); + if (ret) + return ret; + } else { + viortc->num_alarmq_bufs =3D + min(num_elems, viortc->num_alarmq_bufs); + } + } + return 0; } =20 @@ -819,7 +1266,11 @@ static int viortc_init_vqs(struct viortc_dev *viortc) */ static int viortc_probe(struct virtio_device *vdev) { + struct viortc_vq *alarm_viortc_vq; + struct virtqueue *alarm_vq; struct viortc_dev *viortc; + unsigned long flags; + bool notify; int ret; =20 viortc =3D devm_kzalloc(&vdev->dev, sizeof(*viortc), GFP_KERNEL); @@ -839,8 +1290,30 @@ static int viortc_probe(struct virtio_device *vdev) if (ret) goto err_reset_vdev; =20 + if (viortc_alarms_supported(vdev)) { + alarm_viortc_vq =3D &viortc->vqs[VIORTC_ALARMQ]; + alarm_vq =3D alarm_viortc_vq->vq; + + ret =3D viortc_populate_vq(viortc, alarm_viortc_vq, + VIORTC_ALARMQ_BUF_CAP, true); + if (ret) + goto err_deinit_clocks; + + spin_lock_irqsave(&alarm_viortc_vq->lock, flags); + notify =3D virtqueue_kick_prepare(alarm_vq); + spin_unlock_irqrestore(&alarm_viortc_vq->lock, flags); + + if (notify && !virtqueue_notify(alarm_vq)) { + ret =3D -EIO; + goto err_deinit_clocks; + } + } + return 0; =20 +err_deinit_clocks: + viortc_clocks_deinit(viortc); + err_reset_vdev: virtio_reset_device(vdev); vdev->config->del_vqs(vdev); @@ -875,10 +1348,41 @@ static int viortc_freeze(struct virtio_device *dev) static int viortc_restore(struct virtio_device *dev) { struct viortc_dev *viortc =3D dev->priv; + struct viortc_vq *alarm_viortc_vq; + struct virtqueue *alarm_vq; + bool notify =3D false; + int ret; =20 - return viortc_init_vqs(viortc); + ret =3D viortc_init_vqs(viortc); + if (ret) + return ret; + + alarm_viortc_vq =3D &viortc->vqs[VIORTC_ALARMQ]; + alarm_vq =3D alarm_viortc_vq->vq; + + if (viortc_alarms_supported(dev)) { + ret =3D viortc_populate_vq(viortc, alarm_viortc_vq, + VIORTC_ALARMQ_BUF_CAP, false); + if (ret) + return ret; + + notify =3D virtqueue_kick_prepare(alarm_vq); + } + + virtio_device_ready(dev); + + if (notify && !virtqueue_notify(alarm_vq)) + ret =3D -EIO; + + return ret; } =20 +static unsigned int features[] =3D { +#if IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) + VIRTIO_RTC_F_ALARM, +#endif +}; + static struct virtio_device_id id_table[] =3D { { VIRTIO_ID_CLOCK, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -887,6 +1391,8 @@ MODULE_DEVICE_TABLE(virtio, id_table); =20 static struct virtio_driver virtio_rtc_drv =3D { .driver.name =3D KBUILD_MODNAME, + .feature_table =3D features, + .feature_table_size =3D ARRAY_SIZE(features), .id_table =3D id_table, .probe =3D viortc_probe, .remove =3D viortc_remove, diff --git a/drivers/virtio/virtio_rtc_internal.h b/drivers/virtio/virtio_r= tc_internal.h index 2e589903d04f..296afee6719b 100644 --- a/drivers/virtio/virtio_rtc_internal.h +++ b/drivers/virtio/virtio_rtc_internal.h @@ -9,6 +9,8 @@ #ifndef _VIRTIO_RTC_INTERNAL_H_ #define _VIRTIO_RTC_INTERNAL_H_ =20 +#include +#include #include #include =20 @@ -21,6 +23,16 @@ int viortc_read_cross(struct viortc_dev *viortc, u16 vio= _clk_id, u8 hw_counter, u64 *reading, u64 *cycles); int viortc_cross_cap(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_coun= ter, bool *supported); +int viortc_read_alarm(struct viortc_dev *viortc, u16 vio_clk_id, + u64 *alarm_time, bool *enabled); +int viortc_set_alarm(struct viortc_dev *viortc, u16 vio_clk_id, u64 alarm_= time, + bool alarm_enable); +int viortc_set_alarm_enabled(struct viortc_dev *viortc, u16 vio_clk_id, + bool alarm_enable); + +struct viortc_class; + +struct viortc_class *viortc_class_from_dev(struct device *dev); =20 /* PTP IFs */ =20 @@ -67,4 +79,44 @@ static inline int viortc_ptp_unregister(struct viortc_pt= p_clock *vio_ptp, */ int viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id); =20 +/* RTC class IFs */ + +#if IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) + +void viortc_class_alarm(struct viortc_class *viortc_class, u16 vio_clk_id); + +void viortc_class_stop(struct viortc_class *viortc_class); + +int viortc_class_register(struct viortc_class *viortc_class); + +struct viortc_class *viortc_class_init(struct viortc_dev *viortc, + u16 vio_clk_id, bool have_alarm, + struct device *parent_dev); + +#else /* CONFIG_VIRTIO_RTC_CLASS */ + +static inline void viortc_class_alarm(struct viortc_class *viortc_class, + u16 vio_clk_id) +{ +} + +static inline void viortc_class_stop(struct viortc_class *viortc_class) +{ +} + +static inline int viortc_class_register(struct viortc_class *viortc_class) +{ + return -ENODEV; +} + +static inline struct viortc_class *viortc_class_init(struct viortc_dev *vi= ortc, + u16 vio_clk_id, + bool have_alarm, + struct device *parent_dev) +{ + return ERR_PTR(-ENODEV); +} + +#endif /* CONFIG_VIRTIO_RTC_CLASS */ + #endif /* _VIRTIO_RTC_INTERNAL_H_ */ diff --git a/include/uapi/linux/virtio_rtc.h b/include/uapi/linux/virtio_rt= c.h index 6b3af4e9bbfb..85ee8f013661 100644 --- a/include/uapi/linux/virtio_rtc.h +++ b/include/uapi/linux/virtio_rtc.h @@ -9,6 +9,9 @@ =20 #include =20 +/* alarm feature */ +#define VIRTIO_RTC_F_ALARM 0 + /* read request message types */ =20 #define VIRTIO_RTC_REQ_READ 0x0001 @@ -19,6 +22,13 @@ #define VIRTIO_RTC_REQ_CFG 0x1000 #define VIRTIO_RTC_REQ_CLOCK_CAP 0x1001 #define VIRTIO_RTC_REQ_CROSS_CAP 0x1002 +#define VIRTIO_RTC_REQ_READ_ALARM 0x1003 +#define VIRTIO_RTC_REQ_SET_ALARM 0x1004 +#define VIRTIO_RTC_REQ_SET_ALARM_ENABLED 0x1005 + +/* alarmq message types */ + +#define VIRTIO_RTC_NOTIF_ALARM 0x2000 =20 /* Message headers */ =20 @@ -39,6 +49,12 @@ struct virtio_rtc_resp_head { __u8 reserved[7]; }; =20 +/** common notification header */ +struct virtio_rtc_notif_head { + __le16 msg_type; + __u8 reserved[6]; +}; + /* read requests */ =20 /* VIRTIO_RTC_REQ_READ message */ @@ -111,7 +127,9 @@ struct virtio_rtc_resp_clock_cap { #define VIRTIO_RTC_SMEAR_NOON_LINEAR 1 #define VIRTIO_RTC_SMEAR_UTC_SLS 2 __u8 leap_second_smearing; - __u8 reserved[6]; +#define VIRTIO_RTC_FLAG_ALARM_CAP (1 << 0) + __u8 flags; + __u8 reserved[5]; }; =20 /* VIRTIO_RTC_REQ_CROSS_CAP message */ @@ -130,6 +148,53 @@ struct virtio_rtc_resp_cross_cap { __u8 reserved[7]; }; =20 +/* VIRTIO_RTC_REQ_READ_ALARM message */ + +struct virtio_rtc_req_read_alarm { + struct virtio_rtc_req_head head; + __le16 clock_id; + __u8 reserved[6]; +}; + +struct virtio_rtc_resp_read_alarm { + struct virtio_rtc_resp_head head; + __le64 alarm_time; +#define VIRTIO_RTC_FLAG_ALARM_ENABLED (1 << 0) + __u8 flags; + __u8 reserved[7]; +}; + +/* VIRTIO_RTC_REQ_SET_ALARM message */ + +struct virtio_rtc_req_set_alarm { + struct virtio_rtc_req_head head; + __le64 alarm_time; + __le16 clock_id; + /* flag VIRTIO_RTC_FLAG_ALARM_ENABLED */ + __u8 flags; + __u8 reserved[5]; +}; + +struct virtio_rtc_resp_set_alarm { + struct virtio_rtc_resp_head head; + /* no response params */ +}; + +/* VIRTIO_RTC_REQ_SET_ALARM_ENABLED message */ + +struct virtio_rtc_req_set_alarm_enabled { + struct virtio_rtc_req_head head; + __le16 clock_id; + /* flag VIRTIO_RTC_ALARM_ENABLED */ + __u8 flags; + __u8 reserved[5]; +}; + +struct virtio_rtc_resp_set_alarm_enabled { + struct virtio_rtc_resp_head head; + /* no response params */ +}; + /** Union of request types for requestq */ union virtio_rtc_req_requestq { struct virtio_rtc_req_read read; @@ -137,6 +202,9 @@ union virtio_rtc_req_requestq { struct virtio_rtc_req_cfg cfg; struct virtio_rtc_req_clock_cap clock_cap; struct virtio_rtc_req_cross_cap cross_cap; + struct virtio_rtc_req_read_alarm read_alarm; + struct virtio_rtc_req_set_alarm set_alarm; + struct virtio_rtc_req_set_alarm_enabled set_alarm_enabled; }; =20 /** Union of response types for requestq */ @@ -146,6 +214,24 @@ union virtio_rtc_resp_requestq { struct virtio_rtc_resp_cfg cfg; struct virtio_rtc_resp_clock_cap clock_cap; struct virtio_rtc_resp_cross_cap cross_cap; + struct virtio_rtc_resp_read_alarm read_alarm; + struct virtio_rtc_resp_set_alarm set_alarm; + struct virtio_rtc_resp_set_alarm_enabled set_alarm_enabled; +}; + +/* alarmq notifications */ + +/* VIRTIO_RTC_NOTIF_ALARM notification */ + +struct virtio_rtc_notif_alarm { + struct virtio_rtc_notif_head head; + __le16 clock_id; + __u8 reserved[6]; +}; + +/** Union of notification types for alarmq */ +union virtio_rtc_notif_alarmq { + struct virtio_rtc_notif_alarm alarm; }; =20 #endif /* _LINUX_VIRTIO_RTC_H */ --=20 2.43.0