1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * PTP 1588 clock support - User space test program
5 * Copyright (C) 2010 OMICRON electronics GmbH
8 #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
17 #include <sys/ioctl.h>
21 #include <sys/timex.h>
22 #include <sys/types.h>
26 #include <linux/ptp_clock.h>
28 #define DEVICE "/dev/ptp0"
31 #define ADJ_SETOFFSET 0x0100
35 #define CLOCK_INVALID -1
38 #define NSEC_PER_SEC 1000000000LL
40 /* clock_adjtime is not available in GLIBC < 2.14 */
41 #if !__GLIBC_PREREQ(2, 14)
42 #include <sys/syscall.h>
43 static int clock_adjtime(clockid_t id
, struct timex
*tx
)
45 return syscall(__NR_clock_adjtime
, id
, tx
);
49 static void show_flag_test(int rq_index
, unsigned int flags
, int err
)
51 printf("PTP_EXTTS_REQUEST%c flags 0x%08x : (%d) %s\n",
52 rq_index
? '1' + rq_index
: ' ',
53 flags
, err
, strerror(errno
));
54 /* sigh, uClibc ... */
58 static void do_flag_test(int fd
, unsigned int index
)
60 struct ptp_extts_request extts_request
;
61 unsigned long request
[2] = {
65 unsigned int enable_flags
[5] = {
67 PTP_ENABLE_FEATURE
| PTP_RISING_EDGE
,
68 PTP_ENABLE_FEATURE
| PTP_FALLING_EDGE
,
69 PTP_ENABLE_FEATURE
| PTP_RISING_EDGE
| PTP_FALLING_EDGE
,
70 PTP_ENABLE_FEATURE
| (PTP_EXTTS_VALID_FLAGS
+ 1),
74 memset(&extts_request
, 0, sizeof(extts_request
));
75 extts_request
.index
= index
;
77 for (i
= 0; i
< 2; i
++) {
78 for (j
= 0; j
< 5; j
++) {
79 extts_request
.flags
= enable_flags
[j
];
80 err
= ioctl(fd
, request
[i
], &extts_request
);
81 show_flag_test(i
, extts_request
.flags
, err
);
83 extts_request
.flags
= 0;
84 err
= ioctl(fd
, request
[i
], &extts_request
);
89 static clockid_t
get_clockid(int fd
)
92 return (((unsigned int) ~fd
) << 3) | CLOCKFD
;
95 static long ppb_to_scaled_ppm(int ppb
)
98 * The 'freq' field in the 'struct timex' is in parts per
99 * million, but with a 16 bit binary fractional field.
100 * Instead of calculating either one of
102 * scaled_ppm = (ppb / 1000) << 16 [1]
103 * scaled_ppm = (ppb << 16) / 1000 [2]
105 * we simply use double precision math, in order to avoid the
106 * truncation in [1] and the possible overflow in [2].
108 return (long) (ppb
* 65.536);
111 static int64_t pctns(struct ptp_clock_time
*t
)
113 return t
->sec
* 1000000000LL + t
->nsec
;
116 static void usage(char *progname
)
119 "usage: %s [options]\n"
120 " -c query the ptp clock's capabilities\n"
121 " -d name device to open\n"
122 " -e val read 'val' external time stamp events\n"
123 " -f val adjust the ptp clock frequency by 'val' ppb\n"
124 " -g get the ptp clock time\n"
125 " -h prints this message\n"
126 " -i val index for event/trigger\n"
127 " -k val measure the time offset between system and phc clock\n"
128 " for 'val' times (Maximum 25)\n"
129 " -l list the current pin configuration\n"
130 " -L pin,val configure pin index 'pin' with function 'val'\n"
131 " the channel index is taken from the '-i' option\n"
132 " 'val' specifies the auxiliary function:\n"
134 " 1 - external time stamp\n"
135 " 2 - periodic output\n"
136 " -p val enable output with a period of 'val' nanoseconds\n"
137 " -H val set output phase to 'val' nanoseconds (requires -p)\n"
138 " -w val set output pulse width to 'val' nanoseconds (requires -p)\n"
139 " -P val enable or disable (val=1|0) the system clock PPS\n"
140 " -s set the ptp clock time from the system time\n"
141 " -S set the system time from the ptp clock time\n"
142 " -t val shift the ptp clock time by 'val' seconds\n"
143 " -T val set the ptp clock time to 'val' seconds\n"
144 " -z test combinations of rising/falling external time stamp flags\n",
148 int main(int argc
, char *argv
[])
150 struct ptp_clock_caps caps
;
151 struct ptp_extts_event event
;
152 struct ptp_extts_request extts_request
;
153 struct ptp_perout_request perout_request
;
154 struct ptp_pin_desc desc
;
157 struct ptp_clock_time
*pct
;
158 struct ptp_sys_offset
*sysoff
;
164 char *device
= DEVICE
;
166 int adjfreq
= 0x7fffffff;
168 int capabilities
= 0;
176 int pin_index
= -1, pin_func
;
182 int64_t interval
, offset
;
183 int64_t perout_phase
= -1;
184 int64_t pulsewidth
= -1;
187 progname
= strrchr(argv
[0], '/');
188 progname
= progname
? 1+progname
: argv
[0];
189 while (EOF
!= (c
= getopt(argc
, argv
, "cd:e:f:ghH:i:k:lL:p:P:sSt:T:w:z"))) {
198 extts
= atoi(optarg
);
201 adjfreq
= atoi(optarg
);
207 perout_phase
= atoll(optarg
);
210 index
= atoi(optarg
);
214 n_samples
= atoi(optarg
);
220 cnt
= sscanf(optarg
, "%d,%d", &pin_index
, &pin_func
);
227 perout
= atoll(optarg
);
239 adjtime
= atoi(optarg
);
243 seconds
= atoi(optarg
);
246 pulsewidth
= atoi(optarg
);
261 fd
= open(device
, O_RDWR
);
263 fprintf(stderr
, "opening %s: %s\n", device
, strerror(errno
));
267 clkid
= get_clockid(fd
);
268 if (CLOCK_INVALID
== clkid
) {
269 fprintf(stderr
, "failed to read clock id\n");
274 if (ioctl(fd
, PTP_CLOCK_GETCAPS
, &caps
)) {
275 perror("PTP_CLOCK_GETCAPS");
277 printf("capabilities:\n"
278 " %d maximum frequency adjustment (ppb)\n"
279 " %d programmable alarms\n"
280 " %d external time stamp channels\n"
281 " %d programmable periodic signals\n"
282 " %d pulse per second\n"
283 " %d programmable pins\n"
284 " %d cross timestamping\n"
285 " %d adjust_phase\n",
292 caps
.cross_timestamping
,
297 if (0x7fffffff != adjfreq
) {
298 memset(&tx
, 0, sizeof(tx
));
299 tx
.modes
= ADJ_FREQUENCY
;
300 tx
.freq
= ppb_to_scaled_ppm(adjfreq
);
301 if (clock_adjtime(clkid
, &tx
)) {
302 perror("clock_adjtime");
304 puts("frequency adjustment okay");
309 memset(&tx
, 0, sizeof(tx
));
310 tx
.modes
= ADJ_SETOFFSET
;
311 tx
.time
.tv_sec
= adjtime
;
313 if (clock_adjtime(clkid
, &tx
) < 0) {
314 perror("clock_adjtime");
316 puts("time shift okay");
321 if (clock_gettime(clkid
, &ts
)) {
322 perror("clock_gettime");
324 printf("clock time: %ld.%09ld or %s",
325 ts
.tv_sec
, ts
.tv_nsec
, ctime(&ts
.tv_sec
));
330 clock_gettime(CLOCK_REALTIME
, &ts
);
331 if (clock_settime(clkid
, &ts
)) {
332 perror("clock_settime");
334 puts("set time okay");
339 clock_gettime(clkid
, &ts
);
340 if (clock_settime(CLOCK_REALTIME
, &ts
)) {
341 perror("clock_settime");
343 puts("set time okay");
350 if (clock_settime(clkid
, &ts
)) {
351 perror("clock_settime");
353 puts("set time okay");
358 memset(&extts_request
, 0, sizeof(extts_request
));
359 extts_request
.index
= index
;
360 extts_request
.flags
= PTP_ENABLE_FEATURE
;
361 if (ioctl(fd
, PTP_EXTTS_REQUEST
, &extts_request
)) {
362 perror("PTP_EXTTS_REQUEST");
365 puts("external time stamp request okay");
367 for (; extts
; extts
--) {
368 cnt
= read(fd
, &event
, sizeof(event
));
369 if (cnt
!= sizeof(event
)) {
373 printf("event index %u at %lld.%09u\n", event
.index
,
374 event
.t
.sec
, event
.t
.nsec
);
377 /* Disable the feature again. */
378 extts_request
.flags
= 0;
379 if (ioctl(fd
, PTP_EXTTS_REQUEST
, &extts_request
)) {
380 perror("PTP_EXTTS_REQUEST");
385 do_flag_test(fd
, index
);
390 if (ioctl(fd
, PTP_CLOCK_GETCAPS
, &caps
)) {
391 perror("PTP_CLOCK_GETCAPS");
393 n_pins
= caps
.n_pins
;
395 for (i
= 0; i
< n_pins
; i
++) {
397 if (ioctl(fd
, PTP_PIN_GETFUNC
, &desc
)) {
398 perror("PTP_PIN_GETFUNC");
401 printf("name %s index %u func %u chan %u\n",
402 desc
.name
, desc
.index
, desc
.func
, desc
.chan
);
406 if (pulsewidth
>= 0 && perout
< 0) {
407 puts("-w can only be specified together with -p");
411 if (perout_phase
>= 0 && perout
< 0) {
412 puts("-H can only be specified together with -p");
417 if (clock_gettime(clkid
, &ts
)) {
418 perror("clock_gettime");
421 memset(&perout_request
, 0, sizeof(perout_request
));
422 perout_request
.index
= index
;
423 perout_request
.period
.sec
= perout
/ NSEC_PER_SEC
;
424 perout_request
.period
.nsec
= perout
% NSEC_PER_SEC
;
425 perout_request
.flags
= 0;
426 if (pulsewidth
>= 0) {
427 perout_request
.flags
|= PTP_PEROUT_DUTY_CYCLE
;
428 perout_request
.on
.sec
= pulsewidth
/ NSEC_PER_SEC
;
429 perout_request
.on
.nsec
= pulsewidth
% NSEC_PER_SEC
;
431 if (perout_phase
>= 0) {
432 perout_request
.flags
|= PTP_PEROUT_PHASE
;
433 perout_request
.phase
.sec
= perout_phase
/ NSEC_PER_SEC
;
434 perout_request
.phase
.nsec
= perout_phase
% NSEC_PER_SEC
;
436 perout_request
.start
.sec
= ts
.tv_sec
+ 2;
437 perout_request
.start
.nsec
= 0;
440 if (ioctl(fd
, PTP_PEROUT_REQUEST2
, &perout_request
)) {
441 perror("PTP_PEROUT_REQUEST");
443 puts("periodic output request okay");
447 if (pin_index
>= 0) {
448 memset(&desc
, 0, sizeof(desc
));
449 desc
.index
= pin_index
;
450 desc
.func
= pin_func
;
452 if (ioctl(fd
, PTP_PIN_SETFUNC
, &desc
)) {
453 perror("PTP_PIN_SETFUNC");
455 puts("set pin function okay");
460 int enable
= pps
? 1 : 0;
461 if (ioctl(fd
, PTP_ENABLE_PPS
, enable
)) {
462 perror("PTP_ENABLE_PPS");
464 puts("pps for system time request okay");
469 if (n_samples
<= 0 || n_samples
> 25) {
470 puts("n_samples should be between 1 and 25");
475 sysoff
= calloc(1, sizeof(*sysoff
));
480 sysoff
->n_samples
= n_samples
;
482 if (ioctl(fd
, PTP_SYS_OFFSET
, sysoff
))
483 perror("PTP_SYS_OFFSET");
485 puts("system and phc clock time offset request okay");
487 pct
= &sysoff
->ts
[0];
488 for (i
= 0; i
< sysoff
->n_samples
; i
++) {
490 tp
= pctns(pct
+2*i
+1);
491 t2
= pctns(pct
+2*i
+2);
493 offset
= (t2
+ t1
) / 2 - tp
;
495 printf("system time: %lld.%u\n",
496 (pct
+2*i
)->sec
, (pct
+2*i
)->nsec
);
497 printf("phc time: %lld.%u\n",
498 (pct
+2*i
+1)->sec
, (pct
+2*i
+1)->nsec
);
499 printf("system time: %lld.%u\n",
500 (pct
+2*i
+2)->sec
, (pct
+2*i
+2)->nsec
);
501 printf("system/phc clock time offset is %" PRId64
" ns\n"
502 "system clock time delay is %" PRId64
" ns\n",