]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/ptp/ptp_vclock.c
Documentation: Add documentation for Processor MMIO Stale Data
[mirror_ubuntu-jammy-kernel.git] / drivers / ptp / ptp_vclock.c
CommitLineData
5d43f951
YL
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * PTP virtual clock driver
4 *
5 * Copyright 2021 NXP
6 */
7#include <linux/slab.h>
8#include "ptp_private.h"
9
10#define PTP_VCLOCK_CC_SHIFT 31
11#define PTP_VCLOCK_CC_MULT (1 << PTP_VCLOCK_CC_SHIFT)
12#define PTP_VCLOCK_FADJ_SHIFT 9
13#define PTP_VCLOCK_FADJ_DENOMINATOR 15625ULL
14#define PTP_VCLOCK_REFRESH_INTERVAL (HZ * 2)
15
16static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
17{
18 struct ptp_vclock *vclock = info_to_vclock(ptp);
19 unsigned long flags;
20 s64 adj;
21
22 adj = (s64)scaled_ppm << PTP_VCLOCK_FADJ_SHIFT;
23 adj = div_s64(adj, PTP_VCLOCK_FADJ_DENOMINATOR);
24
25 spin_lock_irqsave(&vclock->lock, flags);
26 timecounter_read(&vclock->tc);
27 vclock->cc.mult = PTP_VCLOCK_CC_MULT + adj;
28 spin_unlock_irqrestore(&vclock->lock, flags);
29
30 return 0;
31}
32
33static int ptp_vclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
34{
35 struct ptp_vclock *vclock = info_to_vclock(ptp);
36 unsigned long flags;
37
38 spin_lock_irqsave(&vclock->lock, flags);
39 timecounter_adjtime(&vclock->tc, delta);
40 spin_unlock_irqrestore(&vclock->lock, flags);
41
42 return 0;
43}
44
45static int ptp_vclock_gettime(struct ptp_clock_info *ptp,
46 struct timespec64 *ts)
47{
48 struct ptp_vclock *vclock = info_to_vclock(ptp);
49 unsigned long flags;
50 u64 ns;
51
52 spin_lock_irqsave(&vclock->lock, flags);
53 ns = timecounter_read(&vclock->tc);
54 spin_unlock_irqrestore(&vclock->lock, flags);
55 *ts = ns_to_timespec64(ns);
56
57 return 0;
58}
59
60static int ptp_vclock_settime(struct ptp_clock_info *ptp,
61 const struct timespec64 *ts)
62{
63 struct ptp_vclock *vclock = info_to_vclock(ptp);
64 u64 ns = timespec64_to_ns(ts);
65 unsigned long flags;
66
67 spin_lock_irqsave(&vclock->lock, flags);
68 timecounter_init(&vclock->tc, &vclock->cc, ns);
69 spin_unlock_irqrestore(&vclock->lock, flags);
70
71 return 0;
72}
73
74static long ptp_vclock_refresh(struct ptp_clock_info *ptp)
75{
76 struct ptp_vclock *vclock = info_to_vclock(ptp);
77 struct timespec64 ts;
78
79 ptp_vclock_gettime(&vclock->info, &ts);
80
81 return PTP_VCLOCK_REFRESH_INTERVAL;
82}
83
84static const struct ptp_clock_info ptp_vclock_info = {
85 .owner = THIS_MODULE,
86 .name = "ptp virtual clock",
87 /* The maximum ppb value that long scaled_ppm can support */
88 .max_adj = 32767999,
89 .adjfine = ptp_vclock_adjfine,
90 .adjtime = ptp_vclock_adjtime,
91 .gettime64 = ptp_vclock_gettime,
92 .settime64 = ptp_vclock_settime,
93 .do_aux_work = ptp_vclock_refresh,
94};
95
96static u64 ptp_vclock_read(const struct cyclecounter *cc)
97{
98 struct ptp_vclock *vclock = cc_to_vclock(cc);
99 struct ptp_clock *ptp = vclock->pclock;
100 struct timespec64 ts = {};
101
102 if (ptp->info->gettimex64)
103 ptp->info->gettimex64(ptp->info, &ts, NULL);
104 else
105 ptp->info->gettime64(ptp->info, &ts);
106
107 return timespec64_to_ns(&ts);
108}
109
110static const struct cyclecounter ptp_vclock_cc = {
111 .read = ptp_vclock_read,
112 .mask = CYCLECOUNTER_MASK(32),
113 .mult = PTP_VCLOCK_CC_MULT,
114 .shift = PTP_VCLOCK_CC_SHIFT,
115};
116
117struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock)
118{
119 struct ptp_vclock *vclock;
120
121 vclock = kzalloc(sizeof(*vclock), GFP_KERNEL);
122 if (!vclock)
123 return NULL;
124
125 vclock->pclock = pclock;
126 vclock->info = ptp_vclock_info;
127 vclock->cc = ptp_vclock_cc;
128
129 snprintf(vclock->info.name, PTP_CLOCK_NAME_LEN, "ptp%d_virt",
130 pclock->index);
131
132 spin_lock_init(&vclock->lock);
133
134 vclock->clock = ptp_clock_register(&vclock->info, &pclock->dev);
135 if (IS_ERR_OR_NULL(vclock->clock)) {
136 kfree(vclock);
137 return NULL;
138 }
139
140 timecounter_init(&vclock->tc, &vclock->cc, 0);
141 ptp_schedule_worker(vclock->clock, PTP_VCLOCK_REFRESH_INTERVAL);
142
143 return vclock;
144}
145
146void ptp_vclock_unregister(struct ptp_vclock *vclock)
147{
148 ptp_clock_unregister(vclock->clock);
149 kfree(vclock);
150}
acb288e8 151
e5f31552 152#if IS_BUILTIN(CONFIG_PTP_1588_CLOCK)
acb288e8
YL
153int ptp_get_vclocks_index(int pclock_index, int **vclock_index)
154{
155 char name[PTP_CLOCK_NAME_LEN] = "";
156 struct ptp_clock *ptp;
157 struct device *dev;
158 int num = 0;
159
160 if (pclock_index < 0)
161 return num;
162
163 snprintf(name, PTP_CLOCK_NAME_LEN, "ptp%d", pclock_index);
164 dev = class_find_device_by_name(ptp_class, name);
165 if (!dev)
166 return num;
167
168 ptp = dev_get_drvdata(dev);
169
170 if (mutex_lock_interruptible(&ptp->n_vclocks_mux)) {
171 put_device(dev);
172 return num;
173 }
174
175 *vclock_index = kzalloc(sizeof(int) * ptp->n_vclocks, GFP_KERNEL);
176 if (!(*vclock_index))
177 goto out;
178
179 memcpy(*vclock_index, ptp->vclock_index, sizeof(int) * ptp->n_vclocks);
180 num = ptp->n_vclocks;
181out:
182 mutex_unlock(&ptp->n_vclocks_mux);
183 put_device(dev);
184 return num;
185}
186EXPORT_SYMBOL(ptp_get_vclocks_index);
895487a3 187
9f7d49f8
ML
188ktime_t ptp_convert_timestamp(const struct skb_shared_hwtstamps *hwtstamps,
189 int vclock_index)
895487a3
YL
190{
191 char name[PTP_CLOCK_NAME_LEN] = "";
192 struct ptp_vclock *vclock;
193 struct ptp_clock *ptp;
194 unsigned long flags;
195 struct device *dev;
196 u64 ns;
197
198 snprintf(name, PTP_CLOCK_NAME_LEN, "ptp%d", vclock_index);
199 dev = class_find_device_by_name(ptp_class, name);
200 if (!dev)
9f7d49f8 201 return 0;
895487a3
YL
202
203 ptp = dev_get_drvdata(dev);
204 if (!ptp->is_virtual_clock) {
205 put_device(dev);
9f7d49f8 206 return 0;
895487a3
YL
207 }
208
209 vclock = info_to_vclock(ptp->info);
210
211 ns = ktime_to_ns(hwtstamps->hwtstamp);
212
213 spin_lock_irqsave(&vclock->lock, flags);
214 ns = timecounter_cyc2time(&vclock->tc, ns);
215 spin_unlock_irqrestore(&vclock->lock, flags);
216
217 put_device(dev);
9f7d49f8 218 return ns_to_ktime(ns);
895487a3
YL
219}
220EXPORT_SYMBOL(ptp_convert_timestamp);
e5f31552 221#endif