diff mbox

[08/12] ptp: Added a brand new class driver for ptp clocks.

Message ID 4a030d2bace90f089f2f3f61496b918c6f1dfb52.1276615626.git.richard.cochran@omicron.at
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Richard Cochran June 15, 2010, 4:09 p.m. UTC
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
exposed to user space as a character device with ioctls that allow tuning
of the PTP clock.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 Documentation/ptp/ptp.txt        |   95 +++++++
 Documentation/ptp/testptp.c      |  269 ++++++++++++++++++++
 Documentation/ptp/testptp.mk     |   33 +++
 drivers/Kconfig                  |    2 +
 drivers/Makefile                 |    1 +
 drivers/ptp/Kconfig              |   26 ++
 drivers/ptp/Makefile             |    5 +
 drivers/ptp/ptp_clock.c          |  514 ++++++++++++++++++++++++++++++++++++++
 include/linux/Kbuild             |    1 +
 include/linux/ptp_clock.h        |   79 ++++++
 include/linux/ptp_clock_kernel.h |  137 ++++++++++
 11 files changed, 1162 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ptp/ptp.txt
 create mode 100644 Documentation/ptp/testptp.c
 create mode 100644 Documentation/ptp/testptp.mk
 create mode 100644 drivers/ptp/Kconfig
 create mode 100644 drivers/ptp/Makefile
 create mode 100644 drivers/ptp/ptp_clock.c
 create mode 100644 include/linux/ptp_clock.h
 create mode 100644 include/linux/ptp_clock_kernel.h

Comments

Grant Likely June 15, 2010, 5 p.m. UTC | #1
On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> 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
> exposed to user space as a character device with ioctls that allow tuning
> of the PTP clock.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
> ---
>  Documentation/ptp/ptp.txt        |   95 +++++++
>  Documentation/ptp/testptp.c      |  269 ++++++++++++++++++++
>  Documentation/ptp/testptp.mk     |   33 +++
>  drivers/Kconfig                  |    2 +
>  drivers/Makefile                 |    1 +
>  drivers/ptp/Kconfig              |   26 ++
>  drivers/ptp/Makefile             |    5 +
>  drivers/ptp/ptp_clock.c          |  514 ++++++++++++++++++++++++++++++++++++++
>  include/linux/Kbuild             |    1 +
>  include/linux/ptp_clock.h        |   79 ++++++
>  include/linux/ptp_clock_kernel.h |  137 ++++++++++
>  11 files changed, 1162 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ptp/ptp.txt
>  create mode 100644 Documentation/ptp/testptp.c
>  create mode 100644 Documentation/ptp/testptp.mk
>  create mode 100644 drivers/ptp/Kconfig
>  create mode 100644 drivers/ptp/Makefile
>  create mode 100644 drivers/ptp/ptp_clock.c
>  create mode 100644 include/linux/ptp_clock.h
>  create mode 100644 include/linux/ptp_clock_kernel.h
>
> diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
> new file mode 100644
> index 0000000..46858b3
> --- /dev/null
> +++ b/Documentation/ptp/ptp.txt
> @@ -0,0 +1,95 @@
> +
> +* PTP infrastructure for Linux
> +
> +  This patch set introduces support for IEEE 1588 PTP clocks in
> +  Linux. Together with the SO_TIMESTAMPING socket options, this
> +  presents a standardized method for developing PTP user space
> +  programs, synchronizing Linux with external clocks, and using the
> +  ancillary features of PTP hardware clocks.
> +
> +  A new class driver exports a kernel interface for specific clock
> +  drivers and a user space interface. The infrastructure supports a
> +  complete set of PTP functionality.
> +
> +  + Basic clock operations
> +    - Set time
> +    - Get time
> +    - Shift the clock by a given offset atomically
> +    - Adjust clock frequency
> +
> +  + Ancillary clock features
> +    - One short or periodic alarms, with signal delivery to user program
> +    - Time stamp external events
> +    - Period output signals configurable from user space
> +    - Synchronization of the Linux system time via the PPS subsystem
> +
> +** PTP kernel API
> +
> +   A PTP clock driver registers itself with the class driver. The
> +   class driver handles all of the dealings with user space. The
> +   author of a clock driver need only implement the details of
> +   programming the clock hardware. The clock driver notifies the class
> +   driver of asynchronous events (alarms and external time stamps) via
> +   a simple message passing interface.
> +
> +   The class driver supports multiple PTP clock drivers. In normal use
> +   cases, only one PTP clock is needed. However, for testing and
> +   development, it can be useful to have more than one clock in a
> +   single system, in order to allow performance comparisons.
> +
> +** PTP user space API
> +
> +   The class driver creates a character device for each registered PTP
> +   clock. User space programs may control the clock using standardized
> +   ioctls. A program may query, enable, configure, and disable the
> +   ancillary clock features. User space can receive time stamped
> +   events via blocking read() and poll(). One shot and periodic
> +   signals may be configured via an ioctl API with semantics similar
> +   to the POSIX timer_settime() system call.
> +
> +   As an real life example, the following two patches for ptpd version
> +   1.0.0 demonstrate how the API works.
> +
> +   https://sourceforge.net/tracker/?func=detail&aid=2992845&group_id=139814&atid=744634
> +
> +   https://sourceforge.net/tracker/?func=detail&aid=2992847&group_id=139814&atid=744634

Question from an ignorant reviewer:  Why a new interface instead of
working with the existing high resolution timers infrastructure?

g.

> +
> +** Writing clock drivers
> +
> +   Clock drivers include include/linux/ptp_clock_kernel.h and register
> +   themselves by presenting a 'struct ptp_clock_info' to the
> +   registration method. Clock drivers must implement all of the
> +   functions in the interface. If a clock does not offer a particular
> +   ancillary feature, then the driver should just return -EOPNOTSUPP
> +   from those functions.
> +
> +   Drivers must ensure that all of the methods in interface are
> +   reentrant. Since most hardware implementations treat the time value
> +   as a 64 bit integer accessed as two 32 bit registers, drivers
> +   should use spin_lock_irqsave/spin_unlock_irqrestore to protect
> +   against concurrent access. This locking cannot be accomplished in
> +   class driver, since the lock may also be needed by the clock
> +   driver's interrupt service routine.
> +
> +** Supported hardware
> +
> +   + Standard Linux system timer
> +     - No special PTP features
> +     - For use with software time stamping
> +
> +   + Freescale eTSEC gianfar
> +     - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
> +     - 2 Alarm registers (optional interrupt)
> +     - 3 Periodic signals (optional interrupt)
> +
> +   + National DP83640
> +     - 6 GPIOs programmable as inputs or outputs
> +     - 6 GPIOs with dedicated functions (LED/JTAG/clock) can also be
> +       used as general inputs or outputs
> +     - GPIO inputs can time stamp external triggers
> +     - GPIO outputs can produce periodic signals
> +     - 1 interrupt pin
> +
> +   + Intel IXP465
> +     - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
> +     - Target Time (optional interrupt)
> diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
> new file mode 100644
> index 0000000..e30f758
> --- /dev/null
> +++ b/Documentation/ptp/testptp.c
> @@ -0,0 +1,269 @@
> +/*
> + * PTP 1588 clock support - User space test program
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/time.h>
> +#include <sys/types.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include <linux/ptp_clock.h>
> +
> +static void handle_alarm(int s)
> +{
> +       printf("received signal %d\n", s);
> +}
> +
> +static int install_handler(int signum, void (*handler)(int))
> +{
> +       struct sigaction action;
> +       sigset_t mask;
> +
> +       /* Unblock the signal. */
> +       sigemptyset(&mask);
> +       sigaddset(&mask, signum);
> +       sigprocmask(SIG_UNBLOCK, &mask, NULL);
> +
> +       /* Install the signal handler. */
> +       action.sa_handler = handler;
> +       action.sa_flags = 0;
> +       sigemptyset(&action.sa_mask);
> +       sigaction(signum, &action, NULL);
> +
> +       return 0;
> +}
> +
> +static void usage(char *progname)
> +{
> +       fprintf(stderr,
> +               "usage: %s [options]\n"
> +               " -a val     request a one-shot alarm after 'val' seconds\n"
> +               " -A val     request a periodic alarm every 'val' seconds\n"
> +               " -c         query the ptp clock's capabilities\n"
> +               " -d name    device to open\n"
> +               " -e val     read 'val' external time stamp events\n"
> +               " -f val     adjust the ptp clock frequency by 'val' PPB\n"
> +               " -g         get the ptp clock time\n"
> +               " -h         prints this message\n"
> +               " -s         set the ptp clock time from the system time\n"
> +               " -t val     shift the ptp clock time by 'val' seconds\n"
> +               " -v         query the ptp clock api version\n",
> +               progname);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       struct ptp_clock_caps caps;
> +       struct ptp_clock_timer timer;
> +       struct ptp_extts_event event;
> +       struct ptp_clock_request request;
> +       struct timespec ts;
> +       char *progname;
> +       int c, cnt, fd, val = 0;
> +
> +       char *device = "/dev/ptp_clock_0";
> +       int adjfreq = 0x7fffffff;
> +       int adjtime = 0;
> +       int capabilities = 0;
> +       int extts = 0;
> +       int gettime = 0;
> +       int oneshot = 0;
> +       int periodic = 0;
> +       int settime = 0;
> +       int version = 0;
> +
> +       progname = strrchr(argv[0], '/');
> +       progname = progname ? 1+progname : argv[0];
> +       while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghst:v"))) {
> +               switch (c) {
> +               case 'a':
> +                       oneshot = atoi(optarg);
> +                       break;
> +               case 'A':
> +                       periodic = atoi(optarg);
> +                       break;
> +               case 'c':
> +                       capabilities = 1;
> +                       break;
> +               case 'd':
> +                       device = optarg;
> +                       break;
> +               case 'e':
> +                       extts = atoi(optarg);
> +                       break;
> +               case 'f':
> +                       adjfreq = atoi(optarg);
> +                       break;
> +               case 'g':
> +                       gettime = 1;
> +                       break;
> +               case 's':
> +                       settime = 1;
> +                       break;
> +               case 't':
> +                       adjtime = atoi(optarg);
> +                       break;
> +               case 'v':
> +                       version = 1;
> +                       break;
> +               case 'h':
> +                       usage(progname);
> +                       return 0;
> +               case '?':
> +               default:
> +                       usage(progname);
> +                       return -1;
> +               }
> +       }
> +
> +       fd = open(device, O_RDWR);
> +       if (fd < 0) {
> +               fprintf(stderr, "cannot open %s: %s", device, strerror(errno));
> +               return -1;
> +       }
> +
> +       if (version) {
> +               if (ioctl(fd, PTP_CLOCK_APIVERS, &val)) {
> +                       perror("PTP_CLOCK_APIVERS");
> +               } else {
> +                       printf("version = 0x%08x\n", val);
> +               }
> +       }
> +
> +       if (capabilities) {
> +               if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
> +                       perror("PTP_CLOCK_GETCAPS");
> +               } else {
> +                       printf("capabilities:\n"
> +                              "  %d maximum frequency adjustment (PPB)\n"
> +                              "  %d programmable alarms\n"
> +                              "  %d external time stamp channels\n"
> +                              "  %d programmable periodic signals\n"
> +                              "  %d pulse per second\n",
> +                              caps.max_adj,
> +                              caps.n_alarm,
> +                              caps.n_ext_ts,
> +                              caps.n_per_out,
> +                              caps.pps);
> +               }
> +       }
> +
> +       if (0x7fffffff != adjfreq) {
> +               if (ioctl(fd, PTP_CLOCK_ADJFREQ, adjfreq)) {
> +                       perror("PTP_CLOCK_ADJFREQ");
> +               } else {
> +                       puts("frequency adjustment okay");
> +               }
> +       }
> +
> +       if (adjtime) {
> +               ts.tv_sec = adjtime;
> +               ts.tv_nsec = 0;
> +               if (ioctl(fd, PTP_CLOCK_ADJTIME, &ts)) {
> +                       perror("PTP_CLOCK_ADJTIME");
> +               } else {
> +                       puts("time shift okay");
> +               }
> +       }
> +
> +       if (gettime) {
> +               if (ioctl(fd, PTP_CLOCK_GETTIME, &ts)) {
> +                       perror("PTP_CLOCK_GETTIME");
> +               } else {
> +                       printf("clock time: %ld.%09ld or %s",
> +                              ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
> +               }
> +       }
> +
> +       if (settime) {
> +               clock_gettime(CLOCK_REALTIME, &ts);
> +               if (ioctl(fd, PTP_CLOCK_SETTIME, &ts)) {
> +                       perror("PTP_CLOCK_SETTIME");
> +               } else {
> +                       puts("set time okay");
> +               }
> +       }
> +
> +       if (extts) {
> +               memset(&request, 0, sizeof(request));
> +               request.type = PTP_REQUEST_EXTTS;
> +               request.index = 0;
> +               request.flags = PTP_ENABLE_FEATURE;
> +               if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
> +                       perror("PTP_FEATURE_REQUEST");
> +                       extts = 0;
> +               } else {
> +                       puts("set timer okay");
> +               }
> +               for (; extts; extts--) {
> +                       cnt = read(fd, &event, sizeof(event));
> +                       if (cnt != sizeof(event)) {
> +                               perror("read");
> +                               break;
> +                       }
> +                       printf("event index %d at %ld.%09ld\n", event.index,
> +                              event.ts.tv_sec, event.ts.tv_nsec);
> +               }
> +               /* Disable the feature again. */
> +               request.flags = 0;
> +               if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
> +                       perror("PTP_FEATURE_REQUEST");
> +               }
> +       }
> +
> +       if (oneshot) {
> +               install_handler(SIGALRM, handle_alarm);
> +               memset(&timer, 0, sizeof(timer));
> +               timer.signum = SIGALRM;
> +               timer.tsp.it_value.tv_sec = oneshot;
> +               if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
> +                       perror("PTP_CLOCK_SETTIMER");
> +               } else {
> +                       puts("set timer okay");
> +               }
> +               pause();
> +       }
> +
> +       if (periodic) {
> +               install_handler(SIGALRM, handle_alarm);
> +               memset(&timer, 0, sizeof(timer));
> +               timer.signum = SIGALRM;
> +               timer.tsp.it_value.tv_sec = periodic;
> +               timer.tsp.it_interval.tv_sec = periodic;
> +               if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
> +                       perror("PTP_CLOCK_SETTIMER");
> +               } else {
> +                       puts("set timer okay");
> +               }
> +               while (1) {
> +                       pause();
> +               }
> +       }
> +
> +       close(fd);
> +       return 0;
> +}
> diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
> new file mode 100644
> index 0000000..4ef2d97
> --- /dev/null
> +++ b/Documentation/ptp/testptp.mk
> @@ -0,0 +1,33 @@
> +# PTP 1588 clock support - User space test program
> +#
> +# Copyright (C) 2010 OMICRON electronics GmbH
> +#
> +#  This program is free software; you can redistribute it and/or modify
> +#  it under the terms of the GNU General Public License as published by
> +#  the Free Software Foundation; either version 2 of the License, or
> +#  (at your option) any later version.
> +#
> +#  This program is distributed in the hope that it will be useful,
> +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +#  GNU General Public License for more details.
> +#
> +#  You should have received a copy of the GNU General Public License
> +#  along with this program; if not, write to the Free Software
> +#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> +
> +CC        = $(CROSS_COMPILE)gcc
> +INC       = -I$(KBUILD_OUTPUT)/usr/include
> +CFLAGS    = -Wall $(INC)
> +LDLIBS    = -lrt
> +PROGS     = testptp
> +
> +all: $(PROGS)
> +
> +testptp: testptp.o
> +
> +clean:
> +       rm -f testptp.o
> +
> +distclean: clean
> +       rm -f $(PROGS)
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index a2b902f..774fbd7 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -52,6 +52,8 @@ source "drivers/spi/Kconfig"
>
>  source "drivers/pps/Kconfig"
>
> +source "drivers/ptp/Kconfig"
> +
>  source "drivers/gpio/Kconfig"
>
>  source "drivers/w1/Kconfig"
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 91874e0..6d12b48 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -76,6 +76,7 @@ obj-$(CONFIG_I2O)             += message/
>  obj-$(CONFIG_RTC_LIB)          += rtc/
>  obj-y                          += i2c/ media/
>  obj-$(CONFIG_PPS)              += pps/
> +obj-$(CONFIG_PTP_1588_CLOCK)   += ptp/
>  obj-$(CONFIG_W1)               += w1/
>  obj-$(CONFIG_POWER_SUPPLY)     += power/
>  obj-$(CONFIG_HWMON)            += hwmon/
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> new file mode 100644
> index 0000000..c80a25b
> --- /dev/null
> +++ b/drivers/ptp/Kconfig
> @@ -0,0 +1,26 @@
> +#
> +# PTP clock support configuration
> +#
> +
> +menu "PTP clock support"
> +
> +config PTP_1588_CLOCK
> +       tristate "PTP clock support"
> +       depends on EXPERIMENTAL
> +       help
> +         The IEEE 1588 standard defines a method to precisely
> +         synchronize distributed clocks over Ethernet networks. The
> +         standard defines a Precision Time Protocol (PTP), which can
> +         be used to achieve synchronization within a few dozen
> +         microseconds. In addition, with the help of special hardware
> +         time stamping units, it can be possible to achieve
> +         synchronization to within a few hundred nanoseconds.
> +
> +         This driver adds support for PTP clocks as character
> +         devices. If you want to use a PTP clock, then you should
> +         also enable at least one clock driver as well.
> +
> +         To compile this driver as a module, choose M here: the module
> +         will be called ptp_clock.
> +
> +endmenu
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> new file mode 100644
> index 0000000..b86695c
> --- /dev/null
> +++ b/drivers/ptp/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for PTP 1588 clock support.
> +#
> +
> +obj-$(CONFIG_PTP_1588_CLOCK)           += ptp_clock.o
> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> new file mode 100644
> index 0000000..4753bf3
> --- /dev/null
> +++ b/drivers/ptp/ptp_clock.c
> @@ -0,0 +1,514 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Partially adapted from the Linux PPS driver.
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/bitops.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/ptp_clock.h>
> +
> +#define PTP_MAX_ALARMS 4
> +#define PTP_MAX_CLOCKS BITS_PER_LONG
> +#define PTP_MAX_TIMESTAMPS 128
> +
> +struct alarm {
> +       struct pid *pid;
> +       int sig;
> +};
> +
> +struct timestamp_event_queue {
> +       struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
> +       int head;
> +       int tail;
> +       int overflow;
> +};
> +
> +struct ptp_clock {
> +       struct list_head list;
> +       struct cdev cdev;
> +       struct device *dev;
> +       struct ptp_clock_info *info;
> +       dev_t devid;
> +       int index; /* index into clocks.map, also the minor number */
> +
> +       struct alarm alarm[PTP_MAX_ALARMS];
> +       struct mutex alarm_mux; /* one process at a time setting an alarm */
> +
> +       struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
> +       struct mutex tsevq_mux; /* one process at a time reading the fifo */
> +       wait_queue_head_t tsev_wq;
> +};
> +
> +/* private globals */
> +
> +static const struct file_operations ptp_fops;
> +static dev_t ptp_devt;
> +static struct class *ptp_class;
> +
> +static struct {
> +       struct list_head list;
> +       DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
> +} clocks;
> +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
> +
> +/* time stamp event queue operations */
> +
> +static inline int queue_cnt(struct timestamp_event_queue *q)
> +{
> +       int cnt = q->tail - q->head;
> +       return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
> +}
> +
> +static inline int queue_free(struct timestamp_event_queue *q)
> +{
> +       return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
> +}
> +
> +static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
> +                                      struct ptp_clock_event *src)
> +{
> +       struct ptp_extts_event *dst;
> +       u32 remainder;
> +
> +       dst = &queue->buf[queue->tail];
> +
> +       dst->index = src->index;
> +       dst->ts.tv_sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
> +       dst->ts.tv_nsec = remainder;
> +
> +       if (!queue_free(queue))
> +               queue->overflow++;
> +
> +       queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
> +}
> +
> +/* public interface */
> +
> +struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
> +{
> +       struct ptp_clock *ptp;
> +       int err = 0, index, major = MAJOR(ptp_devt);
> +       unsigned long flags;
> +
> +       if (info->n_alarm > PTP_MAX_ALARMS)
> +               return ERR_PTR(-EINVAL);
> +
> +       /* Find a free clock slot and reserve it. */
> +       err = -EBUSY;
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
> +       if (index < PTP_MAX_CLOCKS) {
> +               set_bit(index, clocks.map);
> +               spin_unlock_irqrestore(&clocks_lock, flags);
> +       } else {
> +               spin_unlock_irqrestore(&clocks_lock, flags);
> +               goto no_clock;
> +       }
> +
> +       /* Initialize a clock structure. */
> +       err = -ENOMEM;
> +       ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
> +       if (ptp == NULL)
> +               goto no_memory;
> +
> +       ptp->info = info;
> +       ptp->devid = MKDEV(major, index);
> +       ptp->index = index;
> +       mutex_init(&ptp->alarm_mux);
> +       mutex_init(&ptp->tsevq_mux);
> +       init_waitqueue_head(&ptp->tsev_wq);
> +
> +       /* Create a new device in our class. */
> +       ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
> +                                "ptp_clock_%d", ptp->index);
> +       if (IS_ERR(ptp->dev))
> +               goto no_device;
> +
> +       dev_set_drvdata(ptp->dev, ptp);
> +
> +       /* Register a character device. */
> +       cdev_init(&ptp->cdev, &ptp_fops);
> +       ptp->cdev.owner = info->owner;
> +       err = cdev_add(&ptp->cdev, ptp->devid, 1);
> +       if (err)
> +               goto no_cdev;
> +
> +       /* Clock is ready, add it into the list. */
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       list_add(&ptp->list, &clocks.list);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +
> +       return ptp;
> +
> +no_cdev:
> +       device_destroy(ptp_class, ptp->devid);
> +no_device:
> +       mutex_destroy(&ptp->alarm_mux);
> +       mutex_destroy(&ptp->tsevq_mux);
> +       kfree(ptp);
> +no_memory:
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       clear_bit(index, clocks.map);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +no_clock:
> +       return ERR_PTR(err);
> +}
> +EXPORT_SYMBOL(ptp_clock_register);
> +
> +int ptp_clock_unregister(struct ptp_clock *ptp)
> +{
> +       unsigned long flags;
> +
> +       /* Release the clock's resources. */
> +       cdev_del(&ptp->cdev);
> +       device_destroy(ptp_class, ptp->devid);
> +       mutex_destroy(&ptp->alarm_mux);
> +       mutex_destroy(&ptp->tsevq_mux);
> +
> +       /* Remove the clock from the list. */
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       list_del(&ptp->list);
> +       clear_bit(ptp->index, clocks.map);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +
> +       kfree(ptp);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(ptp_clock_unregister);
> +
> +void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
> +{
> +       switch (event->type) {
> +
> +       case PTP_CLOCK_ALARM:
> +               kill_pid(ptp->alarm[event->index].pid,
> +                        ptp->alarm[event->index].sig, 1);
> +               break;
> +
> +       case PTP_CLOCK_EXTTS:
> +               enqueue_external_timestamp(&ptp->tsevq, event);
> +               wake_up_interruptible(&ptp->tsev_wq);
> +               break;
> +
> +       case PTP_CLOCK_PPS:
> +               break;
> +       }
> +}
> +EXPORT_SYMBOL(ptp_clock_event);
> +
> +/* character device operations */
> +
> +static int ptp_ioctl(struct inode *node, struct file *fp,
> +                     unsigned int cmd, unsigned long arg)
> +{
> +       struct ptp_clock_caps caps;
> +       struct ptp_clock_request req;
> +       struct ptp_clock_timer timer;
> +       struct ptp_clock *ptp = fp->private_data;
> +       struct ptp_clock_info *ops = ptp->info;
> +       void *priv = ops->priv;
> +       struct timespec ts;
> +       int flags, index;
> +       int err = 0;
> +
> +       switch (cmd) {
> +
> +       case PTP_CLOCK_APIVERS:
> +               err = put_user(PTP_CLOCK_VERSION, (u32 __user *)arg);
> +               break;
> +
> +       case PTP_CLOCK_ADJFREQ:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               err = ops->adjfreq(priv, arg);
> +               break;
> +
> +       case PTP_CLOCK_ADJTIME:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
> +                       err = -EFAULT;
> +               else
> +                       err = ops->adjtime(priv, &ts);
> +               break;
> +
> +       case PTP_CLOCK_GETTIME:
> +               err = ops->gettime(priv, &ts);
> +               if (err)
> +                       break;
> +               err = copy_to_user((void __user *)arg, &ts, sizeof(ts));
> +               break;
> +
> +       case PTP_CLOCK_SETTIME:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
> +                       err = -EFAULT;
> +               else
> +                       err = ops->settime(priv, &ts);
> +               break;
> +
> +       case PTP_CLOCK_GETCAPS:
> +               memset(&caps, 0, sizeof(caps));
> +               caps.max_adj = ptp->info->max_adj;
> +               caps.n_alarm = ptp->info->n_alarm;
> +               caps.n_ext_ts = ptp->info->n_ext_ts;
> +               caps.n_per_out = ptp->info->n_per_out;
> +               caps.pps = ptp->info->pps;
> +               err = copy_to_user((void __user *)arg, &caps, sizeof(caps));
> +               break;
> +
> +       case PTP_CLOCK_GETTIMER:
> +               if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               index = timer.alarm_index;
> +               if (index < 0 || index >= ptp->info->n_alarm) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               err = ops->gettimer(priv, index, &timer.tsp);
> +               if (err)
> +                       break;
> +               err = copy_to_user((void __user *)arg, &timer, sizeof(timer));
> +               break;
> +
> +       case PTP_CLOCK_SETTIMER:
> +               if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               index = timer.alarm_index;
> +               if (index < 0 || index >= ptp->info->n_alarm) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (!valid_signal(timer.signum))
> +                       return -EINVAL;
> +               flags = timer.flags;
> +               if (flags & (flags != TIMER_ABSTIME)) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (mutex_lock_interruptible(&ptp->alarm_mux))
> +                       return -ERESTARTSYS;
> +
> +               if (ptp->alarm[index].pid)
> +                       put_pid(ptp->alarm[index].pid);
> +
> +               ptp->alarm[index].pid = get_pid(task_pid(current));
> +               ptp->alarm[index].sig = timer.signum;
> +               err = ops->settimer(priv, index, flags, &timer.tsp);
> +
> +               mutex_unlock(&ptp->alarm_mux);
> +               break;
> +
> +       case PTP_FEATURE_REQUEST:
> +               if (copy_from_user(&req, (void __user *)arg, sizeof(req))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               switch (req.type) {
> +               case PTP_REQUEST_EXTTS:
> +               case PTP_REQUEST_PEROUT:
> +                       break;
> +               case PTP_REQUEST_PPS:
> +                       if (!capable(CAP_SYS_TIME))
> +                               return -EPERM;
> +                       break;
> +               default:
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (err)
> +                       break;
> +               err = ops->enable(priv, &req,
> +                                 req.flags & PTP_ENABLE_FEATURE ? 1 : 0);
> +               break;
> +
> +       default:
> +               err = -ENOTTY;
> +               break;
> +       }
> +       return err;
> +}
> +
> +static int ptp_open(struct inode *inode, struct file *fp)
> +{
> +       struct ptp_clock *ptp;
> +       ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
> +
> +       fp->private_data = ptp;
> +
> +       return 0;
> +}
> +
> +static unsigned int ptp_poll(struct file *fp, poll_table *wait)
> +{
> +       struct ptp_clock *ptp = fp->private_data;
> +
> +       poll_wait(fp, &ptp->tsev_wq, wait);
> +
> +       return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
> +}
> +
> +static ssize_t ptp_read(struct file *fp, char __user *buf,
> +                       size_t cnt, loff_t *off)
> +{
> +       struct ptp_clock *ptp = fp->private_data;
> +       struct timestamp_event_queue *queue = &ptp->tsevq;
> +       struct ptp_extts_event *event;
> +       size_t qcnt;
> +
> +       if (mutex_lock_interruptible(&ptp->tsevq_mux))
> +               return -ERESTARTSYS;
> +
> +       cnt = cnt / sizeof(struct ptp_extts_event);
> +
> +       if (wait_event_interruptible(ptp->tsev_wq,
> +                                    (qcnt = queue_cnt(&ptp->tsevq)))) {
> +               mutex_unlock(&ptp->tsevq_mux);
> +               return -ERESTARTSYS;
> +       }
> +
> +       if (cnt > qcnt)
> +               cnt = qcnt;
> +
> +       event = &queue->buf[queue->head];
> +
> +       if (copy_to_user(buf, event, cnt * sizeof(struct ptp_extts_event))) {
> +               mutex_unlock(&ptp->tsevq_mux);
> +               return -EFAULT;
> +       }
> +       queue->head = (queue->head + cnt) % PTP_MAX_TIMESTAMPS;
> +
> +       mutex_unlock(&ptp->tsevq_mux);
> +
> +       return cnt * sizeof(struct ptp_extts_event);
> +}
> +
> +static int ptp_release(struct inode *inode, struct file *fp)
> +{
> +       struct ptp_clock *ptp;
> +       struct itimerspec ts = {
> +               {0, 0}, {0, 0}
> +       };
> +       int i;
> +
> +       ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
> +
> +       for (i = 0; i < ptp->info->n_alarm; i++) {
> +               if (ptp->alarm[i].pid) {
> +                       ptp->info->settimer(ptp->info->priv, i, 0, &ts);
> +                       put_pid(ptp->alarm[i].pid);
> +                       ptp->alarm[i].pid = NULL;
> +               }
> +       }
> +       return 0;
> +}
> +
> +static const struct file_operations ptp_fops = {
> +       .owner          = THIS_MODULE,
> +       .ioctl          = ptp_ioctl,
> +       .open           = ptp_open,
> +       .poll           = ptp_poll,
> +       .read           = ptp_read,
> +       .release        = ptp_release,
> +};
> +
> +/* sysfs */
> +
> +static ssize_t ptp_show_status(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       struct ptp_clock *ptp = dev_get_drvdata(dev);
> +       return sprintf(buf,
> +                      "maximum adjustment:  %d\n"
> +                      "programmable alarms: %d\n"
> +                      "external timestamps: %d\n"
> +                      "periodic outputs:    %d\n"
> +                      "has pps:             %d\n"
> +                      "device index:        %d\n",
> +                      ptp->info->max_adj,
> +                      ptp->info->n_alarm,
> +                      ptp->info->n_ext_ts,
> +                      ptp->info->n_per_out,
> +                      ptp->info->pps,
> +                      ptp->index);
> +}
> +
> +struct device_attribute ptp_attrs[] = {
> +       __ATTR(capabilities, S_IRUGO, ptp_show_status, NULL),
> +       __ATTR_NULL,
> +};
> +
> +/* module operations */
> +
> +static void __exit ptp_exit(void)
> +{
> +       class_destroy(ptp_class);
> +       unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
> +}
> +
> +static int __init ptp_init(void)
> +{
> +       int err;
> +
> +       INIT_LIST_HEAD(&clocks.list);
> +
> +       ptp_class = class_create(THIS_MODULE, "ptp");
> +       if (!ptp_class) {
> +               printk(KERN_ERR "ptp: failed to allocate class\n");
> +               return -ENOMEM;
> +       }
> +       ptp_class->dev_attrs = ptp_attrs;
> +
> +       err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
> +       if (err < 0) {
> +               printk(KERN_ERR "ptp: failed to allocate char device region\n");
> +               goto no_region;
> +       }
> +
> +       pr_info("PTP clock support registered\n");
> +       return 0;
> +
> +no_region:
> +       class_destroy(ptp_class);
> +       return err;
> +}
> +
> +subsys_initcall(ptp_init);
> +module_exit(ptp_exit);
> +
> +MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
> +MODULE_DESCRIPTION("PTP clocks support");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
> index 2fc8e14..9959fe4 100644
> --- a/include/linux/Kbuild
> +++ b/include/linux/Kbuild
> @@ -140,6 +140,7 @@ header-y += pkt_sched.h
>  header-y += posix_types.h
>  header-y += ppdev.h
>  header-y += prctl.h
> +header-y += ptp_clock.h
>  header-y += qnxtypes.h
>  header-y += qnx4_fs.h
>  header-y += radeonfb.h
> diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
> new file mode 100644
> index 0000000..5a509c5
> --- /dev/null
> +++ b/include/linux/ptp_clock.h
> @@ -0,0 +1,79 @@
> +/*
> + * PTP 1588 clock support - user space interface
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_H_
> +#define _PTP_CLOCK_H_
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +#define PTP_ENABLE_FEATURE (1<<0)
> +#define PTP_RISING_EDGE    (1<<1)
> +#define PTP_FALLING_EDGE   (1<<2)
> +
> +enum ptp_request_types {
> +       PTP_REQUEST_EXTTS,
> +       PTP_REQUEST_PEROUT,
> +       PTP_REQUEST_PPS,
> +};
> +
> +struct ptp_clock_caps {
> +       __s32 max_adj; /* Maximum frequency adjustment, parts per billon. */
> +       int n_alarm;   /* Number of programmable alarms. */
> +       int n_ext_ts;  /* Number of external time stamp channels. */
> +       int n_per_out; /* Number of programmable periodic signals. */
> +       int pps;       /* Whether the clock supports a PPS callback. */
> +};
> +
> +struct ptp_clock_timer {
> +       int alarm_index;       /* Which alarm to query or configure. */
> +       int signum;            /* Requested signal. */
> +       int flags;             /* Zero or TIMER_ABSTIME, see TIMER_SETTIME(2) */
> +       struct itimerspec tsp; /* See TIMER_SETTIME(2) */
> +};
> +
> +struct ptp_clock_request {
> +       int type;  /* One of the ptp_request_types enumeration values. */
> +       int index; /* Which channel to configure. */
> +       struct timespec ts; /* For period signals, the desired period. */
> +       int flags; /* Bit field for PTP_ENABLE_FEATURE or other flags. */
> +};
> +
> +struct ptp_extts_event {
> +       int index;
> +       struct timespec ts;
> +};
> +
> +#define PTP_CLOCK_VERSION 0x00000001
> +
> +#define PTP_CLK_MAGIC '='
> +
> +#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)
> +#define PTP_CLOCK_ADJFREQ _IO  (PTP_CLK_MAGIC, 2)
> +#define PTP_CLOCK_ADJTIME _IOW (PTP_CLK_MAGIC, 3, struct timespec)
> +#define PTP_CLOCK_GETTIME _IOR (PTP_CLK_MAGIC, 4, struct timespec)
> +#define PTP_CLOCK_SETTIME _IOW (PTP_CLK_MAGIC, 5, struct timespec)
> +
> +#define PTP_CLOCK_GETCAPS   _IOR  (PTP_CLK_MAGIC, 6, struct ptp_clock_caps)
> +#define PTP_CLOCK_GETTIMER  _IOWR (PTP_CLK_MAGIC, 7, struct ptp_clock_timer)
> +#define PTP_CLOCK_SETTIMER  _IOW  (PTP_CLK_MAGIC, 8, struct ptp_clock_timer)
> +#define PTP_FEATURE_REQUEST _IOW  (PTP_CLK_MAGIC, 9, struct ptp_clock_request)
> +
> +#endif
> diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
> new file mode 100644
> index 0000000..d6cc158
> --- /dev/null
> +++ b/include/linux/ptp_clock_kernel.h
> @@ -0,0 +1,137 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_KERNEL_H_
> +#define _PTP_CLOCK_KERNEL_H_
> +
> +#include <linux/ptp_clock.h>
> +
> +/**
> + * struct ptp_clock_info - decribes a PTP hardware clock
> + *
> + * @owner:     The clock driver should set to THIS_MODULE.
> + * @name:      A short name to identify the clock.
> + * @max_adj:   The maximum possible frequency adjustment, in parts per billon.
> + * @n_alarm:   The number of programmable alarms.
> + * @n_ext_ts:  The number of external time stamp channels.
> + * @n_per_out: The number of programmable periodic signals.
> + * @pps:       Indicates whether the clock supports a PPS callback.
> + * @priv:      Passed to the clock operations, for the driver's private use.
> + *
> + * clock operations
> + *
> + * @adjfreq:  Adjusts the frequency of the hardware clock.
> + *            parameter delta: Desired period change in parts per billion.
> + *
> + * @adjtime:  Shifts the time of the hardware clock.
> + *            parameter ts: Desired change in seconds and nanoseconds.
> + *
> + * @gettime:  Reads the current time from the hardware clock.
> + *            parameter ts: Holds the result.
> + *
> + * @settime:  Set the current time on the hardware clock.
> + *            parameter ts: Time value to set.
> + *
> + * @gettimer: Reads the time remaining from the given timer.
> + *            parameter index: Which alarm to query.
> + *            parameter ts: Holds the result.
> + *
> + * @settimer: Arms the given timer for periodic or one shot operation.
> + *            parameter index: Which alarm to set.
> + *            parameter abs: TIMER_ABSTIME, or zero for relative timer.
> + *            parameter ts: Alarm time and period to set.
> + *
> + * @enable:   Request driver to enable or disable an ancillary feature.
> + *            parameter request: Desired resource to enable or disable.
> + *            parameter on: Caller passes one to enable or zero to disable.
> + *
> + * The callbacks must all return zero on success, non-zero otherwise.
> + */
> +
> +struct ptp_clock_info {
> +       struct module *owner;
> +       char name[16];
> +       s32 max_adj;
> +       int n_alarm;
> +       int n_ext_ts;
> +       int n_per_out;
> +       int pps;
> +       void *priv;
> +       int (*adjfreq)(void *priv, s32 delta);
> +       int (*adjtime)(void *priv, struct timespec *ts);
> +       int (*gettime)(void *priv, struct timespec *ts);
> +       int (*settime)(void *priv, struct timespec *ts);
> +       int (*gettimer)(void *priv, int index, struct itimerspec *ts);
> +       int (*settimer)(void *priv, int index, int abs, struct itimerspec *ts);
> +       int (*enable)(void *priv, struct ptp_clock_request *request, int on);
> +};
> +
> +struct ptp_clock;
> +
> +/**
> + * ptp_clock_register() - register a PTP hardware clock driver
> + *
> + * @info:  Structure describing the new clock.
> + */
> +
> +extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info);
> +
> +/**
> + * ptp_clock_unregister() - unregister a PTP hardware clock driver
> + *
> + * @ptp:  The clock to remove from service.
> + */
> +
> +extern int ptp_clock_unregister(struct ptp_clock *ptp);
> +
> +
> +enum ptp_clock_events {
> +       PTP_CLOCK_ALARM,
> +       PTP_CLOCK_EXTTS,
> +       PTP_CLOCK_PPS,
> +};
> +
> +/**
> + * struct ptp_clock_event - decribes a PTP hardware clock event
> + *
> + * @type:  One of the ptp_clock_events enumeration values.
> + * @index: Identifies the source of the event.
> + * @timestamp: When the event occured.
> + */
> +
> +struct ptp_clock_event {
> +       int type;
> +       int index;
> +       u64 timestamp;
> +};
> +
> +/**
> + * ptp_clock_event() - notify the PTP layer about an event
> + *
> + * This function should only be called from interrupt context.
> + *
> + * @ptp:    The clock obtained from ptp_clock_register().
> + * @event:  Message structure describing the event.
> + */
> +
> +extern void ptp_clock_event(struct ptp_clock *ptp,
> +                           struct ptp_clock_event *event);
> +
> +#endif
> --
> 1.6.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Grant Likely June 15, 2010, 7:11 p.m. UTC | #2
On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> 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
> exposed to user space as a character device with ioctls that allow tuning
> of the PTP clock.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>

Hi Richard,

Some more comments on this patch...

> ---
>  Documentation/ptp/ptp.txt        |   95 +++++++
>  Documentation/ptp/testptp.c      |  269 ++++++++++++++++++++
>  Documentation/ptp/testptp.mk     |   33 +++
>  drivers/Kconfig                  |    2 +
>  drivers/Makefile                 |    1 +
>  drivers/ptp/Kconfig              |   26 ++
>  drivers/ptp/Makefile             |    5 +
>  drivers/ptp/ptp_clock.c          |  514 ++++++++++++++++++++++++++++++++++++++
>  include/linux/Kbuild             |    1 +
>  include/linux/ptp_clock.h        |   79 ++++++
>  include/linux/ptp_clock_kernel.h |  137 ++++++++++
>  11 files changed, 1162 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ptp/ptp.txt
>  create mode 100644 Documentation/ptp/testptp.c
>  create mode 100644 Documentation/ptp/testptp.mk
>  create mode 100644 drivers/ptp/Kconfig
>  create mode 100644 drivers/ptp/Makefile
>  create mode 100644 drivers/ptp/ptp_clock.c
>  create mode 100644 include/linux/ptp_clock.h
>  create mode 100644 include/linux/ptp_clock_kernel.h
>
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> new file mode 100644
> index 0000000..b86695c
> --- /dev/null
> +++ b/drivers/ptp/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for PTP 1588 clock support.
> +#
> +
> +obj-$(CONFIG_PTP_1588_CLOCK)           += ptp_clock.o
> diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
> new file mode 100644
> index 0000000..4753bf3
> --- /dev/null
> +++ b/drivers/ptp/ptp_clock.c
> @@ -0,0 +1,514 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Partially adapted from the Linux PPS driver.
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/bitops.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/poll.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/ptp_clock_kernel.h>
> +#include <linux/ptp_clock.h>
> +
> +#define PTP_MAX_ALARMS 4
> +#define PTP_MAX_CLOCKS BITS_PER_LONG
> +#define PTP_MAX_TIMESTAMPS 128
> +
> +struct alarm {
> +       struct pid *pid;
> +       int sig;
> +};
> +
> +struct timestamp_event_queue {
> +       struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
> +       int head;
> +       int tail;
> +       int overflow;
> +};
> +
> +struct ptp_clock {
> +       struct list_head list;
> +       struct cdev cdev;
> +       struct device *dev;
> +       struct ptp_clock_info *info;
> +       dev_t devid;
> +       int index; /* index into clocks.map, also the minor number */
> +
> +       struct alarm alarm[PTP_MAX_ALARMS];
> +       struct mutex alarm_mux; /* one process at a time setting an alarm */
> +
> +       struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
> +       struct mutex tsevq_mux; /* one process at a time reading the fifo */
> +       wait_queue_head_t tsev_wq;
> +};
> +
> +/* private globals */
> +
> +static const struct file_operations ptp_fops;
> +static dev_t ptp_devt;
> +static struct class *ptp_class;
> +
> +static struct {
> +       struct list_head list;
> +       DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
> +} clocks;
> +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */

Doesn't appear that clocks is manipulated at atomic context.  Mutex instead?

> +
> +/* time stamp event queue operations */
> +
> +static inline int queue_cnt(struct timestamp_event_queue *q)
> +{
> +       int cnt = q->tail - q->head;
> +       return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
> +}
> +
> +static inline int queue_free(struct timestamp_event_queue *q)
> +{
> +       return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
> +}
> +
> +static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
> +                                      struct ptp_clock_event *src)
> +{
> +       struct ptp_extts_event *dst;
> +       u32 remainder;
> +
> +       dst = &queue->buf[queue->tail];
> +
> +       dst->index = src->index;
> +       dst->ts.tv_sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
> +       dst->ts.tv_nsec = remainder;
> +
> +       if (!queue_free(queue))
> +               queue->overflow++;
> +
> +       queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
> +}
> +
> +/* public interface */
> +
> +struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
> +{
> +       struct ptp_clock *ptp;
> +       int err = 0, index, major = MAJOR(ptp_devt);
> +       unsigned long flags;
> +
> +       if (info->n_alarm > PTP_MAX_ALARMS)
> +               return ERR_PTR(-EINVAL);

Okay, this is my opinion here, and other maintainers may disagree with
me, but the ERR_PTR() pattern is a horrible idea.  It is non-obvious
when reading code when the pattern is used, and the compiler will not
catch misinterpretation of the return value for you.  Please don't add
new instances of using it.  Just return NULL on error and log it with
printk() or pr_error().  Very seldom do I find the actual error code
to be actually useful anyway.  Generally callers only care about
whether or not the operation succeeded.

> +
> +       /* Find a free clock slot and reserve it. */
> +       err = -EBUSY;
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
> +       if (index < PTP_MAX_CLOCKS) {
> +               set_bit(index, clocks.map);
> +               spin_unlock_irqrestore(&clocks_lock, flags);
> +       } else {
> +               spin_unlock_irqrestore(&clocks_lock, flags);
> +               goto no_clock;
> +       }

If the spinlock is changed to a mutex that is held for the entire
function call, then the logic here can be simpler.

> +
> +       /* Initialize a clock structure. */
> +       err = -ENOMEM;
> +       ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
> +       if (ptp == NULL)
> +               goto no_memory;
> +
> +       ptp->info = info;
> +       ptp->devid = MKDEV(major, index);
> +       ptp->index = index;
> +       mutex_init(&ptp->alarm_mux);
> +       mutex_init(&ptp->tsevq_mux);
> +       init_waitqueue_head(&ptp->tsev_wq);
> +
> +       /* Create a new device in our class. */
> +       ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
> +                                "ptp_clock_%d", ptp->index);
> +       if (IS_ERR(ptp->dev))
> +               goto no_device;
> +
> +       dev_set_drvdata(ptp->dev, ptp);
> +
> +       /* Register a character device. */
> +       cdev_init(&ptp->cdev, &ptp_fops);
> +       ptp->cdev.owner = info->owner;
> +       err = cdev_add(&ptp->cdev, ptp->devid, 1);
> +       if (err)
> +               goto no_cdev;
> +
> +       /* Clock is ready, add it into the list. */
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       list_add(&ptp->list, &clocks.list);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +
> +       return ptp;
> +
> +no_cdev:
> +       device_destroy(ptp_class, ptp->devid);
> +no_device:
> +       mutex_destroy(&ptp->alarm_mux);
> +       mutex_destroy(&ptp->tsevq_mux);
> +       kfree(ptp);
> +no_memory:
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       clear_bit(index, clocks.map);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +no_clock:
> +       return ERR_PTR(err);
> +}
> +EXPORT_SYMBOL(ptp_clock_register);
> +
> +int ptp_clock_unregister(struct ptp_clock *ptp)
> +{
> +       unsigned long flags;
> +
> +       /* Release the clock's resources. */
> +       cdev_del(&ptp->cdev);
> +       device_destroy(ptp_class, ptp->devid);
> +       mutex_destroy(&ptp->alarm_mux);
> +       mutex_destroy(&ptp->tsevq_mux);
> +
> +       /* Remove the clock from the list. */
> +       spin_lock_irqsave(&clocks_lock, flags);
> +       list_del(&ptp->list);
> +       clear_bit(ptp->index, clocks.map);
> +       spin_unlock_irqrestore(&clocks_lock, flags);
> +
> +       kfree(ptp);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(ptp_clock_unregister);
> +
> +void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
> +{
> +       switch (event->type) {
> +
> +       case PTP_CLOCK_ALARM:
> +               kill_pid(ptp->alarm[event->index].pid,
> +                        ptp->alarm[event->index].sig, 1);
> +               break;
> +
> +       case PTP_CLOCK_EXTTS:
> +               enqueue_external_timestamp(&ptp->tsevq, event);
> +               wake_up_interruptible(&ptp->tsev_wq);
> +               break;
> +
> +       case PTP_CLOCK_PPS:
> +               break;
> +       }
> +}
> +EXPORT_SYMBOL(ptp_clock_event);
> +
> +/* character device operations */
> +
> +static int ptp_ioctl(struct inode *node, struct file *fp,
> +                     unsigned int cmd, unsigned long arg)
> +{
> +       struct ptp_clock_caps caps;
> +       struct ptp_clock_request req;
> +       struct ptp_clock_timer timer;
> +       struct ptp_clock *ptp = fp->private_data;
> +       struct ptp_clock_info *ops = ptp->info;
> +       void *priv = ops->priv;
> +       struct timespec ts;
> +       int flags, index;
> +       int err = 0;
> +
> +       switch (cmd) {
> +
> +       case PTP_CLOCK_APIVERS:
> +               err = put_user(PTP_CLOCK_VERSION, (u32 __user *)arg);
> +               break;
> +
> +       case PTP_CLOCK_ADJFREQ:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               err = ops->adjfreq(priv, arg);
> +               break;
> +
> +       case PTP_CLOCK_ADJTIME:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
> +                       err = -EFAULT;
> +               else
> +                       err = ops->adjtime(priv, &ts);
> +               break;
> +
> +       case PTP_CLOCK_GETTIME:
> +               err = ops->gettime(priv, &ts);
> +               if (err)
> +                       break;
> +               err = copy_to_user((void __user *)arg, &ts, sizeof(ts));
> +               break;
> +
> +       case PTP_CLOCK_SETTIME:
> +               if (!capable(CAP_SYS_TIME))
> +                       return -EPERM;
> +               if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
> +                       err = -EFAULT;
> +               else
> +                       err = ops->settime(priv, &ts);
> +               break;
> +
> +       case PTP_CLOCK_GETCAPS:
> +               memset(&caps, 0, sizeof(caps));
> +               caps.max_adj = ptp->info->max_adj;
> +               caps.n_alarm = ptp->info->n_alarm;
> +               caps.n_ext_ts = ptp->info->n_ext_ts;
> +               caps.n_per_out = ptp->info->n_per_out;
> +               caps.pps = ptp->info->pps;
> +               err = copy_to_user((void __user *)arg, &caps, sizeof(caps));
> +               break;
> +
> +       case PTP_CLOCK_GETTIMER:
> +               if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               index = timer.alarm_index;
> +               if (index < 0 || index >= ptp->info->n_alarm) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               err = ops->gettimer(priv, index, &timer.tsp);
> +               if (err)
> +                       break;
> +               err = copy_to_user((void __user *)arg, &timer, sizeof(timer));
> +               break;
> +
> +       case PTP_CLOCK_SETTIMER:
> +               if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               index = timer.alarm_index;
> +               if (index < 0 || index >= ptp->info->n_alarm) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (!valid_signal(timer.signum))
> +                       return -EINVAL;
> +               flags = timer.flags;
> +               if (flags & (flags != TIMER_ABSTIME)) {
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (mutex_lock_interruptible(&ptp->alarm_mux))
> +                       return -ERESTARTSYS;
> +
> +               if (ptp->alarm[index].pid)
> +                       put_pid(ptp->alarm[index].pid);
> +
> +               ptp->alarm[index].pid = get_pid(task_pid(current));
> +               ptp->alarm[index].sig = timer.signum;
> +               err = ops->settimer(priv, index, flags, &timer.tsp);
> +
> +               mutex_unlock(&ptp->alarm_mux);
> +               break;
> +
> +       case PTP_FEATURE_REQUEST:
> +               if (copy_from_user(&req, (void __user *)arg, sizeof(req))) {
> +                       err = -EFAULT;
> +                       break;
> +               }
> +               switch (req.type) {
> +               case PTP_REQUEST_EXTTS:
> +               case PTP_REQUEST_PEROUT:
> +                       break;
> +               case PTP_REQUEST_PPS:
> +                       if (!capable(CAP_SYS_TIME))
> +                               return -EPERM;
> +                       break;
> +               default:
> +                       err = -EINVAL;
> +                       break;
> +               }
> +               if (err)
> +                       break;
> +               err = ops->enable(priv, &req,
> +                                 req.flags & PTP_ENABLE_FEATURE ? 1 : 0);
> +               break;
> +
> +       default:
> +               err = -ENOTTY;
> +               break;
> +       }
> +       return err;
> +}
> +
> +static int ptp_open(struct inode *inode, struct file *fp)
> +{
> +       struct ptp_clock *ptp;
> +       ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
> +
> +       fp->private_data = ptp;
> +
> +       return 0;
> +}
> +
> +static unsigned int ptp_poll(struct file *fp, poll_table *wait)
> +{
> +       struct ptp_clock *ptp = fp->private_data;
> +
> +       poll_wait(fp, &ptp->tsev_wq, wait);
> +
> +       return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
> +}
> +
> +static ssize_t ptp_read(struct file *fp, char __user *buf,
> +                       size_t cnt, loff_t *off)
> +{
> +       struct ptp_clock *ptp = fp->private_data;
> +       struct timestamp_event_queue *queue = &ptp->tsevq;
> +       struct ptp_extts_event *event;
> +       size_t qcnt;
> +
> +       if (mutex_lock_interruptible(&ptp->tsevq_mux))
> +               return -ERESTARTSYS;
> +
> +       cnt = cnt / sizeof(struct ptp_extts_event);
> +
> +       if (wait_event_interruptible(ptp->tsev_wq,
> +                                    (qcnt = queue_cnt(&ptp->tsevq)))) {
> +               mutex_unlock(&ptp->tsevq_mux);
> +               return -ERESTARTSYS;
> +       }
> +
> +       if (cnt > qcnt)
> +               cnt = qcnt;
> +
> +       event = &queue->buf[queue->head];
> +
> +       if (copy_to_user(buf, event, cnt * sizeof(struct ptp_extts_event))) {
> +               mutex_unlock(&ptp->tsevq_mux);
> +               return -EFAULT;
> +       }
> +       queue->head = (queue->head + cnt) % PTP_MAX_TIMESTAMPS;
> +
> +       mutex_unlock(&ptp->tsevq_mux);
> +
> +       return cnt * sizeof(struct ptp_extts_event);
> +}
> +
> +static int ptp_release(struct inode *inode, struct file *fp)
> +{
> +       struct ptp_clock *ptp;
> +       struct itimerspec ts = {
> +               {0, 0}, {0, 0}
> +       };
> +       int i;
> +
> +       ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
> +
> +       for (i = 0; i < ptp->info->n_alarm; i++) {
> +               if (ptp->alarm[i].pid) {
> +                       ptp->info->settimer(ptp->info->priv, i, 0, &ts);
> +                       put_pid(ptp->alarm[i].pid);
> +                       ptp->alarm[i].pid = NULL;
> +               }
> +       }
> +       return 0;
> +}
> +
> +static const struct file_operations ptp_fops = {
> +       .owner          = THIS_MODULE,
> +       .ioctl          = ptp_ioctl,
> +       .open           = ptp_open,
> +       .poll           = ptp_poll,
> +       .read           = ptp_read,
> +       .release        = ptp_release,
> +};
> +
> +/* sysfs */
> +
> +static ssize_t ptp_show_status(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       struct ptp_clock *ptp = dev_get_drvdata(dev);
> +       return sprintf(buf,
> +                      "maximum adjustment:  %d\n"
> +                      "programmable alarms: %d\n"
> +                      "external timestamps: %d\n"
> +                      "periodic outputs:    %d\n"
> +                      "has pps:             %d\n"
> +                      "device index:        %d\n",
> +                      ptp->info->max_adj,
> +                      ptp->info->n_alarm,
> +                      ptp->info->n_ext_ts,
> +                      ptp->info->n_per_out,
> +                      ptp->info->pps,
> +                      ptp->index);
> +}

Don't do this in a sysfs file.  Use a debugfs file if you want to
export freeform data like this.  sysfs files should contain either
only one value, or a simple list-of-same-type values.  Formatted data
is completely out.

> +
> +struct device_attribute ptp_attrs[] = {
> +       __ATTR(capabilities, S_IRUGO, ptp_show_status, NULL),
> +       __ATTR_NULL,
> +};
> +
> +/* module operations */
> +
> +static void __exit ptp_exit(void)
> +{
> +       class_destroy(ptp_class);
> +       unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
> +}
> +
> +static int __init ptp_init(void)
> +{
> +       int err;
> +
> +       INIT_LIST_HEAD(&clocks.list);
> +
> +       ptp_class = class_create(THIS_MODULE, "ptp");
> +       if (!ptp_class) {
> +               printk(KERN_ERR "ptp: failed to allocate class\n");
> +               return -ENOMEM;
> +       }
> +       ptp_class->dev_attrs = ptp_attrs;
> +
> +       err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
> +       if (err < 0) {
> +               printk(KERN_ERR "ptp: failed to allocate char device region\n");
> +               goto no_region;
> +       }
> +
> +       pr_info("PTP clock support registered\n");
> +       return 0;
> +
> +no_region:
> +       class_destroy(ptp_class);
> +       return err;
> +}
> +
> +subsys_initcall(ptp_init);
> +module_exit(ptp_exit);
> +
> +MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
> +MODULE_DESCRIPTION("PTP clocks support");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
> index 2fc8e14..9959fe4 100644
> --- a/include/linux/Kbuild
> +++ b/include/linux/Kbuild
> @@ -140,6 +140,7 @@ header-y += pkt_sched.h
>  header-y += posix_types.h
>  header-y += ppdev.h
>  header-y += prctl.h
> +header-y += ptp_clock.h
>  header-y += qnxtypes.h
>  header-y += qnx4_fs.h
>  header-y += radeonfb.h
> diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
> new file mode 100644
> index 0000000..5a509c5
> --- /dev/null
> +++ b/include/linux/ptp_clock.h
> @@ -0,0 +1,79 @@
> +/*
> + * PTP 1588 clock support - user space interface
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_H_
> +#define _PTP_CLOCK_H_
> +
> +#include <linux/ioctl.h>
> +#include <linux/types.h>
> +
> +#define PTP_ENABLE_FEATURE (1<<0)
> +#define PTP_RISING_EDGE    (1<<1)
> +#define PTP_FALLING_EDGE   (1<<2)
> +
> +enum ptp_request_types {
> +       PTP_REQUEST_EXTTS,
> +       PTP_REQUEST_PEROUT,
> +       PTP_REQUEST_PPS,
> +};
> +
> +struct ptp_clock_caps {
> +       __s32 max_adj; /* Maximum frequency adjustment, parts per billon. */
> +       int n_alarm;   /* Number of programmable alarms. */
> +       int n_ext_ts;  /* Number of external time stamp channels. */
> +       int n_per_out; /* Number of programmable periodic signals. */
> +       int pps;       /* Whether the clock supports a PPS callback. */
> +};
> +
> +struct ptp_clock_timer {
> +       int alarm_index;       /* Which alarm to query or configure. */
> +       int signum;            /* Requested signal. */
> +       int flags;             /* Zero or TIMER_ABSTIME, see TIMER_SETTIME(2) */
> +       struct itimerspec tsp; /* See TIMER_SETTIME(2) */
> +};
> +
> +struct ptp_clock_request {
> +       int type;  /* One of the ptp_request_types enumeration values. */
> +       int index; /* Which channel to configure. */
> +       struct timespec ts; /* For period signals, the desired period. */
> +       int flags; /* Bit field for PTP_ENABLE_FEATURE or other flags. */
> +};
> +
> +struct ptp_extts_event {
> +       int index;
> +       struct timespec ts;
> +};
> +
> +#define PTP_CLOCK_VERSION 0x00000001
> +
> +#define PTP_CLK_MAGIC '='
> +
> +#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)
> +#define PTP_CLOCK_ADJFREQ _IO  (PTP_CLK_MAGIC, 2)
> +#define PTP_CLOCK_ADJTIME _IOW (PTP_CLK_MAGIC, 3, struct timespec)
> +#define PTP_CLOCK_GETTIME _IOR (PTP_CLK_MAGIC, 4, struct timespec)
> +#define PTP_CLOCK_SETTIME _IOW (PTP_CLK_MAGIC, 5, struct timespec)
> +
> +#define PTP_CLOCK_GETCAPS   _IOR  (PTP_CLK_MAGIC, 6, struct ptp_clock_caps)
> +#define PTP_CLOCK_GETTIMER  _IOWR (PTP_CLK_MAGIC, 7, struct ptp_clock_timer)
> +#define PTP_CLOCK_SETTIMER  _IOW  (PTP_CLK_MAGIC, 8, struct ptp_clock_timer)
> +#define PTP_FEATURE_REQUEST _IOW  (PTP_CLK_MAGIC, 9, struct ptp_clock_request)
> +
> +#endif
> diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
> new file mode 100644
> index 0000000..d6cc158
> --- /dev/null
> +++ b/include/linux/ptp_clock_kernel.h
> @@ -0,0 +1,137 @@
> +/*
> + * PTP 1588 clock support
> + *
> + * Copyright (C) 2010 OMICRON electronics GmbH
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _PTP_CLOCK_KERNEL_H_
> +#define _PTP_CLOCK_KERNEL_H_
> +
> +#include <linux/ptp_clock.h>
> +
> +/**
> + * struct ptp_clock_info - decribes a PTP hardware clock
> + *
> + * @owner:     The clock driver should set to THIS_MODULE.
> + * @name:      A short name to identify the clock.
> + * @max_adj:   The maximum possible frequency adjustment, in parts per billon.
> + * @n_alarm:   The number of programmable alarms.
> + * @n_ext_ts:  The number of external time stamp channels.
> + * @n_per_out: The number of programmable periodic signals.
> + * @pps:       Indicates whether the clock supports a PPS callback.
> + * @priv:      Passed to the clock operations, for the driver's private use.
> + *
> + * clock operations
> + *
> + * @adjfreq:  Adjusts the frequency of the hardware clock.
> + *            parameter delta: Desired period change in parts per billion.
> + *
> + * @adjtime:  Shifts the time of the hardware clock.
> + *            parameter ts: Desired change in seconds and nanoseconds.
> + *
> + * @gettime:  Reads the current time from the hardware clock.
> + *            parameter ts: Holds the result.
> + *
> + * @settime:  Set the current time on the hardware clock.
> + *            parameter ts: Time value to set.
> + *
> + * @gettimer: Reads the time remaining from the given timer.
> + *            parameter index: Which alarm to query.
> + *            parameter ts: Holds the result.
> + *
> + * @settimer: Arms the given timer for periodic or one shot operation.
> + *            parameter index: Which alarm to set.
> + *            parameter abs: TIMER_ABSTIME, or zero for relative timer.
> + *            parameter ts: Alarm time and period to set.
> + *
> + * @enable:   Request driver to enable or disable an ancillary feature.
> + *            parameter request: Desired resource to enable or disable.
> + *            parameter on: Caller passes one to enable or zero to disable.
> + *
> + * The callbacks must all return zero on success, non-zero otherwise.
> + */
> +
> +struct ptp_clock_info {
> +       struct module *owner;
> +       char name[16];
> +       s32 max_adj;
> +       int n_alarm;
> +       int n_ext_ts;
> +       int n_per_out;
> +       int pps;
> +       void *priv;
> +       int (*adjfreq)(void *priv, s32 delta);
> +       int (*adjtime)(void *priv, struct timespec *ts);
> +       int (*gettime)(void *priv, struct timespec *ts);
> +       int (*settime)(void *priv, struct timespec *ts);
> +       int (*gettimer)(void *priv, int index, struct itimerspec *ts);
> +       int (*settimer)(void *priv, int index, int abs, struct itimerspec *ts);
> +       int (*enable)(void *priv, struct ptp_clock_request *request, int on);
> +};
> +
> +struct ptp_clock;
> +
> +/**
> + * ptp_clock_register() - register a PTP hardware clock driver
> + *
> + * @info:  Structure describing the new clock.
> + */
> +
> +extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info);
> +
> +/**
> + * ptp_clock_unregister() - unregister a PTP hardware clock driver
> + *
> + * @ptp:  The clock to remove from service.
> + */
> +
> +extern int ptp_clock_unregister(struct ptp_clock *ptp);
> +
> +
> +enum ptp_clock_events {
> +       PTP_CLOCK_ALARM,
> +       PTP_CLOCK_EXTTS,
> +       PTP_CLOCK_PPS,
> +};
> +
> +/**
> + * struct ptp_clock_event - decribes a PTP hardware clock event
> + *
> + * @type:  One of the ptp_clock_events enumeration values.
> + * @index: Identifies the source of the event.
> + * @timestamp: When the event occured.
> + */
> +
> +struct ptp_clock_event {
> +       int type;
> +       int index;
> +       u64 timestamp;
> +};
> +
> +/**
> + * ptp_clock_event() - notify the PTP layer about an event
> + *
> + * This function should only be called from interrupt context.
> + *
> + * @ptp:    The clock obtained from ptp_clock_register().
> + * @event:  Message structure describing the event.
> + */
> +
> +extern void ptp_clock_event(struct ptp_clock *ptp,
> +                           struct ptp_clock_event *event);
> +
> +#endif
> --
> 1.6.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Richard Cochran June 16, 2010, 2:22 p.m. UTC | #3
On Tue, Jun 15, 2010 at 11:00:10AM -0600, Grant Likely wrote:
> 
> Question from an ignorant reviewer:  Why a new interface instead of
> working with the existing high resolution timers infrastructure?

Short answer: Timers are only one part of the PTP API. If you offer
the PTP clock as a Linux clock source, then you could just use the
existing POSIX timers. However, we decided not to offer the PTP clock
in that way. The following excerpts from an upcoming paper explain
why:

\subsection{Basic Clock Operations}

   Based on our experience with a number of commercially available
   hardware clocks, we identified a set of four basic clock
   operations. Besides simply setting or getting the clock's time
   value, two additional operations are needed for clock control.
   Once a PTP slave has an initial estimate of the offset to its
   master, it typically would need to shift the clock by the offset
   atomically.  Also, the servo loop of a slave periodically needs to
   adjust the clock frequency.

\subsection{Ancillary Clock Features}

   Perhaps the most challenging design issue was deciding how to offer
   a PTP clock's capabilities to the GNU/Linux operating system.  As
   John Eidson pointed out~\cite{eidson2006measurement}, modern
   operating systems provide surprisingly little support for
   programming based on absolute time.  As the IEEE 1588 standard is
   being applied to a wide variety of test, measurement, and control
   applications, we can imagine many possible ways to use an embedded
   computer equipped with a PTP hardware clock.  We do not expect that
   any API will be able to cover every conceivable application of this
   technology.  However, the design presented here does cover common
   use cases based on the capabilities of currently available hardware
   clocks.

   The design allows user space programs to control all of a clock's
   ancillary features.  Programs may create one-shot or periodic
   alarms, with signal delivery on expiration.  \Timestamps on
   external events are provided via a First In, First Out (FIFO)
   interface.  If the clock has output signals, then their periods are
   configurable from user space.  Synchronization of the Linux system
   time via the PPS subsystem may be enabled or disabled as desired.

\subsection{Synchronizing the Linux System Clock}

   One important question that needed to be addressed was, now that we
   have a precise time source, how do we synchronize the Linux kernel
   to it?  The Linux kernel offers a modular clock infrastructure
   comprising ``clock sources'' and ``clock event devices.'' Clock
   sources provide a monotonically increasing time base, and clock
   event devices are used to schedule the next interrupt for various
   timer events.

   We considered but ultimately rejected the idea of offering the PTP
   clock to the Linux kernel as a combined clock source and clock
   event device.  The one great advantage of this approach would have
   been that it obviates the need for synchronization when the PTP
   clock is selected as the system timer.

   However, this approach is problematic when using certain kinds of
   clock hardware. For example, physical layer (PHY) chip based clocks
   can only be accessed by the relatively slow 16 bit wide MDIO
   bus. Such a clock would not be suitable for providing high
   resolution timers, which are now a standard Linux kernel feature.
   Furthermore, we cannot even be sure that a given hardware clock
   will offer any interrupt to the system at all.

   Instead, we elected to use the Pulse Per Second (PPS) subsystem as
   a method to optionally synchronize the Linux system time to the PTP
   clock.  This method is feasible even for clocks that do not offer
   fast register access, such as the PHY clocks.  Of course, the main
   disadvantage of this approach is that the Linux system time will
   not be exactly synchronized to the PTP clock time.  Since PTP
   clocks can be synchronized an order of magnitude better than the
   typical operating system scheduling latency, we expect that this
   method will still yield acceptable results for many applications.
   Applications with more demanding time requirements may use the new
   PTP interfaces directly when needed.

\subsection{System Calls or Character Device}

   When adding new functionality to an operating system, a basic design
   decision is how user space programs will call into the kernel.  For
   the Linux kernel, two different ways come into question, namely
   system calls or as a ``character device.''  In an attempt to make
   the PTP clock API easy to understand, we patterned it after the
   existing Network Time Protocol (NTP) and the POSIX timer APIs, as
   described in Section~\ref{UserAPI}.  Both of these services are
   exported to the user space as system calls.  However, we decided to
   offer the PTP clock as a character device because extending the NTP
   and POSIX interfaces seemed impractical.  In addition, the
   character device's \fn{read()} method provides a
   convenient way to deliver time stamped events to user space
   programs.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Richard Cochran Aug. 13, 2010, 9:34 a.m. UTC | #4
On Tue, Jun 15, 2010 at 01:11:30PM -0600, Grant Likely wrote:
> On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
> > +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
> 
> Doesn't appear that clocks is manipulated at atomic context.  Mutex instead?
...
> If the spinlock is changed to a mutex that is held for the entire
> function call, then the logic here can be simpler.

Grant,

I am working on another go at this patch series. Stupid question:

The caller of ptp_clock_register(), which takes the clocks_lock, is
always a module_init() function. Is this always a safe context in
which to call mutex_lock?

Thanks,

Richard

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Grant Likely Aug. 13, 2010, 11:54 p.m. UTC | #5
On Fri, Aug 13, 2010 at 3:34 AM, Richard Cochran
<richardcochran@gmail.com> wrote:
> On Tue, Jun 15, 2010 at 01:11:30PM -0600, Grant Likely wrote:
>> On Tue, Jun 15, 2010 at 10:09 AM, Richard Cochran
>> > +static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
>>
>> Doesn't appear that clocks is manipulated at atomic context.  Mutex instead?
> ...
>> If the spinlock is changed to a mutex that is held for the entire
>> function call, then the logic here can be simpler.
>
> Grant,
>
> I am working on another go at this patch series. Stupid question:
>
> The caller of ptp_clock_register(), which takes the clocks_lock, is
> always a module_init() function. Is this always a safe context in
> which to call mutex_lock?

Yes, you can take mutexes in the module_init context.

g.
diff mbox

Patch

diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
new file mode 100644
index 0000000..46858b3
--- /dev/null
+++ b/Documentation/ptp/ptp.txt
@@ -0,0 +1,95 @@ 
+
+* PTP infrastructure for Linux
+
+  This patch set introduces support for IEEE 1588 PTP clocks in
+  Linux. Together with the SO_TIMESTAMPING socket options, this
+  presents a standardized method for developing PTP user space
+  programs, synchronizing Linux with external clocks, and using the
+  ancillary features of PTP hardware clocks.
+
+  A new class driver exports a kernel interface for specific clock
+  drivers and a user space interface. The infrastructure supports a
+  complete set of PTP functionality.
+
+  + Basic clock operations
+    - Set time
+    - Get time
+    - Shift the clock by a given offset atomically
+    - Adjust clock frequency
+
+  + Ancillary clock features
+    - One short or periodic alarms, with signal delivery to user program
+    - Time stamp external events
+    - Period output signals configurable from user space
+    - Synchronization of the Linux system time via the PPS subsystem
+
+** PTP kernel API
+
+   A PTP clock driver registers itself with the class driver. The
+   class driver handles all of the dealings with user space. The
+   author of a clock driver need only implement the details of
+   programming the clock hardware. The clock driver notifies the class
+   driver of asynchronous events (alarms and external time stamps) via
+   a simple message passing interface.
+
+   The class driver supports multiple PTP clock drivers. In normal use
+   cases, only one PTP clock is needed. However, for testing and
+   development, it can be useful to have more than one clock in a
+   single system, in order to allow performance comparisons.
+
+** PTP user space API
+
+   The class driver creates a character device for each registered PTP
+   clock. User space programs may control the clock using standardized
+   ioctls. A program may query, enable, configure, and disable the
+   ancillary clock features. User space can receive time stamped
+   events via blocking read() and poll(). One shot and periodic
+   signals may be configured via an ioctl API with semantics similar
+   to the POSIX timer_settime() system call.
+
+   As an real life example, the following two patches for ptpd version
+   1.0.0 demonstrate how the API works.
+
+   https://sourceforge.net/tracker/?func=detail&aid=2992845&group_id=139814&atid=744634
+
+   https://sourceforge.net/tracker/?func=detail&aid=2992847&group_id=139814&atid=744634
+
+** Writing clock drivers
+
+   Clock drivers include include/linux/ptp_clock_kernel.h and register
+   themselves by presenting a 'struct ptp_clock_info' to the
+   registration method. Clock drivers must implement all of the
+   functions in the interface. If a clock does not offer a particular
+   ancillary feature, then the driver should just return -EOPNOTSUPP
+   from those functions.
+
+   Drivers must ensure that all of the methods in interface are
+   reentrant. Since most hardware implementations treat the time value
+   as a 64 bit integer accessed as two 32 bit registers, drivers
+   should use spin_lock_irqsave/spin_unlock_irqrestore to protect
+   against concurrent access. This locking cannot be accomplished in
+   class driver, since the lock may also be needed by the clock
+   driver's interrupt service routine.
+
+** Supported hardware
+
+   + Standard Linux system timer
+     - No special PTP features
+     - For use with software time stamping
+
+   + Freescale eTSEC gianfar
+     - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
+     - 2 Alarm registers (optional interrupt)
+     - 3 Periodic signals (optional interrupt)
+
+   + National DP83640
+     - 6 GPIOs programmable as inputs or outputs
+     - 6 GPIOs with dedicated functions (LED/JTAG/clock) can also be
+       used as general inputs or outputs
+     - GPIO inputs can time stamp external triggers
+     - GPIO outputs can produce periodic signals
+     - 1 interrupt pin
+
+   + Intel IXP465
+     - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
+     - Target Time (optional interrupt)
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
new file mode 100644
index 0000000..e30f758
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,269 @@ 
+/*
+ * PTP 1588 clock support - User space test program
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/ptp_clock.h>
+
+static void handle_alarm(int s)
+{
+	printf("received signal %d\n", s);
+}
+
+static int install_handler(int signum, void (*handler)(int))
+{
+	struct sigaction action;
+	sigset_t mask;
+
+	/* Unblock the signal. */
+	sigemptyset(&mask);
+	sigaddset(&mask, signum);
+	sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+	/* Install the signal handler. */
+	action.sa_handler = handler;
+	action.sa_flags = 0;
+	sigemptyset(&action.sa_mask);
+	sigaction(signum, &action, NULL);
+
+	return 0;
+}
+
+static void usage(char *progname)
+{
+	fprintf(stderr,
+		"usage: %s [options]\n"
+		" -a val     request a one-shot alarm after 'val' seconds\n"
+		" -A val     request a periodic alarm every 'val' seconds\n"
+		" -c         query the ptp clock's capabilities\n"
+		" -d name    device to open\n"
+		" -e val     read 'val' external time stamp events\n"
+		" -f val     adjust the ptp clock frequency by 'val' PPB\n"
+		" -g         get the ptp clock time\n"
+		" -h         prints this message\n"
+		" -s         set the ptp clock time from the system time\n"
+		" -t val     shift the ptp clock time by 'val' seconds\n"
+		" -v         query the ptp clock api version\n",
+		progname);
+}
+
+int main(int argc, char *argv[])
+{
+	struct ptp_clock_caps caps;
+	struct ptp_clock_timer timer;
+	struct ptp_extts_event event;
+	struct ptp_clock_request request;
+	struct timespec ts;
+	char *progname;
+	int c, cnt, fd, val = 0;
+
+	char *device = "/dev/ptp_clock_0";
+	int adjfreq = 0x7fffffff;
+	int adjtime = 0;
+	int capabilities = 0;
+	int extts = 0;
+	int gettime = 0;
+	int oneshot = 0;
+	int periodic = 0;
+	int settime = 0;
+	int version = 0;
+
+	progname = strrchr(argv[0], '/');
+	progname = progname ? 1+progname : argv[0];
+	while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghst:v"))) {
+		switch (c) {
+		case 'a':
+			oneshot = atoi(optarg);
+			break;
+		case 'A':
+			periodic = atoi(optarg);
+			break;
+		case 'c':
+			capabilities = 1;
+			break;
+		case 'd':
+			device = optarg;
+			break;
+		case 'e':
+			extts = atoi(optarg);
+			break;
+		case 'f':
+			adjfreq = atoi(optarg);
+			break;
+		case 'g':
+			gettime = 1;
+			break;
+		case 's':
+			settime = 1;
+			break;
+		case 't':
+			adjtime = atoi(optarg);
+			break;
+		case 'v':
+			version = 1;
+			break;
+		case 'h':
+			usage(progname);
+			return 0;
+		case '?':
+		default:
+			usage(progname);
+			return -1;
+		}
+	}
+
+	fd = open(device, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "cannot open %s: %s", device, strerror(errno));
+		return -1;
+	}
+
+	if (version) {
+		if (ioctl(fd, PTP_CLOCK_APIVERS, &val)) {
+			perror("PTP_CLOCK_APIVERS");
+		} else {
+			printf("version = 0x%08x\n", val);
+		}
+	}
+
+	if (capabilities) {
+		if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+			perror("PTP_CLOCK_GETCAPS");
+		} else {
+			printf("capabilities:\n"
+			       "  %d maximum frequency adjustment (PPB)\n"
+			       "  %d programmable alarms\n"
+			       "  %d external time stamp channels\n"
+			       "  %d programmable periodic signals\n"
+			       "  %d pulse per second\n",
+			       caps.max_adj,
+			       caps.n_alarm,
+			       caps.n_ext_ts,
+			       caps.n_per_out,
+			       caps.pps);
+		}
+	}
+
+	if (0x7fffffff != adjfreq) {
+		if (ioctl(fd, PTP_CLOCK_ADJFREQ, adjfreq)) {
+			perror("PTP_CLOCK_ADJFREQ");
+		} else {
+			puts("frequency adjustment okay");
+		}
+	}
+
+	if (adjtime) {
+		ts.tv_sec = adjtime;
+		ts.tv_nsec = 0;
+		if (ioctl(fd, PTP_CLOCK_ADJTIME, &ts)) {
+			perror("PTP_CLOCK_ADJTIME");
+		} else {
+			puts("time shift okay");
+		}
+	}
+
+	if (gettime) {
+		if (ioctl(fd, PTP_CLOCK_GETTIME, &ts)) {
+			perror("PTP_CLOCK_GETTIME");
+		} else {
+			printf("clock time: %ld.%09ld or %s",
+			       ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
+		}
+	}
+
+	if (settime) {
+		clock_gettime(CLOCK_REALTIME, &ts);
+		if (ioctl(fd, PTP_CLOCK_SETTIME, &ts)) {
+			perror("PTP_CLOCK_SETTIME");
+		} else {
+			puts("set time okay");
+		}
+	}
+
+	if (extts) {
+		memset(&request, 0, sizeof(request));
+		request.type = PTP_REQUEST_EXTTS;
+		request.index = 0;
+		request.flags = PTP_ENABLE_FEATURE;
+		if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+			perror("PTP_FEATURE_REQUEST");
+			extts = 0;
+		} else {
+			puts("set timer okay");
+		}
+		for (; extts; extts--) {
+			cnt = read(fd, &event, sizeof(event));
+			if (cnt != sizeof(event)) {
+				perror("read");
+				break;
+			}
+			printf("event index %d at %ld.%09ld\n", event.index,
+			       event.ts.tv_sec, event.ts.tv_nsec);
+		}
+		/* Disable the feature again. */
+		request.flags = 0;
+		if (ioctl(fd, PTP_FEATURE_REQUEST, &request)) {
+			perror("PTP_FEATURE_REQUEST");
+		}
+	}
+
+	if (oneshot) {
+		install_handler(SIGALRM, handle_alarm);
+		memset(&timer, 0, sizeof(timer));
+		timer.signum = SIGALRM;
+		timer.tsp.it_value.tv_sec = oneshot;
+		if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
+			perror("PTP_CLOCK_SETTIMER");
+		} else {
+			puts("set timer okay");
+		}
+		pause();
+	}
+
+	if (periodic) {
+		install_handler(SIGALRM, handle_alarm);
+		memset(&timer, 0, sizeof(timer));
+		timer.signum = SIGALRM;
+		timer.tsp.it_value.tv_sec = periodic;
+		timer.tsp.it_interval.tv_sec = periodic;
+		if (ioctl(fd, PTP_CLOCK_SETTIMER, &timer)) {
+			perror("PTP_CLOCK_SETTIMER");
+		} else {
+			puts("set timer okay");
+		}
+		while (1) {
+			pause();
+		}
+	}
+
+	close(fd);
+	return 0;
+}
diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
new file mode 100644
index 0000000..4ef2d97
--- /dev/null
+++ b/Documentation/ptp/testptp.mk
@@ -0,0 +1,33 @@ 
+# PTP 1588 clock support - User space test program
+#
+# Copyright (C) 2010 OMICRON electronics GmbH
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CC        = $(CROSS_COMPILE)gcc
+INC       = -I$(KBUILD_OUTPUT)/usr/include
+CFLAGS    = -Wall $(INC)
+LDLIBS    = -lrt
+PROGS     = testptp
+
+all: $(PROGS)
+
+testptp: testptp.o
+
+clean:
+	rm -f testptp.o
+
+distclean: clean
+	rm -f $(PROGS)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f..774fbd7 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@  source "drivers/spi/Kconfig"
 
 source "drivers/pps/Kconfig"
 
+source "drivers/ptp/Kconfig"
+
 source "drivers/gpio/Kconfig"
 
 source "drivers/w1/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 91874e0..6d12b48 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,6 +76,7 @@  obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-y				+= i2c/ media/
 obj-$(CONFIG_PPS)		+= pps/
+obj-$(CONFIG_PTP_1588_CLOCK)	+= ptp/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_POWER_SUPPLY)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
new file mode 100644
index 0000000..c80a25b
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,26 @@ 
+#
+# PTP clock support configuration
+#
+
+menu "PTP clock support"
+
+config PTP_1588_CLOCK
+	tristate "PTP clock support"
+	depends on EXPERIMENTAL
+	help
+	  The IEEE 1588 standard defines a method to precisely
+	  synchronize distributed clocks over Ethernet networks. The
+	  standard defines a Precision Time Protocol (PTP), which can
+	  be used to achieve synchronization within a few dozen
+	  microseconds. In addition, with the help of special hardware
+	  time stamping units, it can be possible to achieve
+	  synchronization to within a few hundred nanoseconds.
+
+	  This driver adds support for PTP clocks as character
+	  devices. If you want to use a PTP clock, then you should
+	  also enable at least one clock driver as well.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ptp_clock.
+
+endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
new file mode 100644
index 0000000..b86695c
--- /dev/null
+++ b/drivers/ptp/Makefile
@@ -0,0 +1,5 @@ 
+#
+# Makefile for PTP 1588 clock support.
+#
+
+obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp_clock.o
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..4753bf3
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,514 @@ 
+/*
+ * PTP 1588 clock support
+ *
+ * Partially adapted from the Linux PPS driver.
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/bitops.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/ptp_clock_kernel.h>
+#include <linux/ptp_clock.h>
+
+#define PTP_MAX_ALARMS 4
+#define PTP_MAX_CLOCKS BITS_PER_LONG
+#define PTP_MAX_TIMESTAMPS 128
+
+struct alarm {
+	struct pid *pid;
+	int sig;
+};
+
+struct timestamp_event_queue {
+	struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
+	int head;
+	int tail;
+	int overflow;
+};
+
+struct ptp_clock {
+	struct list_head list;
+	struct cdev cdev;
+	struct device *dev;
+	struct ptp_clock_info *info;
+	dev_t devid;
+	int index; /* index into clocks.map, also the minor number */
+
+	struct alarm alarm[PTP_MAX_ALARMS];
+	struct mutex alarm_mux; /* one process at a time setting an alarm */
+
+	struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+	struct mutex tsevq_mux; /* one process at a time reading the fifo */
+	wait_queue_head_t tsev_wq;
+};
+
+/* private globals */
+
+static const struct file_operations ptp_fops;
+static dev_t ptp_devt;
+static struct class *ptp_class;
+
+static struct {
+	struct list_head list;
+	DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
+} clocks;
+static DEFINE_SPINLOCK(clocks_lock); /* protects 'clocks' */
+
+/* time stamp event queue operations */
+
+static inline int queue_cnt(struct timestamp_event_queue *q)
+{
+	int cnt = q->tail - q->head;
+	return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
+}
+
+static inline int queue_free(struct timestamp_event_queue *q)
+{
+	return PTP_MAX_TIMESTAMPS - queue_cnt(q) - 1;
+}
+
+static void enqueue_external_timestamp(struct timestamp_event_queue *queue,
+				       struct ptp_clock_event *src)
+{
+	struct ptp_extts_event *dst;
+	u32 remainder;
+
+	dst = &queue->buf[queue->tail];
+
+	dst->index = src->index;
+	dst->ts.tv_sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
+	dst->ts.tv_nsec = remainder;
+
+	if (!queue_free(queue))
+		queue->overflow++;
+
+	queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
+}
+
+/* public interface */
+
+struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
+{
+	struct ptp_clock *ptp;
+	int err = 0, index, major = MAJOR(ptp_devt);
+	unsigned long flags;
+
+	if (info->n_alarm > PTP_MAX_ALARMS)
+		return ERR_PTR(-EINVAL);
+
+	/* Find a free clock slot and reserve it. */
+	err = -EBUSY;
+	spin_lock_irqsave(&clocks_lock, flags);
+	index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
+	if (index < PTP_MAX_CLOCKS) {
+		set_bit(index, clocks.map);
+		spin_unlock_irqrestore(&clocks_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&clocks_lock, flags);
+		goto no_clock;
+	}
+
+	/* Initialize a clock structure. */
+	err = -ENOMEM;
+	ptp = kzalloc(sizeof(struct ptp_clock), GFP_KERNEL);
+	if (ptp == NULL)
+		goto no_memory;
+
+	ptp->info = info;
+	ptp->devid = MKDEV(major, index);
+	ptp->index = index;
+	mutex_init(&ptp->alarm_mux);
+	mutex_init(&ptp->tsevq_mux);
+	init_waitqueue_head(&ptp->tsev_wq);
+
+	/* Create a new device in our class. */
+	ptp->dev = device_create(ptp_class, NULL, ptp->devid, ptp,
+				 "ptp_clock_%d", ptp->index);
+	if (IS_ERR(ptp->dev))
+		goto no_device;
+
+	dev_set_drvdata(ptp->dev, ptp);
+
+	/* Register a character device. */
+	cdev_init(&ptp->cdev, &ptp_fops);
+	ptp->cdev.owner = info->owner;
+	err = cdev_add(&ptp->cdev, ptp->devid, 1);
+	if (err)
+		goto no_cdev;
+
+	/* Clock is ready, add it into the list. */
+	spin_lock_irqsave(&clocks_lock, flags);
+	list_add(&ptp->list, &clocks.list);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+
+	return ptp;
+
+no_cdev:
+	device_destroy(ptp_class, ptp->devid);
+no_device:
+	mutex_destroy(&ptp->alarm_mux);
+	mutex_destroy(&ptp->tsevq_mux);
+	kfree(ptp);
+no_memory:
+	spin_lock_irqsave(&clocks_lock, flags);
+	clear_bit(index, clocks.map);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+no_clock:
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+	unsigned long flags;
+
+	/* Release the clock's resources. */
+	cdev_del(&ptp->cdev);
+	device_destroy(ptp_class, ptp->devid);
+	mutex_destroy(&ptp->alarm_mux);
+	mutex_destroy(&ptp->tsevq_mux);
+
+	/* Remove the clock from the list. */
+	spin_lock_irqsave(&clocks_lock, flags);
+	list_del(&ptp->list);
+	clear_bit(ptp->index, clocks.map);
+	spin_unlock_irqrestore(&clocks_lock, flags);
+
+	kfree(ptp);
+
+	return 0;
+}
+EXPORT_SYMBOL(ptp_clock_unregister);
+
+void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+	switch (event->type) {
+
+	case PTP_CLOCK_ALARM:
+		kill_pid(ptp->alarm[event->index].pid,
+			 ptp->alarm[event->index].sig, 1);
+		break;
+
+	case PTP_CLOCK_EXTTS:
+		enqueue_external_timestamp(&ptp->tsevq, event);
+		wake_up_interruptible(&ptp->tsev_wq);
+		break;
+
+	case PTP_CLOCK_PPS:
+		break;
+	}
+}
+EXPORT_SYMBOL(ptp_clock_event);
+
+/* character device operations */
+
+static int ptp_ioctl(struct inode *node, struct file *fp,
+		      unsigned int cmd, unsigned long arg)
+{
+	struct ptp_clock_caps caps;
+	struct ptp_clock_request req;
+	struct ptp_clock_timer timer;
+	struct ptp_clock *ptp = fp->private_data;
+	struct ptp_clock_info *ops = ptp->info;
+	void *priv = ops->priv;
+	struct timespec ts;
+	int flags, index;
+	int err = 0;
+
+	switch (cmd) {
+
+	case PTP_CLOCK_APIVERS:
+		err = put_user(PTP_CLOCK_VERSION, (u32 __user *)arg);
+		break;
+
+	case PTP_CLOCK_ADJFREQ:
+		if (!capable(CAP_SYS_TIME))
+			return -EPERM;
+		err = ops->adjfreq(priv, arg);
+		break;
+
+	case PTP_CLOCK_ADJTIME:
+		if (!capable(CAP_SYS_TIME))
+			return -EPERM;
+		if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
+			err = -EFAULT;
+		else
+			err = ops->adjtime(priv, &ts);
+		break;
+
+	case PTP_CLOCK_GETTIME:
+		err = ops->gettime(priv, &ts);
+		if (err)
+			break;
+		err = copy_to_user((void __user *)arg, &ts, sizeof(ts));
+		break;
+
+	case PTP_CLOCK_SETTIME:
+		if (!capable(CAP_SYS_TIME))
+			return -EPERM;
+		if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
+			err = -EFAULT;
+		else
+			err = ops->settime(priv, &ts);
+		break;
+
+	case PTP_CLOCK_GETCAPS:
+		memset(&caps, 0, sizeof(caps));
+		caps.max_adj = ptp->info->max_adj;
+		caps.n_alarm = ptp->info->n_alarm;
+		caps.n_ext_ts = ptp->info->n_ext_ts;
+		caps.n_per_out = ptp->info->n_per_out;
+		caps.pps = ptp->info->pps;
+		err = copy_to_user((void __user *)arg, &caps, sizeof(caps));
+		break;
+
+	case PTP_CLOCK_GETTIMER:
+		if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
+			err = -EFAULT;
+			break;
+		}
+		index = timer.alarm_index;
+		if (index < 0 || index >= ptp->info->n_alarm) {
+			err = -EINVAL;
+			break;
+		}
+		err = ops->gettimer(priv, index, &timer.tsp);
+		if (err)
+			break;
+		err = copy_to_user((void __user *)arg, &timer, sizeof(timer));
+		break;
+
+	case PTP_CLOCK_SETTIMER:
+		if (copy_from_user(&timer, (void __user *)arg, sizeof(timer))) {
+			err = -EFAULT;
+			break;
+		}
+		index = timer.alarm_index;
+		if (index < 0 || index >= ptp->info->n_alarm) {
+			err = -EINVAL;
+			break;
+		}
+		if (!valid_signal(timer.signum))
+			return -EINVAL;
+		flags = timer.flags;
+		if (flags & (flags != TIMER_ABSTIME)) {
+			err = -EINVAL;
+			break;
+		}
+		if (mutex_lock_interruptible(&ptp->alarm_mux))
+			return -ERESTARTSYS;
+
+		if (ptp->alarm[index].pid)
+			put_pid(ptp->alarm[index].pid);
+
+		ptp->alarm[index].pid = get_pid(task_pid(current));
+		ptp->alarm[index].sig = timer.signum;
+		err = ops->settimer(priv, index, flags, &timer.tsp);
+
+		mutex_unlock(&ptp->alarm_mux);
+		break;
+
+	case PTP_FEATURE_REQUEST:
+		if (copy_from_user(&req, (void __user *)arg, sizeof(req))) {
+			err = -EFAULT;
+			break;
+		}
+		switch (req.type) {
+		case PTP_REQUEST_EXTTS:
+		case PTP_REQUEST_PEROUT:
+			break;
+		case PTP_REQUEST_PPS:
+			if (!capable(CAP_SYS_TIME))
+				return -EPERM;
+			break;
+		default:
+			err = -EINVAL;
+			break;
+		}
+		if (err)
+			break;
+		err = ops->enable(priv, &req,
+				  req.flags & PTP_ENABLE_FEATURE ? 1 : 0);
+		break;
+
+	default:
+		err = -ENOTTY;
+		break;
+	}
+	return err;
+}
+
+static int ptp_open(struct inode *inode, struct file *fp)
+{
+	struct ptp_clock *ptp;
+	ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
+
+	fp->private_data = ptp;
+
+	return 0;
+}
+
+static unsigned int ptp_poll(struct file *fp, poll_table *wait)
+{
+	struct ptp_clock *ptp = fp->private_data;
+
+	poll_wait(fp, &ptp->tsev_wq, wait);
+
+	return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
+}
+
+static ssize_t ptp_read(struct file *fp, char __user *buf,
+			size_t cnt, loff_t *off)
+{
+	struct ptp_clock *ptp = fp->private_data;
+	struct timestamp_event_queue *queue = &ptp->tsevq;
+	struct ptp_extts_event *event;
+	size_t qcnt;
+
+	if (mutex_lock_interruptible(&ptp->tsevq_mux))
+		return -ERESTARTSYS;
+
+	cnt = cnt / sizeof(struct ptp_extts_event);
+
+	if (wait_event_interruptible(ptp->tsev_wq,
+				     (qcnt = queue_cnt(&ptp->tsevq)))) {
+		mutex_unlock(&ptp->tsevq_mux);
+		return -ERESTARTSYS;
+	}
+
+	if (cnt > qcnt)
+		cnt = qcnt;
+
+	event = &queue->buf[queue->head];
+
+	if (copy_to_user(buf, event, cnt * sizeof(struct ptp_extts_event))) {
+		mutex_unlock(&ptp->tsevq_mux);
+		return -EFAULT;
+	}
+	queue->head = (queue->head + cnt) % PTP_MAX_TIMESTAMPS;
+
+	mutex_unlock(&ptp->tsevq_mux);
+
+	return cnt * sizeof(struct ptp_extts_event);
+}
+
+static int ptp_release(struct inode *inode, struct file *fp)
+{
+	struct ptp_clock *ptp;
+	struct itimerspec ts = {
+		{0, 0}, {0, 0}
+	};
+	int i;
+
+	ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
+
+	for (i = 0; i < ptp->info->n_alarm; i++) {
+		if (ptp->alarm[i].pid) {
+			ptp->info->settimer(ptp->info->priv, i, 0, &ts);
+			put_pid(ptp->alarm[i].pid);
+			ptp->alarm[i].pid = NULL;
+		}
+	}
+	return 0;
+}
+
+static const struct file_operations ptp_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= ptp_ioctl,
+	.open		= ptp_open,
+	.poll		= ptp_poll,
+	.read		= ptp_read,
+	.release	= ptp_release,
+};
+
+/* sysfs */
+
+static ssize_t ptp_show_status(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	return sprintf(buf,
+		       "maximum adjustment:  %d\n"
+		       "programmable alarms: %d\n"
+		       "external timestamps: %d\n"
+		       "periodic outputs:    %d\n"
+		       "has pps:             %d\n"
+		       "device index:        %d\n",
+		       ptp->info->max_adj,
+		       ptp->info->n_alarm,
+		       ptp->info->n_ext_ts,
+		       ptp->info->n_per_out,
+		       ptp->info->pps,
+		       ptp->index);
+}
+
+struct device_attribute ptp_attrs[] = {
+	__ATTR(capabilities, S_IRUGO, ptp_show_status, NULL),
+	__ATTR_NULL,
+};
+
+/* module operations */
+
+static void __exit ptp_exit(void)
+{
+	class_destroy(ptp_class);
+	unregister_chrdev_region(ptp_devt, PTP_MAX_CLOCKS);
+}
+
+static int __init ptp_init(void)
+{
+	int err;
+
+	INIT_LIST_HEAD(&clocks.list);
+
+	ptp_class = class_create(THIS_MODULE, "ptp");
+	if (!ptp_class) {
+		printk(KERN_ERR "ptp: failed to allocate class\n");
+		return -ENOMEM;
+	}
+	ptp_class->dev_attrs = ptp_attrs;
+
+	err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
+	if (err < 0) {
+		printk(KERN_ERR "ptp: failed to allocate char device region\n");
+		goto no_region;
+	}
+
+	pr_info("PTP clock support registered\n");
+	return 0;
+
+no_region:
+	class_destroy(ptp_class);
+	return err;
+}
+
+subsys_initcall(ptp_init);
+module_exit(ptp_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clocks support");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 2fc8e14..9959fe4 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -140,6 +140,7 @@  header-y += pkt_sched.h
 header-y += posix_types.h
 header-y += ppdev.h
 header-y += prctl.h
+header-y += ptp_clock.h
 header-y += qnxtypes.h
 header-y += qnx4_fs.h
 header-y += radeonfb.h
diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
new file mode 100644
index 0000000..5a509c5
--- /dev/null
+++ b/include/linux/ptp_clock.h
@@ -0,0 +1,79 @@ 
+/*
+ * PTP 1588 clock support - user space interface
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PTP_CLOCK_H_
+#define _PTP_CLOCK_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define PTP_ENABLE_FEATURE (1<<0)
+#define PTP_RISING_EDGE    (1<<1)
+#define PTP_FALLING_EDGE   (1<<2)
+
+enum ptp_request_types {
+	PTP_REQUEST_EXTTS,
+	PTP_REQUEST_PEROUT,
+	PTP_REQUEST_PPS,
+};
+
+struct ptp_clock_caps {
+	__s32 max_adj; /* Maximum frequency adjustment, parts per billon. */
+	int n_alarm;   /* Number of programmable alarms. */
+	int n_ext_ts;  /* Number of external time stamp channels. */
+	int n_per_out; /* Number of programmable periodic signals. */
+	int pps;       /* Whether the clock supports a PPS callback. */
+};
+
+struct ptp_clock_timer {
+	int alarm_index;       /* Which alarm to query or configure. */
+	int signum;            /* Requested signal. */
+	int flags;             /* Zero or TIMER_ABSTIME, see TIMER_SETTIME(2) */
+	struct itimerspec tsp; /* See TIMER_SETTIME(2) */
+};
+
+struct ptp_clock_request {
+	int type;  /* One of the ptp_request_types enumeration values. */
+	int index; /* Which channel to configure. */
+	struct timespec ts; /* For period signals, the desired period. */
+	int flags; /* Bit field for PTP_ENABLE_FEATURE or other flags. */
+};
+
+struct ptp_extts_event {
+	int index;
+	struct timespec ts;
+};
+
+#define PTP_CLOCK_VERSION 0x00000001
+
+#define PTP_CLK_MAGIC '='
+
+#define PTP_CLOCK_APIVERS _IOR (PTP_CLK_MAGIC, 1, __u32)
+#define PTP_CLOCK_ADJFREQ _IO  (PTP_CLK_MAGIC, 2)
+#define PTP_CLOCK_ADJTIME _IOW (PTP_CLK_MAGIC, 3, struct timespec)
+#define PTP_CLOCK_GETTIME _IOR (PTP_CLK_MAGIC, 4, struct timespec)
+#define PTP_CLOCK_SETTIME _IOW (PTP_CLK_MAGIC, 5, struct timespec)
+
+#define PTP_CLOCK_GETCAPS   _IOR  (PTP_CLK_MAGIC, 6, struct ptp_clock_caps)
+#define PTP_CLOCK_GETTIMER  _IOWR (PTP_CLK_MAGIC, 7, struct ptp_clock_timer)
+#define PTP_CLOCK_SETTIMER  _IOW  (PTP_CLK_MAGIC, 8, struct ptp_clock_timer)
+#define PTP_FEATURE_REQUEST _IOW  (PTP_CLK_MAGIC, 9, struct ptp_clock_request)
+
+#endif
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
new file mode 100644
index 0000000..d6cc158
--- /dev/null
+++ b/include/linux/ptp_clock_kernel.h
@@ -0,0 +1,137 @@ 
+/*
+ * PTP 1588 clock support
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _PTP_CLOCK_KERNEL_H_
+#define _PTP_CLOCK_KERNEL_H_
+
+#include <linux/ptp_clock.h>
+
+/**
+ * struct ptp_clock_info - decribes a PTP hardware clock
+ *
+ * @owner:     The clock driver should set to THIS_MODULE.
+ * @name:      A short name to identify the clock.
+ * @max_adj:   The maximum possible frequency adjustment, in parts per billon.
+ * @n_alarm:   The number of programmable alarms.
+ * @n_ext_ts:  The number of external time stamp channels.
+ * @n_per_out: The number of programmable periodic signals.
+ * @pps:       Indicates whether the clock supports a PPS callback.
+ * @priv:      Passed to the clock operations, for the driver's private use.
+ *
+ * clock operations
+ *
+ * @adjfreq:  Adjusts the frequency of the hardware clock.
+ *            parameter delta: Desired period change in parts per billion.
+ *
+ * @adjtime:  Shifts the time of the hardware clock.
+ *            parameter ts: Desired change in seconds and nanoseconds.
+ *
+ * @gettime:  Reads the current time from the hardware clock.
+ *            parameter ts: Holds the result.
+ *
+ * @settime:  Set the current time on the hardware clock.
+ *            parameter ts: Time value to set.
+ *
+ * @gettimer: Reads the time remaining from the given timer.
+ *            parameter index: Which alarm to query.
+ *            parameter ts: Holds the result.
+ *
+ * @settimer: Arms the given timer for periodic or one shot operation.
+ *            parameter index: Which alarm to set.
+ *            parameter abs: TIMER_ABSTIME, or zero for relative timer.
+ *            parameter ts: Alarm time and period to set.
+ *
+ * @enable:   Request driver to enable or disable an ancillary feature.
+ *            parameter request: Desired resource to enable or disable.
+ *            parameter on: Caller passes one to enable or zero to disable.
+ *
+ * The callbacks must all return zero on success, non-zero otherwise.
+ */
+
+struct ptp_clock_info {
+	struct module *owner;
+	char name[16];
+	s32 max_adj;
+	int n_alarm;
+	int n_ext_ts;
+	int n_per_out;
+	int pps;
+	void *priv;
+	int (*adjfreq)(void *priv, s32 delta);
+	int (*adjtime)(void *priv, struct timespec *ts);
+	int (*gettime)(void *priv, struct timespec *ts);
+	int (*settime)(void *priv, struct timespec *ts);
+	int (*gettimer)(void *priv, int index, struct itimerspec *ts);
+	int (*settimer)(void *priv, int index, int abs, struct itimerspec *ts);
+	int (*enable)(void *priv, struct ptp_clock_request *request, int on);
+};
+
+struct ptp_clock;
+
+/**
+ * ptp_clock_register() - register a PTP hardware clock driver
+ *
+ * @info:  Structure describing the new clock.
+ */
+
+extern struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info);
+
+/**
+ * ptp_clock_unregister() - unregister a PTP hardware clock driver
+ *
+ * @ptp:  The clock to remove from service.
+ */
+
+extern int ptp_clock_unregister(struct ptp_clock *ptp);
+
+
+enum ptp_clock_events {
+	PTP_CLOCK_ALARM,
+	PTP_CLOCK_EXTTS,
+	PTP_CLOCK_PPS,
+};
+
+/**
+ * struct ptp_clock_event - decribes a PTP hardware clock event
+ *
+ * @type:  One of the ptp_clock_events enumeration values.
+ * @index: Identifies the source of the event.
+ * @timestamp: When the event occured.
+ */
+
+struct ptp_clock_event {
+	int type;
+	int index;
+	u64 timestamp;
+};
+
+/**
+ * ptp_clock_event() - notify the PTP layer about an event
+ *
+ * This function should only be called from interrupt context.
+ *
+ * @ptp:    The clock obtained from ptp_clock_register().
+ * @event:  Message structure describing the event.
+ */
+
+extern void ptp_clock_event(struct ptp_clock *ptp,
+			    struct ptp_clock_event *event);
+
+#endif