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