Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Richard Cochran and committed by
John Stultz
d94ba80e caebc160

+1695
+98
Documentation/ABI/testing/sysfs-ptp
··· 1 + What: /sys/class/ptp/ 2 + Date: September 2010 3 + Contact: Richard Cochran <richardcochran@gmail.com> 4 + Description: 5 + This directory contains files and directories 6 + providing a standardized interface to the ancillary 7 + features of PTP hardware clocks. 8 + 9 + What: /sys/class/ptp/ptpN/ 10 + Date: September 2010 11 + Contact: Richard Cochran <richardcochran@gmail.com> 12 + Description: 13 + This directory contains the attributes of the Nth PTP 14 + hardware clock registered into the PTP class driver 15 + subsystem. 16 + 17 + What: /sys/class/ptp/ptpN/clock_name 18 + Date: September 2010 19 + Contact: Richard Cochran <richardcochran@gmail.com> 20 + Description: 21 + This file contains the name of the PTP hardware clock 22 + as a human readable string. 23 + 24 + What: /sys/class/ptp/ptpN/max_adjustment 25 + Date: September 2010 26 + Contact: Richard Cochran <richardcochran@gmail.com> 27 + Description: 28 + This file contains the PTP hardware clock's maximum 29 + frequency adjustment value (a positive integer) in 30 + parts per billion. 31 + 32 + What: /sys/class/ptp/ptpN/n_alarms 33 + Date: September 2010 34 + Contact: Richard Cochran <richardcochran@gmail.com> 35 + Description: 36 + This file contains the number of periodic or one shot 37 + alarms offer by the PTP hardware clock. 38 + 39 + What: /sys/class/ptp/ptpN/n_external_timestamps 40 + Date: September 2010 41 + Contact: Richard Cochran <richardcochran@gmail.com> 42 + Description: 43 + This file contains the number of external timestamp 44 + channels offered by the PTP hardware clock. 45 + 46 + What: /sys/class/ptp/ptpN/n_periodic_outputs 47 + Date: September 2010 48 + Contact: Richard Cochran <richardcochran@gmail.com> 49 + Description: 50 + This file contains the number of programmable periodic 51 + output channels offered by the PTP hardware clock. 52 + 53 + What: /sys/class/ptp/ptpN/pps_avaiable 54 + Date: September 2010 55 + Contact: Richard Cochran <richardcochran@gmail.com> 56 + Description: 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 + 62 + What: /sys/class/ptp/ptpN/extts_enable 63 + Date: September 2010 64 + Contact: Richard Cochran <richardcochran@gmail.com> 65 + Description: 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 + 72 + What: /sys/class/ptp/ptpN/fifo 73 + Date: September 2010 74 + Contact: Richard Cochran <richardcochran@gmail.com> 75 + Description: 76 + This file provides timestamps on external events, in 77 + the form of three integers: channel index, seconds, 78 + and nanoseconds. 79 + 80 + What: /sys/class/ptp/ptpN/period 81 + Date: September 2010 82 + Contact: Richard Cochran <richardcochran@gmail.com> 83 + Description: 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 + 91 + What: /sys/class/ptp/ptpN/pps_enable 92 + Date: September 2010 93 + Contact: Richard Cochran <richardcochran@gmail.com> 94 + Description: 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.
+89
Documentation/ptp/ptp.txt
··· 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)
+381
Documentation/ptp/testptp.c
··· 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> 50 + static int clock_adjtime(clockid_t id, struct timex *tx) 51 + { 52 + return syscall(__NR_clock_adjtime, id, tx); 53 + } 54 + 55 + static 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 + 63 + static void handle_alarm(int s) 64 + { 65 + printf("received signal %d\n", s); 66 + } 67 + 68 + static 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 + 87 + static 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 + 103 + static 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 + 123 + int 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 + }
+33
Documentation/ptp/testptp.mk
··· 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 + 19 + CC = $(CROSS_COMPILE)gcc 20 + INC = -I$(KBUILD_OUTPUT)/usr/include 21 + CFLAGS = -Wall $(INC) 22 + LDLIBS = -lrt 23 + PROGS = testptp 24 + 25 + all: $(PROGS) 26 + 27 + testptp: testptp.o 28 + 29 + clean: 30 + rm -f testptp.o 31 + 32 + distclean: clean 33 + rm -f $(PROGS)
+2
drivers/Kconfig
··· 54 54 55 55 source "drivers/pps/Kconfig" 56 56 57 + source "drivers/ptp/Kconfig" 58 + 57 59 source "drivers/gpio/Kconfig" 58 60 59 61 source "drivers/w1/Kconfig"
+1
drivers/Makefile
··· 76 76 obj-$(CONFIG_RTC_LIB) += rtc/ 77 77 obj-y += i2c/ media/ 78 78 obj-$(CONFIG_PPS) += pps/ 79 + obj-$(CONFIG_PTP_1588_CLOCK) += ptp/ 79 80 obj-$(CONFIG_W1) += w1/ 80 81 obj-$(CONFIG_POWER_SUPPLY) += power/ 81 82 obj-$(CONFIG_HWMON) += hwmon/
+30
drivers/ptp/Kconfig
··· 1 + # 2 + # PTP clock support configuration 3 + # 4 + 5 + menu "PTP clock support" 6 + 7 + comment "Enable Device Drivers -> PPS to see the PTP clock options." 8 + depends on PPS=n 9 + 10 + config 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 + 30 + endmenu
+6
drivers/ptp/Makefile
··· 1 + # 2 + # Makefile for PTP 1588 clock support. 3 + # 4 + 5 + ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o 6 + obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
+159
drivers/ptp/ptp_chardev.c
··· 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 + 27 + int ptp_open(struct posix_clock *pc, fmode_t fmode) 28 + { 29 + return 0; 30 + } 31 + 32 + long 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 + 97 + unsigned 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 + 106 + ssize_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 + }
+343
drivers/ptp/ptp_clock.c
··· 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 + 42 + static dev_t ptp_devt; 43 + static struct class *ptp_class; 44 + 45 + static DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS); 46 + static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */ 47 + 48 + /* time stamp event queue operations */ 49 + 50 + static inline int queue_free(struct timestamp_event_queue *q) 51 + { 52 + return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1; 53 + } 54 + 55 + static 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 + 80 + static 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 + 102 + static int ptp_clock_getres(struct posix_clock *pc, struct timespec *tp) 103 + { 104 + return 1; /* always round timer functions to one nanosecond */ 105 + } 106 + 107 + static 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 + 113 + static 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 + 119 + static 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 + 153 + static 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 + 165 + static 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 + 181 + struct 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 + 249 + no_clock: 250 + if (ptp->pps_source) 251 + pps_unregister_source(ptp->pps_source); 252 + no_pps: 253 + ptp_cleanup_sysfs(ptp); 254 + no_sysfs: 255 + device_destroy(ptp_class, ptp->devid); 256 + no_device: 257 + mutex_destroy(&ptp->tsevq_mux); 258 + kfree(ptp); 259 + no_memory: 260 + clear_bit(index, ptp_clocks_map); 261 + no_slot: 262 + mutex_unlock(&ptp_clocks_mutex); 263 + return ERR_PTR(err); 264 + } 265 + EXPORT_SYMBOL(ptp_clock_register); 266 + 267 + int 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 + } 281 + EXPORT_SYMBOL(ptp_clock_unregister); 282 + 283 + void 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 + } 303 + EXPORT_SYMBOL(ptp_clock_event); 304 + 305 + /* module operations */ 306 + 307 + static void __exit ptp_exit(void) 308 + { 309 + class_destroy(ptp_class); 310 + unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS); 311 + } 312 + 313 + static 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 + 333 + no_region: 334 + class_destroy(ptp_class); 335 + return err; 336 + } 337 + 338 + subsys_initcall(ptp_init); 339 + module_exit(ptp_exit); 340 + 341 + MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>"); 342 + MODULE_DESCRIPTION("PTP clocks support"); 343 + MODULE_LICENSE("GPL");
+92
drivers/ptp/ptp_private.h
··· 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 + 34 + struct timestamp_event_queue { 35 + struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS]; 36 + int head; 37 + int tail; 38 + spinlock_t lock; 39 + }; 40 + 41 + struct 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 + */ 61 + static 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 + 71 + long ptp_ioctl(struct posix_clock *pc, 72 + unsigned int cmd, unsigned long arg); 73 + 74 + int ptp_open(struct posix_clock *pc, fmode_t fmode); 75 + 76 + ssize_t ptp_read(struct posix_clock *pc, 77 + uint flags, char __user *buf, size_t cnt); 78 + 79 + uint ptp_poll(struct posix_clock *pc, 80 + struct file *fp, poll_table *wait); 81 + 82 + /* 83 + * see ptp_sysfs.c 84 + */ 85 + 86 + extern struct device_attribute ptp_dev_attrs[]; 87 + 88 + int ptp_cleanup_sysfs(struct ptp_clock *ptp); 89 + 90 + int ptp_populate_sysfs(struct ptp_clock *ptp); 91 + 92 + #endif
+230
drivers/ptp/ptp_sysfs.c
··· 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 + 24 + static 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) \ 32 + static 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 + 39 + PTP_SHOW_INT(max_adj); 40 + PTP_SHOW_INT(n_alarm); 41 + PTP_SHOW_INT(n_ext_ts); 42 + PTP_SHOW_INT(n_per_out); 43 + PTP_SHOW_INT(pps); 44 + 45 + #define PTP_RO_ATTR(_var, _name) { \ 46 + .attr = { .name = __stringify(_name), .mode = 0444 }, \ 47 + .show = _var##_show, \ 48 + } 49 + 50 + struct 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 + 60 + static 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; 81 + out: 82 + return err; 83 + } 84 + 85 + static 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); 113 + out: 114 + mutex_unlock(&ptp->tsevq_mux); 115 + return cnt; 116 + } 117 + 118 + static 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; 141 + out: 142 + return err; 143 + } 144 + 145 + static 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; 167 + out: 168 + return err; 169 + } 170 + 171 + static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store); 172 + static DEVICE_ATTR(fifo, 0444, extts_fifo_show, NULL); 173 + static DEVICE_ATTR(period, 0220, NULL, period_store); 174 + static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store); 175 + 176 + int 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 + 194 + int 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; 219 + out4: 220 + if (info->n_per_out) 221 + device_remove_file(dev, &dev_attr_period); 222 + out3: 223 + if (info->n_ext_ts) 224 + device_remove_file(dev, &dev_attr_fifo); 225 + out2: 226 + if (info->n_ext_ts) 227 + device_remove_file(dev, &dev_attr_extts_enable); 228 + out1: 229 + return err; 230 + }
+1
include/linux/Kbuild
··· 302 302 header-y += ppp_defs.h 303 303 header-y += pps.h 304 304 header-y += prctl.h 305 + header-y += ptp_clock.h 305 306 header-y += ptrace.h 306 307 header-y += qnx4_fs.h 307 308 header-y += qnxtypes.h
+7
include/linux/ptp_classify.h
··· 25 25 26 26 #include <linux/if_ether.h> 27 27 #include <linux/if_vlan.h> 28 + #include <linux/ip.h> 28 29 #include <linux/filter.h> 29 30 #ifdef __KERNEL__ 30 31 #include <linux/in.h> ··· 58 57 #define OFF_PROTO4 23 59 58 #define OFF_NEXT 6 60 59 #define OFF_UDP_DST 2 60 + 61 + #define OFF_PTP_SOURCE_UUID 22 /* PTPv1 only */ 62 + #define OFF_PTP_SEQUENCE_ID 30 63 + #define OFF_PTP_CONTROL 32 /* PTPv1 only */ 64 + 65 + #define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2) 61 66 62 67 #define IP6_HLEN 40 63 68 #define UDP_HLEN 8
+84
include/linux/ptp_clock.h
··· 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 + */ 41 + struct ptp_clock_time { 42 + __s64 sec; /* seconds */ 43 + __u32 nsec; /* nanoseconds */ 44 + __u32 reserved; 45 + }; 46 + 47 + struct 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 + 56 + struct 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 + 62 + struct 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 + 77 + struct 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
+139
include/linux/ptp_clock_kernel.h
··· 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 + 27 + struct 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 + 74 + struct 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 + 90 + struct ptp_clock; 91 + 92 + /** 93 + * ptp_clock_register() - register a PTP hardware clock driver 94 + * 95 + * @info: Structure describing the new clock. 96 + */ 97 + 98 + extern 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 + 106 + extern int ptp_clock_unregister(struct ptp_clock *ptp); 107 + 108 + 109 + enum 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 + 123 + struct 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 + 136 + extern void ptp_clock_event(struct ptp_clock *ptp, 137 + struct ptp_clock_event *event); 138 + 139 + #endif