aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/ABI/testing/sysfs-ptp98
-rw-r--r--Documentation/ptp/ptp.txt89
-rw-r--r--Documentation/ptp/testptp.c381
-rw-r--r--Documentation/ptp/testptp.mk33
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/ptp/Kconfig30
-rw-r--r--drivers/ptp/Makefile6
-rw-r--r--drivers/ptp/ptp_chardev.c159
-rw-r--r--drivers/ptp/ptp_clock.c343
-rw-r--r--drivers/ptp/ptp_private.h92
-rw-r--r--drivers/ptp/ptp_sysfs.c230
-rw-r--r--include/linux/Kbuild1
-rw-r--r--include/linux/ptp_classify.h7
-rw-r--r--include/linux/ptp_clock.h84
-rw-r--r--include/linux/ptp_clock_kernel.h139
16 files changed, 1695 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
new file mode 100644
index 000000000000..d40d2b550502
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -0,0 +1,98 @@
1What: /sys/class/ptp/
2Date: September 2010
3Contact: Richard Cochran <richardcochran@gmail.com>
4Description:
5 This directory contains files and directories
6 providing a standardized interface to the ancillary
7 features of PTP hardware clocks.
8
9What: /sys/class/ptp/ptpN/
10Date: September 2010
11Contact: Richard Cochran <richardcochran@gmail.com>
12Description:
13 This directory contains the attributes of the Nth PTP
14 hardware clock registered into the PTP class driver
15 subsystem.
16
17What: /sys/class/ptp/ptpN/clock_name
18Date: September 2010
19Contact: Richard Cochran <richardcochran@gmail.com>
20Description:
21 This file contains the name of the PTP hardware clock
22 as a human readable string.
23
24What: /sys/class/ptp/ptpN/max_adjustment
25Date: September 2010
26Contact: Richard Cochran <richardcochran@gmail.com>
27Description:
28 This file contains the PTP hardware clock's maximum
29 frequency adjustment value (a positive integer) in
30 parts per billion.
31
32What: /sys/class/ptp/ptpN/n_alarms
33Date: September 2010
34Contact: Richard Cochran <richardcochran@gmail.com>
35Description:
36 This file contains the number of periodic or one shot
37 alarms offer by the PTP hardware clock.
38
39What: /sys/class/ptp/ptpN/n_external_timestamps
40Date: September 2010
41Contact: Richard Cochran <richardcochran@gmail.com>
42Description:
43 This file contains the number of external timestamp
44 channels offered by the PTP hardware clock.
45
46What: /sys/class/ptp/ptpN/n_periodic_outputs
47Date: September 2010
48Contact: Richard Cochran <richardcochran@gmail.com>
49Description:
50 This file contains the number of programmable periodic
51 output channels offered by the PTP hardware clock.
52
53What: /sys/class/ptp/ptpN/pps_avaiable
54Date: September 2010
55Contact: Richard Cochran <richardcochran@gmail.com>
56Description:
57 This file indicates whether the PTP hardware clock
58 supports a Pulse Per Second to the host CPU. Reading
59 "1" means that the PPS is supported, while "0" means
60 not supported.
61
62What: /sys/class/ptp/ptpN/extts_enable
63Date: September 2010
64Contact: Richard Cochran <richardcochran@gmail.com>
65Description:
66 This write-only file enables or disables external
67 timestamps. To enable external timestamps, write the
68 channel index followed by a "1" into the file.
69 To disable external timestamps, write the channel
70 index followed by a "0" into the file.
71
72What: /sys/class/ptp/ptpN/fifo
73Date: September 2010
74Contact: Richard Cochran <richardcochran@gmail.com>
75Description:
76 This file provides timestamps on external events, in
77 the form of three integers: channel index, seconds,
78 and nanoseconds.
79
80What: /sys/class/ptp/ptpN/period
81Date: September 2010
82Contact: Richard Cochran <richardcochran@gmail.com>
83Description:
84 This write-only file enables or disables periodic
85 outputs. To enable a periodic output, write five
86 integers into the file: channel index, start time
87 seconds, start time nanoseconds, period seconds, and
88 period nanoseconds. To disable a periodic output, set
89 all the seconds and nanoseconds values to zero.
90
91What: /sys/class/ptp/ptpN/pps_enable
92Date: September 2010
93Contact: Richard Cochran <richardcochran@gmail.com>
94Description:
95 This write-only file enables or disables delivery of
96 PPS events to the Linux PPS subsystem. To enable PPS
97 events, write a "1" into the file. To disable events,
98 write a "0" into the file.
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)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 61631edfecc2..3bb154d8c8cc 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
54 54
55source "drivers/pps/Kconfig" 55source "drivers/pps/Kconfig"
56 56
57source "drivers/ptp/Kconfig"
58
57source "drivers/gpio/Kconfig" 59source "drivers/gpio/Kconfig"
58 60
59source "drivers/w1/Kconfig" 61source "drivers/w1/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index a29527f4ded6..3c2960128a7f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_I2O) += message/
76obj-$(CONFIG_RTC_LIB) += rtc/ 76obj-$(CONFIG_RTC_LIB) += rtc/
77obj-y += i2c/ media/ 77obj-y += i2c/ media/
78obj-$(CONFIG_PPS) += pps/ 78obj-$(CONFIG_PPS) += pps/
79obj-$(CONFIG_PTP_1588_CLOCK) += ptp/
79obj-$(CONFIG_W1) += w1/ 80obj-$(CONFIG_W1) += w1/
80obj-$(CONFIG_POWER_SUPPLY) += power/ 81obj-$(CONFIG_POWER_SUPPLY) += power/
81obj-$(CONFIG_HWMON) += hwmon/ 82obj-$(CONFIG_HWMON) += hwmon/
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
new file mode 100644
index 000000000000..70d4bb1cbdab
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,30 @@
1#
2# PTP clock support configuration
3#
4
5menu "PTP clock support"
6
7comment "Enable Device Drivers -> PPS to see the PTP clock options."
8 depends on PPS=n
9
10config PTP_1588_CLOCK
11 tristate "PTP clock support"
12 depends on EXPERIMENTAL
13 depends on PPS
14 help
15 The IEEE 1588 standard defines a method to precisely
16 synchronize distributed clocks over Ethernet networks. The
17 standard defines a Precision Time Protocol (PTP), which can
18 be used to achieve synchronization within a few dozen
19 microseconds. In addition, with the help of special hardware
20 time stamping units, it can be possible to achieve
21 synchronization to within a few hundred nanoseconds.
22
23 This driver adds support for PTP clocks as character
24 devices. If you want to use a PTP clock, then you should
25 also enable at least one clock driver as well.
26
27 To compile this driver as a module, choose M here: the module
28 will be called ptp.
29
30endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
new file mode 100644
index 000000000000..480e2afdc999
--- /dev/null
+++ b/drivers/ptp/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for PTP 1588 clock support.
3#
4
5ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
6obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
new file mode 100644
index 000000000000..a8d03aeb4051
--- /dev/null
+++ b/drivers/ptp/ptp_chardev.c
@@ -0,0 +1,159 @@
1/*
2 * PTP 1588 clock support - character device implementation.
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 <linux/module.h>
21#include <linux/posix-clock.h>
22#include <linux/poll.h>
23#include <linux/sched.h>
24
25#include "ptp_private.h"
26
27int ptp_open(struct posix_clock *pc, fmode_t fmode)
28{
29 return 0;
30}
31
32long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
33{
34 struct ptp_clock_caps caps;
35 struct ptp_clock_request req;
36 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
37 struct ptp_clock_info *ops = ptp->info;
38 int enable, err = 0;
39
40 switch (cmd) {
41
42 case PTP_CLOCK_GETCAPS:
43 memset(&caps, 0, sizeof(caps));
44 caps.max_adj = ptp->info->max_adj;
45 caps.n_alarm = ptp->info->n_alarm;
46 caps.n_ext_ts = ptp->info->n_ext_ts;
47 caps.n_per_out = ptp->info->n_per_out;
48 caps.pps = ptp->info->pps;
49 err = copy_to_user((void __user *)arg, &caps, sizeof(caps));
50 break;
51
52 case PTP_EXTTS_REQUEST:
53 if (copy_from_user(&req.extts, (void __user *)arg,
54 sizeof(req.extts))) {
55 err = -EFAULT;
56 break;
57 }
58 if (req.extts.index >= ops->n_ext_ts) {
59 err = -EINVAL;
60 break;
61 }
62 req.type = PTP_CLK_REQ_EXTTS;
63 enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
64 err = ops->enable(ops, &req, enable);
65 break;
66
67 case PTP_PEROUT_REQUEST:
68 if (copy_from_user(&req.perout, (void __user *)arg,
69 sizeof(req.perout))) {
70 err = -EFAULT;
71 break;
72 }
73 if (req.perout.index >= ops->n_per_out) {
74 err = -EINVAL;
75 break;
76 }
77 req.type = PTP_CLK_REQ_PEROUT;
78 enable = req.perout.period.sec || req.perout.period.nsec;
79 err = ops->enable(ops, &req, enable);
80 break;
81
82 case PTP_ENABLE_PPS:
83 if (!capable(CAP_SYS_TIME))
84 return -EPERM;
85 req.type = PTP_CLK_REQ_PPS;
86 enable = arg ? 1 : 0;
87 err = ops->enable(ops, &req, enable);
88 break;
89
90 default:
91 err = -ENOTTY;
92 break;
93 }
94 return err;
95}
96
97unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
98{
99 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
100
101 poll_wait(fp, &ptp->tsev_wq, wait);
102
103 return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
104}
105
106ssize_t ptp_read(struct posix_clock *pc,
107 uint rdflags, char __user *buf, size_t cnt)
108{
109 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
110 struct timestamp_event_queue *queue = &ptp->tsevq;
111 struct ptp_extts_event event[PTP_BUF_TIMESTAMPS];
112 unsigned long flags;
113 size_t qcnt, i;
114
115 if (cnt % sizeof(struct ptp_extts_event) != 0)
116 return -EINVAL;
117
118 if (cnt > sizeof(event))
119 cnt = sizeof(event);
120
121 cnt = cnt / sizeof(struct ptp_extts_event);
122
123 if (mutex_lock_interruptible(&ptp->tsevq_mux))
124 return -ERESTARTSYS;
125
126 if (wait_event_interruptible(ptp->tsev_wq,
127 ptp->defunct || queue_cnt(queue))) {
128 mutex_unlock(&ptp->tsevq_mux);
129 return -ERESTARTSYS;
130 }
131
132 if (ptp->defunct)
133 return -ENODEV;
134
135 spin_lock_irqsave(&queue->lock, flags);
136
137 qcnt = queue_cnt(queue);
138
139 if (cnt > qcnt)
140 cnt = qcnt;
141
142 for (i = 0; i < cnt; i++) {
143 event[i] = queue->buf[queue->head];
144 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
145 }
146
147 spin_unlock_irqrestore(&queue->lock, flags);
148
149 cnt = cnt * sizeof(struct ptp_extts_event);
150
151 mutex_unlock(&ptp->tsevq_mux);
152
153 if (copy_to_user(buf, event, cnt)) {
154 mutex_unlock(&ptp->tsevq_mux);
155 return -EFAULT;
156 }
157
158 return cnt;
159}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 000000000000..cf3f9997546d
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,343 @@
1/*
2 * PTP 1588 clock support
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 <linux/bitops.h>
21#include <linux/device.h>
22#include <linux/err.h>
23#include <linux/init.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/posix-clock.h>
27#include <linux/pps_kernel.h>
28#include <linux/slab.h>
29#include <linux/syscalls.h>
30#include <linux/uaccess.h>
31
32#include "ptp_private.h"
33
34#define PTP_MAX_ALARMS 4
35#define PTP_MAX_CLOCKS 8
36#define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
37#define PTP_PPS_EVENT PPS_CAPTUREASSERT
38#define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
39
40/* private globals */
41
42static dev_t ptp_devt;
43static struct class *ptp_class;
44
45static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS);
46static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */
47
48/* time stamp event queue operations */
49
50static inline int queue_free(struct timestamp_event_queue *q)
51{
52 return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
53}
54
55static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
56 struct ptp_clock_event *src)
57{
58 struct ptp_extts_event *dst;
59 unsigned long flags;
60 s64 seconds;
61 u32 remainder;
62
63 seconds = div_u64_rem(src->timestamp, 1000000000, &remainder);
64
65 spin_lock_irqsave(&queue->lock, flags);
66
67 dst = &queue->buf[queue->tail];
68 dst->index = src->index;
69 dst->t.sec = seconds;
70 dst->t.nsec = remainder;
71
72 if (!queue_free(queue))
73 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
74
75 queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
76
77 spin_unlock_irqrestore(&queue->lock, flags);
78}
79
80static s32 scaled_ppm_to_ppb(long ppm)
81{
82 /*
83 * The 'freq' field in the 'struct timex' is in parts per
84 * million, but with a 16 bit binary fractional field.
85 *
86 * We want to calculate
87 *
88 * ppb = scaled_ppm * 1000 / 2^16
89 *
90 * which simplifies to
91 *
92 * ppb = scaled_ppm * 125 / 2^13
93 */
94 s64 ppb = 1 + ppm;
95 ppb *= 125;
96 ppb >>= 13;
97 return (s32) ppb;
98}
99
100/* posix clock implementation */
101
102static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp)
103{
104 return 1; /* always round timer functions to one nanosecond */
105}
106
107static int ptp_clock_settime(struct posix_clock *pc, const struct timespec *tp)
108{
109 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
110 return ptp->info->settime(ptp->info, tp);
111}
112
113static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp)
114{
115 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
116 return ptp->info->gettime(ptp->info, tp);
117}
118
119static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx)
120{
121 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
122 struct ptp_clock_info *ops;
123 int err = -EOPNOTSUPP;
124
125 ops = ptp->info;
126
127 if (tx->modes & ADJ_SETOFFSET) {
128 struct timespec ts;
129 ktime_t kt;
130 s64 delta;
131
132 ts.tv_sec = tx->time.tv_sec;
133 ts.tv_nsec = tx->time.tv_usec;
134
135 if (!(tx->modes & ADJ_NANO))
136 ts.tv_nsec *= 1000;
137
138 if ((unsigned long) ts.tv_nsec >= NSEC_PER_SEC)
139 return -EINVAL;
140
141 kt = timespec_to_ktime(ts);
142 delta = ktime_to_ns(kt);
143 err = ops->adjtime(ops, delta);
144
145 } else if (tx->modes & ADJ_FREQUENCY) {
146
147 err = ops->adjfreq(ops, scaled_ppm_to_ppb(tx->freq));
148 }
149
150 return err;
151}
152
153static struct posix_clock_operations ptp_clock_ops = {
154 .owner = THIS_MODULE,
155 .clock_adjtime = ptp_clock_adjtime,
156 .clock_gettime = ptp_clock_gettime,
157 .clock_getres = ptp_clock_getres,
158 .clock_settime = ptp_clock_settime,
159 .ioctl = ptp_ioctl,
160 .open = ptp_open,
161 .poll = ptp_poll,
162 .read = ptp_read,
163};
164
165static void delete_ptp_clock(struct posix_clock *pc)
166{
167 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
168
169 mutex_destroy(&ptp->tsevq_mux);
170
171 /* Remove the clock from the bit map. */
172 mutex_lock(&ptp_clocks_mutex);
173 clear_bit(ptp->index, ptp_clocks_map);
174 mutex_unlock(&ptp_clocks_mutex);
175
176 kfree(ptp);
177}
178
179/* public interface */
180
181struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
182{
183 struct ptp_clock *ptp;
184 int err = 0, index, major = MAJOR(ptp_devt);
185
186 if (info->n_alarm > PTP_MAX_ALARMS)
187 return ERR_PTR(-EINVAL);
188
189 /* Find a free clock slot and reserve it. */
190 err = -EBUSY;
191 mutex_lock(&ptp_clocks_mutex);
192 index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS);
193 if (index < PTP_MAX_CLOCKS)
194 set_bit(index, ptp_clocks_map);
195 else
196 goto no_slot;
197
198 /* Initialize a clock structure. */
199 err = -ENOMEM;
200 ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
201 if (ptp == NULL)
202 goto no_memory;
203
204 ptp->clock.ops = ptp_clock_ops;
205 ptp->clock.release = delete_ptp_clock;
206 ptp->info = info;
207 ptp->devid = MKDEV(major, index);
208 ptp->index = index;
209 spin_lock_init(&ptp->tsevq.lock);
210 mutex_init(&ptp->tsevq_mux);
211 init_waitqueue_head(&ptp->tsev_wq);
212
213 /* Create a new device in our class. */
214 ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
215 "ptp%d", ptp->index);
216 if (IS_ERR(ptp->dev))
217 goto no_device;
218
219 dev_set_drvdata(ptp->dev, ptp);
220
221 err = ptp_populate_sysfs(ptp);
222 if (err)
223 goto no_sysfs;
224
225 /* Register a new PPS source. */
226 if (info->pps) {
227 struct pps_source_info pps;
228 memset(&pps, 0, sizeof(pps));
229 snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
230 pps.mode = PTP_PPS_MODE;
231 pps.owner = info->owner;
232 ptp->pps_source = pps_register_source(&pps, PTP_PPS_DEFAULTS);
233 if (!ptp->pps_source) {
234 pr_err("failed to register pps source\n");
235 goto no_pps;
236 }
237 }
238
239 /* Create a posix clock. */
240 err = posix_clock_register(&ptp->clock, ptp->devid);
241 if (err) {
242 pr_err("failed to create posix clock\n");
243 goto no_clock;
244 }
245
246 mutex_unlock(&ptp_clocks_mutex);
247 return ptp;
248
249no_clock:
250 if (ptp->pps_source)
251 pps_unregister_source(ptp->pps_source);
252no_pps:
253 ptp_cleanup_sysfs(ptp);
254no_sysfs:
255 device_destroy(ptp_class, ptp->devid);
256no_device:
257 mutex_destroy(&ptp->tsevq_mux);
258 kfree(ptp);
259no_memory:
260 clear_bit(index, ptp_clocks_map);
261no_slot:
262 mutex_unlock(&ptp_clocks_mutex);
263 return ERR_PTR(err);
264}
265EXPORT_SYMBOL(ptp_clock_register);
266
267int ptp_clock_unregister(struct ptp_clock *ptp)
268{
269 ptp->defunct = 1;
270 wake_up_interruptible(&ptp->tsev_wq);
271
272 /* Release the clock's resources. */
273 if (ptp->pps_source)
274 pps_unregister_source(ptp->pps_source);
275 ptp_cleanup_sysfs(ptp);
276 device_destroy(ptp_class, ptp->devid);
277
278 posix_clock_unregister(&ptp->clock);
279 return 0;
280}
281EXPORT_SYMBOL(ptp_clock_unregister);
282
283void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
284{
285 struct pps_event_time evt;
286
287 switch (event->type) {
288
289 case PTP_CLOCK_ALARM:
290 break;
291
292 case PTP_CLOCK_EXTTS:
293 enqueue_external_timestamp(&ptp->tsevq, event);
294 wake_up_interruptible(&ptp->tsev_wq);
295 break;
296
297 case PTP_CLOCK_PPS:
298 pps_get_ts(&evt);
299 pps_event(ptp->pps_source, &evt, PTP_PPS_EVENT, NULL);
300 break;
301 }
302}
303EXPORT_SYMBOL(ptp_clock_event);
304
305/* module operations */
306
307static void __exit ptp_exit(void)
308{
309 class_destroy(ptp_class);
310 unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
311}
312
313static int __init ptp_init(void)
314{
315 int err;
316
317 ptp_class = class_create(THIS_MODULE, "ptp");
318 if (IS_ERR(ptp_class)) {
319 pr_err("ptp: failed to allocate class\n");
320 return PTR_ERR(ptp_class);
321 }
322
323 err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
324 if (err < 0) {
325 pr_err("ptp: failed to allocate device region\n");
326 goto no_region;
327 }
328
329 ptp_class->dev_attrs = ptp_dev_attrs;
330 pr_info("PTP clock support registered\n");
331 return 0;
332
333no_region:
334 class_destroy(ptp_class);
335 return err;
336}
337
338subsys_initcall(ptp_init);
339module_exit(ptp_exit);
340
341MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
342MODULE_DESCRIPTION("PTP clocks support");
343MODULE_LICENSE("GPL");
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
new file mode 100644
index 000000000000..4d5b5082c3b1
--- /dev/null
+++ b/drivers/ptp/ptp_private.h
@@ -0,0 +1,92 @@
1/*
2 * PTP 1588 clock support - private declarations for the core module.
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#ifndef _PTP_PRIVATE_H_
21#define _PTP_PRIVATE_H_
22
23#include <linux/cdev.h>
24#include <linux/device.h>
25#include <linux/mutex.h>
26#include <linux/posix-clock.h>
27#include <linux/ptp_clock.h>
28#include <linux/ptp_clock_kernel.h>
29#include <linux/time.h>
30
31#define PTP_MAX_TIMESTAMPS 128
32#define PTP_BUF_TIMESTAMPS 30
33
34struct timestamp_event_queue {
35 struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
36 int head;
37 int tail;
38 spinlock_t lock;
39};
40
41struct ptp_clock {
42 struct posix_clock clock;
43 struct device *dev;
44 struct ptp_clock_info *info;
45 dev_t devid;
46 int index; /* index into clocks.map */
47 struct pps_device *pps_source;
48 struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
49 struct mutex tsevq_mux; /* one process at a time reading the fifo */
50 wait_queue_head_t tsev_wq;
51 int defunct; /* tells readers to go away when clock is being removed */
52};
53
54/*
55 * The function queue_cnt() is safe for readers to call without
56 * holding q->lock. Readers use this function to verify that the queue
57 * is nonempty before proceeding with a dequeue operation. The fact
58 * that a writer might concurrently increment the tail does not
59 * matter, since the queue remains nonempty nonetheless.
60 */
61static inline int queue_cnt(struct timestamp_event_queue *q)
62{
63 int cnt = q->tail - q->head;
64 return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
65}
66
67/*
68 * see ptp_chardev.c
69 */
70
71long ptp_ioctl(struct posix_clock *pc,
72 unsigned int cmd, unsigned long arg);
73
74int ptp_open(struct posix_clock *pc, fmode_t fmode);
75
76ssize_t ptp_read(struct posix_clock *pc,
77 uint flags, char __user *buf, size_t cnt);
78
79uint ptp_poll(struct posix_clock *pc,
80 struct file *fp, poll_table *wait);
81
82/*
83 * see ptp_sysfs.c
84 */
85
86extern struct device_attribute ptp_dev_attrs[];
87
88int ptp_cleanup_sysfs(struct ptp_clock *ptp);
89
90int ptp_populate_sysfs(struct ptp_clock *ptp);
91
92#endif
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
new file mode 100644
index 000000000000..2f93926ac976
--- /dev/null
+++ b/drivers/ptp/ptp_sysfs.c
@@ -0,0 +1,230 @@
1/*
2 * PTP 1588 clock support - sysfs interface.
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 <linux/capability.h>
21
22#include "ptp_private.h"
23
24static ssize_t clock_name_show(struct device *dev,
25 struct device_attribute *attr, char *page)
26{
27 struct ptp_clock *ptp = dev_get_drvdata(dev);
28 return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name);
29}
30
31#define PTP_SHOW_INT(name) \
32static ssize_t name##_show(struct device *dev, \
33 struct device_attribute *attr, char *page) \
34{ \
35 struct ptp_clock *ptp = dev_get_drvdata(dev); \
36 return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->name); \
37}
38
39PTP_SHOW_INT(max_adj);
40PTP_SHOW_INT(n_alarm);
41PTP_SHOW_INT(n_ext_ts);
42PTP_SHOW_INT(n_per_out);
43PTP_SHOW_INT(pps);
44
45#define PTP_RO_ATTR(_var, _name) { \
46 .attr = { .name = __stringify(_name), .mode = 0444 }, \
47 .show = _var##_show, \
48}
49
50struct device_attribute ptp_dev_attrs[] = {
51 PTP_RO_ATTR(clock_name, clock_name),
52 PTP_RO_ATTR(max_adj, max_adjustment),
53 PTP_RO_ATTR(n_alarm, n_alarms),
54 PTP_RO_ATTR(n_ext_ts, n_external_timestamps),
55 PTP_RO_ATTR(n_per_out, n_periodic_outputs),
56 PTP_RO_ATTR(pps, pps_available),
57 __ATTR_NULL,
58};
59
60static ssize_t extts_enable_store(struct device *dev,
61 struct device_attribute *attr,
62 const char *buf, size_t count)
63{
64 struct ptp_clock *ptp = dev_get_drvdata(dev);
65 struct ptp_clock_info *ops = ptp->info;
66 struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS };
67 int cnt, enable;
68 int err = -EINVAL;
69
70 cnt = sscanf(buf, "%u %d", &req.extts.index, &enable);
71 if (cnt != 2)
72 goto out;
73 if (req.extts.index >= ops->n_ext_ts)
74 goto out;
75
76 err = ops->enable(ops, &req, enable ? 1 : 0);
77 if (err)
78 goto out;
79
80 return count;
81out:
82 return err;
83}
84
85static ssize_t extts_fifo_show(struct device *dev,
86 struct device_attribute *attr, char *page)
87{
88 struct ptp_clock *ptp = dev_get_drvdata(dev);
89 struct timestamp_event_queue *queue = &ptp->tsevq;
90 struct ptp_extts_event event;
91 unsigned long flags;
92 size_t qcnt;
93 int cnt = 0;
94
95 memset(&event, 0, sizeof(event));
96
97 if (mutex_lock_interruptible(&ptp->tsevq_mux))
98 return -ERESTARTSYS;
99
100 spin_lock_irqsave(&queue->lock, flags);
101 qcnt = queue_cnt(queue);
102 if (qcnt) {
103 event = queue->buf[queue->head];
104 queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
105 }
106 spin_unlock_irqrestore(&queue->lock, flags);
107
108 if (!qcnt)
109 goto out;
110
111 cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n",
112 event.index, event.t.sec, event.t.nsec);
113out:
114 mutex_unlock(&ptp->tsevq_mux);
115 return cnt;
116}
117
118static ssize_t period_store(struct device *dev,
119 struct device_attribute *attr,
120 const char *buf, size_t count)
121{
122 struct ptp_clock *ptp = dev_get_drvdata(dev);
123 struct ptp_clock_info *ops = ptp->info;
124 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT };
125 int cnt, enable, err = -EINVAL;
126
127 cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index,
128 &req.perout.start.sec, &req.perout.start.nsec,
129 &req.perout.period.sec, &req.perout.period.nsec);
130 if (cnt != 5)
131 goto out;
132 if (req.perout.index >= ops->n_per_out)
133 goto out;
134
135 enable = req.perout.period.sec || req.perout.period.nsec;
136 err = ops->enable(ops, &req, enable);
137 if (err)
138 goto out;
139
140 return count;
141out:
142 return err;
143}
144
145static ssize_t pps_enable_store(struct device *dev,
146 struct device_attribute *attr,
147 const char *buf, size_t count)
148{
149 struct ptp_clock *ptp = dev_get_drvdata(dev);
150 struct ptp_clock_info *ops = ptp->info;
151 struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS };
152 int cnt, enable;
153 int err = -EINVAL;
154
155 if (!capable(CAP_SYS_TIME))
156 return -EPERM;
157
158 cnt = sscanf(buf, "%d", &enable);
159 if (cnt != 1)
160 goto out;
161
162 err = ops->enable(ops, &req, enable ? 1 : 0);
163 if (err)
164 goto out;
165
166 return count;
167out:
168 return err;
169}
170
171static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
172static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL);
173static DEVICE_ATTR(period, 0220, NULL, period_store);
174static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);
175
176int ptp_cleanup_sysfs(struct ptp_clock *ptp)
177{
178 struct device *dev = ptp->dev;
179 struct ptp_clock_info *info = ptp->info;
180
181 if (info->n_ext_ts) {
182 device_remove_file(dev, &dev_attr_extts_enable);
183 device_remove_file(dev, &dev_attr_fifo);
184 }
185 if (info->n_per_out)
186 device_remove_file(dev, &dev_attr_period);
187
188 if (info->pps)
189 device_remove_file(dev, &dev_attr_pps_enable);
190
191 return 0;
192}
193
194int ptp_populate_sysfs(struct ptp_clock *ptp)
195{
196 struct device *dev = ptp->dev;
197 struct ptp_clock_info *info = ptp->info;
198 int err;
199
200 if (info->n_ext_ts) {
201 err = device_create_file(dev, &dev_attr_extts_enable);
202 if (err)
203 goto out1;
204 err = device_create_file(dev, &dev_attr_fifo);
205 if (err)
206 goto out2;
207 }
208 if (info->n_per_out) {
209 err = device_create_file(dev, &dev_attr_period);
210 if (err)
211 goto out3;
212 }
213 if (info->pps) {
214 err = device_create_file(dev, &dev_attr_pps_enable);
215 if (err)
216 goto out4;
217 }
218 return 0;
219out4:
220 if (info->n_per_out)
221 device_remove_file(dev, &dev_attr_period);
222out3:
223 if (info->n_ext_ts)
224 device_remove_file(dev, &dev_attr_fifo);
225out2:
226 if (info->n_ext_ts)
227 device_remove_file(dev, &dev_attr_extts_enable);
228out1:
229 return err;
230}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 75cf611641e6..4585836ba664 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -302,6 +302,7 @@ header-y += ppp-comp.h
302header-y += ppp_defs.h 302header-y += ppp_defs.h
303header-y += pps.h 303header-y += pps.h
304header-y += prctl.h 304header-y += prctl.h
305header-y += ptp_clock.h
305header-y += ptrace.h 306header-y += ptrace.h
306header-y += qnx4_fs.h 307header-y += qnx4_fs.h
307header-y += qnxtypes.h 308header-y += qnxtypes.h
diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
index 943a85ab0020..e07e2742a865 100644
--- a/include/linux/ptp_classify.h
+++ b/include/linux/ptp_classify.h
@@ -25,6 +25,7 @@
25 25
26#include <linux/if_ether.h> 26#include <linux/if_ether.h>
27#include <linux/if_vlan.h> 27#include <linux/if_vlan.h>
28#include <linux/ip.h>
28#include <linux/filter.h> 29#include <linux/filter.h>
29#ifdef __KERNEL__ 30#ifdef __KERNEL__
30#include <linux/in.h> 31#include <linux/in.h>
@@ -58,6 +59,12 @@
58#define OFF_NEXT 6 59#define OFF_NEXT 6
59#define OFF_UDP_DST 2 60#define OFF_UDP_DST 2
60 61
62#define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */
63#define OFF_PTP_SEQUENCE_ID 30
64#define OFF_PTP_CONTROL 32 /* PTPv1 only */
65
66#define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2)
67
61#define IP6_HLEN 40 68#define IP6_HLEN 40
62#define UDP_HLEN 8 69#define UDP_HLEN 8
63 70
diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
new file mode 100644
index 000000000000..94e981f810a2
--- /dev/null
+++ b/include/linux/ptp_clock.h
@@ -0,0 +1,84 @@
1/*
2 * PTP 1588 clock support - user space interface
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
21#ifndef _PTP_CLOCK_H_
22#define _PTP_CLOCK_H_
23
24#include <linux/ioctl.h>
25#include <linux/types.h>
26
27/* PTP_xxx bits, for the flags field within the request structures. */
28#define PTP_ENABLE_FEATURE (1<<0)
29#define PTP_RISING_EDGE (1<<1)
30#define PTP_FALLING_EDGE (1<<2)
31
32/*
33 * struct ptp_clock_time - represents a time value
34 *
35 * The sign of the seconds field applies to the whole value. The
36 * nanoseconds field is always unsigned. The reserved field is
37 * included for sub-nanosecond resolution, should the demand for
38 * this ever appear.
39 *
40 */
41struct ptp_clock_time {
42 __s64 sec; /* seconds */
43 __u32 nsec; /* nanoseconds */
44 __u32 reserved;
45};
46
47struct ptp_clock_caps {
48 int max_adj; /* Maximum frequency adjustment in parts per billon. */
49 int n_alarm; /* Number of programmable alarms. */
50 int n_ext_ts; /* Number of external time stamp channels. */
51 int n_per_out; /* Number of programmable periodic signals. */
52 int pps; /* Whether the clock supports a PPS callback. */
53 int rsv[15]; /* Reserved for future use. */
54};
55
56struct ptp_extts_request {
57 unsigned int index; /* Which channel to configure. */
58 unsigned int flags; /* Bit field for PTP_xxx flags. */
59 unsigned int rsv[2]; /* Reserved for future use. */
60};
61
62struct ptp_perout_request {
63 struct ptp_clock_time start; /* Absolute start time. */
64 struct ptp_clock_time period; /* Desired period, zero means disable. */
65 unsigned int index; /* Which channel to configure. */
66 unsigned int flags; /* Reserved for future use. */
67 unsigned int rsv[4]; /* Reserved for future use. */
68};
69
70#define PTP_CLK_MAGIC '='
71
72#define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
73#define PTP_EXTTS_REQUEST _IOW(PTP_CLK_MAGIC, 2, struct ptp_extts_request)
74#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
75#define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int)
76
77struct ptp_extts_event {
78 struct ptp_clock_time t; /* Time event occured. */
79 unsigned int index; /* Which channel produced the event. */
80 unsigned int flags; /* Reserved for future use. */
81 unsigned int rsv[2]; /* Reserved for future use. */
82};
83
84#endif
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
new file mode 100644
index 000000000000..dd2e44fba63e
--- /dev/null
+++ b/include/linux/ptp_clock_kernel.h
@@ -0,0 +1,139 @@
1/*
2 * PTP 1588 clock support
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
21#ifndef _PTP_CLOCK_KERNEL_H_
22#define _PTP_CLOCK_KERNEL_H_
23
24#include <linux/ptp_clock.h>
25
26
27struct ptp_clock_request {
28 enum {
29 PTP_CLK_REQ_EXTTS,
30 PTP_CLK_REQ_PEROUT,
31 PTP_CLK_REQ_PPS,
32 } type;
33 union {
34 struct ptp_extts_request extts;
35 struct ptp_perout_request perout;
36 };
37};
38
39/**
40 * struct ptp_clock_info - decribes a PTP hardware clock
41 *
42 * @owner: The clock driver should set to THIS_MODULE.
43 * @name: A short name to identify the clock.
44 * @max_adj: The maximum possible frequency adjustment, in parts per billon.
45 * @n_alarm: The number of programmable alarms.
46 * @n_ext_ts: The number of external time stamp channels.
47 * @n_per_out: The number of programmable periodic signals.
48 * @pps: Indicates whether the clock supports a PPS callback.
49 *
50 * clock operations
51 *
52 * @adjfreq: Adjusts the frequency of the hardware clock.
53 * parameter delta: Desired period change in parts per billion.
54 *
55 * @adjtime: Shifts the time of the hardware clock.
56 * parameter delta: Desired change in nanoseconds.
57 *
58 * @gettime: Reads the current time from the hardware clock.
59 * parameter ts: Holds the result.
60 *
61 * @settime: Set the current time on the hardware clock.
62 * parameter ts: Time value to set.
63 *
64 * @enable: Request driver to enable or disable an ancillary feature.
65 * parameter request: Desired resource to enable or disable.
66 * parameter on: Caller passes one to enable or zero to disable.
67 *
68 * Drivers should embed their ptp_clock_info within a private
69 * structure, obtaining a reference to it using container_of().
70 *
71 * The callbacks must all return zero on success, non-zero otherwise.
72 */
73
74struct ptp_clock_info {
75 struct module *owner;
76 char name[16];
77 s32 max_adj;
78 int n_alarm;
79 int n_ext_ts;
80 int n_per_out;
81 int pps;
82 int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
83 int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
84 int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts);
85 int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts);
86 int (*enable)(struct ptp_clock_info *ptp,
87 struct ptp_clock_request *request, int on);
88};
89
90struct ptp_clock;
91
92/**
93 * ptp_clock_register() - register a PTP hardware clock driver
94 *
95 * @info: Structure describing the new clock.
96 */
97
98extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info);
99
100/**
101 * ptp_clock_unregister() - unregister a PTP hardware clock driver
102 *
103 * @ptp: The clock to remove from service.
104 */
105
106extern int ptp_clock_unregister(struct ptp_clock *ptp);
107
108
109enum ptp_clock_events {
110 PTP_CLOCK_ALARM,
111 PTP_CLOCK_EXTTS,
112 PTP_CLOCK_PPS,
113};
114
115/**
116 * struct ptp_clock_event - decribes a PTP hardware clock event
117 *
118 * @type: One of the ptp_clock_events enumeration values.
119 * @index: Identifies the source of the event.
120 * @timestamp: When the event occured.
121 */
122
123struct ptp_clock_event {
124 int type;
125 int index;
126 u64 timestamp;
127};
128
129/**
130 * ptp_clock_event() - notify the PTP layer about an event
131 *
132 * @ptp: The clock obtained from ptp_clock_register().
133 * @event: Message structure describing the event.
134 */
135
136extern void ptp_clock_event(struct ptp_clock *ptp,
137 struct ptp_clock_event *event);
138
139#endif