File Index
Files and directories
Sign in
FILE

virtio_rtc_ptp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/virtio/virtio_rtc_ptp.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Expose virtio_rtc clocks as PTP clocks.
  4 *
  5 * Copyright (C) 2022-2023 OpenSynergy GmbH
  6 * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
  7 *
  8 * Derived from ptp_kvm_common.c, virtual PTP 1588 clock for use with KVM
  9 * guests.
 10 *
 11 * Copyright (C) 2017 Red Hat Inc.
 12 */
 13
 14#include <linux/device.h>
 15#include <linux/err.h>
 16#include <linux/ptp_clock_kernel.h>
 17
 18#include <uapi/linux/virtio_rtc.h>
 19
 20#include "virtio_rtc_internal.h"
 21
 22/**
 23 * struct viortc_ptp_clock - PTP clock abstraction
 24 * @ptp_clock: PTP clock handle for unregistering
 25 * @viortc: virtio_rtc device data
 26 * @ptp_info: PTP clock description
 27 * @vio_clk_id: virtio_rtc clock id
 28 * @have_cross: device supports crosststamp with available HW counter
 29 */
 30struct viortc_ptp_clock {
 31	struct ptp_clock *ptp_clock;
 32	struct viortc_dev *viortc;
 33	struct ptp_clock_info ptp_info;
 34	u16 vio_clk_id;
 35	bool have_cross;
 36};
 37
 38/**
 39 * struct viortc_ptp_cross_ctx - context for get_device_system_crosststamp()
 40 * @device_time: device clock reading
 41 * @system_counterval: HW counter value at device_time
 42 *
 43 * Provides the already obtained crosststamp to get_device_system_crosststamp().
 44 */
 45struct viortc_ptp_cross_ctx {
 46	ktime_t device_time;
 47	struct system_counterval_t system_counterval;
 48};
 49
 50/* Weak function in case get_device_system_crosststamp() is not supported */
 51int __weak viortc_hw_xtstamp_params(u8 *hw_counter, enum clocksource_ids *cs_id)
 52{
 53	return -EOPNOTSUPP;
 54}
 55
 56/**
 57 * viortc_ptp_get_time_fn() - callback for get_device_system_crosststamp()
 58 * @device_time: device clock reading
 59 * @system_counterval: HW counter value at device_time
 60 * @ctx: context with already obtained crosststamp
 61 *
 62 * Return: zero (success).
 63 */
 64static int viortc_ptp_get_time_fn(ktime_t *device_time,
 65				  struct system_counterval_t *system_counterval,
 66				  void *ctx)
 67{
 68	struct viortc_ptp_cross_ctx *vio_ctx = ctx;
 69
 70	*device_time = vio_ctx->device_time;
 71	*system_counterval = vio_ctx->system_counterval;
 72
 73	return 0;
 74}
 75
 76/**
 77 * viortc_ptp_do_xtstamp() - get crosststamp from device
 78 * @vio_ptp: virtio_rtc PTP clock
 79 * @hw_counter: virtio_rtc HW counter type
 80 * @cs_id: clocksource id corresponding to hw_counter
 81 * @ctx: context for get_device_system_crosststamp()
 82 *
 83 * Reads HW-specific crosststamp from device.
 84 *
 85 * Context: Process context.
 86 * Return: Zero on success, negative error code otherwise.
 87 */
 88static int viortc_ptp_do_xtstamp(struct viortc_ptp_clock *vio_ptp,
 89				 u8 hw_counter, enum clocksource_ids cs_id,
 90				 struct viortc_ptp_cross_ctx *ctx)
 91{
 92	u64 max_ns, ns;
 93	int ret;
 94
 95	ctx->system_counterval.cs_id = cs_id;
 96
 97	ret = viortc_read_cross(vio_ptp->viortc, vio_ptp->vio_clk_id,
 98				hw_counter, &ns,
 99				&ctx->system_counterval.cycles);
100	if (ret)
101		return ret;
102
103	max_ns = (u64)ktime_to_ns(KTIME_MAX);
104	if (ns > max_ns)
105		return -EINVAL;
106
107	ctx->device_time = ns_to_ktime(ns);
108
109	return 0;
110}
111
112/*
113 * PTP clock operations
114 */
115
116/**
117 * viortc_ptp_getcrosststamp() - PTP clock getcrosststamp op
118 * @ptp: PTP clock info
119 * @xtstamp: crosststamp
120 *
121 * Context: Process context.
122 * Return: Zero on success, negative error code otherwise.
123 */
124static int viortc_ptp_getcrosststamp(struct ptp_clock_info *ptp,
125				     struct system_device_crosststamp *xtstamp)
126{
127	struct viortc_ptp_clock *vio_ptp =
128		container_of(ptp, struct viortc_ptp_clock, ptp_info);
129	struct system_time_snapshot history_begin;
130	struct viortc_ptp_cross_ctx ctx;
131	enum clocksource_ids cs_id;
132	u8 hw_counter;
133	int ret;
134
135	if (!vio_ptp->have_cross)
136		return -EOPNOTSUPP;
137
138	ret = viortc_hw_xtstamp_params(&hw_counter, &cs_id);
139	if (ret)
140		return ret;
141
142	ktime_get_snapshot(&history_begin);
143	if (history_begin.cs_id != cs_id)
144		return -EOPNOTSUPP;
145
146	/*
147	 * Getting the timestamp can take many milliseconds with a slow Virtio
148	 * device. This is too long for viortc_ptp_get_time_fn() passed to
149	 * get_device_system_crosststamp(), which has to usually return before
150	 * the timekeeper seqcount increases (every tick or so).
151	 *
152	 * So, get the actual cross-timestamp first.
153	 */
154	ret = viortc_ptp_do_xtstamp(vio_ptp, hw_counter, cs_id, &ctx);
155	if (ret)
156		return ret;
157
158	ret = get_device_system_crosststamp(viortc_ptp_get_time_fn, &ctx,
159					    &history_begin, xtstamp);
160	if (ret)
161		pr_debug("%s: get_device_system_crosststamp() returned %d\n",
162			 __func__, ret);
163
164	return ret;
165}
166
167/* viortc_ptp_adjfine() - unsupported PTP clock adjfine op */
168static int viortc_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
169{
170	return -EOPNOTSUPP;
171}
172
173/* viortc_ptp_adjtime() - unsupported PTP clock adjtime op */
174static int viortc_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
175{
176	return -EOPNOTSUPP;
177}
178
179/* viortc_ptp_settime64() - unsupported PTP clock settime64 op */
180static int viortc_ptp_settime64(struct ptp_clock_info *ptp,
181				const struct timespec64 *ts)
182{
183	return -EOPNOTSUPP;
184}
185
186/*
187 * viortc_ptp_gettimex64() - PTP clock gettimex64 op
188 *
189 * Context: Process context.
190 */
191static int viortc_ptp_gettimex64(struct ptp_clock_info *ptp,
192				 struct timespec64 *ts,
193				 struct ptp_system_timestamp *sts)
194{
195	struct viortc_ptp_clock *vio_ptp =
196		container_of(ptp, struct viortc_ptp_clock, ptp_info);
197	int ret;
198	u64 ns;
199
200	ptp_read_system_prets(sts);
201	ret = viortc_read(vio_ptp->viortc, vio_ptp->vio_clk_id, &ns);
202	ptp_read_system_postts(sts);
203
204	if (ret)
205		return ret;
206
207	if (ns > (u64)S64_MAX)
208		return -EINVAL;
209
210	*ts = ns_to_timespec64((s64)ns);
211
212	return 0;
213}
214
215/* viortc_ptp_enable() - unsupported PTP clock enable op */
216static int viortc_ptp_enable(struct ptp_clock_info *ptp,
217			     struct ptp_clock_request *rq, int on)
218{
219	return -EOPNOTSUPP;
220}
221
222/*
223 * viortc_ptp_info_template - ptp_clock_info template
224 *
225 * The .name member will be set for individual virtio_rtc PTP clocks.
226 *
227 * The .getcrosststamp member will be cleared for PTP clocks not supporting
228 * crosststamp.
229 */
230static const struct ptp_clock_info viortc_ptp_info_template = {
231	.owner = THIS_MODULE,
232	/* .name is set according to clock type */
233	.adjfine = viortc_ptp_adjfine,
234	.adjtime = viortc_ptp_adjtime,
235	.gettimex64 = viortc_ptp_gettimex64,
236	.settime64 = viortc_ptp_settime64,
237	.enable = viortc_ptp_enable,
238	.getcrosststamp = viortc_ptp_getcrosststamp,
239};
240
241/**
242 * viortc_ptp_unregister() - PTP clock unregistering wrapper
243 * @vio_ptp: virtio_rtc PTP clock
244 * @parent_dev: parent device of PTP clock
245 *
246 * Return: Zero on success, negative error code otherwise.
247 */
248int viortc_ptp_unregister(struct viortc_ptp_clock *vio_ptp,
249			  struct device *parent_dev)
250{
251	int ret = ptp_clock_unregister(vio_ptp->ptp_clock);
252
253	if (!ret)
254		devm_kfree(parent_dev, vio_ptp);
255
256	return ret;
257}
258
259/**
260 * viortc_ptp_get_cross_cap() - get xtstamp support info from device
261 * @viortc: virtio_rtc device data
262 * @vio_ptp: virtio_rtc PTP clock abstraction
263 *
264 * Context: Process context.
265 * Return: Zero on success, negative error code otherwise.
266 */
267static int viortc_ptp_get_cross_cap(struct viortc_dev *viortc,
268				    struct viortc_ptp_clock *vio_ptp)
269{
270	enum clocksource_ids cs_id;
271	bool xtstamp_supported;
272	u8 hw_counter;
273	int ret;
274
275	ret = viortc_hw_xtstamp_params(&hw_counter, &cs_id);
276	if (ret) {
277		vio_ptp->have_cross = false;
278		return 0;
279	}
280
281	ret = viortc_cross_cap(viortc, vio_ptp->vio_clk_id, hw_counter,
282			       &xtstamp_supported);
283	if (ret)
284		return ret;
285
286	vio_ptp->have_cross = xtstamp_supported;
287
288	return 0;
289}
290
291/**
292 * viortc_ptp_register() - prepare and register PTP clock
293 * @viortc: virtio_rtc device data
294 * @parent_dev: parent device for PTP clock
295 * @vio_clk_id: id of virtio_rtc clock which backs PTP clock
296 * @ptp_clock_name: PTP clock name
297 *
298 * Context: Process context.
299 * Return: Pointer on success, ERR_PTR() otherwise; NULL if PTP clock support
300 *         not available.
301 */
302struct viortc_ptp_clock *viortc_ptp_register(struct viortc_dev *viortc,
303					     struct device *parent_dev,
304					     u16 vio_clk_id,
305					     const char *ptp_clock_name)
306{
307	struct viortc_ptp_clock *vio_ptp;
308	struct ptp_clock *ptp_clock;
309	ssize_t len;
310	int ret;
311
312	vio_ptp = devm_kzalloc(parent_dev, sizeof(*vio_ptp), GFP_KERNEL);
313	if (!vio_ptp)
314		return ERR_PTR(-ENOMEM);
315
316	vio_ptp->viortc = viortc;
317	vio_ptp->vio_clk_id = vio_clk_id;
318	vio_ptp->ptp_info = viortc_ptp_info_template;
319	len = strscpy(vio_ptp->ptp_info.name, ptp_clock_name,
320		      sizeof(vio_ptp->ptp_info.name));
321	if (len < 0) {
322		ret = len;
323		goto err_free_dev;
324	}
325
326	ret = viortc_ptp_get_cross_cap(viortc, vio_ptp);
327	if (ret)
328		goto err_free_dev;
329
330	if (!vio_ptp->have_cross)
331		vio_ptp->ptp_info.getcrosststamp = NULL;
332
333	ptp_clock = ptp_clock_register(&vio_ptp->ptp_info, parent_dev);
334	if (IS_ERR(ptp_clock))
335		goto err_on_register;
336
337	vio_ptp->ptp_clock = ptp_clock;
338
339	return vio_ptp;
340
341err_on_register:
342	ret = PTR_ERR(ptp_clock);
343
344err_free_dev:
345	devm_kfree(parent_dev, vio_ptp);
346	return ERR_PTR(ret);
347}
~/ Read-only file preview.