aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/ptp
diff options
context:
space:
mode:
authorRichard Cochran <richardcochran@gmail.com>2011-04-22 06:03:08 -0400
committerJohn Stultz <john.stultz@linaro.org>2011-05-23 16:01:00 -0400
commitd94ba80ebbea17f036cecb104398fbcd788aa742 (patch)
tree7fe40228c5ea2bb77f2892b722d27155df8c1157 /Documentation/ptp
parentcaebc160ce3f76761cc62ad96ef6d6f30f54e3dd (diff)
ptp: Added a brand new class driver for ptp clocks.
This patch adds an infrastructure for hardware clocks that implement IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a registration method to particular hardware clock drivers. Each clock is presented as a standard POSIX clock. The ancillary clock features are exposed in two different ways, via the sysfs and by a character device. Signed-off-by: Richard Cochran <richard.cochran@omicron.at> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: David S. Miller <davem@davemloft.net> Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'Documentation/ptp')
-rw-r--r--Documentation/ptp/ptp.txt89
-rw-r--r--Documentation/ptp/testptp.c381
-rw-r--r--Documentation/ptp/testptp.mk33
3 files changed, 503 insertions, 0 deletions
diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
new file mode 100644
index 000000000000..ae8fef86b832
--- /dev/null
+++ b/Documentation/ptp/ptp.txt
@@ -0,0 +1,89 @@
1
2* PTP hardware clock infrastructure for Linux
3
4 This patch set introduces support for IEEE 1588 PTP clocks in
5 Linux. Together with the SO_TIMESTAMPING socket options, this
6 presents a standardized method for developing PTP user space
7 programs, synchronizing Linux with external clocks, and using the
8 ancillary features of PTP hardware clocks.
9
10 A new class driver exports a kernel interface for specific clock
11 drivers and a user space interface. The infrastructure supports a
12 complete set of PTP hardware clock functionality.
13
14 + Basic clock operations
15 - Set time
16 - Get time
17 - Shift the clock by a given offset atomically
18 - Adjust clock frequency
19
20 + Ancillary clock features
21 - One short or periodic alarms, with signal delivery to user program
22 - Time stamp external events
23 - Period output signals configurable from user space
24 - Synchronization of the Linux system time via the PPS subsystem
25
26** PTP hardware clock kernel API
27
28 A PTP clock driver registers itself with the class driver. The
29 class driver handles all of the dealings with user space. The
30 author of a clock driver need only implement the details of
31 programming the clock hardware. The clock driver notifies the class
32 driver of asynchronous events (alarms and external time stamps) via
33 a simple message passing interface.
34
35 The class driver supports multiple PTP clock drivers. In normal use
36 cases, only one PTP clock is needed. However, for testing and
37 development, it can be useful to have more than one clock in a
38 single system, in order to allow performance comparisons.
39
40** PTP hardware clock user space API
41
42 The class driver also creates a character device for each
43 registered clock. User space can use an open file descriptor from
44 the character device as a POSIX clock id and may call
45 clock_gettime, clock_settime, and clock_adjtime. These calls
46 implement the basic clock operations.
47
48 User space programs may control the clock using standardized
49 ioctls. A program may query, enable, configure, and disable the
50 ancillary clock features. User space can receive time stamped
51 events via blocking read() and poll(). One shot and periodic
52 signals may be configured via the POSIX timer_settime() system
53 call.
54
55** Writing clock drivers
56
57 Clock drivers include include/linux/ptp_clock_kernel.h and register
58 themselves by presenting a 'struct ptp_clock_info' to the
59 registration method. Clock drivers must implement all of the
60 functions in the interface. If a clock does not offer a particular
61 ancillary feature, then the driver should just return -EOPNOTSUPP
62 from those functions.
63
64 Drivers must ensure that all of the methods in interface are
65 reentrant. Since most hardware implementations treat the time value
66 as a 64 bit integer accessed as two 32 bit registers, drivers
67 should use spin_lock_irqsave/spin_unlock_irqrestore to protect
68 against concurrent access. This locking cannot be accomplished in
69 class driver, since the lock may also be needed by the clock
70 driver's interrupt service routine.
71
72** Supported hardware
73
74 + Freescale eTSEC gianfar
75 - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
76 - 2 Alarm registers (optional interrupt)
77 - 3 Periodic signals (optional interrupt)
78
79 + National DP83640
80 - 6 GPIOs programmable as inputs or outputs
81 - 6 GPIOs with dedicated functions (LED/JTAG/clock) can also be
82 used as general inputs or outputs
83 - GPIO inputs can time stamp external triggers
84 - GPIO outputs can produce periodic signals
85 - 1 interrupt pin
86
87 + Intel IXP465
88 - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
89 - Target Time (optional interrupt)
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
new file mode 100644
index 000000000000..f59ded066108
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,381 @@
1/*
2 * PTP 1588 clock support - User space test program
3 *
4 * Copyright (C) 2010 OMICRON electronics GmbH
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20#include <errno.h>
21#include <fcntl.h>
22#include <math.h>
23#include <signal.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/ioctl.h>
28#include <sys/mman.h>
29#include <sys/stat.h>
30#include <sys/time.h>
31#include <sys/timex.h>
32#include <sys/types.h>
33#include <time.h>
34#include <unistd.h>
35
36#include <linux/ptp_clock.h>
37
38#define DEVICE "/dev/ptp0"
39
40#ifndef ADJ_SETOFFSET
41#define ADJ_SETOFFSET 0x0100
42#endif
43
44#ifndef CLOCK_INVALID
45#define CLOCK_INVALID -1
46#endif
47
48/* When glibc offers the syscall, this will go away. */
49#include <sys/syscall.h>
50static int clock_adjtime(clockid_t id, struct timex *tx)
51{
52 return syscall(__NR_clock_adjtime, id, tx);
53}
54
55static clockid_t get_clockid(int fd)
56{
57#define CLOCKFD 3
58#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
59
60 return FD_TO_CLOCKID(fd);
61}
62
63static void handle_alarm(int s)
64{
65 printf("received signal %d\n", s);
66}
67
68static int install_handler(int signum, void (*handler)(int))
69{
70 struct sigaction action;
71 sigset_t mask;
72
73 /* Unblock the signal. */
74 sigemptyset(&mask);
75 sigaddset(&mask, signum);
76 sigprocmask(SIG_UNBLOCK, &mask, NULL);
77
78 /* Install the signal handler. */
79 action.sa_handler = handler;
80 action.sa_flags = 0;
81 sigemptyset(&action.sa_mask);
82 sigaction(signum, &action, NULL);
83
84 return 0;
85}
86
87static long ppb_to_scaled_ppm(int ppb)
88{
89 /*
90 * The 'freq' field in the 'struct timex' is in parts per
91 * million, but with a 16 bit binary fractional field.
92 * Instead of calculating either one of
93 *
94 * scaled_ppm = (ppb / 1000) << 16 [1]
95 * scaled_ppm = (ppb << 16) / 1000 [2]
96 *
97 * we simply use double precision math, in order to avoid the
98 * truncation in [1] and the possible overflow in [2].
99 */
100 return (long) (ppb * 65.536);
101}
102
103static void usage(char *progname)
104{
105 fprintf(stderr,
106 "usage: %s [options]\n"
107 " -a val request a one-shot alarm after 'val' seconds\n"
108 " -A val request a periodic alarm every 'val' seconds\n"
109 " -c query the ptp clock's capabilities\n"
110 " -d name device to open\n"
111 " -e val read 'val' external time stamp events\n"
112 " -f val adjust the ptp clock frequency by 'val' ppb\n"
113 " -g get the ptp clock time\n"
114 " -h prints this message\n"
115 " -p val enable output with a period of 'val' nanoseconds\n"
116 " -P val enable or disable (val=1|0) the system clock PPS\n"
117 " -s set the ptp clock time from the system time\n"
118 " -S set the system time from the ptp clock time\n"
119 " -t val shift the ptp clock time by 'val' seconds\n",
120 progname);
121}
122
123int main(int argc, char *argv[])
124{
125 struct ptp_clock_caps caps;
126 struct ptp_extts_event event;
127 struct ptp_extts_request extts_request;
128 struct ptp_perout_request perout_request;
129 struct timespec ts;
130 struct timex tx;
131
132 static timer_t timerid;
133 struct itimerspec timeout;
134 struct sigevent sigevent;
135
136 char *progname;
137 int c, cnt, fd;
138
139 char *device = DEVICE;
140 clockid_t clkid;
141 int adjfreq = 0x7fffffff;
142 int adjtime = 0;
143 int capabilities = 0;
144 int extts = 0;
145 int gettime = 0;
146 int oneshot = 0;
147 int periodic = 0;
148 int perout = -1;
149 int pps = -1;
150 int settime = 0;
151
152 progname = strrchr(argv[0], '/');
153 progname = progname ? 1+progname : argv[0];
154 while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:sSt:v"))) {
155 switch (c) {
156 case 'a':
157 oneshot = atoi(optarg);
158 break;
159 case 'A':
160 periodic = atoi(optarg);
161 break;
162 case 'c':
163 capabilities = 1;
164 break;
165 case 'd':
166 device = optarg;
167 break;
168 case 'e':
169 extts = atoi(optarg);
170 break;
171 case 'f':
172 adjfreq = atoi(optarg);
173 break;
174 case 'g':
175 gettime = 1;
176 break;
177 case 'p':
178 perout = atoi(optarg);
179 break;
180 case 'P':
181 pps = atoi(optarg);
182 break;
183 case 's':
184 settime = 1;
185 break;
186 case 'S':
187 settime = 2;
188 break;
189 case 't':
190 adjtime = atoi(optarg);
191 break;
192 case 'h':
193 usage(progname);
194 return 0;
195 case '?':
196 default:
197 usage(progname);
198 return -1;
199 }
200 }
201
202 fd = open(device, O_RDWR);
203 if (fd < 0) {
204 fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
205 return -1;
206 }
207
208 clkid = get_clockid(fd);
209 if (CLOCK_INVALID == clkid) {
210 fprintf(stderr, "failed to read clock id\n");
211 return -1;
212 }
213
214 if (capabilities) {
215 if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
216 perror("PTP_CLOCK_GETCAPS");
217 } else {
218 printf("capabilities:\n"
219 " %d maximum frequency adjustment (ppb)\n"
220 " %d programmable alarms\n"
221 " %d external time stamp channels\n"
222 " %d programmable periodic signals\n"
223 " %d pulse per second\n",
224 caps.max_adj,
225 caps.n_alarm,
226 caps.n_ext_ts,
227 caps.n_per_out,
228 caps.pps);
229 }
230 }
231
232 if (0x7fffffff != adjfreq) {
233 memset(&tx, 0, sizeof(tx));
234 tx.modes = ADJ_FREQUENCY;
235 tx.freq = ppb_to_scaled_ppm(adjfreq);
236 if (clock_adjtime(clkid, &tx)) {
237 perror("clock_adjtime");
238 } else {
239 puts("frequency adjustment okay");
240 }
241 }
242
243 if (adjtime) {
244 memset(&tx, 0, sizeof(tx));
245 tx.modes = ADJ_SETOFFSET;
246 tx.time.tv_sec = adjtime;
247 tx.time.tv_usec = 0;
248 if (clock_adjtime(clkid, &tx) < 0) {
249 perror("clock_adjtime");
250 } else {
251 puts("time shift okay");
252 }
253 }
254
255 if (gettime) {
256 if (clock_gettime(clkid, &ts)) {
257 perror("clock_gettime");
258 } else {
259 printf("clock time: %ld.%09ld or %s",
260 ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
261 }
262 }
263
264 if (settime == 1) {
265 clock_gettime(CLOCK_REALTIME, &ts);
266 if (clock_settime(clkid, &ts)) {
267 perror("clock_settime");
268 } else {
269 puts("set time okay");
270 }
271 }
272
273 if (settime == 2) {
274 clock_gettime(clkid, &ts);
275 if (clock_settime(CLOCK_REALTIME, &ts)) {
276 perror("clock_settime");
277 } else {
278 puts("set time okay");
279 }
280 }
281
282 if (extts) {
283 memset(&extts_request, 0, sizeof(extts_request));
284 extts_request.index = 0;
285 extts_request.flags = PTP_ENABLE_FEATURE;
286 if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
287 perror("PTP_EXTTS_REQUEST");
288 extts = 0;
289 } else {
290 puts("external time stamp request okay");
291 }
292 for (; extts; extts--) {
293 cnt = read(fd, &event, sizeof(event));
294 if (cnt != sizeof(event)) {
295 perror("read");
296 break;
297 }
298 printf("event index %u at %lld.%09u\n", event.index,
299 event.t.sec, event.t.nsec);
300 fflush(stdout);
301 }
302 /* Disable the feature again. */
303 extts_request.flags = 0;
304 if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
305 perror("PTP_EXTTS_REQUEST");
306 }
307 }
308
309 if (oneshot) {
310 install_handler(SIGALRM, handle_alarm);
311 /* Create a timer. */
312 sigevent.sigev_notify = SIGEV_SIGNAL;
313 sigevent.sigev_signo = SIGALRM;
314 if (timer_create(clkid, &sigevent, &timerid)) {
315 perror("timer_create");
316 return -1;
317 }
318 /* Start the timer. */
319 memset(&timeout, 0, sizeof(timeout));
320 timeout.it_value.tv_sec = oneshot;
321 if (timer_settime(timerid, 0, &timeout, NULL)) {
322 perror("timer_settime");
323 return -1;
324 }
325 pause();
326 timer_delete(timerid);
327 }
328
329 if (periodic) {
330 install_handler(SIGALRM, handle_alarm);
331 /* Create a timer. */
332 sigevent.sigev_notify = SIGEV_SIGNAL;
333 sigevent.sigev_signo = SIGALRM;
334 if (timer_create(clkid, &sigevent, &timerid)) {
335 perror("timer_create");
336 return -1;
337 }
338 /* Start the timer. */
339 memset(&timeout, 0, sizeof(timeout));
340 timeout.it_interval.tv_sec = periodic;
341 timeout.it_value.tv_sec = periodic;
342 if (timer_settime(timerid, 0, &timeout, NULL)) {
343 perror("timer_settime");
344 return -1;
345 }
346 while (1) {
347 pause();
348 }
349 timer_delete(timerid);
350 }
351
352 if (perout >= 0) {
353 if (clock_gettime(clkid, &ts)) {
354 perror("clock_gettime");
355 return -1;
356 }
357 memset(&perout_request, 0, sizeof(perout_request));
358 perout_request.index = 0;
359 perout_request.start.sec = ts.tv_sec + 2;
360 perout_request.start.nsec = 0;
361 perout_request.period.sec = 0;
362 perout_request.period.nsec = perout;
363 if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
364 perror("PTP_PEROUT_REQUEST");
365 } else {
366 puts("periodic output request okay");
367 }
368 }
369
370 if (pps != -1) {
371 int enable = pps ? 1 : 0;
372 if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
373 perror("PTP_ENABLE_PPS");
374 } else {
375 puts("pps for system time request okay");
376 }
377 }
378
379 close(fd);
380 return 0;
381}
diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
new file mode 100644
index 000000000000..4ef2d9755421
--- /dev/null
+++ b/Documentation/ptp/testptp.mk
@@ -0,0 +1,33 @@
1# PTP 1588 clock support - User space test program
2#
3# Copyright (C) 2010 OMICRON electronics GmbH
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19CC = $(CROSS_COMPILE)gcc
20INC = -I$(KBUILD_OUTPUT)/usr/include
21CFLAGS = -Wall $(INC)
22LDLIBS = -lrt
23PROGS = testptp
24
25all: $(PROGS)
26
27testptp: testptp.o
28
29clean:
30 rm -f testptp.o
31
32distclean: clean
33 rm -f $(PROGS)