Skip to content

Commit 73f3706

Browse files
yangbolu1991davem330
authored andcommitted
ptp: support ptp physical/virtual clocks conversion
Support ptp physical/virtual clocks conversion via sysfs. There will be a new attribute n_vclocks under ptp physical clock sysfs. - In default, the value is 0 meaning only ptp physical clock is in use. - Setting the value can create corresponding number of ptp virtual clocks to use. But current physical clock is guaranteed to stay free running. - Setting the value back to 0 can delete virtual clocks and back use physical clock again. Another new attribute max_vclocks control the maximum number of ptp vclocks. Signed-off-by: Yangbo Lu <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5d43f95 commit 73f3706

File tree

4 files changed

+205
-0
lines changed

4 files changed

+205
-0
lines changed

Documentation/ABI/testing/sysfs-ptp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ Description:
3333
frequency adjustment value (a positive integer) in
3434
parts per billion.
3535

36+
What: /sys/class/ptp/ptpN/max_vclocks
37+
Date: May 2021
38+
Contact: Yangbo Lu <[email protected]>
39+
Description:
40+
This file contains the maximum number of ptp vclocks.
41+
Write integer to re-configure it.
42+
3643
What: /sys/class/ptp/ptpN/n_alarms
3744
Date: September 2010
3845
Contact: Richard Cochran <[email protected]>
@@ -61,6 +68,19 @@ Description:
6168
This file contains the number of programmable pins
6269
offered by the PTP hardware clock.
6370

71+
What: /sys/class/ptp/ptpN/n_vclocks
72+
Date: May 2021
73+
Contact: Yangbo Lu <[email protected]>
74+
Description:
75+
This file contains the number of virtual PTP clocks in
76+
use. By default, the value is 0 meaning that only the
77+
physical clock is in use. Setting the value creates
78+
the corresponding number of virtual clocks and causes
79+
the physical clock to become free running. Setting the
80+
value back to 0 deletes the virtual clocks and
81+
switches the physical clock back to normal, adjustable
82+
operation.
83+
6484
What: /sys/class/ptp/ptpN/pins
6585
Date: March 2014
6686
Contact: Richard Cochran <[email protected]>

drivers/ptp/ptp_clock.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp
7676
{
7777
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
7878

79+
if (ptp_vclock_in_use(ptp)) {
80+
pr_err("ptp: virtual clock in use\n");
81+
return -EBUSY;
82+
}
83+
7984
return ptp->info->settime64(ptp->info, tp);
8085
}
8186

@@ -97,6 +102,11 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
97102
struct ptp_clock_info *ops;
98103
int err = -EOPNOTSUPP;
99104

105+
if (ptp_vclock_in_use(ptp)) {
106+
pr_err("ptp: virtual clock in use\n");
107+
return -EBUSY;
108+
}
109+
100110
ops = ptp->info;
101111

102112
if (tx->modes & ADJ_SETOFFSET) {
@@ -161,6 +171,7 @@ static void ptp_clock_release(struct device *dev)
161171
ptp_cleanup_pin_groups(ptp);
162172
mutex_destroy(&ptp->tsevq_mux);
163173
mutex_destroy(&ptp->pincfg_mux);
174+
mutex_destroy(&ptp->n_vclocks_mux);
164175
ida_simple_remove(&ptp_clocks_map, ptp->index);
165176
kfree(ptp);
166177
}
@@ -208,6 +219,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
208219
spin_lock_init(&ptp->tsevq.lock);
209220
mutex_init(&ptp->tsevq_mux);
210221
mutex_init(&ptp->pincfg_mux);
222+
mutex_init(&ptp->n_vclocks_mux);
211223
init_waitqueue_head(&ptp->tsev_wq);
212224

213225
if (ptp->info->do_aux_work) {
@@ -221,6 +233,14 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
221233
ptp->pps_source->lookup_cookie = ptp;
222234
}
223235

236+
/* PTP virtual clock is being registered under physical clock */
237+
if (parent->class && parent->class->name &&
238+
strcmp(parent->class->name, "ptp") == 0)
239+
ptp->is_virtual_clock = true;
240+
241+
if (!ptp->is_virtual_clock)
242+
ptp->max_vclocks = PTP_DEFAULT_MAX_VCLOCKS;
243+
224244
err = ptp_populate_pin_groups(ptp);
225245
if (err)
226246
goto no_pin_groups;
@@ -270,6 +290,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
270290
kworker_err:
271291
mutex_destroy(&ptp->tsevq_mux);
272292
mutex_destroy(&ptp->pincfg_mux);
293+
mutex_destroy(&ptp->n_vclocks_mux);
273294
ida_simple_remove(&ptp_clocks_map, index);
274295
no_slot:
275296
kfree(ptp);
@@ -280,6 +301,11 @@ EXPORT_SYMBOL(ptp_clock_register);
280301

281302
int ptp_clock_unregister(struct ptp_clock *ptp)
282303
{
304+
if (ptp_vclock_in_use(ptp)) {
305+
pr_err("ptp: virtual clock in use\n");
306+
return -EBUSY;
307+
}
308+
283309
ptp->defunct = 1;
284310
wake_up_interruptible(&ptp->tsev_wq);
285311

drivers/ptp/ptp_private.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#define PTP_MAX_TIMESTAMPS 128
2020
#define PTP_BUF_TIMESTAMPS 30
21+
#define PTP_DEFAULT_MAX_VCLOCKS 20
2122

2223
struct timestamp_event_queue {
2324
struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
@@ -46,6 +47,10 @@ struct ptp_clock {
4647
const struct attribute_group *pin_attr_groups[2];
4748
struct kthread_worker *kworker;
4849
struct kthread_delayed_work aux_work;
50+
unsigned int max_vclocks;
51+
unsigned int n_vclocks;
52+
struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */
53+
bool is_virtual_clock;
4954
};
5055

5156
#define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
@@ -74,6 +79,22 @@ static inline int queue_cnt(struct timestamp_event_queue *q)
7479
return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
7580
}
7681

82+
/* Check if ptp virtual clock is in use */
83+
static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
84+
{
85+
bool in_use = false;
86+
87+
if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
88+
return true;
89+
90+
if (!ptp->is_virtual_clock && ptp->n_vclocks)
91+
in_use = true;
92+
93+
mutex_unlock(&ptp->n_vclocks_mux);
94+
95+
return in_use;
96+
}
97+
7798
/*
7899
* see ptp_chardev.c
79100
*/

drivers/ptp/ptp_sysfs.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* PTP 1588 clock support - sysfs interface.
44
*
55
* Copyright (C) 2010 OMICRON electronics GmbH
6+
* Copyright 2021 NXP
67
*/
78
#include <linux/capability.h>
89
#include <linux/slab.h>
@@ -148,6 +149,137 @@ static ssize_t pps_enable_store(struct device *dev,
148149
}
149150
static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);
150151

152+
static int unregister_vclock(struct device *dev, void *data)
153+
{
154+
struct ptp_clock *ptp = dev_get_drvdata(dev);
155+
struct ptp_clock_info *info = ptp->info;
156+
struct ptp_vclock *vclock;
157+
u8 *num = data;
158+
159+
vclock = info_to_vclock(info);
160+
dev_info(dev->parent, "delete virtual clock ptp%d\n",
161+
vclock->clock->index);
162+
163+
ptp_vclock_unregister(vclock);
164+
(*num)--;
165+
166+
/* For break. Not error. */
167+
if (*num == 0)
168+
return -EINVAL;
169+
170+
return 0;
171+
}
172+
173+
static ssize_t n_vclocks_show(struct device *dev,
174+
struct device_attribute *attr, char *page)
175+
{
176+
struct ptp_clock *ptp = dev_get_drvdata(dev);
177+
ssize_t size;
178+
179+
if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
180+
return -ERESTARTSYS;
181+
182+
size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->n_vclocks);
183+
184+
mutex_unlock(&ptp->n_vclocks_mux);
185+
186+
return size;
187+
}
188+
189+
static ssize_t n_vclocks_store(struct device *dev,
190+
struct device_attribute *attr,
191+
const char *buf, size_t count)
192+
{
193+
struct ptp_clock *ptp = dev_get_drvdata(dev);
194+
struct ptp_vclock *vclock;
195+
int err = -EINVAL;
196+
u32 num, i;
197+
198+
if (kstrtou32(buf, 0, &num))
199+
return err;
200+
201+
if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
202+
return -ERESTARTSYS;
203+
204+
if (num > ptp->max_vclocks) {
205+
dev_err(dev, "max value is %d\n", ptp->max_vclocks);
206+
goto out;
207+
}
208+
209+
/* Need to create more vclocks */
210+
if (num > ptp->n_vclocks) {
211+
for (i = 0; i < num - ptp->n_vclocks; i++) {
212+
vclock = ptp_vclock_register(ptp);
213+
if (!vclock)
214+
goto out;
215+
216+
dev_info(dev, "new virtual clock ptp%d\n",
217+
vclock->clock->index);
218+
}
219+
}
220+
221+
/* Need to delete vclocks */
222+
if (num < ptp->n_vclocks) {
223+
i = ptp->n_vclocks - num;
224+
device_for_each_child_reverse(dev, &i,
225+
unregister_vclock);
226+
}
227+
228+
if (num == 0)
229+
dev_info(dev, "only physical clock in use now\n");
230+
else
231+
dev_info(dev, "guarantee physical clock free running\n");
232+
233+
ptp->n_vclocks = num;
234+
mutex_unlock(&ptp->n_vclocks_mux);
235+
236+
return count;
237+
out:
238+
mutex_unlock(&ptp->n_vclocks_mux);
239+
return err;
240+
}
241+
static DEVICE_ATTR_RW(n_vclocks);
242+
243+
static ssize_t max_vclocks_show(struct device *dev,
244+
struct device_attribute *attr, char *page)
245+
{
246+
struct ptp_clock *ptp = dev_get_drvdata(dev);
247+
ssize_t size;
248+
249+
size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->max_vclocks);
250+
251+
return size;
252+
}
253+
254+
static ssize_t max_vclocks_store(struct device *dev,
255+
struct device_attribute *attr,
256+
const char *buf, size_t count)
257+
{
258+
struct ptp_clock *ptp = dev_get_drvdata(dev);
259+
u32 max;
260+
261+
if (kstrtou32(buf, 0, &max) || max == 0)
262+
return -EINVAL;
263+
264+
if (max == ptp->max_vclocks)
265+
return count;
266+
267+
if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
268+
return -ERESTARTSYS;
269+
270+
if (max < ptp->n_vclocks) {
271+
mutex_unlock(&ptp->n_vclocks_mux);
272+
return -EINVAL;
273+
}
274+
275+
ptp->max_vclocks = max;
276+
277+
mutex_unlock(&ptp->n_vclocks_mux);
278+
279+
return count;
280+
}
281+
static DEVICE_ATTR_RW(max_vclocks);
282+
151283
static struct attribute *ptp_attrs[] = {
152284
&dev_attr_clock_name.attr,
153285

@@ -162,6 +294,8 @@ static struct attribute *ptp_attrs[] = {
162294
&dev_attr_fifo.attr,
163295
&dev_attr_period.attr,
164296
&dev_attr_pps_enable.attr,
297+
&dev_attr_n_vclocks.attr,
298+
&dev_attr_max_vclocks.attr,
165299
NULL
166300
};
167301

@@ -183,6 +317,10 @@ static umode_t ptp_is_attribute_visible(struct kobject *kobj,
183317
} else if (attr == &dev_attr_pps_enable.attr) {
184318
if (!info->pps)
185319
mode = 0;
320+
} else if (attr == &dev_attr_n_vclocks.attr ||
321+
attr == &dev_attr_max_vclocks.attr) {
322+
if (ptp->is_virtual_clock)
323+
mode = 0;
186324
}
187325

188326
return mode;

0 commit comments

Comments
 (0)