linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
@ 2010-09-23 17:30 Richard Cochran
  2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
                   ` (10 more replies)
  0 siblings, 11 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:30 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

Here is the sixth version of my patch set adding PTP hardware clock
support to the Linux kernel. The main difference to v5 is that the
character device interface has been replaced with one based on the
posix clock system calls.

The first three patches add necessary background support in the posix
clock code. The last five add the new PTP hardware clock features.
Previously, I had tried to present the posix clock changes all by
themselves, but commentators asked to see the whole context.

What follows is a rather lengthy discussion of the various design
issues.

Table of Contents
=================
1 Introduction 
2 Previous Discussions 
3 Design Issues 
    3.1 Clock Operations 
    3.2 Character Device vs System Calls 
        3.2.1 Using the POSIX Clock API 
        3.2.2 Tuning a POSIX Clock 
        3.2.3 Dynamic POSIX Clock IDs 
    3.3 Synchronizing the Linux System Time 
    3.4 Ancillary PHC Operations 
    3.5 User timers 
4 Drivers 
    4.1 Supported Hardware Clocks 
    4.2 Open Driver Issues 
        4.2.1 DP83640 
        4.2.2 IXP465 


1 Introduction 
~~~~~~~~~~~~~~~

  The aim of this patch set is to add support for PTP hardware clocks
  into the Linux kernel. In the following description, we use the
  abbreviation "PHC" to mean "PTP hardware clock." 

  Support for obtaining timestamps from a PHC already exists via the
  SO_TIMESTAMPING socket option, integrated in kernel version 2.6.30.
  This patch set completes the picture by allow user space programs to
  adjust the PHC and to control its ancillary features.

2 Previous Discussions 
~~~~~~~~~~~~~~~~~~~~~~~

  This patch set previously appeared on the netdev list. Since V5 of
  the character device patch set, the discussion has moved to the
  lkml.

  - PTP hardware clock as a character device V5
    [http://lkml.org/lkml/2010/8/16/90]

  - POSIX clock tuning syscall with static clock ids
    [http://lkml.org/lkml/2010/8/23/49]

  - POSIX clock tuning syscall with dynamic clock ids
    [http://lkml.org/lkml/2010/9/3/119]

3 Design Issues 
~~~~~~~~~~~~~~~~

3.1 Clock Operations 
=====================

   Based on experience with several commercially available PHCs, we
   identified a set of essential operations and a set of ancillary
   operations.

   - Basic clock operations

     1. Set time
     2. Get time
     3. Shift the clock by a given offset atomically
     4. Adjust clock frequency

   - Ancillary clock features

     1. Time stamp external events
     2. Enable Linux PPS subsystem events
     3. Periodic output signals
     4. One shot or periodic alarms, with CPU interrupt

    The patch set includes examples of the first two ancillary
    features, and implementing the third point for a particular PHC is
    fairly straightforward. The fourth point is discussed below.

3.2 Character Device vs System Calls 
=====================================

   This patch set started out as a class driver that exposes the PHC
   as a character device with standardized ioctls. Since several clock
   operations in the ioctl interface mimic the POSIX clock API, the
   suggestion was made to expose the PHC as a new clockid_t.

   POSIX defines the CLOCK_REALTIME, CLOCK_MONOTONIC,
   CLOCK_PROCESS_CPUTIME_ID, and CLOCK_THREAD_CPUTIME_ID clock ids.
   As to other possible clock ids, the standard offers the following
   hint:

      An implementation may also support additional clocks. The
      interpretation of time values for these clocks is unspecified.

   So as far as the POSIX standard is concerned, offering a clock id
   to represent the PHC would be acceptable.

   From discussions on the lkml, a repeated wish was to ensure that
   any changes in the POSIX clock code would be general enough to
   support other new hardware clocks that might appear in the future,
   not just the particulars of PHCs.

3.2.1 Using the POSIX Clock API 
--------------------------------

    Looking at the mapping from PHC operation to the POSIX clock API,
    we see that two of the basic clock operations, marked with *, have
    no POSIX equivalent. The items marked NA are peculiar to PHCs and
    will be discussed separately, below.
    
      Clock Operation               POSIX function               
     -----------------------------+-----------------------------
      Set time                      clock_gettime                
      Get time                      clock_settime                
      Shift the clock               *                            
      Adjust clock frequency        *                            
     -----------------------------+-----------------------------
      Time stamp external events    NA                           
      Enable PPS events             NA                           
      Periodic output signals       NA                           
      One shot or periodic alarms   timer_create, timer_settime  

    In contrast to the standard Linux system clock, a PHC is
    adjustable in hardware, for example using frequency compensation
    registers or a VCO. The ability to directly tune the PHC is
    essential to reap the benefit of hardware timestamping.

3.2.2 Tuning a POSIX Clock 
---------------------------

    The patch set introduces a new system call which allows tuning of
    a POSIX clock. The function combines the 'struct timex' from the
    NTP adjtimex syscall with a POSIX clock id.

    clock_adjtime(clockid_t, struct timex *);

    Although the timex interface is a bit baroque (John Stultz said:
    "a little crufty"), using it as the basis of the new call allows
    supporting the tried and true NTP semantics.  By adding one
    additional mode flag to the struct timex, the requirements for
    PHCs are also satisfied. In the future, if new clocks appear that
    require more elaborate control, then the padding at the end of the
    struct reserves 44 bytes for new fields.

3.2.3 Dynamic POSIX Clock IDs 
------------------------------

    The reaction on the list to having a static id like CLOCK_PTP was
    mostly negative. However, the idea of generating a clock id
    dynamically seems to have gained acceptance. The general idea is
    to advertise the available clock ids to user space via sysfs. This
    patch set implements two different ways:

    /sys/class/timesource/<name>/id
    /sys/class/ptp/ptp_clock_X/id

    Note: I am not too sure that this is exactly what people imagined,
          but it is my best understanding so far. I gleaned two
          different ideas about where to offer the clock id. In order
          to keep just one way, I will be happy to remove the less
          popular one.

3.3 Synchronizing the Linux System Time 
========================================

   One could offer a PHC as a combined clock source and clock event
   device. The advantage of this approach would be that it obviates
   the need for synchronization when the PHC is selected as the system
   timer. However, some PHCs, namely the PHY based clocks, cannot be
   used in this way.

   Instead, the patch set provides a way to offer a Pulse Per Second
   (PPS) event from the PHC to the Linux PPS subsystem. A user space
   application can read the PPS events and tune the system clock, just
   like when using other external time sources like radio clocks or
   GPS.

3.4 Ancillary PHC Operations 
=============================

   Most PHCs offer hardware interfaces to the outside world, that is,
   the "real world". It is important to offer support for these
   operations, since leaving them out would defeat the utility of
   having a PHC in the first place. These operations do not map at all
   to the POSIX clock functions, but one could offer them as a
   character device or via sysfs.

   Fearing a lkml debate on the merits of either one, I went ahead and
   implemented both cases. Both approaches adequately cover the needed
   functionality, in my opinion. The code for each is in its own .c
   file, so it will be easy enough to remove one of them. I don't mind
   leaving both ways in, either.

3.5 User timers 
================

   Using the POSIX clock API gived user space the possibility to
   create and use timers with timer_create and timer_settime. In the
   current patch set the kernel functionality is not implemented,
   since there are some issues to consider first. I see two ways to do
   about this.

   1. Implement the functionality anew. This approach might end up
      duplicating similar code that already exists. Also, looking at
      the hrtimer code, getting user timers right seems to have a
      number of gotchas and thorny issues.

   2. Reuse the hrtimer code. Since the hrtimer code uses a clock
      event device under the hood, it might be possible (in theory) to
      offer capable PHCs as clock event devices. However, the current
      hrtimers are hard-coded to the event device via a per-cpu
      global. Perhaps one could associate an event device with a
      hrtimer via the timer itself.

   At this point I am not optimistic about either approach, and I
   would vote for postponing the timer issue indefinitely. The
   implementation effort would be high, but the utility low.

   If the Linux system time is synchronized to the PHC via the PPS
   method, then using standard hrtimers would be good enough for most
   purposes. Consider the time scales involved. The PHC can be
   synchronized to within 100 nanoseconds of an external time source,
   while timer wakeup latency (even with rt kernels) is tens of
   microseconds.

4 Drivers 
~~~~~~~~~~

4.1 Supported Hardware Clocks 
==============================

   + Standard Linux system timer
     This driver exports the standard Linux timer as a PTP clock.
     Although this duplicates CLOCK_REALTIME, the code serves as a
     simple example for driver development and lets people who without
     special hardware try the new API.

   + Freescale eTSEC gianfar
     - 2 Time stamp external triggers, programmable polarity (opt. interrupt)
     - 2 Alarm registers (optional interrupt)
     - 3 Periodic signals (optional interrupt)

   + National Semiconductor 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)

4.2 Open Driver Issues 
=======================

4.2.1 DP83640 
--------------
    In order to make this work, one line must be added into the MAC
    driver. If you have the DP83640 and want to try the driver, you
    need to add this one line to your MAC driver: In the
    .ndo_start_xmit function, add skb_tx_timestamp(skb).

4.2.2 IXP465 
-------------
    I do not know how to correctly choose the timestamp "channel"
    based on the port identifier:

+#define PORT2CHANNEL(p)                1
+/*
+ * PHYSICAL_ID(p->id) ?
+ * TODO - Figure out correct mapping.
+ */

   Krzysztof, can you help?


Richard Cochran (8):
  posix clocks: introduce a syscall for clock tuning.
  posix clocks: dynamic clock ids.
  posix clocks: introduce a sysfs presence.
  ptp: Added a brand new class driver for ptp clocks.
  ptp: Added a simulated PTP hardware clock.
  ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  ptp: Added a clock driver for the IXP46x.
  ptp: Added a clock driver for the National Semiconductor PHYTER.

 Documentation/ABI/testing/sysfs-ptp             |  107 +++
 Documentation/ABI/testing/sysfs-timesource      |   24 +
 Documentation/powerpc/dts-bindings/fsl/tsec.txt |   57 ++
 Documentation/ptp/ptp.txt                       |   94 +++
 Documentation/ptp/testptp.c                     |  358 +++++++++
 Documentation/ptp/testptp.mk                    |   33 +
 arch/arm/include/asm/unistd.h                   |    1 +
 arch/arm/kernel/calls.S                         |    1 +
 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h   |   78 ++
 arch/blackfin/include/asm/unistd.h              |    3 +-
 arch/blackfin/mach-common/entry.S               |    1 +
 arch/powerpc/boot/dts/mpc8313erdb.dts           |   14 +
 arch/powerpc/boot/dts/mpc8572ds.dts             |   14 +
 arch/powerpc/boot/dts/p2020ds.dts               |   14 +
 arch/powerpc/boot/dts/p2020rdb.dts              |   14 +
 arch/powerpc/include/asm/systbl.h               |    1 +
 arch/powerpc/include/asm/unistd.h               |    3 +-
 arch/x86/ia32/ia32entry.S                       |    1 +
 arch/x86/include/asm/unistd_32.h                |    3 +-
 arch/x86/include/asm/unistd_64.h                |    2 +
 arch/x86/kernel/syscall_table_32.S              |    1 +
 drivers/Kconfig                                 |    2 +
 drivers/Makefile                                |    1 +
 drivers/char/mmtimer.c                          |    1 +
 drivers/net/Makefile                            |    1 +
 drivers/net/arm/ixp4xx_eth.c                    |  191 +++++
 drivers/net/gianfar_ptp.c                       |  447 ++++++++++++
 drivers/net/gianfar_ptp_reg.h                   |  113 +++
 drivers/net/phy/Kconfig                         |   29 +
 drivers/net/phy/Makefile                        |    1 +
 drivers/net/phy/dp83640.c                       |  887 +++++++++++++++++++++++
 drivers/net/phy/dp83640_reg.h                   |  261 +++++++
 drivers/ptp/Kconfig                             |   67 ++
 drivers/ptp/Makefile                            |    8 +
 drivers/ptp/ptp_chardev.c                       |  178 +++++
 drivers/ptp/ptp_clock.c                         |  382 ++++++++++
 drivers/ptp/ptp_ixp46x.c                        |  345 +++++++++
 drivers/ptp/ptp_linux.c                         |  165 +++++
 drivers/ptp/ptp_private.h                       |   64 ++
 drivers/ptp/ptp_sysfs.c                         |  235 ++++++
 include/linux/Kbuild                            |    1 +
 include/linux/posix-timers.h                    |   14 +-
 include/linux/ptp_clock.h                       |   79 ++
 include/linux/ptp_clock_kernel.h                |  139 ++++
 include/linux/syscalls.h                        |    2 +
 include/linux/time.h                            |    2 +
 include/linux/timex.h                           |    3 +-
 kernel/compat.c                                 |  136 +++--
 kernel/posix-cpu-timers.c                       |    6 +
 kernel/posix-timers.c                           |   98 +++-
 kernel/time/ntp.c                               |    2 +
 51 files changed, 4624 insertions(+), 60 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-ptp
 create mode 100644 Documentation/ABI/testing/sysfs-timesource
 create mode 100644 Documentation/ptp/ptp.txt
 create mode 100644 Documentation/ptp/testptp.c
 create mode 100644 Documentation/ptp/testptp.mk
 create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
 create mode 100644 drivers/net/gianfar_ptp.c
 create mode 100644 drivers/net/gianfar_ptp_reg.h
 create mode 100644 drivers/net/phy/dp83640.c
 create mode 100644 drivers/net/phy/dp83640_reg.h
 create mode 100644 drivers/ptp/Kconfig
 create mode 100644 drivers/ptp/Makefile
 create mode 100644 drivers/ptp/ptp_chardev.c
 create mode 100644 drivers/ptp/ptp_clock.c
 create mode 100644 drivers/ptp/ptp_ixp46x.c
 create mode 100644 drivers/ptp/ptp_linux.c
 create mode 100644 drivers/ptp/ptp_private.h
 create mode 100644 drivers/ptp/ptp_sysfs.c
 create mode 100644 include/linux/ptp_clock.h
 create mode 100644 include/linux/ptp_clock_kernel.h

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-09-23 17:31 ` Richard Cochran
  2010-09-23 19:48   ` john stultz
  2010-09-23 22:03   ` Benjamin Herrenschmidt
  2010-09-23 17:31 ` [PATCH 2/8] posix clocks: dynamic clock ids Richard Cochran
                   ` (9 subsequent siblings)
  10 siblings, 2 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:31 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

A new syscall is introduced that allows tuning of a POSIX clock. The
syscall is implemented for four architectures: arm, blackfin, powerpc,
and x86.

The new syscall, clock_adjtime, takes two parameters, the clock ID,
and a pointer to a struct timex. The semantics of the timex struct
have been expanded by one additional mode flag, which allows an
absolute offset correction. When specificied, the clock offset is
immediately corrected by adding the given time value to the current
time value.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 arch/arm/include/asm/unistd.h      |    1 +
 arch/arm/kernel/calls.S            |    1 +
 arch/blackfin/include/asm/unistd.h |    3 +-
 arch/blackfin/mach-common/entry.S  |    1 +
 arch/powerpc/include/asm/systbl.h  |    1 +
 arch/powerpc/include/asm/unistd.h  |    3 +-
 arch/x86/ia32/ia32entry.S          |    1 +
 arch/x86/include/asm/unistd_32.h   |    3 +-
 arch/x86/include/asm/unistd_64.h   |    2 +
 arch/x86/kernel/syscall_table_32.S |    1 +
 include/linux/posix-timers.h       |    3 +
 include/linux/syscalls.h           |    2 +
 include/linux/timex.h              |    3 +-
 kernel/compat.c                    |  136 +++++++++++++++++++++++-------------
 kernel/posix-cpu-timers.c          |    4 +
 kernel/posix-timers.c              |   17 +++++
 16 files changed, 130 insertions(+), 52 deletions(-)

diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index c891eb7..f58d881 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -396,6 +396,7 @@
 #define __NR_fanotify_init		(__NR_SYSCALL_BASE+367)
 #define __NR_fanotify_mark		(__NR_SYSCALL_BASE+368)
 #define __NR_prlimit64			(__NR_SYSCALL_BASE+369)
+#define __NR_clock_adjtime		(__NR_SYSCALL_BASE+370)
 
 /*
  * The following SWIs are ARM private.
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 5c26ecc..430de4c 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -379,6 +379,7 @@
 		CALL(sys_fanotify_init)
 		CALL(sys_fanotify_mark)
 		CALL(sys_prlimit64)
+/* 370 */	CALL(sys_clock_adjtime)
 #ifndef syscalls_counted
 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
 #define syscalls_counted
diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h
index 14fcd25..79ad99b 100644
--- a/arch/blackfin/include/asm/unistd.h
+++ b/arch/blackfin/include/asm/unistd.h
@@ -392,8 +392,9 @@
 #define __NR_fanotify_init	371
 #define __NR_fanotify_mark	372
 #define __NR_prlimit64		373
+#define __NR_clock_adjtime	374
 
-#define __NR_syscall		374
+#define __NR_syscall		375
 #define NR_syscalls		__NR_syscall
 
 /* Old optional stuff no one actually uses */
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index af1bffa..ee68730 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1631,6 +1631,7 @@ ENTRY(_sys_call_table)
 	.long _sys_fanotify_init
 	.long _sys_fanotify_mark
 	.long _sys_prlimit64
+	.long _sys_clock_adjtime
 
 	.rept NR_syscalls-(.-_sys_call_table)/4
 	.long _sys_ni_syscall
diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
index 3d21266..2485d8f 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -329,3 +329,4 @@ COMPAT_SYS(rt_tgsigqueueinfo)
 SYSCALL(fanotify_init)
 COMPAT_SYS(fanotify_mark)
 SYSCALL_SPU(prlimit64)
+COMPAT_SYS_SPU(clock_adjtime)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index 597e6f9..85d5067 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -348,10 +348,11 @@
 #define __NR_fanotify_init	323
 #define __NR_fanotify_mark	324
 #define __NR_prlimit64		325
+#define __NR_clock_adjtime	326
 
 #ifdef __KERNEL__
 
-#define __NR_syscalls		326
+#define __NR_syscalls		327
 
 #define __NR__exit __NR_exit
 #define NR_syscalls	__NR_syscalls
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index 518bb99..0ed7896 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -851,4 +851,5 @@ ia32_sys_call_table:
 	.quad sys_fanotify_init
 	.quad sys32_fanotify_mark
 	.quad sys_prlimit64		/* 340 */
+	.quad compat_sys_clock_adjtime
 ia32_syscall_end:
diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
index b766a5e..b6f73f1 100644
--- a/arch/x86/include/asm/unistd_32.h
+++ b/arch/x86/include/asm/unistd_32.h
@@ -346,10 +346,11 @@
 #define __NR_fanotify_init	338
 #define __NR_fanotify_mark	339
 #define __NR_prlimit64		340
+#define __NR_clock_adjtime	341
 
 #ifdef __KERNEL__
 
-#define NR_syscalls 341
+#define NR_syscalls 342
 
 #define __ARCH_WANT_IPC_PARSE_VERSION
 #define __ARCH_WANT_OLD_READDIR
diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h
index 363e9b8..5ee3085 100644
--- a/arch/x86/include/asm/unistd_64.h
+++ b/arch/x86/include/asm/unistd_64.h
@@ -669,6 +669,8 @@ __SYSCALL(__NR_fanotify_init, sys_fanotify_init)
 __SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
 #define __NR_prlimit64				302
 __SYSCALL(__NR_prlimit64, sys_prlimit64)
+#define __NR_clock_adjtime			303
+__SYSCALL(__NR_clock_adjtime, sys_clock_adjtime)
 
 #ifndef __NO_STUBS
 #define __ARCH_WANT_OLD_READDIR
diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
index b35786d..68c7b9a 100644
--- a/arch/x86/kernel/syscall_table_32.S
+++ b/arch/x86/kernel/syscall_table_32.S
@@ -340,3 +340,4 @@ ENTRY(sys_call_table)
 	.long sys_fanotify_init
 	.long sys_fanotify_mark
 	.long sys_prlimit64		/* 340 */
+	.long sys_clock_adjtime
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 3e23844..abf61cc 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -4,6 +4,7 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/sched.h>
+#include <linux/timex.h>
 
 union cpu_time_count {
 	cputime_t cpu;
@@ -71,6 +72,7 @@ struct k_clock {
 	int (*clock_getres) (const clockid_t which_clock, struct timespec *tp);
 	int (*clock_set) (const clockid_t which_clock, struct timespec * tp);
 	int (*clock_get) (const clockid_t which_clock, struct timespec * tp);
+	int (*clock_adj) (const clockid_t which_clock, struct timex *tx);
 	int (*timer_create) (struct k_itimer *timer);
 	int (*nsleep) (const clockid_t which_clock, int flags,
 		       struct timespec *, struct timespec __user *);
@@ -97,6 +99,7 @@ int posix_timer_event(struct k_itimer *timr, int si_private);
 int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *ts);
 int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *ts);
 int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *ts);
+int posix_cpu_clock_adj(const clockid_t which_clock, struct timex *tx);
 int posix_cpu_timer_create(struct k_itimer *timer);
 int posix_cpu_nsleep(const clockid_t which_clock, int flags,
 		     struct timespec *rqtp, struct timespec __user *rmtp);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index e6319d1..0b24775 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -313,6 +313,8 @@ asmlinkage long sys_clock_settime(clockid_t which_clock,
 				const struct timespec __user *tp);
 asmlinkage long sys_clock_gettime(clockid_t which_clock,
 				struct timespec __user *tp);
+asmlinkage long sys_clock_adjtime(clockid_t which_clock,
+				struct timex __user *tx);
 asmlinkage long sys_clock_getres(clockid_t which_clock,
 				struct timespec __user *tp);
 asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags,
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 32d852f..82d4b24 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -73,7 +73,7 @@ struct timex {
 	long tolerance;		/* clock frequency tolerance (ppm)
 				 * (read only)
 				 */
-	struct timeval time;	/* (read only) */
+	struct timeval time;	/* (read only, except for ADJ_SETOFFSET) */
 	long tick;		/* (modified) usecs between clock ticks */
 
 	long ppsfreq;           /* pps frequency (scaled ppm) (ro) */
@@ -101,6 +101,7 @@ struct timex {
 #define ADJ_ESTERROR		0x0008	/* estimated time error */
 #define ADJ_STATUS		0x0010	/* clock status */
 #define ADJ_TIMECONST		0x0020	/* pll time constant */
+#define ADJ_SETOFFSET		0x0040  /* add 'time' to current time */
 #define ADJ_TAI			0x0080	/* set TAI offset */
 #define ADJ_MICRO		0x1000	/* select microsecond resolution */
 #define ADJ_NANO		0x2000	/* select nanosecond resolution */
diff --git a/kernel/compat.c b/kernel/compat.c
index c9e2ec0..38b1d2c 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -52,6 +52,64 @@ static int compat_put_timeval(struct compat_timeval __user *o,
 		put_user(i->tv_usec, &o->tv_usec)) ? -EFAULT : 0;
 }
 
+static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp)
+{
+	memset(txc, 0, sizeof(struct timex));
+
+	if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
+			__get_user(txc->modes, &utp->modes) ||
+			__get_user(txc->offset, &utp->offset) ||
+			__get_user(txc->freq, &utp->freq) ||
+			__get_user(txc->maxerror, &utp->maxerror) ||
+			__get_user(txc->esterror, &utp->esterror) ||
+			__get_user(txc->status, &utp->status) ||
+			__get_user(txc->constant, &utp->constant) ||
+			__get_user(txc->precision, &utp->precision) ||
+			__get_user(txc->tolerance, &utp->tolerance) ||
+			__get_user(txc->time.tv_sec, &utp->time.tv_sec) ||
+			__get_user(txc->time.tv_usec, &utp->time.tv_usec) ||
+			__get_user(txc->tick, &utp->tick) ||
+			__get_user(txc->ppsfreq, &utp->ppsfreq) ||
+			__get_user(txc->jitter, &utp->jitter) ||
+			__get_user(txc->shift, &utp->shift) ||
+			__get_user(txc->stabil, &utp->stabil) ||
+			__get_user(txc->jitcnt, &utp->jitcnt) ||
+			__get_user(txc->calcnt, &utp->calcnt) ||
+			__get_user(txc->errcnt, &utp->errcnt) ||
+			__get_user(txc->stbcnt, &utp->stbcnt))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int compat_put_timex(struct compat_timex __user *utp, struct timex *txc)
+{
+	if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
+			__put_user(txc->modes, &utp->modes) ||
+			__put_user(txc->offset, &utp->offset) ||
+			__put_user(txc->freq, &utp->freq) ||
+			__put_user(txc->maxerror, &utp->maxerror) ||
+			__put_user(txc->esterror, &utp->esterror) ||
+			__put_user(txc->status, &utp->status) ||
+			__put_user(txc->constant, &utp->constant) ||
+			__put_user(txc->precision, &utp->precision) ||
+			__put_user(txc->tolerance, &utp->tolerance) ||
+			__put_user(txc->time.tv_sec, &utp->time.tv_sec) ||
+			__put_user(txc->time.tv_usec, &utp->time.tv_usec) ||
+			__put_user(txc->tick, &utp->tick) ||
+			__put_user(txc->ppsfreq, &utp->ppsfreq) ||
+			__put_user(txc->jitter, &utp->jitter) ||
+			__put_user(txc->shift, &utp->shift) ||
+			__put_user(txc->stabil, &utp->stabil) ||
+			__put_user(txc->jitcnt, &utp->jitcnt) ||
+			__put_user(txc->calcnt, &utp->calcnt) ||
+			__put_user(txc->errcnt, &utp->errcnt) ||
+			__put_user(txc->stbcnt, &utp->stbcnt) ||
+			__put_user(txc->tai, &utp->tai))
+		return -EFAULT;
+	return 0;
+}
+
 asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv,
 		struct timezone __user *tz)
 {
@@ -617,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock,
 	return err;
 }
 
+long compat_sys_clock_adjtime(clockid_t which_clock,
+		struct compat_timex __user *utp)
+{
+	struct timex txc;
+	mm_segment_t oldfs;
+	int err, ret;
+
+	err = compat_get_timex(&txc, utp);
+	if (err)
+		return err;
+
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc);
+	set_fs(oldfs);
+
+	err = compat_put_timex(utp, &txc);
+	if (err)
+		return err;
+
+	return ret;
+}
+
 long compat_sys_clock_getres(clockid_t which_clock,
 		struct compat_timespec __user *tp)
 {
@@ -951,58 +1032,17 @@ asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat
 asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp)
 {
 	struct timex txc;
-	int ret;
-
-	memset(&txc, 0, sizeof(struct timex));
+	int err, ret;
 
-	if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
-			__get_user(txc.modes, &utp->modes) ||
-			__get_user(txc.offset, &utp->offset) ||
-			__get_user(txc.freq, &utp->freq) ||
-			__get_user(txc.maxerror, &utp->maxerror) ||
-			__get_user(txc.esterror, &utp->esterror) ||
-			__get_user(txc.status, &utp->status) ||
-			__get_user(txc.constant, &utp->constant) ||
-			__get_user(txc.precision, &utp->precision) ||
-			__get_user(txc.tolerance, &utp->tolerance) ||
-			__get_user(txc.time.tv_sec, &utp->time.tv_sec) ||
-			__get_user(txc.time.tv_usec, &utp->time.tv_usec) ||
-			__get_user(txc.tick, &utp->tick) ||
-			__get_user(txc.ppsfreq, &utp->ppsfreq) ||
-			__get_user(txc.jitter, &utp->jitter) ||
-			__get_user(txc.shift, &utp->shift) ||
-			__get_user(txc.stabil, &utp->stabil) ||
-			__get_user(txc.jitcnt, &utp->jitcnt) ||
-			__get_user(txc.calcnt, &utp->calcnt) ||
-			__get_user(txc.errcnt, &utp->errcnt) ||
-			__get_user(txc.stbcnt, &utp->stbcnt))
-		return -EFAULT;
+	err = compat_get_timex(&txc, utp);
+	if (err)
+		return err;
 
 	ret = do_adjtimex(&txc);
 
-	if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
-			__put_user(txc.modes, &utp->modes) ||
-			__put_user(txc.offset, &utp->offset) ||
-			__put_user(txc.freq, &utp->freq) ||
-			__put_user(txc.maxerror, &utp->maxerror) ||
-			__put_user(txc.esterror, &utp->esterror) ||
-			__put_user(txc.status, &utp->status) ||
-			__put_user(txc.constant, &utp->constant) ||
-			__put_user(txc.precision, &utp->precision) ||
-			__put_user(txc.tolerance, &utp->tolerance) ||
-			__put_user(txc.time.tv_sec, &utp->time.tv_sec) ||
-			__put_user(txc.time.tv_usec, &utp->time.tv_usec) ||
-			__put_user(txc.tick, &utp->tick) ||
-			__put_user(txc.ppsfreq, &utp->ppsfreq) ||
-			__put_user(txc.jitter, &utp->jitter) ||
-			__put_user(txc.shift, &utp->shift) ||
-			__put_user(txc.stabil, &utp->stabil) ||
-			__put_user(txc.jitcnt, &utp->jitcnt) ||
-			__put_user(txc.calcnt, &utp->calcnt) ||
-			__put_user(txc.errcnt, &utp->errcnt) ||
-			__put_user(txc.stbcnt, &utp->stbcnt) ||
-			__put_user(txc.tai, &utp->tai))
-		ret = -EFAULT;
+	err = compat_put_timex(utp, &txc);
+	if (err)
+		return err;
 
 	return ret;
 }
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index 6842eeb..e1c2e7b 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -207,6 +207,10 @@ int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp)
 	return error;
 }
 
+int posix_cpu_clock_adj(const clockid_t which_clock, struct timex *tx)
+{
+	return -EOPNOTSUPP;
+}
 
 /*
  * Sample a per-thread clock for the given task.
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 9ca4973..446b566 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -197,6 +197,14 @@ static int common_timer_create(struct k_itimer *new_timer)
 	return 0;
 }
 
+static inline int common_clock_adj(const clockid_t which_clock, struct timex *t)
+{
+	if (CLOCK_REALTIME == which_clock)
+		return do_adjtimex(t);
+	else
+		return -EOPNOTSUPP;
+}
+
 static int no_timer_create(struct k_itimer *new_timer)
 {
 	return -EOPNOTSUPP;
@@ -969,6 +977,15 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
 
 }
 
+SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
+		struct timex __user *, tx)
+{
+	if (invalid_clockid(which_clock))
+		return -EINVAL;
+
+	return CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, tx));
+}
+
 SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
 		struct timespec __user *, tp)
 {
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 2/8] posix clocks: dynamic clock ids.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
  2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
@ 2010-09-23 17:31 ` Richard Cochran
  2010-09-23 17:31 ` [PATCH 3/8] posix clocks: introduce a sysfs presence Richard Cochran
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:31 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch augments the POSIX clock code to offer a dynamic clock
creation method. Instead of registering a hard coded clock ID, modules
may call create_posix_clock(), which returns a new clock ID.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 include/linux/posix-timers.h |    7 ++++++-
 include/linux/time.h         |    2 ++
 kernel/posix-timers.c        |   41 ++++++++++++++++++++++++++++++++++-------
 3 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index abf61cc..08aa4da 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -68,6 +68,7 @@ struct k_itimer {
 };
 
 struct k_clock {
+	clockid_t id;
 	int res;		/* in nanoseconds */
 	int (*clock_getres) (const clockid_t which_clock, struct timespec *tp);
 	int (*clock_set) (const clockid_t which_clock, struct timespec * tp);
@@ -86,7 +87,11 @@ struct k_clock {
 			   struct itimerspec * cur_setting);
 };
 
-void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock);
+/* Regsiter a posix clock with a "well known" clock id. */
+int register_posix_clock(const clockid_t id, struct k_clock *clock);
+
+/* Create a new posix clock with a dynamic clock id. */
+clockid_t create_posix_clock(struct k_clock *clock);
 
 /* error handlers for timer_create, nanosleep and settime */
 int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *,
diff --git a/include/linux/time.h b/include/linux/time.h
index 9f15ac7..914c48d 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -299,6 +299,8 @@ struct itimerval {
 #define CLOCKS_MASK			(CLOCK_REALTIME | CLOCK_MONOTONIC)
 #define CLOCKS_MONO			CLOCK_MONOTONIC
 
+#define CLOCK_INVALID			-1
+
 /*
  * The various flags for setting POSIX.1b interval timers:
  */
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 446b566..67fba5c 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -132,6 +132,8 @@ static DEFINE_SPINLOCK(idr_lock);
  */
 
 static struct k_clock posix_clocks[MAX_CLOCKS];
+static DECLARE_BITMAP(clocks_map, MAX_CLOCKS);
+static DEFINE_MUTEX(clocks_mux); /* protects 'posix_clocks' and 'clocks_map' */
 
 /*
  * These ones are defined below.
@@ -484,18 +486,43 @@ static struct pid *good_sigevent(sigevent_t * event)
 	return task_pid(rtn);
 }
 
-void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock)
+int register_posix_clock(const clockid_t id, struct k_clock *clock)
 {
-	if ((unsigned) clock_id >= MAX_CLOCKS) {
-		printk("POSIX clock register failed for clock_id %d\n",
-		       clock_id);
-		return;
-	}
+	struct k_clock *kc;
+	int err = 0;
 
-	posix_clocks[clock_id] = *new_clock;
+	mutex_lock(&clocks_mux);
+	if (test_bit(id, clocks_map)) {
+		pr_err("clock_id %d already registered\n", id);
+		err = -EBUSY;
+		goto out;
+	}
+	kc = &posix_clocks[id];
+	*kc = *clock;
+	kc->id = id;
+	set_bit(id, clocks_map);
+out:
+	mutex_unlock(&clocks_mux);
+	return err;
 }
 EXPORT_SYMBOL_GPL(register_posix_clock);
 
+clockid_t create_posix_clock(struct k_clock *clock)
+{
+	clockid_t id;
+
+	mutex_lock(&clocks_mux);
+	id = find_first_zero_bit(clocks_map, MAX_CLOCKS);
+	mutex_unlock(&clocks_mux);
+
+	if (id < MAX_CLOCKS) {
+		register_posix_clock(id, clock);
+		return id;
+	}
+	return CLOCK_INVALID;
+}
+EXPORT_SYMBOL_GPL(create_posix_clock);
+
 static struct k_itimer * alloc_posix_timer(void)
 {
 	struct k_itimer *tmr;
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 3/8] posix clocks: introduce a sysfs presence.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
  2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
  2010-09-23 17:31 ` [PATCH 2/8] posix clocks: dynamic clock ids Richard Cochran
@ 2010-09-23 17:31 ` Richard Cochran
  2010-09-23 17:32 ` [PATCH 4/8] ptp: Added a brand new class driver for ptp clocks Richard Cochran
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:31 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch adds a 'timesource' class into sysfs. Each registered POSIX
clock appears by name under /sys/class/timesource. The idea is to
expose to user space the dynamic mapping between clock devices and
clock IDs.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 Documentation/ABI/testing/sysfs-timesource |   24 ++++++++++++++++
 drivers/char/mmtimer.c                     |    1 +
 include/linux/posix-timers.h               |    4 +++
 kernel/posix-cpu-timers.c                  |    2 +
 kernel/posix-timers.c                      |   40 ++++++++++++++++++++++++++++
 5 files changed, 71 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-timesource

diff --git a/Documentation/ABI/testing/sysfs-timesource b/Documentation/ABI/testing/sysfs-timesource
new file mode 100644
index 0000000..f991de2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-timesource
@@ -0,0 +1,24 @@
+What:		/sys/class/timesource/
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This directory contains files and directories
+		providing a standardized interface to the available
+		time sources.
+
+What:		/sys/class/timesource/<name>/
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This directory contains the attributes of a time
+		source registered with the POSIX clock subsystem.
+
+What:		/sys/class/timesource/<name>/id
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the clock ID (a non-negative
+		integer) of the named time source registered with the
+		POSIX clock subsystem. This value may be passed as the
+		first argument to the POSIX clock and timer system
+		calls. See man CLOCK_GETRES(2) and TIMER_CREATE(2).
diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c
index ea7c99f..e9173e3 100644
--- a/drivers/char/mmtimer.c
+++ b/drivers/char/mmtimer.c
@@ -758,6 +758,7 @@ static int sgi_timer_set(struct k_itimer *timr, int flags,
 }
 
 static struct k_clock sgi_clock = {
+	.name = "sgi_cycle",
 	.res = 0,
 	.clock_set = sgi_clock_set,
 	.clock_get = sgi_clock_get,
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 08aa4da..64e6fee 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -67,7 +67,11 @@ struct k_itimer {
 	} it;
 };
 
+#define KCLOCK_MAX_NAME 32
+
 struct k_clock {
+	char name[KCLOCK_MAX_NAME];
+	struct device *dev;
 	clockid_t id;
 	int res;		/* in nanoseconds */
 	int (*clock_getres) (const clockid_t which_clock, struct timespec *tp);
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index e1c2e7b..df9cbab 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -1611,6 +1611,7 @@ static long thread_cpu_nsleep_restart(struct restart_block *restart_block)
 static __init int init_posix_cpu_timers(void)
 {
 	struct k_clock process = {
+		.name = "process_cputime",
 		.clock_getres = process_cpu_clock_getres,
 		.clock_get = process_cpu_clock_get,
 		.clock_set = do_posix_clock_nosettime,
@@ -1619,6 +1620,7 @@ static __init int init_posix_cpu_timers(void)
 		.nsleep_restart = process_cpu_nsleep_restart,
 	};
 	struct k_clock thread = {
+		.name = "thread_cputime",
 		.clock_getres = thread_cpu_clock_getres,
 		.clock_get = thread_cpu_clock_get,
 		.clock_set = do_posix_clock_nosettime,
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 67fba5c..719aa11 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -46,6 +46,7 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/module.h>
+#include <linux/device.h>
 
 /*
  * Management arrays for POSIX timers.	 Timers are kept in slab memory
@@ -135,6 +136,8 @@ static struct k_clock posix_clocks[MAX_CLOCKS];
 static DECLARE_BITMAP(clocks_map, MAX_CLOCKS);
 static DEFINE_MUTEX(clocks_mux); /* protects 'posix_clocks' and 'clocks_map' */
 
+static struct class *timesource_class;
+
 /*
  * These ones are defined below.
  */
@@ -271,20 +274,40 @@ static int posix_get_coarse_res(const clockid_t which_clock, struct timespec *tp
 	*tp = ktime_to_timespec(KTIME_LOW_RES);
 	return 0;
 }
+
+/*
+ * sysfs attributes
+ */
+
+static ssize_t show_clock_id(struct device *dev,
+			     struct device_attribute *attr, char *page)
+{
+	struct k_clock *kc = dev_get_drvdata(dev);
+	return snprintf(page, PAGE_SIZE-1, "%d\n", kc->id);
+}
+
+static struct device_attribute timesource_dev_attrs[] = {
+	__ATTR(id,   0444, show_clock_id,   NULL),
+	__ATTR_NULL,
+};
+
 /*
  * Initialize everything, well, just everything in Posix clocks/timers ;)
  */
 static __init int init_posix_timers(void)
 {
 	struct k_clock clock_realtime = {
+		.name = "realtime",
 		.clock_getres = hrtimer_get_res,
 	};
 	struct k_clock clock_monotonic = {
+		.name = "monotonic",
 		.clock_getres = hrtimer_get_res,
 		.clock_get = posix_ktime_get_ts,
 		.clock_set = do_posix_clock_nosettime,
 	};
 	struct k_clock clock_monotonic_raw = {
+		.name = "monotonic_raw",
 		.clock_getres = hrtimer_get_res,
 		.clock_get = posix_get_monotonic_raw,
 		.clock_set = do_posix_clock_nosettime,
@@ -292,6 +315,7 @@ static __init int init_posix_timers(void)
 		.nsleep = no_nsleep,
 	};
 	struct k_clock clock_realtime_coarse = {
+		.name = "realtime_coarse",
 		.clock_getres = posix_get_coarse_res,
 		.clock_get = posix_get_realtime_coarse,
 		.clock_set = do_posix_clock_nosettime,
@@ -299,6 +323,7 @@ static __init int init_posix_timers(void)
 		.nsleep = no_nsleep,
 	};
 	struct k_clock clock_monotonic_coarse = {
+		.name = "monotonic_coarse",
 		.clock_getres = posix_get_coarse_res,
 		.clock_get = posix_get_monotonic_coarse,
 		.clock_set = do_posix_clock_nosettime,
@@ -306,6 +331,13 @@ static __init int init_posix_timers(void)
 		.nsleep = no_nsleep,
 	};
 
+	timesource_class = class_create(NULL, "timesource");
+	if (IS_ERR(timesource_class)) {
+		pr_err("posix-timers: failed to allocate timesource class\n");
+		return PTR_ERR(timesource_class);
+	}
+	timesource_class->dev_attrs = timesource_dev_attrs;
+
 	register_posix_clock(CLOCK_REALTIME, &clock_realtime);
 	register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
 	register_posix_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
@@ -500,6 +532,14 @@ int register_posix_clock(const clockid_t id, struct k_clock *clock)
 	kc = &posix_clocks[id];
 	*kc = *clock;
 	kc->id = id;
+	kc->dev = device_create(timesource_class, NULL, MKDEV(0, 0),
+				kc, "%s", kc->name);
+	if (IS_ERR(kc->dev)) {
+		pr_err("failed to create device clock_id %d\n", id);
+		err = PTR_ERR(kc->dev);
+		goto out;
+	}
+	dev_set_drvdata(kc->dev, kc);
 	set_bit(id, clocks_map);
 out:
 	mutex_unlock(&clocks_mux);
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 4/8] ptp: Added a brand new class driver for ptp clocks.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (2 preceding siblings ...)
  2010-09-23 17:31 ` [PATCH 3/8] posix clocks: introduce a sysfs presence Richard Cochran
@ 2010-09-23 17:32 ` Richard Cochran
  2010-09-23 17:32 ` [PATCH 5/8] ptp: Added a simulated PTP hardware clock Richard Cochran
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:32 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch adds an infrastructure for hardware clocks that implement
IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a
registration method to particular hardware clock drivers. Each clock is
presented as a standard POSIX clock.

The ancillary clock features are exposed in two different ways, via
the sysfs and by a character device.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 Documentation/ABI/testing/sysfs-ptp |  107 ++++++++++
 Documentation/ptp/ptp.txt           |   94 +++++++++
 Documentation/ptp/testptp.c         |  358 ++++++++++++++++++++++++++++++++
 Documentation/ptp/testptp.mk        |   33 +++
 drivers/Kconfig                     |    2 +
 drivers/Makefile                    |    1 +
 drivers/ptp/Kconfig                 |   27 +++
 drivers/ptp/Makefile                |    6 +
 drivers/ptp/ptp_chardev.c           |  178 ++++++++++++++++
 drivers/ptp/ptp_clock.c             |  382 +++++++++++++++++++++++++++++++++++
 drivers/ptp/ptp_private.h           |   64 ++++++
 drivers/ptp/ptp_sysfs.c             |  235 +++++++++++++++++++++
 include/linux/Kbuild                |    1 +
 include/linux/ptp_clock.h           |   79 +++++++
 include/linux/ptp_clock_kernel.h    |  139 +++++++++++++
 15 files changed, 1706 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-ptp
 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_chardev.c
 create mode 100644 drivers/ptp/ptp_clock.c
 create mode 100644 drivers/ptp/ptp_private.h
 create mode 100644 drivers/ptp/ptp_sysfs.c
 create mode 100644 include/linux/ptp_clock.h
 create mode 100644 include/linux/ptp_clock_kernel.h

diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
new file mode 100644
index 0000000..47142ce
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -0,0 +1,107 @@
+What:		/sys/class/ptp/
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This directory contains files and directories
+		providing a standardized interface to the ancillary
+		features of PTP hardware clocks.
+
+What:		/sys/class/ptp/ptpN/
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This directory contains the attributes of the Nth PTP
+		hardware clock registered into the PTP class driver
+		subsystem.
+
+What:		/sys/class/ptp/ptpN/clock_id
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the POSIX clock ID (a non-negative
+		integer) corresponding to the PTP hardware clock. This
+		value may be passed as the first argument to the POSIX
+		clock and timer system calls. See man CLOCK_GETRES(2)
+		and TIMER_CREATE(2).
+
+What:		/sys/class/ptp/ptpN/clock_name
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the name of the PTP hardware clock
+		as a human readable string.
+
+What:		/sys/class/ptp/ptpN/max_adjustment
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the PTP hardware clock's maximum
+		frequency adjustment value (a positive integer) in
+		parts per billion.
+
+What:		/sys/class/ptp/ptpN/n_alarms
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the number of periodic or one shot
+		alarms offer by the PTP hardware clock.
+
+What:		/sys/class/ptp/ptpN/n_external_timestamps
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the number of external timestamp
+		channels offered by the PTP hardware clock.
+
+What:		/sys/class/ptp/ptpN/n_periodic_outputs
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file contains the number of programmable periodic
+		output channels offered by the PTP hardware clock.
+
+What:		/sys/class/ptp/ptpN/pps_avaiable
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file indicates whether the PTP hardware clock
+		supports a Pulse Per Second to the host CPU. Reading
+		"1" means that the PPS is supported, while "0" means
+		not supported.
+
+What:		/sys/class/ptp/ptpN/extts_enable
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This write-only file enables or disables external
+		timestamps. To enable external timestamps, write the
+		channel index followed by a "1" into the file.
+		To disable external timestamps, write the channel
+		index followed by a "0" into the file.
+
+What:		/sys/class/ptp/ptpN/fifo
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This file provides timestamps on external events, in
+		the form of three integers: channel index, seconds,
+		and nanoseconds.
+
+What:		/sys/class/ptp/ptpN/period
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This write-only file enables or disables periodic
+		outputs. To enable a periodic output, write three
+		integers into the file: channel index, seconds, and
+		nanoseconds. To disable a periodic output, set seconds
+		and nanoseconds to zero.
+
+What:		/sys/class/ptp/ptpN/pps_enable
+Date:		September 2010
+Contact:	Richard Cochran <richardcochran@gmail.com>
+Description:
+		This write-only file enables or disables delivery of
+		PPS events to the Linux PPS subsystem. To enable PPS
+		events, write a "1" into the file. To disable events,
+		write a "0" into the file.
diff --git a/Documentation/ptp/ptp.txt b/Documentation/ptp/ptp.txt
new file mode 100644
index 0000000..d198772
--- /dev/null
+++ b/Documentation/ptp/ptp.txt
@@ -0,0 +1,94 @@
+
+* PTP hardware clock 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 hardware clock 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 hardware clock 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 hardware clock user space API
+
+   Each clock is presented as a POSIX clock id. User space can
+   discover the clock id by reading the appropriate sysfs file (see
+   Documentation/ABI/testing/sysfs-ptp for details). Using the clock
+   id, user space may call clock_gettime, clock_settime, and
+   clock_adjtime. These calls implement the basic clock operations.
+
+   The class driver also creates a character device for each
+   registered 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 the POSIX timer_settime()
+   system call.
+
+** 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..1d907a9
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,358 @@
+/*
+ * 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 <math.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/timex.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/ptp_clock.h>
+
+#define DEVICE "/dev/ptp0"
+#define ATTRIBUTE "/sys/class/ptp/ptp0/clock_id"
+
+#ifndef ADJ_SETOFFSET
+#define ADJ_SETOFFSET 0x0040
+#endif
+
+#ifndef CLOCK_INVALID
+#define CLOCK_INVALID -1
+#endif
+
+/* When glibc offers the syscall, this will go away. */
+#include <sys/syscall.h>
+static int clock_adjtime(clockid_t id, struct timex *tx)
+{
+	return syscall(__NR_clock_adjtime, id, tx);
+}
+
+static clockid_t get_clockid(void)
+{
+	clockid_t id = CLOCK_INVALID;
+	FILE *fp = fopen(ATTRIBUTE, "r");
+	if (!fp) {
+		return CLOCK_INVALID;
+	}
+	if (1 != fscanf(fp, " %d", &id)) {
+		fprintf(stderr, "failed to scan clock id\n");
+	}
+	return id;
+}
+
+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"
+		" -p val     enable output with a period of 'val' nanoseconds\n"
+		" -P val     enable or disable (val=1|0) the system clock PPS\n"
+		" -s         set the ptp clock time from the system time\n"
+		" -t val     shift the ptp clock time by 'val' seconds\n",
+		progname);
+}
+
+int main(int argc, char *argv[])
+{
+	struct ptp_clock_caps caps;
+	struct ptp_extts_event event;
+	struct ptp_extts_request extts_request;
+	struct ptp_perout_request perout_request;
+	struct timespec ts;
+	struct timex tx;
+
+	static timer_t timerid;
+	struct itimerspec timeout;
+	struct sigevent sigevent;
+
+	char *progname;
+	int c, cnt, fd;
+
+	char *device = DEVICE;
+	clockid_t clkid;
+	int adjfreq = 0x7fffffff;
+	int adjtime = 0;
+	int capabilities = 0;
+	int extts = 0;
+	int gettime = 0;
+	int oneshot = 0;
+	int periodic = 0;
+	int perout = -1;
+	int pps = -1;
+	int settime = 0;
+
+	progname = strrchr(argv[0], '/');
+	progname = progname ? 1+progname : argv[0];
+	while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:st: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 'p':
+			perout = atoi(optarg);
+			break;
+		case 'P':
+			pps = atoi(optarg);
+			break;
+		case 's':
+			settime = 1;
+			break;
+		case 't':
+			adjtime = atoi(optarg);
+			break;
+		case 'h':
+			usage(progname);
+			return 0;
+		case '?':
+		default:
+			usage(progname);
+			return -1;
+		}
+	}
+
+	clkid = get_clockid();
+	if (CLOCK_INVALID == clkid) {
+		fprintf(stderr, "failed to read clock id\n");
+		return -1;
+	}
+
+	fd = open(device, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "cannot open %s: %s", device, strerror(errno));
+		return -1;
+	}
+
+	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) {
+		memset(&tx, 0, sizeof(tx));
+		tx.modes = ADJ_FREQUENCY;
+		tx.freq = (long) (adjfreq * 65.536);
+		if (clock_adjtime(clkid, &tx)) {
+			perror("clock_adjtime");
+		} else {
+			puts("frequency adjustment okay");
+		}
+	}
+
+	if (adjtime) {
+		memset(&tx, 0, sizeof(tx));
+		tx.modes = ADJ_SETOFFSET;
+		tx.time.tv_sec = adjtime;
+		tx.time.tv_usec = 0;
+		if (clock_adjtime(clkid, &tx)) {
+			perror("clock_adjtime");
+		} else {
+			puts("time shift okay");
+		}
+	}
+
+	if (gettime) {
+		if (clock_gettime(clkid, &ts)) {
+			perror("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 (clock_settime(clkid, &ts)) {
+			perror("clock_settime");
+		} else {
+			puts("set time okay");
+		}
+	}
+
+	if (extts) {
+		memset(&extts_request, 0, sizeof(extts_request));
+		extts_request.index = 0;
+		extts_request.flags = PTP_ENABLE_FEATURE;
+		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+			perror("PTP_EXTTS_REQUEST");
+			extts = 0;
+		} else {
+			puts("external time stamp request okay");
+		}
+		for (; extts; extts--) {
+			cnt = read(fd, &event, sizeof(event));
+			if (cnt != sizeof(event)) {
+				perror("read");
+				break;
+			}
+			printf("event index %u at %lld.%09u\n", event.index,
+			       event.t.sec, event.t.nsec);
+			fflush(stdout);
+		}
+		/* Disable the feature again. */
+		extts_request.flags = 0;
+		if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+			perror("PTP_EXTTS_REQUEST");
+		}
+	}
+
+	if (oneshot) {
+		install_handler(SIGALRM, handle_alarm);
+		/* Create a timer. */
+		sigevent.sigev_notify = SIGEV_SIGNAL;
+		sigevent.sigev_signo = SIGALRM;
+		if (timer_create(clkid, &sigevent, &timerid)) {
+			perror("timer_create");
+			return -1;
+		}
+		/* Start the timer. */
+		memset(&timeout, 0, sizeof(timeout));
+		timeout.it_value.tv_sec = oneshot;
+		if (timer_settime(timerid, 0, &timeout, NULL)) {
+			perror("timer_settime");
+			return -1;
+		}
+		pause();
+		timer_delete(timerid);
+	}
+
+	if (periodic) {
+		install_handler(SIGALRM, handle_alarm);
+		/* Create a timer. */
+		sigevent.sigev_notify = SIGEV_SIGNAL;
+		sigevent.sigev_signo = SIGALRM;
+		if (timer_create(clkid, &sigevent, &timerid)) {
+			perror("timer_create");
+			return -1;
+		}
+		/* Start the timer. */
+		memset(&timeout, 0, sizeof(timeout));
+		timeout.it_interval.tv_sec = periodic;
+		timeout.it_value.tv_sec = periodic;
+		if (timer_settime(timerid, 0, &timeout, NULL)) {
+			perror("timer_settime");
+			return -1;
+		}
+		while (1) {
+			pause();
+		}
+		timer_delete(timerid);
+	}
+
+	if (perout >= 0) {
+		if (clock_gettime(clkid, &ts)) {
+			perror("clock_gettime");
+			return -1;
+		}
+		memset(&perout_request, 0, sizeof(perout_request));
+		perout_request.index = 0;
+		perout_request.start.sec = ts.tv_sec + 2;
+		perout_request.start.nsec = 0;
+		perout_request.period.sec = 0;
+		perout_request.period.nsec = perout;
+		if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
+			perror("PTP_PEROUT_REQUEST");
+		} else {
+			puts("periodic output request okay");
+		}
+	}
+
+	if (pps != -1) {
+		int enable = pps ? 1 : 0;
+		if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
+			perror("PTP_ENABLE_PPS");
+		} else {
+			puts("pps for system time request okay");
+		}
+	}
+
+	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 a2aea53..802d100 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..17be208
--- /dev/null
+++ b/drivers/ptp/Kconfig
@@ -0,0 +1,27 @@
+#
+# PTP clock support configuration
+#
+
+menu "PTP clock support"
+
+config PTP_1588_CLOCK
+	tristate "PTP clock support"
+	depends on EXPERIMENTAL
+	depends on PPS
+	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.
+
+endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
new file mode 100644
index 0000000..480e2af
--- /dev/null
+++ b/drivers/ptp/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for PTP 1588 clock support.
+#
+
+ptp-y					:= ptp_clock.o ptp_chardev.o ptp_sysfs.o
+obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp.o
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
new file mode 100644
index 0000000..096da3b
--- /dev/null
+++ b/drivers/ptp/ptp_chardev.c
@@ -0,0 +1,178 @@
+/*
+ * PTP 1588 clock support - character device implementation.
+ *
+ * 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/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+
+#include <ptp_private.h>
+
+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 long ptp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+	struct ptp_clock_caps caps;
+	struct ptp_clock_request req;
+	struct ptp_clock *ptp = fp->private_data;
+	struct ptp_clock_info *ops = ptp->info;
+	void *priv = ops->priv;
+	int enable, err = 0;
+
+	switch (cmd) {
+
+	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_EXTTS_REQUEST:
+		if (copy_from_user(&req.extts, (void __user *)arg,
+				   sizeof(req.extts))) {
+			err = -EFAULT;
+			break;
+		}
+		if (req.extts.index >= ops->n_ext_ts) {
+			err = -EINVAL;
+			break;
+		}
+		req.type = PTP_CLK_REQ_EXTTS;
+		enable = req.extts.flags & PTP_ENABLE_FEATURE ? 1 : 0;
+		err = ops->enable(priv, &req, enable);
+		break;
+
+	case PTP_PEROUT_REQUEST:
+		if (copy_from_user(&req.perout, (void __user *)arg,
+				   sizeof(req.perout))) {
+			err = -EFAULT;
+			break;
+		}
+		if (req.perout.index >= ops->n_per_out) {
+			err = -EINVAL;
+			break;
+		}
+		req.type = PTP_CLK_REQ_PEROUT;
+		enable = req.perout.period.sec || req.perout.period.nsec;
+		err = ops->enable(priv, &req, enable);
+		break;
+
+	case PTP_ENABLE_PPS:
+		if (!capable(CAP_SYS_TIME))
+			return -EPERM;
+		req.type = PTP_CLK_REQ_PPS;
+		enable = arg ? 1 : 0;
+		err = ops->enable(priv, &req, enable);
+		break;
+
+	default:
+		err = -ENOTTY;
+		break;
+	}
+	return err;
+}
+
+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;
+	ptp = container_of(inode->i_cdev, struct ptp_clock, cdev);
+	return 0;
+}
+
+static const struct file_operations ptp_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= ptp_ioctl,
+	.open		= ptp_open,
+	.poll		= ptp_poll,
+	.read		= ptp_read,
+	.release	= ptp_release,
+};
+
+int ptp_register_chardev(struct ptp_clock *ptp)
+{
+	int err;
+
+	cdev_init(&ptp->cdev, &ptp_fops);
+	ptp->cdev.owner = ptp->info->owner;
+	err = cdev_add(&ptp->cdev, ptp->devid, 1);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+int ptp_unregister_chardev(struct ptp_clock *ptp)
+{
+	cdev_del(&ptp->cdev);
+	return 0;
+}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..3a62a90
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,382 @@
+/*
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/posix-timers.h>
+#include <linux/pps_kernel.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+
+#include <ptp_private.h>
+
+#define PTP_MAX_ALARMS 4
+#define PTP_MAX_CLOCKS (MAX_CLOCKS/2)
+#define PTP_PPS_DEFAULTS (PPS_CAPTUREASSERT | PPS_OFFSETASSERT)
+#define PTP_PPS_EVENT PPS_CAPTUREASSERT
+#define PTP_PPS_MODE (PTP_PPS_DEFAULTS | PPS_CANWAIT | PPS_TSFMT_TSPEC)
+
+/* private globals */
+
+extern struct device_attribute ptp_dev_attrs[]; /* see ptp_sysfs.c */
+static dev_t ptp_devt;
+static struct class *ptp_class;
+
+static struct {
+	struct list_head list;
+	DECLARE_BITMAP(map, PTP_MAX_CLOCKS);
+	struct ptp_clock *data[MAX_CLOCKS];
+} clocks;
+static DEFINE_MUTEX(clocks_mux); /* protects 'clocks' */
+
+static struct ptp_clock *clockid_to_ptpclock(const clockid_t clkid)
+{
+	return clocks.data[clkid];
+}
+
+/* time stamp event queue operations */
+
+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->t.sec = div_u64_rem(src->timestamp, 1000000000, &remainder);
+	dst->t.nsec = remainder;
+
+	if (!queue_free(queue))
+		queue->overflow++;
+
+	queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS;
+}
+
+/* posix clock implementation */
+
+static int ptp_clock_getres(const clockid_t clkid, struct timespec *tp)
+{
+	return 1; /* always round timer functions to one nanosecond */
+}
+
+static int ptp_clock_set(const clockid_t clkid, struct timespec *tp)
+{
+	struct ptp_clock *ptp;
+	int err;
+
+	mutex_lock(&clocks_mux);
+	ptp = clockid_to_ptpclock(clkid);
+	err = ptp->info->settime(ptp->info->priv, tp);
+	mutex_unlock(&clocks_mux);
+	return err;
+}
+
+static int ptp_clock_get(const clockid_t clkid, struct timespec *tp)
+{
+	struct ptp_clock *ptp;
+	int err;
+
+	mutex_lock(&clocks_mux);
+	ptp = clockid_to_ptpclock(clkid);
+	err = ptp->info->gettime(ptp->info->priv, tp);
+	mutex_unlock(&clocks_mux);
+	return err;
+}
+
+static int ptp_clock_adj(const clockid_t clkid, struct timex *tx)
+{
+	struct ptp_clock *ptp;
+	struct ptp_clock_info *ops;
+	int err = -ENODEV;
+
+	mutex_lock(&clocks_mux);
+	ptp = clockid_to_ptpclock(clkid);
+	if (!ptp)
+		goto out;
+	ops = ptp->info;
+	err = -EOPNOTSUPP;
+
+	if (tx->modes & ADJ_SETOFFSET) {
+		struct timespec ts;
+		ts.tv_sec = tx->time.tv_sec;
+		ts.tv_nsec = tx->modes & ADJ_NANO ? tx->time.tv_usec :
+			tx->time.tv_usec * 1000;
+		err = ops->adjtime(ops->priv, &ts);
+
+	} else if (tx->modes & ADJ_FREQUENCY) {
+		s64 ppb = 1 + tx->freq;
+		ppb *= 125;
+		ppb >>= 13;
+		err = ops->adjfreq(ops->priv, (s32)ppb);
+	}
+
+out:
+	mutex_unlock(&clocks_mux);
+	return err;
+}
+
+static int ptp_timer_create(struct k_itimer *timer)
+{
+	return -EOPNOTSUPP;
+}
+
+static int ptp_nsleep(const clockid_t clkid, int flags,
+		      struct timespec *ts, struct timespec __user *u)
+{
+	return -EOPNOTSUPP;
+}
+
+static long ptp_nsleep_restart(struct restart_block *restart_block)
+{
+	return -EOPNOTSUPP;
+}
+
+static int ptp_timer_set(struct k_itimer *timer, int flags,
+			 struct itimerspec *newits, struct itimerspec *oldits)
+{
+	return -EOPNOTSUPP;
+}
+
+static int ptp_timer_del(struct k_itimer *timer)
+{
+	return -EOPNOTSUPP;
+}
+
+static void ptp_timer_get(struct k_itimer *timer, struct itimerspec *its)
+{
+}
+
+/* public interface */
+
+struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
+{
+	struct k_clock clk;
+	struct ptp_clock *ptp;
+	int err = 0, index, major = MAJOR(ptp_devt);
+
+	if (info->n_alarm > PTP_MAX_ALARMS)
+		return ERR_PTR(-EINVAL);
+
+	/* Find a free clock slot and reserve it. */
+	err = -EBUSY;
+	mutex_lock(&clocks_mux);
+	index = find_first_zero_bit(clocks.map, PTP_MAX_CLOCKS);
+	if (index < PTP_MAX_CLOCKS)
+		set_bit(index, clocks.map);
+	else
+		goto no_slot;
+
+	/* 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->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%d", ptp->index);
+	if (IS_ERR(ptp->dev))
+		goto no_device;
+
+	dev_set_drvdata(ptp->dev, ptp);
+
+	err = ptp_register_chardev(ptp);
+	if (err)
+		goto no_chardev;
+
+	err = ptp_populate_sysfs(ptp);
+	if (err)
+		goto no_sysfs;
+
+	/* Register a new PPS source. */
+	if (info->pps) {
+		struct pps_source_info pps;
+		memset(&pps, 0, sizeof(pps));
+		snprintf(pps.name, PPS_MAX_NAME_LEN, "ptp%d", index);
+		pps.mode = PTP_PPS_MODE;
+		pps.owner = info->owner;
+		err = pps_register_source(&pps, PTP_PPS_DEFAULTS);
+		if (err < 0) {
+			pr_err("failed to register pps source\n");
+			goto no_pps;
+		} else
+			ptp->pps_source = err;
+	}
+
+	/* Create a posix clock. */
+	memset(&clk, 0, sizeof(clk));
+	clk.clock_getres	= ptp_clock_getres;
+	clk.clock_set		= ptp_clock_set;
+	clk.clock_get		= ptp_clock_get;
+	clk.clock_adj		= ptp_clock_adj;
+	clk.timer_create	= ptp_timer_create;
+	clk.nsleep		= ptp_nsleep;
+	clk.nsleep_restart	= ptp_nsleep_restart;
+	clk.timer_set		= ptp_timer_set;
+	clk.timer_del		= ptp_timer_del;
+	clk.timer_get		= ptp_timer_get;
+
+	snprintf(clk.name, KCLOCK_MAX_NAME, "ptp%d", index);
+
+	ptp->clock_id = create_posix_clock(&clk);
+	if (CLOCK_INVALID == ptp->clock_id) {
+		pr_err("failed to create posix clock\n");
+		err = -EBUSY;
+		goto no_clock;
+	}
+
+	/* Prevent this module from unloading. */
+	try_module_get(info->owner);
+
+	/* Clock is ready, add it into the list. */
+	list_add(&ptp->list, &clocks.list);
+	clocks.data[ptp->clock_id] = ptp;
+
+	mutex_unlock(&clocks_mux);
+	return ptp;
+
+no_clock:
+	if (ptp->info->pps)
+		pps_unregister_source(ptp->pps_source);
+no_pps:
+	ptp_cleanup_sysfs(ptp);
+no_sysfs:
+	ptp_unregister_chardev(ptp);
+no_chardev:
+	device_destroy(ptp_class, ptp->devid);
+no_device:
+	mutex_destroy(&ptp->tsevq_mux);
+	kfree(ptp);
+no_memory:
+	clear_bit(index, clocks.map);
+no_slot:
+	mutex_unlock(&clocks_mux);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+	/* Release the clock's resources. */
+	if (ptp->info->pps)
+		pps_unregister_source(ptp->pps_source);
+	ptp_cleanup_sysfs(ptp);
+	ptp_unregister_chardev(ptp);
+	device_destroy(ptp_class, ptp->devid);
+	mutex_destroy(&ptp->tsevq_mux);
+
+	/* Remove the clock from the list. */
+	mutex_lock(&clocks_mux);
+	clocks.data[ptp->clock_id] = NULL;
+	list_del(&ptp->list);
+	clear_bit(ptp->index, clocks.map);
+	mutex_unlock(&clocks_mux);
+
+	kfree(ptp);
+
+	return 0;
+}
+EXPORT_SYMBOL(ptp_clock_unregister);
+
+void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
+{
+	struct timespec ts;
+	struct pps_ktime pps_ts;
+
+	switch (event->type) {
+
+	case PTP_CLOCK_ALARM:
+		break;
+
+	case PTP_CLOCK_EXTTS:
+		enqueue_external_timestamp(&ptp->tsevq, event);
+		wake_up_interruptible(&ptp->tsev_wq);
+		break;
+
+	case PTP_CLOCK_PPS:
+		getnstimeofday(&ts);
+		pps_ts.sec = ts.tv_sec;
+		pps_ts.nsec = ts.tv_nsec;
+		pps_event(ptp->pps_source, &pps_ts, PTP_PPS_EVENT, NULL);
+		break;
+	}
+}
+EXPORT_SYMBOL(ptp_clock_event);
+
+/* 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 (IS_ERR(ptp_class)) {
+		pr_err("ptp: failed to allocate class\n");
+		return PTR_ERR(ptp_class);
+	}
+
+	err = alloc_chrdev_region(&ptp_devt, 0, PTP_MAX_CLOCKS, "ptp");
+	if (err < 0) {
+		pr_err("ptp: failed to allocate device region\n");
+		goto no_region;
+	}
+
+	ptp_class->dev_attrs = ptp_dev_attrs;
+	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/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
new file mode 100644
index 0000000..2cb46ac
--- /dev/null
+++ b/drivers/ptp/ptp_private.h
@@ -0,0 +1,64 @@
+/*
+ * PTP 1588 clock support - private declarations for the core module.
+ *
+ * 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_PRIVATE_H_
+#define _PTP_PRIVATE_H_
+
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/ptp_clock.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/time.h>
+
+#define PTP_MAX_TIMESTAMPS 128
+
+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 device *dev;
+	struct ptp_clock_info *info;
+	clockid_t clock_id;
+	dev_t devid;
+	int index; /* index into clocks.map */
+	int pps_source;
+	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;
+	struct cdev cdev;
+};
+
+static inline int queue_cnt(struct timestamp_event_queue *q)
+{
+	int cnt = q->tail - q->head;
+	return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
+}
+
+int ptp_register_chardev(struct ptp_clock *ptp);
+int ptp_unregister_chardev(struct ptp_clock *ptp);
+
+int ptp_cleanup_sysfs(struct ptp_clock *ptp);
+int ptp_populate_sysfs(struct ptp_clock *ptp);
+
+#endif
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
new file mode 100644
index 0000000..7b5764e
--- /dev/null
+++ b/drivers/ptp/ptp_sysfs.c
@@ -0,0 +1,235 @@
+/*
+ * PTP 1588 clock support - sysfs 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.
+ */
+#include <linux/capability.h>
+#include <linux/device.h>
+
+#include <ptp_private.h>
+
+static ssize_t clock_id_show(struct device *dev,
+			     struct device_attribute *attr, char *page)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->clock_id);
+}
+
+static ssize_t clock_name_show(struct device *dev,
+			       struct device_attribute *attr, char *page)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	return snprintf(page, PAGE_SIZE-1, "%s\n", ptp->info->name);
+}
+
+#define PTP_SHOW_INT(name)						\
+static ssize_t name##_show(struct device *dev,				\
+			   struct device_attribute *attr, char *page)	\
+{									\
+	struct ptp_clock *ptp = dev_get_drvdata(dev);			\
+	return snprintf(page, PAGE_SIZE-1, "%d\n", ptp->info->name);	\
+}
+
+PTP_SHOW_INT(max_adj);
+PTP_SHOW_INT(n_alarm);
+PTP_SHOW_INT(n_ext_ts);
+PTP_SHOW_INT(n_per_out);
+PTP_SHOW_INT(pps);
+
+#define PTP_RO_ATTR(_var, _name) {				\
+	.attr	= { .name = __stringify(_name), .mode = 0444 },	\
+	.show	= _var##_show,					\
+}
+
+struct device_attribute ptp_dev_attrs[] = {
+	PTP_RO_ATTR(clock_id,	clock_id),
+	PTP_RO_ATTR(clock_name,	clock_name),
+	PTP_RO_ATTR(max_adj,	max_adjustment),
+	PTP_RO_ATTR(n_alarm,	n_alarms),
+	PTP_RO_ATTR(n_ext_ts,	n_external_timestamps),
+	PTP_RO_ATTR(n_per_out,	n_periodic_outputs),
+	PTP_RO_ATTR(pps,	pps_available),
+	__ATTR_NULL,
+};
+
+static ssize_t extts_enable_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	struct ptp_clock_info *ops = ptp->info;
+	void *priv = ops->priv;
+	struct ptp_clock_request req = { .type = PTP_CLK_REQ_EXTTS };
+	int cnt, enable;
+	int err = -EINVAL;
+
+	cnt = sscanf(buf, "%u %d", &req.extts.index, &enable);
+	if (cnt != 2)
+		goto out;
+	if (req.extts.index >= ops->n_ext_ts)
+		goto out;
+
+	err = ops->enable(priv, &req, enable ? 1 : 0);
+	if (err)
+		goto out;
+
+	return count;
+out:
+	return err;
+}
+
+static ssize_t extts_fifo_show(struct device *dev,
+			       struct device_attribute *attr, char *page)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	struct timestamp_event_queue *queue = &ptp->tsevq;
+	struct ptp_extts_event *event;
+	size_t qcnt;
+	int cnt;
+
+	if (mutex_lock_interruptible(&ptp->tsevq_mux))
+		return -ERESTARTSYS;
+
+	qcnt = queue_cnt(&ptp->tsevq);
+	if (!qcnt) {
+		mutex_unlock(&ptp->tsevq_mux);
+		return 0;
+	}
+	event = &queue->buf[queue->head];
+
+	cnt = snprintf(page, PAGE_SIZE, "%u %lld %u\n",
+		       event->index, event->t.sec, event->t.nsec);
+
+	queue->head = (1 + queue->head) % PTP_MAX_TIMESTAMPS;
+
+	mutex_unlock(&ptp->tsevq_mux);
+
+	return cnt;
+}
+
+static ssize_t period_store(struct device *dev,
+			    struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	struct ptp_clock_info *ops = ptp->info;
+	void *priv = ops->priv;
+	struct ptp_clock_request req = { .type = PTP_CLK_REQ_PEROUT };
+	int cnt, enable, err = -EINVAL;
+
+	cnt = sscanf(buf, "%u %lld %u %lld %u", &req.perout.index,
+		     &req.perout.start.sec, &req.perout.start.nsec,
+		     &req.perout.period.sec, &req.perout.period.nsec);
+	if (cnt != 5)
+		goto out;
+	if (req.perout.index >= ops->n_per_out)
+		goto out;
+
+	enable = req.perout.period.sec || req.perout.period.nsec;
+	err = ops->enable(priv, &req, enable);
+	if (err)
+		goto out;
+
+	return count;
+out:
+	return err;
+}
+
+static ssize_t pps_enable_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct ptp_clock *ptp = dev_get_drvdata(dev);
+	struct ptp_clock_info *ops = ptp->info;
+	void *priv = ops->priv;
+	struct ptp_clock_request req = { .type = PTP_CLK_REQ_PPS };
+	int cnt, enable;
+	int err = -EINVAL;
+
+	if (!capable(CAP_SYS_TIME))
+		return -EPERM;
+
+	cnt = sscanf(buf, "%d", &enable);
+	if (cnt != 1)
+		goto out;
+
+	err = ops->enable(priv, &req, enable ? 1 : 0);
+	if (err)
+		goto out;
+
+	return count;
+out:
+	return err;
+}
+
+static DEVICE_ATTR(extts_enable, 0220, NULL, extts_enable_store);
+static DEVICE_ATTR(fifo,         0444, extts_fifo_show, NULL);
+static DEVICE_ATTR(period,       0220, NULL, period_store);
+static DEVICE_ATTR(pps_enable,   0220, NULL, pps_enable_store);
+
+int ptp_cleanup_sysfs(struct ptp_clock *ptp)
+{
+	struct device *dev = ptp->dev;
+	struct ptp_clock_info *info = ptp->info;
+
+	if (info->n_ext_ts) {
+		device_remove_file(dev, &dev_attr_extts_enable);
+		device_remove_file(dev, &dev_attr_fifo);
+	}
+	if (info->n_per_out) {
+		device_remove_file(dev, &dev_attr_period);
+	}
+	if (info->pps) {
+		device_remove_file(dev, &dev_attr_pps_enable);
+	}
+	return 0;
+}
+
+int ptp_populate_sysfs(struct ptp_clock *ptp)
+{
+	struct device *dev = ptp->dev;
+	struct ptp_clock_info *info = ptp->info;
+	int err;
+
+	if (info->n_ext_ts) {
+		err = device_create_file(dev, &dev_attr_extts_enable);
+		if (err)
+			goto out1;
+		err = device_create_file(dev, &dev_attr_fifo);
+		if (err)
+			goto out2;
+	}
+	if (info->n_per_out) {
+		err = device_create_file(dev, &dev_attr_period);
+		if (err)
+			goto out3;
+	}
+	if (info->pps) {
+		err = device_create_file(dev, &dev_attr_pps_enable);
+		if (err)
+			goto out4;
+	}
+	return 0;
+out4:
+	device_remove_file(dev, &dev_attr_period);
+out3:
+	device_remove_file(dev, &dev_attr_fifo);
+out2:
+	device_remove_file(dev, &dev_attr_extts_enable);
+out1:
+	return err;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 626b629..1e2468a 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -295,6 +295,7 @@ header-y += ppp-comp.h
 header-y += ppp_defs.h
 header-y += pps.h
 header-y += prctl.h
+header-y += ptp_clock.h
 header-y += ptrace.h
 header-y += qnx4_fs.h
 header-y += qnxtypes.h
diff --git a/include/linux/ptp_clock.h b/include/linux/ptp_clock.h
new file mode 100644
index 0000000..b4ef2cb
--- /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>
+
+/* PTP_xxx bits, for the flags field within the request structures. */
+#define PTP_ENABLE_FEATURE (1<<0)
+#define PTP_RISING_EDGE    (1<<1)
+#define PTP_FALLING_EDGE   (1<<2)
+
+/*
+ * struct ptp_clock_time - represents a time value
+ *
+ * The sign of the seconds field applies to the whole value. The
+ * nanoseconds field is always unsigned. The reserved field is
+ * included for sub-nanosecond resolution, should the demand for
+ * this ever appear.
+ *
+ */
+struct ptp_clock_time {
+	__s64 sec;  /* seconds */
+	__u32 nsec; /* nanoseconds */
+	__u32 reserved;
+};
+
+struct ptp_clock_caps {
+	int max_adj;   /* Maximum frequency adjustment in 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_extts_request {
+	unsigned int index; /* Which channel to configure. */
+	unsigned int flags; /* Bit field for PTP_xxx flags. */
+};
+
+struct ptp_perout_request {
+	struct ptp_clock_time start;  /* Absolute start time. */
+	struct ptp_clock_time period; /* Desired period, zero means disable. */
+	unsigned int index;           /* Which channel to configure. */
+	unsigned int flags;           /* Reserved for future use. */
+};
+
+#define PTP_CLK_MAGIC '='
+
+#define PTP_CLOCK_GETCAPS  _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps)
+#define PTP_EXTTS_REQUEST  _IOW(PTP_CLK_MAGIC, 2, struct ptp_extts_request)
+#define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request)
+#define PTP_ENABLE_PPS     _IOW(PTP_CLK_MAGIC, 4, int)
+
+struct ptp_extts_event {
+	struct ptp_clock_time t; /* Time event occured. */
+	unsigned int index;      /* Which channel produced the event. */
+};
+
+#endif
diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h
new file mode 100644
index 0000000..1f8d9e0
--- /dev/null
+++ b/include/linux/ptp_clock_kernel.h
@@ -0,0 +1,139 @@
+/*
+ * 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_request {
+	enum {
+		PTP_CLK_REQ_EXTTS,
+		PTP_CLK_REQ_PEROUT,
+		PTP_CLK_REQ_PPS,
+	} type;
+	union {
+		struct ptp_extts_request extts;
+		struct ptp_perout_request perout;
+	};
+};
+
+/**
+ * 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.
+ *
+ * @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 (*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.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 5/8] ptp: Added a simulated PTP hardware clock.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (3 preceding siblings ...)
  2010-09-23 17:32 ` [PATCH 4/8] ptp: Added a brand new class driver for ptp clocks Richard Cochran
@ 2010-09-23 17:32 ` Richard Cochran
  2010-09-23 17:33 ` [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx Richard Cochran
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:32 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch adds a driver that simulates a PTP hardware clock. The
driver serves as a simple example for writing real clock driver and
can be used for testing the PTP clock API.

The basic clock operations are implemented using the system clock,
and the ancillary clock operations are simulated.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 drivers/ptp/Kconfig     |   14 ++++
 drivers/ptp/Makefile    |    1 +
 drivers/ptp/ptp_linux.c |  165 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/time/ntp.c       |    2 +
 4 files changed, 182 insertions(+), 0 deletions(-)
 create mode 100644 drivers/ptp/ptp_linux.c

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 17be208..94f329f 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -24,4 +24,18 @@ config PTP_1588_CLOCK
 	  To compile this driver as a module, choose M here: the module
 	  will be called ptp.
 
+config PTP_1588_CLOCK_LINUX
+	tristate "Simulated PTP clock"
+	depends on PTP_1588_CLOCK
+	help
+	  This driver adds support for a simulated PTP clock. It
+	  implements the basic clock operations by using the standard
+	  Linux system time. The driver simulates the ancillary clock
+	  operations. This clock can be used to test PTP programs
+	  provided they use software time stamps for the PTP Ethernet
+	  packets.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ptp_linux.
+
 endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 480e2af..266d4f2 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -4,3 +4,4 @@
 
 ptp-y					:= ptp_clock.o ptp_chardev.o ptp_sysfs.o
 obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp.o
+obj-$(CONFIG_PTP_1588_CLOCK_LINUX)	+= ptp_linux.o
diff --git a/drivers/ptp/ptp_linux.c b/drivers/ptp/ptp_linux.c
new file mode 100644
index 0000000..57b3da4
--- /dev/null
+++ b/drivers/ptp/ptp_linux.c
@@ -0,0 +1,165 @@
+/*
+ * PTP 1588 clock using the Linux system clock
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timex.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+static struct ptp_clock *linux_clock;
+
+DEFINE_SPINLOCK(adjtime_lock);
+
+static int ptp_linux_adjfreq(void *priv, s32 ppb)
+{
+	struct timex txc;
+	s64 tmp = ppb;
+	int err;
+	pr_debug("ptp_linux: adjfreq ppb=%d\n", ppb);
+	txc.freq = div_s64(tmp<<16, 1000);
+	txc.modes = ADJ_FREQUENCY;
+	err = do_adjtimex(&txc);
+	return err < 0 ? err : 0;
+}
+
+static int ptp_linux_adjtime(void *priv, struct timespec *ts)
+{
+	s64 delta;
+	ktime_t now;
+	struct timespec t2;
+	unsigned long flags;
+	int err;
+
+	delta = 1000000000LL * ts->tv_sec + ts->tv_nsec;
+
+	spin_lock_irqsave(&adjtime_lock, flags);
+
+	now = ktime_get_real();
+
+	now = delta < 0 ? ktime_sub_ns(now, -delta) : ktime_add_ns(now, delta);
+
+	t2 = ktime_to_timespec(now);
+
+	err = do_settimeofday(&t2);
+
+	spin_unlock_irqrestore(&adjtime_lock, flags);
+
+	return err;
+}
+
+static int ptp_linux_gettime(void *priv, struct timespec *ts)
+{
+	getnstimeofday(ts);
+	return 0;
+}
+
+static int ptp_linux_settime(void *priv, struct timespec *ts)
+{
+	return do_settimeofday(ts);
+}
+
+#define sim(x...) pr_warn("ptp_linux simulation: " x)
+
+static int ptp_linux_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct ptp_clock_event event;
+	ktime_t kt;
+	int i;
+
+	switch (rq->type) {
+
+	case PTP_CLK_REQ_EXTTS:
+		if (on) {
+			sim("enable external timestamped events\n");
+			for (i = 0; i < 100; i++) {
+				kt = ktime_get_real();
+				event.type = PTP_CLOCK_EXTTS;
+				event.index = 0;
+				event.timestamp = ktime_to_ns(kt);
+				ptp_clock_event(linux_clock, &event);
+			}
+		} else
+			sim("disable external timestamped events\n");
+		break;
+
+	case PTP_CLK_REQ_PEROUT:
+		if (on)
+			sim("channel %d start %lld %u period %lld %u\n",
+			    rq->perout.index,
+			    rq->perout.start.sec, rq->perout.start.nsec,
+			    rq->perout.period.sec, rq->perout.period.nsec);
+		else
+			sim("periodic output %d disabled\n", rq->perout.index);
+		break;
+
+	case PTP_CLK_REQ_PPS:
+		if (on)
+			sim("clock to host PPS enabled\n");
+		else
+			sim("clock to host PPS disabled\n");
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static struct ptp_clock_info ptp_linux_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "Linux timer",
+	.max_adj	= 512000,
+	.n_alarm	= 0,
+	.n_ext_ts	= 1,
+	.n_per_out	= 1,
+	.pps		= 1,
+	.priv		= NULL,
+	.adjfreq	= ptp_linux_adjfreq,
+	.adjtime	= ptp_linux_adjtime,
+	.gettime	= ptp_linux_gettime,
+	.settime	= ptp_linux_settime,
+	.enable		= ptp_linux_enable,
+};
+
+/* module operations */
+
+static void __exit ptp_linux_exit(void)
+{
+	ptp_clock_unregister(linux_clock);
+}
+
+static int __init ptp_linux_init(void)
+{
+	linux_clock = ptp_clock_register(&ptp_linux_caps);
+
+	return IS_ERR(linux_clock) ? PTR_ERR(linux_clock) : 0;
+}
+
+module_init(ptp_linux_init);
+module_exit(ptp_linux_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the Linux system timer");
+MODULE_LICENSE("GPL");
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index c631168..eba3bcf 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -14,6 +14,7 @@
 #include <linux/timex.h>
 #include <linux/time.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 
 /*
  * NTP timekeeping variables:
@@ -535,6 +536,7 @@ int do_adjtimex(struct timex *txc)
 
 	return result;
 }
+EXPORT_SYMBOL(do_adjtimex);
 
 static int __init ntp_tick_adj_setup(char *str)
 {
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (4 preceding siblings ...)
  2010-09-23 17:32 ` [PATCH 5/8] ptp: Added a simulated PTP hardware clock Richard Cochran
@ 2010-09-23 17:33 ` Richard Cochran
  2010-09-23 19:17   ` Christoph Lameter
  2010-09-23 17:33 ` [PATCH 7/8] ptp: Added a clock driver for the IXP46x Richard Cochran
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:33 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

The eTSEC includes a PTP clock with quite a few features. This patch adds
support for the basic clock adjustment functions, plus two external time
stamps, one alarm, and the PPS callback.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 Documentation/powerpc/dts-bindings/fsl/tsec.txt |   57 +++
 arch/powerpc/boot/dts/mpc8313erdb.dts           |   14 +
 arch/powerpc/boot/dts/mpc8572ds.dts             |   14 +
 arch/powerpc/boot/dts/p2020ds.dts               |   14 +
 arch/powerpc/boot/dts/p2020rdb.dts              |   14 +
 drivers/net/Makefile                            |    1 +
 drivers/net/gianfar_ptp.c                       |  447 +++++++++++++++++++++++
 drivers/net/gianfar_ptp_reg.h                   |  113 ++++++
 drivers/ptp/Kconfig                             |   13 +
 9 files changed, 687 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/gianfar_ptp.c
 create mode 100644 drivers/net/gianfar_ptp_reg.h

diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
index edb7ae1..f6edbb8 100644
--- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
@@ -74,3 +74,60 @@ Example:
 		interrupt-parent = <&mpic>;
 		phy-handle = <&phy0>
 	};
+
+* Gianfar PTP clock nodes
+
+General Properties:
+
+  - compatible   Should be "fsl,etsec-ptp"
+  - reg          Offset and length of the register set for the device
+  - interrupts   There should be at least two interrupts. Some devices
+                 have as many as four PTP related interrupts.
+
+Clock Properties:
+
+  - tclk-period  Timer reference clock period in nanoseconds.
+  - tmr-prsc     Prescaler, divides the output clock.
+  - tmr-add      Frequency compensation value.
+  - cksel        0= external clock, 1= eTSEC system clock, 3= RTC clock input.
+                 Currently the driver only supports choice "1".
+  - tmr-fiper1   Fixed interval period pulse generator.
+  - tmr-fiper2   Fixed interval period pulse generator.
+  - max-adj      Maximum frequency adjustment in parts per billion.
+
+  These properties set the operational parameters for the PTP
+  clock. You must choose these carefully for the clock to work right.
+  Here is how to figure good values:
+
+  TimerOsc     = system clock               MHz
+  tclk_period  = desired clock period       nanoseconds
+  NominalFreq  = 1000 / tclk_period         MHz
+  FreqDivRatio = TimerOsc / NominalFreq     (must be greater that 1.0)
+  tmr_add      = ceil(2^32 / FreqDivRatio)
+  OutputClock  = NominalFreq / tmr_prsc     MHz
+  PulseWidth   = 1 / OutputClock            microseconds
+  FiperFreq1   = desired frequency in Hz
+  FiperDiv1    = 1000000 * OutputClock / FiperFreq1
+  tmr_fiper1   = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
+  max_adj      = 1000000000 * (FreqDivRatio - 1.0) - 1
+
+  The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
+  driver expects that tmr_fiper1 will be correctly set to produce a 1
+  Pulse Per Second (PPS) signal, since this will be offered to the PPS
+  subsystem to synchronize the Linux clock.
+
+Example:
+
+	ptp_clock@24E00 {
+		compatible = "fsl,etsec-ptp";
+		reg = <0x24E00 0xB0>;
+		interrupts = <12 0x8 13 0x8>;
+		interrupt-parent = < &ipic >;
+		tclk-period = <10>;
+		tmr-prsc    = <100>;
+		tmr-add     = <0x999999A4>;
+		cksel       = <0x1>;
+		tmr-fiper1  = <0x3B9AC9F6>;
+		tmr-fiper2  = <0x00018696>;
+		max-adj     = <659999998>;
+	};
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 183f2aa..85a7eaa 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -208,6 +208,20 @@
 			sleep = <&pmc 0x00300000>;
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <12 0x8 13 0x8>;
+			interrupt-parent = < &ipic >;
+			tclk-period = <10>;
+			tmr-prsc    = <100>;
+			tmr-add     = <0x999999A4>;
+			cksel       = <0x1>;
+			tmr-fiper1  = <0x3B9AC9F6>;
+			tmr-fiper2  = <0x00018696>;
+			max-adj     = <659999998>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts
index cafc128..74208cd 100644
--- a/arch/powerpc/boot/dts/mpc8572ds.dts
+++ b/arch/powerpc/boot/dts/mpc8572ds.dts
@@ -324,6 +324,20 @@
 			};
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2 71 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xAAAAAAAB>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x3B9AC9FB>;
+			max-adj = <499999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
index 1101914..39d73bb 100644
--- a/arch/powerpc/boot/dts/p2020ds.dts
+++ b/arch/powerpc/boot/dts/p2020ds.dts
@@ -336,6 +336,20 @@
 			phy_type = "ulpi";
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xCCCCCCCD>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x0001869B>;
+			max-adj = <249999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts
index da4cb0d..5498fb9 100644
--- a/arch/powerpc/boot/dts/p2020rdb.dts
+++ b/arch/powerpc/boot/dts/p2020rdb.dts
@@ -396,6 +396,20 @@
 			phy_type = "ulpi";
 		};
 
+		ptp_clock@24E00 {
+			compatible = "fsl,etsec-ptp";
+			reg = <0x24E00 0xB0>;
+			interrupts = <68 2 69 2 70 2>;
+			interrupt-parent = < &mpic >;
+			tclk-period = <5>;
+			tmr-prsc = <200>;
+			tmr-add = <0xCCCCCCCD>;
+			cksel = <1>;
+			tmr-fiper1 = <0x3B9AC9FB>;
+			tmr-fiper2 = <0x0001869B>;
+			max-adj = <249999999>;
+		};
+
 		enet0: ethernet@24000 {
 			#address-cells = <1>;
 			#size-cells = <1>;
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3e8f150..46737b3 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ATL2) += atlx/
 obj-$(CONFIG_ATL1E) += atl1e/
 obj-$(CONFIG_ATL1C) += atl1c/
 obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
 obj-$(CONFIG_TEHUTI) += tehuti.o
 obj-$(CONFIG_ENIC) += enic/
 obj-$(CONFIG_JME) += jme.o
diff --git a/drivers/net/gianfar_ptp.c b/drivers/net/gianfar_ptp.c
new file mode 100644
index 0000000..ec3a25d
--- /dev/null
+++ b/drivers/net/gianfar_ptp.c
@@ -0,0 +1,447 @@
+/*
+ * PTP 1588 clock using the eTSEC
+ *
+ * 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/device.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+#include "gianfar_ptp_reg.h"
+#include "gianfar.h"
+
+#define DRIVER		"gianfar_ptp"
+#define N_ALARM		1 /* first alarm is used internally to reset fipers */
+#define N_EXT_TS	2
+#define REG_SIZE	sizeof(struct gianfar_ptp_registers)
+
+struct etsects {
+	struct gianfar_ptp_registers *regs;
+	struct ptp_clock *clock;
+	int irq;
+	u64 alarm_interval; /* for periodic alarm */
+	u64 alarm_value;
+	u32 tclk_period;  /* nanoseconds */
+	u32 tmr_prsc;
+	u32 tmr_add;
+	u32 cksel;
+	u32 tmr_fiper1;
+	u32 tmr_fiper2;
+};
+
+/* Private globals */
+static struct etsects the_clock;
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static u64 tmr_cnt_read(struct etsects *etsects)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	lo = gfar_read(&etsects->regs->tmr_cnt_l);
+	hi = gfar_read(&etsects->regs->tmr_cnt_h);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	return ns;
+}
+
+static void tmr_cnt_write(struct etsects *etsects, u64 ns)
+{
+	u32 hi = ns >> 32;
+	u32 lo = ns & 0xffffffff;
+
+	gfar_write(&etsects->regs->tmr_cnt_l, lo);
+	gfar_write(&etsects->regs->tmr_cnt_h, hi);
+}
+
+static void set_alarm(struct etsects *etsects)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	ns = tmr_cnt_read(etsects) + 1500000000ULL;
+	ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
+	ns -= etsects->tclk_period;
+	hi = ns >> 32;
+	lo = ns & 0xffffffff;
+	gfar_write(&etsects->regs->tmr_alarm1_l, lo);
+	gfar_write(&etsects->regs->tmr_alarm1_h, hi);
+}
+
+static void set_fipers(struct etsects *etsects)
+{
+	u32 tmr_ctrl = gfar_read(&etsects->regs->tmr_ctrl);
+
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl & (~TE));
+	gfar_write(&etsects->regs->tmr_prsc,   etsects->tmr_prsc);
+	gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+	gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+	set_alarm(etsects);
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl|TE);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+	struct etsects *etsects = priv;
+	struct ptp_clock_event event;
+	u64 ns;
+	u32 ack = 0, lo, hi, mask, val;
+
+	val = gfar_read(&etsects->regs->tmr_tevent);
+
+	if (val & ETS1) {
+		ack |= ETS1;
+		hi = gfar_read(&etsects->regs->tmr_etts1_h);
+		lo = gfar_read(&etsects->regs->tmr_etts1_l);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 0;
+		event.timestamp = ((u64) hi) << 32;
+		event.timestamp |= lo;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (val & ETS2) {
+		ack |= ETS2;
+		hi = gfar_read(&etsects->regs->tmr_etts2_h);
+		lo = gfar_read(&etsects->regs->tmr_etts2_l);
+		event.type = PTP_CLOCK_EXTTS;
+		event.index = 1;
+		event.timestamp = ((u64) hi) << 32;
+		event.timestamp |= lo;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (val & ALM2) {
+		ack |= ALM2;
+		if (etsects->alarm_value) {
+			event.type = PTP_CLOCK_ALARM;
+			event.index = 0;
+			event.timestamp = etsects->alarm_value;
+			ptp_clock_event(etsects->clock, &event);
+		}
+		if (etsects->alarm_interval) {
+			ns = etsects->alarm_value + etsects->alarm_interval;
+			hi = ns >> 32;
+			lo = ns & 0xffffffff;
+			spin_lock(&register_lock);
+			gfar_write(&etsects->regs->tmr_alarm2_l, lo);
+			gfar_write(&etsects->regs->tmr_alarm2_h, hi);
+			spin_unlock(&register_lock);
+			etsects->alarm_value = ns;
+		} else {
+			gfar_write(&etsects->regs->tmr_tevent, ALM2);
+			spin_lock(&register_lock);
+			mask = gfar_read(&etsects->regs->tmr_temask);
+			mask &= ~ALM2EN;
+			gfar_write(&etsects->regs->tmr_temask, mask);
+			spin_unlock(&register_lock);
+			etsects->alarm_value = 0;
+			etsects->alarm_interval = 0;
+		}
+	}
+
+	if (val & PP1) {
+		ack |= PP1;
+		event.type = PTP_CLOCK_PPS;
+		ptp_clock_event(etsects->clock, &event);
+	}
+
+	if (ack) {
+		gfar_write(&etsects->regs->tmr_tevent, ack);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_gianfar_adjfreq(void *priv, s32 ppb)
+{
+	u64 adj;
+	u32 diff, tmr_add;
+	int neg_adj = 0;
+	struct etsects *etsects = priv;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	tmr_add = etsects->tmr_add;
+	adj = tmr_add;
+	adj *= ppb;
+	diff = div_u64(adj, 1000000000ULL);
+
+	tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
+
+	gfar_write(&etsects->regs->tmr_add, tmr_add);
+
+	return 0;
+}
+
+static int ptp_gianfar_adjtime(void *priv, struct timespec *ts)
+{
+	s64 delta, now;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	delta = 1000000000LL * ts->tv_sec;
+	delta += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	now = tmr_cnt_read(etsects);
+	now += delta;
+	tmr_cnt_write(etsects, now);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	set_fipers(etsects);
+
+	return 0;
+}
+
+static int ptp_gianfar_gettime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	u32 remainder;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	ns = tmr_cnt_read(etsects);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->tv_nsec = remainder;
+	return 0;
+}
+
+static int ptp_gianfar_settime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	unsigned long flags;
+	struct etsects *etsects = priv;
+
+	ns = ts->tv_sec * 1000000000ULL;
+	ns += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	tmr_cnt_write(etsects, ns);
+	set_fipers(etsects);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_gianfar_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct etsects *etsects = priv;
+	unsigned long flags;
+	u32 bit, mask;
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_EXTTS:
+		switch (rq->extts.index) {
+		case 0:
+			bit = ETS1EN;
+			break;
+		case 1:
+			bit = ETS2EN;
+			break;
+		default:
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&register_lock, flags);
+		mask = gfar_read(&etsects->regs->tmr_temask);
+		if (on)
+			mask |= bit;
+		else
+			mask &= ~bit;
+		gfar_write(&etsects->regs->tmr_temask, mask);
+		spin_unlock_irqrestore(&register_lock, flags);
+		return 0;
+
+	case PTP_CLK_REQ_PPS:
+		spin_lock_irqsave(&register_lock, flags);
+		mask = gfar_read(&etsects->regs->tmr_temask);
+		if (on)
+			mask |= PP1EN;
+		else
+			mask &= ~PP1EN;
+		gfar_write(&etsects->regs->tmr_temask, mask);
+		spin_unlock_irqrestore(&register_lock, flags);
+		return 0;
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_gianfar_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "gianfar clock",
+	.max_adj	= 512000,
+	.n_alarm	= N_ALARM,
+	.n_ext_ts	= N_EXT_TS,
+	.n_per_out	= 0,
+	.pps		= 1,
+	.priv		= &the_clock,
+	.adjfreq	= ptp_gianfar_adjfreq,
+	.adjtime	= ptp_gianfar_adjtime,
+	.gettime	= ptp_gianfar_gettime,
+	.settime	= ptp_gianfar_settime,
+	.enable		= ptp_gianfar_enable,
+};
+
+/* OF device tree */
+
+static int get_of_u32(struct device_node *node, char *str, u32 *val)
+{
+	int plen;
+	const u32 *prop = of_get_property(node, str, &plen);
+
+	if (!prop || plen != sizeof(*prop))
+		return -1;
+	*val = *prop;
+	return 0;
+}
+
+static int gianfar_ptp_probe(struct platform_device *dev,
+			     const struct of_device_id *match)
+{
+	struct device_node *node = dev->dev.of_node;
+	struct etsects *etsects = &the_clock;
+	struct timespec now;
+	u32 tmr_ctrl;
+
+	if (get_of_u32(node, "tclk-period", &etsects->tclk_period) ||
+	    get_of_u32(node, "tmr-prsc", &etsects->tmr_prsc) ||
+	    get_of_u32(node, "tmr-add", &etsects->tmr_add) ||
+	    get_of_u32(node, "cksel", &etsects->cksel) ||
+	    get_of_u32(node, "tmr-fiper1", &etsects->tmr_fiper1) ||
+	    get_of_u32(node, "tmr-fiper2", &etsects->tmr_fiper2) ||
+	    get_of_u32(node, "max-adj", &ptp_gianfar_caps.max_adj)) {
+		pr_err("device tree node missing required elements\n");
+		return -ENODEV;
+	}
+
+	etsects->irq = irq_of_parse_and_map(node, 0);
+
+	if (etsects->irq == NO_IRQ) {
+		pr_err("irq not in device tree\n");
+		return -ENODEV;
+	}
+	if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) {
+		pr_err("request_irq failed\n");
+		return -ENODEV;
+	}
+	etsects->regs = of_iomap(node, 0);
+	if (!etsects->regs) {
+		pr_err("of_iomap ptp registers failed\n");
+		return -EINVAL;
+	}
+	getnstimeofday(&now);
+	ptp_gianfar_settime(etsects, &now);
+
+	tmr_ctrl =
+	  (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
+	  (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT;
+
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl);
+	gfar_write(&etsects->regs->tmr_add,    etsects->tmr_add);
+	gfar_write(&etsects->regs->tmr_prsc,   etsects->tmr_prsc);
+	gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1);
+	gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2);
+	set_alarm(etsects);
+	gfar_write(&etsects->regs->tmr_ctrl,   tmr_ctrl|FS|RTPE|TE);
+
+	etsects->clock = ptp_clock_register(&ptp_gianfar_caps);
+
+	return IS_ERR(etsects->clock) ? PTR_ERR(etsects->clock) : 0;
+}
+
+static int gianfar_ptp_remove(struct platform_device *dev)
+{
+	gfar_write(&the_clock.regs->tmr_temask, 0);
+	gfar_write(&the_clock.regs->tmr_ctrl,   0);
+
+	ptp_clock_unregister(the_clock.clock);
+	free_irq(the_clock.irq, &the_clock);
+	iounmap(the_clock.regs);
+
+	return 0;
+}
+
+static struct of_device_id match_table[] = {
+	{ .compatible = "fsl,etsec-ptp" },
+	{},
+};
+
+static struct of_platform_driver gianfar_ptp_driver = {
+	.driver = {
+		.name		= "gianfar_ptp",
+		.of_match_table	= match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe       = gianfar_ptp_probe,
+	.remove      = gianfar_ptp_remove,
+};
+
+/* module operations */
+
+static int __init ptp_gianfar_init(void)
+{
+	return of_register_platform_driver(&gianfar_ptp_driver);
+}
+
+module_init(ptp_gianfar_init);
+
+static void __exit ptp_gianfar_exit(void)
+{
+	of_unregister_platform_driver(&gianfar_ptp_driver);
+}
+
+module_exit(ptp_gianfar_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the eTSEC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/gianfar_ptp_reg.h b/drivers/net/gianfar_ptp_reg.h
new file mode 100644
index 0000000..95e171f
--- /dev/null
+++ b/drivers/net/gianfar_ptp_reg.h
@@ -0,0 +1,113 @@
+/* gianfar_ptp_reg.h
+ * Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010
+ *
+ * PTP 1588 clock using the gianfar eTSEC
+ *
+ * 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 _GIANFAR_PTP_REG_H_
+#define _GIANFAR_PTP_REG_H_
+
+struct gianfar_ptp_registers {
+	u32 tmr_ctrl;     /* Timer control register */
+	u32 tmr_tevent;   /* Timestamp event register */
+	u32 tmr_temask;   /* Timer event mask register */
+	u32 tmr_pevent;   /* Timestamp event register */
+	u32 tmr_pemask;   /* Timer event mask register */
+	u32 tmr_stat;     /* Timestamp status register */
+	u32 tmr_cnt_h;    /* Timer counter high register */
+	u32 tmr_cnt_l;    /* Timer counter low register */
+	u32 tmr_add;      /* Timer drift compensation addend register */
+	u32 tmr_acc;      /* Timer accumulator register */
+	u32 tmr_prsc;     /* Timer prescale */
+	u8  res1[4];
+	u32 tmroff_h;     /* Timer offset high */
+	u32 tmroff_l;     /* Timer offset low */
+	u8  res2[8];
+	u32 tmr_alarm1_h; /* Timer alarm 1 high register */
+	u32 tmr_alarm1_l; /* Timer alarm 1 high register */
+	u32 tmr_alarm2_h; /* Timer alarm 2 high register */
+	u32 tmr_alarm2_l; /* Timer alarm 2 high register */
+	u8  res3[48];
+	u32 tmr_fiper1;   /* Timer fixed period interval */
+	u32 tmr_fiper2;   /* Timer fixed period interval */
+	u32 tmr_fiper3;   /* Timer fixed period interval */
+	u8  res4[20];
+	u32 tmr_etts1_h;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts1_l;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts2_h;  /* Timestamp of general purpose external trigger */
+	u32 tmr_etts2_l;  /* Timestamp of general purpose external trigger */
+};
+
+/* Bit definitions for the TMR_CTRL register */
+#define ALM1P                 (1<<31) /* Alarm1 output polarity */
+#define ALM2P                 (1<<30) /* Alarm2 output polarity */
+#define FS                    (1<<28) /* FIPER start indication */
+#define PP1L                  (1<<27) /* Fiper1 pulse loopback mode enabled. */
+#define PP2L                  (1<<26) /* Fiper2 pulse loopback mode enabled. */
+#define TCLK_PERIOD_SHIFT     (16) /* 1588 timer reference clock period. */
+#define TCLK_PERIOD_MASK      (0x3ff)
+#define RTPE                  (1<<15) /* Record Tx Timestamp to PAL Enable. */
+#define FRD                   (1<<14) /* FIPER Realignment Disable */
+#define ESFDP                 (1<<11) /* External Tx/Rx SFD Polarity. */
+#define ESFDE                 (1<<10) /* External Tx/Rx SFD Enable. */
+#define ETEP2                 (1<<9) /* External trigger 2 edge polarity */
+#define ETEP1                 (1<<8) /* External trigger 1 edge polarity */
+#define COPH                  (1<<7) /* Generated clock (TSEC_1588_GCLK) output phase. */
+#define CIPH                  (1<<6) /* External oscillator input clock phase. */
+#define TMSR                  (1<<5) /* Timer soft reset. When enabled, it resets all the timer registers and state machines. */
+#define BYP                   (1<<3) /* Bypass drift compensated clock */
+#define TE                    (1<<2) /* 1588 timer enable. If not enabled, all the timer registers and state machines are disabled. */
+#define CKSEL_SHIFT           (0) /* 1588 Timer reference clock source select. */
+#define CKSEL_MASK            (0x3)
+
+/* Bit definitions for the TMR_TEVENT register */
+#define ETS2                  (1<<25) /* External trigger 2 timestamp sampled */
+#define ETS1                  (1<<24) /* External trigger 1 timestamp sampled */
+#define ALM2                  (1<<17) /* Current time equaled alarm time register 2 */
+#define ALM1                  (1<<16) /* Current time equaled alarm time register 1 */
+#define PP1                   (1<<7) /* Indicates that a periodic pulse has been generated based on FIPER1 register */
+#define PP2                   (1<<6) /* Indicates that a periodic pulse has been generated based on FIPER2 register */
+#define PP3                   (1<<5) /* Indicates that a periodic pulse has been generated based on FIPER3 register */
+
+/* Bit definitions for the TMR_TEMASK register */
+#define ETS2EN                (1<<25) /* External trigger 2 timestamp sample event enable */
+#define ETS1EN                (1<<24) /* External trigger 1 timestamp sample event enable */
+#define ALM2EN                (1<<17) /* Timer ALM2 event enable */
+#define ALM1EN                (1<<16) /* Timer ALM1 event enable */
+#define PP1EN                 (1<<7) /* Periodic pulse event 1 enable */
+#define PP2EN                 (1<<6) /* Periodic pulse event 2 enable */
+
+/* Bit definitions for the TMR_PEVENT register */
+#define TXP2                  (1<<9) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS2 register */
+#define TXP1                  (1<<8) /* Indicates that a PTP frame has been transmitted and its timestamp is stored in TXTS1 register */
+#define RXP                   (1<<0) /* Indicates that a PTP frame has been received */
+
+/* Bit definitions for the TMR_PEMASK register */
+#define TXP2EN                (1<<9) /* Transmit PTP packet event 2 enable */
+#define TXP1EN                (1<<8) /* Transmit PTP packet event 1 enable */
+#define RXPEN                 (1<<0) /* Receive PTP packet event enable */
+
+/* Bit definitions for the TMR_STAT register */
+#define STAT_VEC_SHIFT        (0) /* Timer general purpose status vector */
+#define STAT_VEC_MASK         (0x3f)
+
+/* Bit definitions for the TMR_PRSC register */
+#define PRSC_OCK_SHIFT        (0) /* Output clock division/prescale factor. */
+#define PRSC_OCK_MASK         (0xffff)
+
+#endif
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 94f329f..764a844 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -38,4 +38,17 @@ config PTP_1588_CLOCK_LINUX
 	  To compile this driver as a module, choose M here: the module
 	  will be called ptp_linux.
 
+config PTP_1588_CLOCK_GIANFAR
+	tristate "Freescale eTSEC as PTP clock"
+	depends on PTP_1588_CLOCK
+	depends on GIANFAR
+	help
+	  This driver adds support for using the eTSEC as a PTP
+	  clock. This clock is only useful if your PTP programs are
+	  getting hardware time stamps on the PTP Ethernet packets
+	  using the SO_TIMESTAMPING API.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called gianfar_ptp.
+
 endmenu
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 7/8] ptp: Added a clock driver for the IXP46x.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (5 preceding siblings ...)
  2010-09-23 17:33 ` [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx Richard Cochran
@ 2010-09-23 17:33 ` Richard Cochran
  2010-09-23 17:34 ` [PATCH 8/8] ptp: Added a clock driver for the National Semiconductor PHYTER Richard Cochran
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:33 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch adds a driver for the hardware time stamping unit found on the
IXP465. The basic clock operations and an external trigger are implemented.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h |   78 ++++++
 drivers/net/arm/ixp4xx_eth.c                  |  191 ++++++++++++++
 drivers/ptp/Kconfig                           |   13 +
 drivers/ptp/Makefile                          |    1 +
 drivers/ptp/ptp_ixp46x.c                      |  345 +++++++++++++++++++++++++
 5 files changed, 628 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
 create mode 100644 drivers/ptp/ptp_ixp46x.c

diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
new file mode 100644
index 0000000..729a6b2
--- /dev/null
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h
@@ -0,0 +1,78 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * 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 _IXP46X_TS_H_
+#define _IXP46X_TS_H_
+
+#define DEFAULT_ADDEND 0xF0000029
+#define TICKS_NS_SHIFT 4
+
+struct ixp46x_channel_ctl {
+	u32 Ch_Control; /* 0x40 Time Synchronization Channel Control */
+	u32 Ch_Event;   /* 0x44 Time Synchronization Channel Event */
+	u32 TxSnapLo;   /* 0x48 Transmit Snapshot Low Register */
+	u32 TxSnapHi;   /* 0x4C Transmit Snapshot High Register */
+	u32 RxSnapLo;   /* 0x50 Receive Snapshot Low Register */
+	u32 RxSnapHi;   /* 0x54 Receive Snapshot High Register */
+	u32 SrcUUIDLo;  /* 0x58 Source UUID0 Low Register */
+	u32 SrcUUIDHi;  /* 0x5C Sequence Identifier/Source UUID0 High */
+};
+
+struct ixp46x_ts_regs {
+	u32 Control;     /* 0x00 Time Sync Control Register */
+	u32 Event;       /* 0x04 Time Sync Event Register */
+	u32 Addend;      /* 0x08 Time Sync Addend Register */
+	u32 Accum;       /* 0x0C Time Sync Accumulator Register */
+	u32 Test;        /* 0x10 Time Sync Test Register */
+	u32 Unused;      /* 0x14 */
+	u32 RSysTime_Lo; /* 0x18 RawSystemTime_Low Register */
+	u32 RSysTimeHi;  /* 0x1C RawSystemTime_High Register */
+	u32 SysTimeLo;   /* 0x20 SystemTime_Low Register */
+	u32 SysTimeHi;   /* 0x24 SystemTime_High Register */
+	u32 TrgtLo;      /* 0x28 TargetTime_Low Register */
+	u32 TrgtHi;      /* 0x2C TargetTime_High Register */
+	u32 ASMSLo;      /* 0x30 Auxiliary Slave Mode Snapshot Low  */
+	u32 ASMSHi;      /* 0x34 Auxiliary Slave Mode Snapshot High */
+	u32 AMMSLo;      /* 0x38 Auxiliary Master Mode Snapshot Low */
+	u32 AMMSHi;      /* 0x3C Auxiliary Master Mode Snapshot High */
+
+	struct ixp46x_channel_ctl channel[3];
+};
+
+/* 0x00 Time Sync Control Register Bits */
+#define TSCR_AMM (1<<3)
+#define TSCR_ASM (1<<2)
+#define TSCR_TTM (1<<1)
+#define TSCR_RST (1<<0)
+
+/* 0x04 Time Sync Event Register Bits */
+#define TSER_SNM (1<<3)
+#define TSER_SNS (1<<2)
+#define TTIPEND  (1<<1)
+
+/* 0x40 Time Synchronization Channel Control Register Bits */
+#define MASTER_MODE   (1<<0)
+#define TIMESTAMP_ALL (1<<1)
+
+/* 0x44 Time Synchronization Channel Event Register Bits */
+#define TX_SNAPSHOT_LOCKED (1<<0)
+#define RX_SNAPSHOT_LOCKED (1<<1)
+
+#endif
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index 6028226..eaff9dd 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -30,9 +30,12 @@
 #include <linux/etherdevice.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/net_tstamp.h>
 #include <linux/phy.h>
 #include <linux/platform_device.h>
+#include <linux/ptp_classify.h>
 #include <linux/slab.h>
+#include <mach/ixp46x_ts.h>
 #include <mach/npe.h>
 #include <mach/qmgr.h>
 
@@ -67,6 +70,14 @@
 #define RXFREE_QUEUE(port_id)	(NPE_ID(port_id) + 26)
 #define TXDONE_QUEUE		31
 
+#define PTP_SLAVE_MODE		1
+#define PTP_MASTER_MODE		2
+#define PORT2CHANNEL(p)		1
+/*
+ * PHYSICAL_ID(p->id) ?
+ * TODO - Figure out correct mapping.
+ */
+
 /* TX Control Registers */
 #define TX_CNTRL0_TX_EN		0x01
 #define TX_CNTRL0_HALFDUPLEX	0x02
@@ -171,6 +182,8 @@ struct port {
 	int id;			/* logical port ID */
 	int speed, duplex;
 	u8 firmware[4];
+	int hwts_tx_en;
+	int hwts_rx_en;
 };
 
 /* NPE message structure */
@@ -246,6 +259,171 @@ static int ports_open;
 static struct port *npe_port_tab[MAX_NPES];
 static struct dma_pool *dma_pool;
 
+static struct sock_filter ptp_filter[] = {
+	PTP_FILTER
+};
+
+static int match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seq)
+{
+	unsigned int type;
+	u16 *hi, *id;
+	u8 *lo, *data = skb->data;
+
+	type = sk_run_filter(skb, ptp_filter, ARRAY_SIZE(ptp_filter));
+
+	if (PTP_CLASS_V1_IPV4 == type) {
+
+		id = (u16 *)(data + 42 + 30);
+		hi = (u16 *)(data + 42 + 22);
+		lo = data + 42 + 24;
+
+		return (uid_hi == *hi &&
+			0 == memcmp(&uid_lo, lo, sizeof(uid_lo)) &&
+			seq == *id);
+	}
+
+	return 0;
+}
+
+static void do_rx_timestamp(struct port *port, struct sk_buff *skb)
+{
+	struct skb_shared_hwtstamps *shhwtstamps;
+	struct ixp46x_ts_regs *regs;
+	u64 ns;
+	u32 ch, hi, lo, val;
+	u16 uid, seq;
+
+	if (!port->hwts_rx_en)
+		return;
+
+	ch = PORT2CHANNEL(port);
+
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	val = __raw_readl(&regs->channel[ch].Ch_Event);
+
+	if (!(val & RX_SNAPSHOT_LOCKED))
+		return;
+
+	lo = __raw_readl(&regs->channel[ch].SrcUUIDLo);
+	hi = __raw_readl(&regs->channel[ch].SrcUUIDHi);
+
+	uid = hi & 0xffff;
+	seq = (hi >> 16) & 0xffff;
+
+	if (!match(skb, htons(uid), htonl(lo), htons(seq)))
+		goto out;
+
+	lo = __raw_readl(&regs->channel[ch].RxSnapLo);
+	hi = __raw_readl(&regs->channel[ch].RxSnapHi);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	shhwtstamps = skb_hwtstamps(skb);
+	memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+	shhwtstamps->hwtstamp = ns_to_ktime(ns);
+out:
+	__raw_writel(RX_SNAPSHOT_LOCKED, &regs->channel[ch].Ch_Event);
+}
+
+static void do_tx_timestamp(struct port *port, struct sk_buff *skb)
+{
+#ifdef __ARMEB__
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct ixp46x_ts_regs *regs;
+	union skb_shared_tx *shtx;
+	u64 ns;
+	u32 ch, cnt, hi, lo, val;
+
+	shtx = skb_tx(skb);
+	if (unlikely(shtx->hardware && port->hwts_tx_en))
+		shtx->in_progress = 1;
+	else
+		return;
+
+	ch = PORT2CHANNEL(port);
+
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	/*
+	 * This really stinks, but we have to poll for the Tx time stamp.
+	 * Usually, the time stamp is ready after 4 to 6 microseconds.
+	 */
+	for (cnt = 0; cnt < 100; cnt++) {
+		val = __raw_readl(&regs->channel[ch].Ch_Event);
+		if (val & TX_SNAPSHOT_LOCKED)
+			break;
+		udelay(1);
+	}
+	if (!(val & TX_SNAPSHOT_LOCKED)) {
+		shtx->in_progress = 0;
+		return;
+	}
+
+	lo = __raw_readl(&regs->channel[ch].TxSnapLo);
+	hi = __raw_readl(&regs->channel[ch].TxSnapHi);
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+	skb_tstamp_tx(skb, &shhwtstamps);
+
+	__raw_writel(TX_SNAPSHOT_LOCKED, &regs->channel[ch].Ch_Event);
+#endif
+}
+
+static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+	struct hwtstamp_config cfg;
+	struct ixp46x_ts_regs *regs;
+	struct port *port = netdev_priv(netdev);
+	int ch;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.flags) /* reserved for future extensions */
+		return -EINVAL;
+
+	ch = PORT2CHANNEL(port);
+	regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT;
+
+	switch (cfg.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		port->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		port->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		port->hwts_rx_en = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+		port->hwts_rx_en = PTP_SLAVE_MODE;
+		__raw_writel(0, &regs->channel[ch].Ch_Control);
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		port->hwts_rx_en = PTP_MASTER_MODE;
+		__raw_writel(MASTER_MODE, &regs->channel[ch].Ch_Control);
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	/* Clear out any old time stamps. */
+	__raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
+		     &regs->channel[ch].Ch_Event);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
 
 static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location,
 			   int write, u16 cmd)
@@ -573,6 +751,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
 
 		debug_pkt(dev, "eth_poll", skb->data, skb->len);
 
+		do_rx_timestamp(port, skb);
 		skb->protocol = eth_type_trans(skb, dev);
 		dev->stats.rx_packets++;
 		dev->stats.rx_bytes += skb->len;
@@ -728,6 +907,10 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
 #if DEBUG_TX
 	printk(KERN_DEBUG "%s: eth_xmit end\n", dev->name);
 #endif
+
+	do_tx_timestamp(port, skb);
+	skb_tx_timestamp(skb);
+
 	return NETDEV_TX_OK;
 }
 
@@ -783,6 +966,9 @@ static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
 	if (!netif_running(dev))
 		return -EINVAL;
 
+	if (cpu_is_ixp46x() && cmd == SIOCSHWTSTAMP)
+		return hwtstamp_ioctl(dev, req, cmd);
+
 	return phy_mii_ioctl(port->phydev, req, cmd);
 }
 
@@ -1171,6 +1357,11 @@ static int __devinit eth_init_one(struct platform_device *pdev)
 	char phy_id[MII_BUS_ID_SIZE + 3];
 	int err;
 
+	if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
+		pr_err("ixp4xx_eth: bad ptp filter\n");
+		return -EINVAL;
+	}
+
 	if (!(dev = alloc_etherdev(sizeof(struct port))))
 		return -ENOMEM;
 
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 764a844..e338ded 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -51,4 +51,17 @@ config PTP_1588_CLOCK_GIANFAR
 	  To compile this driver as a module, choose M here: the module
 	  will be called gianfar_ptp.
 
+config PTP_1588_CLOCK_IXP46X
+	tristate "Intel IXP46x as PTP clock"
+	depends on PTP_1588_CLOCK
+	depends on IXP4XX_ETH
+	help
+	  This driver adds support for using the IXP46X as a PTP
+	  clock. This clock is only useful if your PTP programs are
+	  getting hardware time stamps on the PTP Ethernet packets
+	  using the SO_TIMESTAMPING API.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ptp_ixp46x.
+
 endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 266d4f2..8b7848b 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -5,3 +5,4 @@
 ptp-y					:= ptp_clock.o ptp_chardev.o ptp_sysfs.o
 obj-$(CONFIG_PTP_1588_CLOCK)		+= ptp.o
 obj-$(CONFIG_PTP_1588_CLOCK_LINUX)	+= ptp_linux.o
+obj-$(CONFIG_PTP_1588_CLOCK_IXP46X)	+= ptp_ixp46x.o
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
new file mode 100644
index 0000000..32b94ba
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,345 @@
+/*
+ * PTP 1588 clock using the IXP46X
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/ptp_clock_kernel.h>
+#include <mach/ixp46x_ts.h>
+
+#define DRIVER		"ptp_ixp46x"
+#define N_EXT_TS	2
+#define MASTER_GPIO	8
+#define MASTER_IRQ	25
+#define SLAVE_GPIO	7
+#define SLAVE_IRQ	24
+
+struct ixp_clock {
+	struct ixp46x_ts_regs *regs;
+	struct ptp_clock *ptp_clock;
+	int exts0_enabled;
+	int exts1_enabled;
+};
+
+DEFINE_SPINLOCK(register_lock);
+
+/*
+ * Register access functions
+ */
+
+static inline u32 ixp_read(volatile unsigned __iomem *addr)
+{
+	u32 val;
+	val = __raw_readl(addr);
+	return val;
+}
+
+static inline void ixp_write(volatile unsigned __iomem *addr, u32 val)
+{
+	__raw_writel(val, addr);
+}
+
+static u64 sys_time_read(struct ixp46x_ts_regs *regs)
+{
+	u64 ns;
+	u32 lo, hi;
+
+	lo = ixp_read(&regs->SysTimeLo);
+	hi = ixp_read(&regs->SysTimeHi);
+
+	ns = ((u64) hi) << 32;
+	ns |= lo;
+	ns <<= TICKS_NS_SHIFT;
+
+	return ns;
+}
+
+static void sys_time_write(struct ixp46x_ts_regs *regs, u64 ns)
+{
+	u32 hi, lo;
+
+	ns >>= TICKS_NS_SHIFT;
+	hi = ns >> 32;
+	lo = ns & 0xffffffff;
+
+	ixp_write(&regs->SysTimeLo, lo);
+	ixp_write(&regs->SysTimeHi, hi);
+}
+
+/*
+ * Interrupt service routine
+ */
+
+static irqreturn_t isr(int irq, void *priv)
+{
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+	struct ptp_clock_event event;
+	u32 ack = 0, lo, hi, val;
+
+	val = ixp_read(&regs->Event);
+
+	if (val & TSER_SNS) {
+		ack |= TSER_SNS;
+		if (ixp_clock->exts0_enabled) {
+			hi = ixp_read(&regs->ASMSHi);
+			lo = ixp_read(&regs->ASMSLo);
+			event.type = PTP_CLOCK_EXTTS;
+			event.index = 0;
+			event.timestamp = ((u64) hi) << 32;
+			event.timestamp |= lo;
+			event.timestamp <<= TICKS_NS_SHIFT;
+			ptp_clock_event(ixp_clock->ptp_clock, &event);
+		}
+	}
+
+	if (val & TSER_SNM) {
+		ack |= TSER_SNM;
+		if (ixp_clock->exts1_enabled) {
+			hi = ixp_read(&regs->AMMSHi);
+			lo = ixp_read(&regs->AMMSLo);
+			event.type = PTP_CLOCK_EXTTS;
+			event.index = 1;
+			event.timestamp = ((u64) hi) << 32;
+			event.timestamp |= lo;
+			event.timestamp <<= TICKS_NS_SHIFT;
+			ptp_clock_event(ixp_clock->ptp_clock, &event);
+		}
+	}
+
+	if (val & TTIPEND)
+		ack |= TTIPEND; /* this bit seems to be always set */
+
+	if (ack) {
+		ixp_write(&regs->Event, ack);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_ixp_adjfreq(void *priv, s32 ppb)
+{
+	u64 adj;
+	u32 diff, addend;
+	int neg_adj = 0;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	addend = DEFAULT_ADDEND;
+	adj = addend;
+	adj *= ppb;
+	diff = div_u64(adj, 1000000000ULL);
+
+	addend = neg_adj ? addend - diff : addend + diff;
+
+	ixp_write(&regs->Addend, addend);
+
+	return 0;
+}
+
+static int ptp_ixp_adjtime(void *priv, struct timespec *ts)
+{
+	s64 delta, now;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	delta = 1000000000LL * ts->tv_sec;
+	delta += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	now = sys_time_read(regs);
+	now += delta;
+	sys_time_write(regs, now);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_ixp_gettime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	u32 remainder;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	ns = sys_time_read(regs);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+	ts->tv_nsec = remainder;
+	return 0;
+}
+
+static int ptp_ixp_settime(void *priv, struct timespec *ts)
+{
+	u64 ns;
+	unsigned long flags;
+	struct ixp_clock *ixp_clock = priv;
+	struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+	ns = ts->tv_sec * 1000000000ULL;
+	ns += ts->tv_nsec;
+
+	spin_lock_irqsave(&register_lock, flags);
+
+	sys_time_write(regs, ns);
+
+	spin_unlock_irqrestore(&register_lock, flags);
+
+	return 0;
+}
+
+static int ptp_ixp_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct ixp_clock *ixp_clock = priv;
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_EXTTS:
+		switch (rq->extts.index) {
+		case 0:
+			ixp_clock->exts0_enabled = on ? 1 : 0;
+			break;
+		case 1:
+			ixp_clock->exts1_enabled = on ? 1 : 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return 0;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_ixp_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "IXP46X timer",
+	.max_adj	= 66666655,
+	.n_ext_ts	= N_EXT_TS,
+	.pps		= 0,
+	.priv		= NULL,
+	.adjfreq	= ptp_ixp_adjfreq,
+	.adjtime	= ptp_ixp_adjtime,
+	.gettime	= ptp_ixp_gettime,
+	.settime	= ptp_ixp_settime,
+	.enable		= ptp_ixp_enable,
+};
+
+/* module operations */
+
+static struct ixp_clock ixp_clock;
+
+static int setup_interrupt(int gpio)
+{
+	int irq;
+
+	gpio_line_config(gpio, IXP4XX_GPIO_IN);
+
+	irq = gpio_to_irq(gpio);
+
+	if (NO_IRQ == irq)
+		return NO_IRQ;
+
+	if (set_irq_type(irq, IRQF_TRIGGER_FALLING)) {
+		pr_err("cannot set trigger type for irq %d\n", irq);
+		return NO_IRQ;
+	}
+
+	if (request_irq(irq, isr, 0, DRIVER, &ixp_clock)) {
+		pr_err("request_irq failed for irq %d\n", irq);
+		return NO_IRQ;
+	}
+
+	return irq;
+}
+
+static void __exit ptp_ixp_exit(void)
+{
+	free_irq(MASTER_IRQ, &ixp_clock);
+	free_irq(SLAVE_IRQ, &ixp_clock);
+	ptp_clock_unregister(ixp_clock.ptp_clock);
+}
+
+static int __init ptp_ixp_init(void)
+{
+	if (!cpu_is_ixp46x())
+		return -ENODEV;
+
+	ixp_clock.regs = 
+		(struct ixp46x_ts_regs __iomem *)IXP4XX_TIMESYNC_BASE_VIRT;
+
+	ptp_ixp_caps.priv = &ixp_clock;
+
+	ixp_clock.ptp_clock = ptp_clock_register(&ptp_ixp_caps);
+
+	if (IS_ERR(ixp_clock.ptp_clock))
+		return PTR_ERR(ixp_clock.ptp_clock);
+
+	ixp_write(&ixp_clock.regs->Addend, DEFAULT_ADDEND);
+	ixp_write(&ixp_clock.regs->TrgtLo, 1);
+	ixp_write(&ixp_clock.regs->TrgtHi, 0);
+	ixp_write(&ixp_clock.regs->Event, TTIPEND);
+
+	if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) {
+		pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO);
+		goto no_master;
+	}
+	if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) {
+		pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO);
+		goto no_slave;
+	}
+
+	return 0;
+no_slave:
+	free_irq(MASTER_IRQ, &ixp_clock);
+no_master:
+	ptp_clock_unregister(ixp_clock.ptp_clock);
+	return -ENODEV;
+}
+
+module_init(ptp_ixp_init);
+module_exit(ptp_ixp_exit);
+
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 8/8] ptp: Added a clock driver for the National Semiconductor PHYTER.
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (6 preceding siblings ...)
  2010-09-23 17:33 ` [PATCH 7/8] ptp: Added a clock driver for the IXP46x Richard Cochran
@ 2010-09-23 17:34 ` Richard Cochran
  2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-23 17:34 UTC (permalink / raw)
  To: linux-kernel
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

This patch adds support for the PTP clock found on the DP83640.
The basic clock operations and one external time stamp have
been implemented.

Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
 drivers/net/phy/Kconfig       |   29 ++
 drivers/net/phy/Makefile      |    1 +
 drivers/net/phy/dp83640.c     |  887 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/dp83640_reg.h |  261 ++++++++++++
 4 files changed, 1178 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/phy/dp83640.c
 create mode 100644 drivers/net/phy/dp83640_reg.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index eb799b3..2e6463d 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -77,6 +77,35 @@ config NATIONAL_PHY
 	---help---
 	  Currently supports the DP83865 PHY.
 
+config DP83640_PHY
+	tristate "Driver for the National Semiconductor DP83640 PHYTER"
+	depends on PTP_1588_CLOCK
+	depends on NETWORK_PHY_TIMESTAMPING
+	---help---
+	  Supports the DP83640 PHYTER with IEEE 1588 features.
+
+	  This driver adds support for using the DP83640 as a PTP
+	  clock. This clock is only useful if your PTP programs are
+	  getting hardware time stamps on the PTP Ethernet packets
+	  using the SO_TIMESTAMPING API.
+
+	  In order for this to work, your MAC driver must also
+	  implement the skb_tx_timetamp() function.
+
+config DP83640_PHY_STATUS_FRAMES
+	bool "DP83640 Status Frames"
+	default y
+	depends on DP83640_PHY
+	---help---
+	  This option allows the DP83640 PHYTER driver to obtain time
+	  stamps from the PHY via special status frames, rather than
+	  reading over the MDIO bus. Using status frames is therefore
+	  more efficient. However, if enabled, this option will cause
+	  the driver to add a mutlicast address to the MAC.
+
+	  Say Y here, unless your MAC does not support multicast
+	  destination addresses.
+
 config STE10XP
 	depends on PHYLIB
 	tristate "Driver for STMicroelectronics STe10Xp PHYs"
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 13bebab..2333215 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_FIXED_PHY)		+= fixed.o
 obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o
 obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o
 obj-$(CONFIG_NATIONAL_PHY)	+= national.o
+obj-$(CONFIG_DP83640_PHY)	+= dp83640.o
 obj-$(CONFIG_STE10XP)		+= ste10Xp.o
 obj-$(CONFIG_MICREL_PHY)	+= micrel.o
 obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
new file mode 100644
index 0000000..4cabd0d
--- /dev/null
+++ b/drivers/net/phy/dp83640.c
@@ -0,0 +1,887 @@
+/*
+ * Driver for the National Semiconductor DP83640 PHYTER
+ *
+ * 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/ethtool.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/net_tstamp.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "dp83640_reg.h"
+
+#ifdef CONFIG_DP83640_PHY_STATUS_FRAMES
+#define USE_STATUS_FRAMES
+#endif
+
+#define DP83640_PHY_ID	0x20005ce1
+#define PAGESEL		0x13
+#define LAYER4		0x02
+#define LAYER2		0x01
+#define MAX_RXTS	4
+#define MAX_TXTS	4
+#define N_EXT_TS	1
+#define PSF_PTPVER	2
+#define PSF_EVNT	0x4000
+#define PSF_RX		0x2000
+#define PSF_TX		0x1000
+#define EXT_EVENT	1
+#define EXT_GPIO	1
+
+#if defined(__BIG_ENDIAN)
+#define ENDIAN_FLAG	0
+#elif defined(__LITTLE_ENDIAN)
+#define ENDIAN_FLAG	PSF_ENDIAN
+#endif
+
+#define SKB_PTP_TYPE(__skb) (*(unsigned int *)((__skb)->cb))
+
+struct phy_rxts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+	u16 seqid;   /* sequenceId[15:0] */
+	u16 msgtype; /* messageType[3:0], hash[11:0] */
+};
+
+struct phy_txts {
+	u16 ns_lo;   /* ns[15:0] */
+	u16 ns_hi;   /* overflow[1:0], ns[29:16] */
+	u16 sec_lo;  /* sec[15:0] */
+	u16 sec_hi;  /* sec[31:16] */
+};
+
+struct rxts {
+	struct list_head list;
+	unsigned long tmo;
+	u64 ns;
+	u16 seqid;
+	u8  msgtype;
+	u16 hash;
+};
+
+struct dp83640_private {
+	struct phy_device *phydev;
+	struct work_struct ts_work;
+	int hwts_tx_en;
+	int hwts_rx_en;
+	int layer;
+	int version;
+	/* protects extended registers from concurrent access */
+	struct mutex extreg_mux;
+	int page;
+	/* remember the last event time stamp */
+	struct phy_txts edata;
+	/* list of rx timestamps */
+	struct list_head rxts;
+	struct list_head rxpool;
+	struct rxts rx_pool_data[MAX_RXTS];
+	/* protects above three fields from concurrent access */
+	spinlock_t rx_lock;
+
+	struct sk_buff_head rx_queue;
+	struct sk_buff_head tx_queue;
+};
+
+/* globals */
+
+static struct ptp_clock *dp83640_clock;
+DEFINE_MUTEX(clock_lock); /* protects the one and only dp83640_clock */
+
+static void do_timestamp_work(struct work_struct *work);
+
+/* extended register access functions */
+
+static int ext_read(struct phy_device *phydev, int page, u32 regnum)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	int val;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	val = phy_read(phydev, regnum);
+
+	return val;
+}
+
+static void ext_write(struct phy_device *phydev, int page, u32 regnum, u16 val)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (dp83640->page != page) {
+		phy_write(phydev, PAGESEL, page);
+		dp83640->page = page;
+	}
+	phy_write(phydev, regnum, val);
+}
+
+static int tdr_write(struct phy_device *phydev, struct timespec *ts, u16 cmd)
+{
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_nsec >> 16);   /* ns[31:16] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */
+	ext_write(phydev, PAGE4, PTP_TDR, ts->tv_sec >> 16);    /* sec[31:16] */
+
+	ext_write(phydev, PAGE4, PTP_CTL, cmd);
+
+	return 0;
+}
+
+/* convert phy timestamps into driver timestamps */
+
+static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
+{
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	rxts->ns = p->ns_lo;
+	rxts->ns |= (p->ns_hi & 0x3fff) << 16;
+	rxts->ns += ((u64)sec) * 1000000000ULL;
+	rxts->seqid = p->seqid;
+	rxts->msgtype = (p->msgtype >> 12) & 0xf;
+	rxts->hash = p->msgtype & 0x0fff;
+}
+
+static u64 phy2txts(struct phy_txts *p)
+{
+	u64 ns;
+	u32 sec;
+
+	sec = p->sec_lo;
+	sec |= p->sec_hi << 16;
+
+	ns = p->ns_lo;
+	ns |= (p->ns_hi & 0x3fff) << 16;
+	ns += ((u64)sec) * 1000000000ULL;
+
+	return ns;
+}
+
+/* ptp clock methods */
+
+static int ptp_dp83640_adjfreq(void *priv, s32 ppb)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	u64 rate;
+	int neg_adj = 0;
+	u16 hi, lo;
+
+	if (ppb < 0) {
+		neg_adj = 1;
+		ppb = -ppb;
+	}
+	rate = ppb;
+	rate <<= 26;
+	rate = div_u64(rate, 1953125);
+
+	hi = (rate >> 16) & PTP_RATE_HI_MASK;
+	if (neg_adj)
+		hi |= PTP_RATE_DIR;
+
+	lo = rate & 0xffff;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_RATEH, hi);
+	ext_write(phydev, PAGE4, PTP_RATEL, lo);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return 0;
+}
+
+static int ptp_dp83640_adjtime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	int err;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, ts, PTP_STEP_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_gettime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	unsigned int val[4];
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	ext_write(phydev, PAGE4, PTP_CTL, PTP_RD_CLK);
+
+	val[0] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[15:0] */
+	val[1] = ext_read(phydev, PAGE4, PTP_TDR); /* ns[31:16] */
+	val[2] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[15:0] */
+	val[3] = ext_read(phydev, PAGE4, PTP_TDR); /* sec[31:16] */
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	ts->tv_nsec = val[0] | (val[1] << 16);
+	ts->tv_sec  = val[2] | (val[3] << 16);
+
+	return 0;
+}
+
+static int ptp_dp83640_settime(void *priv, struct timespec *ts)
+{
+	struct dp83640_private *dp83640 = priv;
+	int err;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	err = tdr_write(dp83640->phydev, ts, PTP_LOAD_CLK);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return err;
+}
+
+static int ptp_dp83640_enable(void *priv, struct ptp_clock_request *rq, int on)
+{
+	struct dp83640_private *dp83640 = priv;
+	struct phy_device *phydev = dp83640->phydev;
+	u16 evnt;
+
+	switch (rq->type) {
+	case PTP_CLK_REQ_EXTTS:
+		if (rq->extts.index != 0)
+			return -EINVAL;
+		evnt = EVNT_WR | (EXT_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
+		if (on) {
+			evnt |= (EXT_GPIO & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
+			evnt |= EVNT_RISE;
+		}
+		ext_write(phydev, PAGE5, PTP_EVNT, evnt);
+		return 0;
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info ptp_dp83640_caps = {
+	.owner		= THIS_MODULE,
+	.name		= "dp83640 timer",
+	.max_adj	= 1953124,
+	.n_alarm	= 0,
+	.n_ext_ts	= N_EXT_TS,
+	.n_per_out	= 0,
+	.pps		= 0,
+	.priv		= NULL,
+	.adjfreq	= ptp_dp83640_adjfreq,
+	.adjtime	= ptp_dp83640_adjtime,
+	.gettime	= ptp_dp83640_gettime,
+	.settime	= ptp_dp83640_settime,
+	.enable		= ptp_dp83640_enable,
+};
+
+/* status frame conditional code */
+
+#ifdef USE_STATUS_FRAMES
+
+static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
+static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+	u16 cfg0 = 0, ver;
+
+	if (on)
+		cfg0 = PSF_EVNT_EN | PSF_RXTS_EN | PSF_TXTS_EN | ENDIAN_FLAG;
+
+	ver = (PSF_PTPVER & VERSIONPTP_MASK) << VERSIONPTP_SHIFT;
+
+	ext_write(phydev, PAGE5, PSF_CFG0, cfg0);
+	ext_write(phydev, PAGE6, PSF_CFG1, ver);
+
+	if (!phydev->attached_dev) {
+		pr_err("expected to find an attached netdevice\n");
+		BUG();
+	}
+
+	if (on) {
+		if (dev_mc_add(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to add mc address\n");
+	} else {
+		if (dev_mc_del(phydev->attached_dev, status_frame_dst))
+			pr_warning("dp83640: failed to delete mc address\n");
+	}
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	struct ethhdr *h = eth_hdr(skb);
+
+	if (PTP_CLASS_V2_L2 == type &&
+	    !memcmp(h->h_source, status_frame_src, sizeof(status_frame_src)))
+		return true;
+	else
+		return false;
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+}
+
+#else /* USE_STATUS_FRAMES */
+
+static void enable_status_frames(struct phy_device *phydev, bool on)
+{
+}
+
+static bool is_status_frame(struct sk_buff *skb, int type)
+{
+	return false;
+}
+
+static void read_rxts(struct phy_device *phydev, struct rxts *rxts)
+{
+	struct phy_rxts p;
+
+	p.ns_lo   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.ns_hi   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_lo  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.sec_hi  = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.seqid   = ext_read(phydev, PAGE4, PTP_RXTS);
+	p.msgtype = ext_read(phydev, PAGE4, PTP_RXTS);
+
+	rxts->tmo = jiffies + HZ;
+	phy2rxts(&p, rxts);
+}
+
+static u64 read_txts(struct phy_device *phydev)
+{
+	struct phy_txts p;
+
+	p.ns_lo  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.ns_hi  = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_lo = ext_read(phydev, PAGE4, PTP_TXTS);
+	p.sec_hi = ext_read(phydev, PAGE4, PTP_TXTS);
+
+	return phy2txts(&p);
+}
+
+static void rx_reading_work(struct dp83640_private *dp83640)
+{
+	struct rxts *rxts;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	for (;;) {
+		int val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & RXTS_RDY))
+			break;
+		if (list_empty(&dp83640->rxpool)) {
+			pr_warning("dp83640: rx timestamp pool is empty\n");
+			break;
+		}
+		rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+		list_del_init(&rxts->list);
+		read_rxts(dp83640->phydev, rxts);
+		list_add_tail(&rxts->list, &dp83640->rxts);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+static void tx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+	int val;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	while ((skb = skb_dequeue(&dp83640->tx_queue)) != NULL) {
+
+		val = ext_read(dp83640->phydev, PAGE4, PTP_STS);
+		if (!(val & TXTS_RDY)) {
+			skb_queue_head(&dp83640->tx_queue, skb);
+			break;
+		}
+		ns = read_txts(dp83640->phydev);
+		memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+		shhwtstamps.hwtstamp = ns_to_ktime(ns);
+		skb_complete_tx_timestamp(skb, &shhwtstamps);
+	}
+
+	mutex_unlock(&dp83640->extreg_mux);
+}
+
+#endif /* !USE_STATUS_FRAMES */
+
+/* time stamping methods */
+
+static void decode_evnt(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts, u16 ests)
+{
+	struct ptp_clock_event event;
+	int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
+
+	switch (words) { /* fall through in every case */
+	case 3:
+		dp83640->edata.sec_hi = phy_txts->sec_hi;
+	case 2:
+		dp83640->edata.sec_lo = phy_txts->sec_lo;
+	case 1:
+		dp83640->edata.ns_hi = phy_txts->ns_hi;
+	case 0:
+		dp83640->edata.ns_lo = phy_txts->ns_lo;
+	}
+
+	event.type = PTP_CLOCK_EXTTS;
+	event.index = 0;
+	event.timestamp = phy2txts(&dp83640->edata);
+
+	ptp_clock_event(dp83640_clock, &event);
+}
+
+static void decode_rxts(struct dp83640_private *dp83640,
+			struct phy_rxts *phy_rxts)
+{
+	struct rxts *rxts;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+
+	if (list_empty(&dp83640->rxpool)) {
+		pr_warning("dp83640: rx timestamp pool is empty\n");
+		goto out;
+	}
+	rxts = list_first_entry(&dp83640->rxpool, struct rxts, list);
+	list_del_init(&rxts->list);
+	phy2rxts(phy_rxts, rxts);
+	list_add_tail(&rxts->list, &dp83640->rxts);
+out:
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void decode_txts(struct dp83640_private *dp83640,
+			struct phy_txts *phy_txts)
+{
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u64 ns;
+
+	/* We must already have the skb that triggered this. */
+
+	skb = skb_dequeue(&dp83640->tx_queue);
+
+	if (!skb) {
+		pr_warning("dp83640: have timestamp but tx_queue empty\n");
+		return;
+	}
+	ns = phy2txts(phy_txts);
+	memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+	shhwtstamps.hwtstamp = ns_to_ktime(ns);
+	skb_complete_tx_timestamp(skb, &shhwtstamps);
+}
+
+static void decode_status_frame(struct dp83640_private *dp83640,
+				struct sk_buff *skb)
+{
+	struct phy_rxts *phy_rxts;
+	struct phy_txts *phy_txts;
+	u8 *ptr;
+	int len, size;
+	u16 ests, type;
+
+	ptr = skb->data + 2;
+
+	for (len = skb_headlen(skb) - 2; len > sizeof(type); len -= size) {
+
+		type = *(u16 *)ptr;
+		ests = type & 0x0fff;
+		type = type & 0xf000;
+		len -= sizeof(type);
+		ptr += sizeof(type);
+
+		if (PSF_RX == type && len >= sizeof(*phy_rxts)) {
+
+			phy_rxts = (struct phy_rxts *) ptr;
+			decode_rxts(dp83640, phy_rxts);
+			size = sizeof(*phy_rxts);
+
+		} else if (PSF_TX == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_txts(dp83640, phy_txts);
+			size = sizeof(*phy_txts);
+
+		} else if (PSF_EVNT == type && len >= sizeof(*phy_txts)) {
+
+			phy_txts = (struct phy_txts *) ptr;
+			decode_evnt(dp83640, phy_txts, ests);
+			size = sizeof(*phy_txts);
+
+		} else {
+			size = 0;
+			break;
+		}
+		ptr += size;
+	}
+}
+
+static int expired(struct rxts *rxts)
+{
+	return time_after(jiffies, rxts->tmo);
+}
+
+static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
+{
+	u16 *seqid;
+	u8 *msgtype, *data = skb_mac_header(skb);
+
+	/* check sequenceID, messageType, 12 bit hash of offset 20-29 */
+	/* We assume that the IPv4 header has no options. */
+
+	switch (type) {
+	case PTP_CLASS_V1_IPV4:
+		msgtype = data + 42 + 32;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V1_IPV6:
+		msgtype = data + 62 + 32;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_IPV4:
+		msgtype = data + 42 + 0;
+		seqid = (u16 *)(data + 42 + 30);
+		break;
+	case PTP_CLASS_V2_IPV6:
+		msgtype = data + 62 + 0;
+		seqid = (u16 *)(data + 62 + 30);
+		break;
+	case PTP_CLASS_V2_L2:
+		msgtype = data + 14 + 0;
+		seqid = (u16 *)(data + 14 + 30);
+		break;
+	case PTP_CLASS_V2_VLAN:
+		msgtype = data + 18 + 0;
+		seqid = (u16 *)(data + 18 + 30);
+		break;
+	default:
+		return 0;
+	}
+
+	return ((*msgtype & 0xf) == rxts->msgtype && *seqid == rxts->seqid);
+}
+
+static int dp83640_probe(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640;
+	int i;
+
+	dp83640 = kzalloc(sizeof(struct dp83640_private), GFP_KERNEL);
+	if (!dp83640)
+		return -ENOMEM;
+
+	dp83640->phydev = phydev;
+	INIT_WORK(&dp83640->ts_work, do_timestamp_work);
+	mutex_init(&dp83640->extreg_mux);
+
+	INIT_LIST_HEAD(&dp83640->rxts);
+	INIT_LIST_HEAD(&dp83640->rxpool);
+	for (i = 0; i < MAX_RXTS; i++)
+		list_add(&dp83640->rx_pool_data[i].list, &dp83640->rxpool);
+
+	phydev->priv = dp83640;
+
+	spin_lock_init(&dp83640->rx_lock);
+	skb_queue_head_init(&dp83640->rx_queue);
+	skb_queue_head_init(&dp83640->tx_queue);
+
+	mutex_lock(&clock_lock);
+
+	if (!dp83640_clock) {
+		ptp_dp83640_caps.priv = dp83640;
+		dp83640_clock = ptp_clock_register(&ptp_dp83640_caps);
+		if (IS_ERR(dp83640_clock)) {
+			mutex_unlock(&clock_lock);
+			kfree(dp83640);
+			return PTR_ERR(dp83640_clock);
+		}
+	}
+	mutex_unlock(&clock_lock);
+
+	return 0;
+}
+
+static void dp83640_remove(struct phy_device *phydev)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	enable_status_frames(phydev, false);
+
+	cancel_work_sync(&dp83640->ts_work);
+
+	mutex_lock(&clock_lock);
+
+	if (ptp_dp83640_caps.priv == dp83640) {
+		ptp_clock_unregister(dp83640_clock);
+		dp83640_clock = NULL;
+		ptp_dp83640_caps.priv = NULL;
+	}
+	mutex_unlock(&clock_lock);
+
+	mutex_destroy(&dp83640->extreg_lock);
+
+	kfree(dp83640);
+}
+
+static int dp83640_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+	struct hwtstamp_config cfg;
+	u16 txcfg0, rxcfg0;
+
+	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.flags) /* reserved for future extensions */
+		return -EINVAL;
+
+	switch (cfg.tx_type) {
+	case HWTSTAMP_TX_OFF:
+		dp83640->hwts_tx_en = 0;
+		break;
+	case HWTSTAMP_TX_ON:
+		dp83640->hwts_tx_en = 1;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (cfg.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		dp83640->hwts_rx_en = 0;
+		dp83640->layer = 0;
+		dp83640->version = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 1;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER2;
+		dp83640->version = 2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		dp83640->hwts_rx_en = 1;
+		dp83640->layer = LAYER4|LAYER2;
+		dp83640->version = 2;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	txcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+	rxcfg0 = (dp83640->version & TX_PTP_VER_MASK) << TX_PTP_VER_SHIFT;
+
+	if (dp83640->layer & LAYER2) {
+		txcfg0 |= TX_L2_EN;
+		rxcfg0 |= RX_L2_EN;
+	}
+	if (dp83640->layer & LAYER4) {
+		txcfg0 |= TX_IPV6_EN | TX_IPV4_EN;
+		rxcfg0 |= RX_IPV6_EN | RX_IPV4_EN;
+	}
+
+	if (dp83640->hwts_tx_en)
+		txcfg0 |= TX_TS_EN;
+
+	if (dp83640->hwts_rx_en)
+		rxcfg0 |= RX_TS_EN;
+
+	mutex_lock(&dp83640->extreg_mux);
+
+	if (dp83640->hwts_tx_en || dp83640->hwts_rx_en) {
+		enable_status_frames(phydev, true);
+		ext_write(phydev, PAGE4, PTP_CTL, PTP_ENABLE);
+	}
+
+	ext_write(phydev, PAGE5, PTP_TXCFG0, txcfg0);
+	ext_write(phydev, PAGE5, PTP_RXCFG0, rxcfg0);
+
+	mutex_unlock(&dp83640->extreg_mux);
+
+	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static void rx_timestamp_work(struct dp83640_private *dp83640)
+{
+	struct list_head *this, *next;
+	struct rxts *rxts;
+	struct skb_shared_hwtstamps *shhwtstamps;
+	struct sk_buff *skb;
+	unsigned int type;
+	unsigned long flags;
+
+	/* Deliver each deferred packet, with or without a time stamp. */
+
+	while ((skb = skb_dequeue(&dp83640->rx_queue)) != NULL) {
+		type = SKB_PTP_TYPE(skb);
+		spin_lock_irqsave(&dp83640->rx_lock, flags);
+		list_for_each_safe(this, next, &dp83640->rxts) {
+			rxts = list_entry(this, struct rxts, list);
+			if (match(skb, type, rxts)) {
+				shhwtstamps = skb_hwtstamps(skb);
+				memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+				shhwtstamps->hwtstamp = ns_to_ktime(rxts->ns);
+				list_del_init(&rxts->list);
+				list_add(&rxts->list, &dp83640->rxpool);
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+		netif_rx(skb);
+	}
+
+	/* Clear out expired time stamps. */
+
+	spin_lock_irqsave(&dp83640->rx_lock, flags);
+	list_for_each_safe(this, next, &dp83640->rxts) {
+		rxts = list_entry(this, struct rxts, list);
+		if (expired(rxts)) {
+			list_del_init(&rxts->list);
+			list_add(&rxts->list, &dp83640->rxpool);
+		}
+	}
+	spin_unlock_irqrestore(&dp83640->rx_lock, flags);
+}
+
+static void do_timestamp_work(struct work_struct *work)
+{
+	struct dp83640_private *dp83640 =
+		container_of(work, struct dp83640_private, ts_work);
+
+	rx_reading_work(dp83640);
+	rx_timestamp_work(dp83640);
+	tx_timestamp_work(dp83640);
+}
+
+static bool dp83640_rxtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_rx_en)
+		return false;
+
+	if (is_status_frame(skb, type)) {
+		decode_status_frame(dp83640, skb);
+		/* Let the stack drop this frame. */
+		return false;
+	}
+
+	SKB_PTP_TYPE(skb) = type;
+	skb_queue_tail(&dp83640->rx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+
+	return true;
+}
+
+static void dp83640_txtstamp(struct phy_device *phydev,
+			     struct sk_buff *skb, int type)
+{
+	struct dp83640_private *dp83640 = phydev->priv;
+
+	if (!dp83640->hwts_tx_en) {
+		kfree_skb(skb);
+		return;
+	}
+	skb_queue_tail(&dp83640->tx_queue, skb);
+	schedule_work(&dp83640->ts_work);
+}
+
+static struct phy_driver dp83640_driver = {
+	.phy_id		= DP83640_PHY_ID,
+	.phy_id_mask	= 0xfffffff0,
+	.name		= "NatSemi DP83640",
+	.features	= PHY_BASIC_FEATURES,
+	.flags		= 0,
+	.probe		= dp83640_probe,
+	.remove		= dp83640_remove,
+	.config_aneg	= genphy_config_aneg,
+	.read_status	= genphy_read_status,
+	.hwtstamp	= dp83640_hwtstamp,
+	.rxtstamp	= dp83640_rxtstamp,
+	.txtstamp	= dp83640_txtstamp,
+	.driver		= {.owner = THIS_MODULE,}
+};
+
+static int __init dp83640_init(void)
+{
+	return phy_driver_register(&dp83640_driver);
+}
+
+static void __exit dp83640_exit(void)
+{
+	phy_driver_unregister(&dp83640_driver);
+}
+
+MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
+MODULE_AUTHOR("Richard Cochran <richard.cochran@omicron.at>");
+MODULE_LICENSE("GPL");
+
+module_init(dp83640_init);
+module_exit(dp83640_exit);
+
+static struct mdio_device_id dp83640_tbl[] = {
+	{ DP83640_PHY_ID, 0xfffffff0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(mdio, dp83640_tbl);
diff --git a/drivers/net/phy/dp83640_reg.h b/drivers/net/phy/dp83640_reg.h
new file mode 100644
index 0000000..9d34bb6
--- /dev/null
+++ b/drivers/net/phy/dp83640_reg.h
@@ -0,0 +1,261 @@
+/* dp83640_reg.h
+ * Generated by regen.tcl on Fri Jul 23 09:45:18 AM CEST 2010
+ */
+#ifndef HAVE_DP83640_REGISTERS
+#define HAVE_DP83640_REGISTERS
+
+#define PAGE4                     0x0004
+#define PTP_CTL                   0x0014 /* PTP Control Register */
+#define PTP_TDR                   0x0015 /* PTP Time Data Register */
+#define PTP_STS                   0x0016 /* PTP Status Register */
+#define PTP_TSTS                  0x0017 /* PTP Trigger Status Register */
+#define PTP_RATEL                 0x0018 /* PTP Rate Low Register */
+#define PTP_RATEH                 0x0019 /* PTP Rate High Register */
+#define PTP_RDCKSUM               0x001a /* PTP Read Checksum */
+#define PTP_WRCKSUM               0x001b /* PTP Write Checksum */
+#define PTP_TXTS                  0x001c /* PTP Transmit Timestamp Register, in four 16-bit reads */
+#define PTP_RXTS                  0x001d /* PTP Receive Timestamp Register, in six? 16-bit reads */
+#define PTP_ESTS                  0x001e /* PTP Event Status Register */
+#define PTP_EDATA                 0x001f /* PTP Event Data Register */
+
+#define PAGE5                     0x0005
+#define PTP_TRIG                  0x0014 /* PTP Trigger Configuration Register */
+#define PTP_EVNT                  0x0015 /* PTP Event Configuration Register */
+#define PTP_TXCFG0                0x0016 /* PTP Transmit Configuration Register 0 */
+#define PTP_TXCFG1                0x0017 /* PTP Transmit Configuration Register 1 */
+#define PSF_CFG0                  0x0018 /* PHY Status Frame Configuration Register 0 */
+#define PTP_RXCFG0                0x0019 /* PTP Receive Configuration Register 0 */
+#define PTP_RXCFG1                0x001a /* PTP Receive Configuration Register 1 */
+#define PTP_RXCFG2                0x001b /* PTP Receive Configuration Register 2 */
+#define PTP_RXCFG3                0x001c /* PTP Receive Configuration Register 3 */
+#define PTP_RXCFG4                0x001d /* PTP Receive Configuration Register 4 */
+#define PTP_TRDL                  0x001e /* PTP Temporary Rate Duration Low Register */
+#define PTP_TRDH                  0x001f /* PTP Temporary Rate Duration High Register */
+
+#define PAGE6                     0x0006
+#define PTP_COC                   0x0014 /* PTP Clock Output Control Register */
+#define PSF_CFG1                  0x0015 /* PHY Status Frame Configuration Register 1 */
+#define PSF_CFG2                  0x0016 /* PHY Status Frame Configuration Register 2 */
+#define PSF_CFG3                  0x0017 /* PHY Status Frame Configuration Register 3 */
+#define PSF_CFG4                  0x0018 /* PHY Status Frame Configuration Register 4 */
+#define PTP_SFDCFG                0x0019 /* PTP SFD Configuration Register */
+#define PTP_INTCTL                0x001a /* PTP Interrupt Control Register */
+#define PTP_CLKSRC                0x001b /* PTP Clock Source Register */
+#define PTP_ETR                   0x001c /* PTP Ethernet Type Register */
+#define PTP_OFF                   0x001d /* PTP Offset Register */
+#define PTP_GPIOMON               0x001e /* PTP GPIO Monitor Register */
+#define PTP_RXHASH                0x001f /* PTP Receive Hash Register */
+
+/* Bit definitions for the PTP_CTL register */
+#define TRIG_SEL_SHIFT            (10) /* PTP Trigger Select */
+#define TRIG_SEL_MASK             (0x7)
+#define TRIG_DIS                  (1<<9) /* Disable PTP Trigger */
+#define TRIG_EN                   (1<<8) /* Enable PTP Trigger */
+#define TRIG_READ                 (1<<7) /* Read PTP Trigger */
+#define TRIG_LOAD                 (1<<6) /* Load PTP Trigger */
+#define PTP_RD_CLK                (1<<5) /* Read PTP Clock */
+#define PTP_LOAD_CLK              (1<<4) /* Load PTP Clock */
+#define PTP_STEP_CLK              (1<<3) /* Step PTP Clock */
+#define PTP_ENABLE                (1<<2) /* Enable PTP Clock */
+#define PTP_DISABLE               (1<<1) /* Disable PTP Clock */
+#define PTP_RESET                 (1<<0) /* Reset PTP Clock */
+
+/* Bit definitions for the PTP_STS register */
+#define TXTS_RDY                  (1<<11) /* Transmit Timestamp Ready */
+#define RXTS_RDY                  (1<<10) /* Receive Timestamp Ready */
+#define TRIG_DONE                 (1<<9) /* PTP Trigger Done */
+#define EVENT_RDY                 (1<<8) /* PTP Event Timestamp Ready */
+#define TXTS_IE                   (1<<3) /* Transmit Timestamp Interrupt Enable */
+#define RXTS_IE                   (1<<2) /* Receive Timestamp Interrupt Enable */
+#define TRIG_IE                   (1<<1) /* Trigger Interrupt Enable */
+#define EVENT_IE                  (1<<0) /* Event Interrupt Enable */
+
+/* Bit definitions for the PTP_TSTS register */
+#define TRIG7_ERROR               (1<<15) /* Trigger 7 Error */
+#define TRIG7_ACTIVE              (1<<14) /* Trigger 7 Active */
+#define TRIG6_ERROR               (1<<13) /* Trigger 6 Error */
+#define TRIG6_ACTIVE              (1<<12) /* Trigger 6 Active */
+#define TRIG5_ERROR               (1<<11) /* Trigger 5 Error */
+#define TRIG5_ACTIVE              (1<<10) /* Trigger 5 Active */
+#define TRIG4_ERROR               (1<<9) /* Trigger 4 Error */
+#define TRIG4_ACTIVE              (1<<8) /* Trigger 4 Active */
+#define TRIG3_ERROR               (1<<7) /* Trigger 3 Error */
+#define TRIG3_ACTIVE              (1<<6) /* Trigger 3 Active */
+#define TRIG2_ERROR               (1<<5) /* Trigger 2 Error */
+#define TRIG2_ACTIVE              (1<<4) /* Trigger 2 Active */
+#define TRIG1_ERROR               (1<<3) /* Trigger 1 Error */
+#define TRIG1_ACTIVE              (1<<2) /* Trigger 1 Active */
+#define TRIG0_ERROR               (1<<1) /* Trigger 0 Error */
+#define TRIG0_ACTIVE              (1<<0) /* Trigger 0 Active */
+
+/* Bit definitions for the PTP_RATEH register */
+#define PTP_RATE_DIR              (1<<15) /* PTP Rate Direction */
+#define PTP_TMP_RATE              (1<<14) /* PTP Temporary Rate */
+#define PTP_RATE_HI_SHIFT         (0) /* PTP Rate High 10-bits */
+#define PTP_RATE_HI_MASK          (0x3ff)
+
+/* Bit definitions for the PTP_ESTS register */
+#define EVNTS_MISSED_SHIFT        (8) /* Indicates number of events missed */
+#define EVNTS_MISSED_MASK         (0x7)
+#define EVNT_TS_LEN_SHIFT         (6) /* Indicates length of the Timestamp field in 16-bit words minus 1 */
+#define EVNT_TS_LEN_MASK          (0x3)
+#define EVNT_RF                   (1<<5) /* Indicates whether the event is a rise or falling event */
+#define EVNT_NUM_SHIFT            (2) /* Indicates Event Timestamp Unit which detected an event */
+#define EVNT_NUM_MASK             (0x7)
+#define MULT_EVNT                 (1<<1) /* Indicates multiple events were detected at the same time */
+#define EVENT_DET                 (1<<0) /* PTP Event Detected */
+
+/* Bit definitions for the PTP_EDATA register */
+#define E7_RISE                   (1<<15) /* Indicates direction of Event 7 */
+#define E7_DET                    (1<<14) /* Indicates Event 7 detected */
+#define E6_RISE                   (1<<13) /* Indicates direction of Event 6 */
+#define E6_DET                    (1<<12) /* Indicates Event 6 detected */
+#define E5_RISE                   (1<<11) /* Indicates direction of Event 5 */
+#define E5_DET                    (1<<10) /* Indicates Event 5 detected */
+#define E4_RISE                   (1<<9) /* Indicates direction of Event 4 */
+#define E4_DET                    (1<<8) /* Indicates Event 4 detected */
+#define E3_RISE                   (1<<7) /* Indicates direction of Event 3 */
+#define E3_DET                    (1<<6) /* Indicates Event 3 detected */
+#define E2_RISE                   (1<<5) /* Indicates direction of Event 2 */
+#define E2_DET                    (1<<4) /* Indicates Event 2 detected */
+#define E1_RISE                   (1<<3) /* Indicates direction of Event 1 */
+#define E1_DET                    (1<<2) /* Indicates Event 1 detected */
+#define E0_RISE                   (1<<1) /* Indicates direction of Event 0 */
+#define E0_DET                    (1<<0) /* Indicates Event 0 detected */
+
+/* Bit definitions for the PTP_TRIG register */
+#define TRIG_PULSE                (1<<15) /* generate a Pulse rather than a single edge */
+#define TRIG_PER                  (1<<14) /* generate a periodic signal */
+#define TRIG_IF_LATE              (1<<13) /* trigger immediately if already past */
+#define TRIG_NOTIFY               (1<<12) /* Trigger Notification Enable */
+#define TRIG_GPIO_SHIFT           (8) /* Trigger GPIO Connection, value 1-12 */
+#define TRIG_GPIO_MASK            (0xf)
+#define TRIG_TOGGLE               (1<<7) /* Trigger Toggle Mode Enable */
+#define TRIG_CSEL_SHIFT           (1) /* Trigger Configuration Select */
+#define TRIG_CSEL_MASK            (0x7)
+#define TRIG_WR                   (1<<0) /* Trigger Configuration Write */
+
+/* Bit definitions for the PTP_EVNT register */
+#define EVNT_RISE                 (1<<14) /* Event Rise Detect Enable */
+#define EVNT_FALL                 (1<<13) /* Event Fall Detect Enable */
+#define EVNT_SINGLE               (1<<12) /* enable single event capture operation */
+#define EVNT_GPIO_SHIFT           (8) /* Event GPIO Connection, value 1-12 */
+#define EVNT_GPIO_MASK            (0xf)
+#define EVNT_SEL_SHIFT            (1) /* Event Select */
+#define EVNT_SEL_MASK             (0x7)
+#define EVNT_WR                   (1<<0) /* Event Configuration Write */
+
+/* Bit definitions for the PTP_TXCFG0 register */
+#define SYNC_1STEP                (1<<15) /* insert timestamp into transmit Sync Messages */
+#define DR_INSERT                 (1<<13) /* Insert Delay_Req Timestamp in Delay_Resp (dangerous) */
+#define NTP_TS_EN                 (1<<12) /* Enable Timestamping of NTP Packets */
+#define IGNORE_2STEP              (1<<11) /* Ignore Two_Step flag for One-Step operation */
+#define CRC_1STEP                 (1<<10) /* Disable checking of CRC for One-Step operation */
+#define CHK_1STEP                 (1<<9) /* Enable UDP Checksum correction for One-Step Operation */
+#define IP1588_EN                 (1<<8) /* Enable IEEE 1588 defined IP address filter */
+#define TX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define TX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define TX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define TX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define TX_PTP_VER_MASK           (0xf)
+#define TX_TS_EN                  (1<<0) /* Transmit Timestamp Enable */
+
+/* Bit definitions for the PTP_TXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG0 register */
+#define MAC_SRC_ADD_SHIFT         (11) /* Status Frame Mac Source Address */
+#define MAC_SRC_ADD_MASK          (0x3)
+#define MIN_PRE_SHIFT             (8) /* Status Frame Minimum Preamble */
+#define MIN_PRE_MASK              (0x7)
+#define PSF_ENDIAN                (1<<7) /* Status Frame Endian Control */
+#define PSF_IPV4                  (1<<6) /* Status Frame IPv4 Enable */
+#define PSF_PCF_RD                (1<<5) /* Control Frame Read PHY Status Frame Enable */
+#define PSF_ERR_EN                (1<<4) /* Error PHY Status Frame Enable */
+#define PSF_TXTS_EN               (1<<3) /* Transmit Timestamp PHY Status Frame Enable */
+#define PSF_RXTS_EN               (1<<2) /* Receive Timestamp PHY Status Frame Enable */
+#define PSF_TRIG_EN               (1<<1) /* Trigger PHY Status Frame Enable */
+#define PSF_EVNT_EN               (1<<0) /* Event PHY Status Frame Enable */
+
+/* Bit definitions for the PTP_RXCFG0 register */
+#define DOMAIN_EN                 (1<<15) /* Domain Match Enable */
+#define ALT_MAST_DIS              (1<<14) /* Alternate Master Timestamp Disable */
+#define USER_IP_SEL               (1<<13) /* Selects portion of IP address accessible thru PTP_RXCFG2 */
+#define USER_IP_EN                (1<<12) /* Enable User-programmed IP address filter */
+#define RX_SLAVE                  (1<<11) /* Receive Slave Only */
+#define IP1588_EN_SHIFT           (8) /* Enable IEEE 1588 defined IP address filters */
+#define IP1588_EN_MASK            (0xf)
+#define RX_L2_EN                  (1<<7) /* Layer2 Timestamp Enable */
+#define RX_IPV6_EN                (1<<6) /* IPv6 Timestamp Enable */
+#define RX_IPV4_EN                (1<<5) /* IPv4 Timestamp Enable */
+#define RX_PTP_VER_SHIFT          (1) /* Enable Timestamp capture for IEEE 1588 version X */
+#define RX_PTP_VER_MASK           (0xf)
+#define RX_TS_EN                  (1<<0) /* Receive Timestamp Enable */
+
+/* Bit definitions for the PTP_RXCFG1 register */
+#define BYTE0_MASK_SHIFT          (8) /* Bit mask to be used for matching Byte0 of the PTP Message */
+#define BYTE0_MASK_MASK           (0xff)
+#define BYTE0_DATA_SHIFT          (0) /* Data to be used for matching Byte0 of the PTP Message */
+#define BYTE0_DATA_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG3 register */
+#define TS_MIN_IFG_SHIFT          (12) /* Minimum Inter-frame Gap */
+#define TS_MIN_IFG_MASK           (0xf)
+#define ACC_UDP                   (1<<11) /* Record Timestamp if UDP Checksum Error */
+#define ACC_CRC                   (1<<10) /* Record Timestamp if CRC Error */
+#define TS_APPEND                 (1<<9) /* Append Timestamp for L2 */
+#define TS_INSERT                 (1<<8) /* Enable Timestamp Insertion */
+#define PTP_DOMAIN_SHIFT          (0) /* PTP Message domainNumber field */
+#define PTP_DOMAIN_MASK           (0xff)
+
+/* Bit definitions for the PTP_RXCFG4 register */
+#define IPV4_UDP_MOD              (1<<15) /* Enable IPV4 UDP Modification */
+#define TS_SEC_EN                 (1<<14) /* Enable Timestamp Seconds */
+#define TS_SEC_LEN_SHIFT          (12) /* Inserted Timestamp Seconds Length */
+#define TS_SEC_LEN_MASK           (0x3)
+#define RXTS_NS_OFF_SHIFT         (6) /* Receive Timestamp Nanoseconds offset */
+#define RXTS_NS_OFF_MASK          (0x3f)
+#define RXTS_SEC_OFF_SHIFT        (0) /* Receive Timestamp Seconds offset */
+#define RXTS_SEC_OFF_MASK         (0x3f)
+
+/* Bit definitions for the PTP_COC register */
+#define PTP_CLKOUT_EN             (1<<15) /* PTP Clock Output Enable */
+#define PTP_CLKOUT_SEL            (1<<14) /* PTP Clock Output Source Select */
+#define PTP_CLKOUT_SPEEDSEL       (1<<13) /* PTP Clock Output I/O Speed Select */
+#define PTP_CLKDIV_SHIFT          (0) /* PTP Clock Divide-by Value */
+#define PTP_CLKDIV_MASK           (0xff)
+
+/* Bit definitions for the PSF_CFG1 register */
+#define PTPRESERVED_SHIFT         (12) /* PTP v2 reserved field */
+#define PTPRESERVED_MASK          (0xf)
+#define VERSIONPTP_SHIFT          (8) /* PTP v2 versionPTP field */
+#define VERSIONPTP_MASK           (0xf)
+#define TRANSPORT_SPECIFIC_SHIFT  (4) /* PTP v2 Header transportSpecific field */
+#define TRANSPORT_SPECIFIC_MASK   (0xf)
+#define MESSAGETYPE_SHIFT         (0) /* PTP v2 messageType field */
+#define MESSAGETYPE_MASK          (0xf)
+
+/* Bit definitions for the PTP_SFDCFG register */
+#define TX_SFD_GPIO_SHIFT         (4) /* TX SFD GPIO Select, value 1-12 */
+#define TX_SFD_GPIO_MASK          (0xf)
+#define RX_SFD_GPIO_SHIFT         (0) /* RX SFD GPIO Select, value 1-12 */
+#define RX_SFD_GPIO_MASK          (0xf)
+
+/* Bit definitions for the PTP_INTCTL register */
+#define PTP_INT_GPIO_SHIFT        (0) /* PTP Interrupt GPIO Select */
+#define PTP_INT_GPIO_MASK         (0xf)
+
+/* Bit definitions for the PTP_CLKSRC register */
+#define CLK_SRC_SHIFT             (14) /* PTP Clock Source Select */
+#define CLK_SRC_MASK              (0x3)
+#define CLK_SRC_PER_SHIFT         (0) /* PTP Clock Source Period */
+#define CLK_SRC_PER_MASK          (0x7f)
+
+/* Bit definitions for the PTP_OFF register */
+#define PTP_OFFSET_SHIFT          (0) /* PTP Message offset from preceding header */
+#define PTP_OFFSET_MASK           (0xff)
+
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (7 preceding siblings ...)
  2010-09-23 17:34 ` [PATCH 8/8] ptp: Added a clock driver for the National Semiconductor PHYTER Richard Cochran
@ 2010-09-23 17:53 ` Christoph Lameter
  2010-09-23 18:21   ` Jacob Keller
                     ` (2 more replies)
  2010-09-23 19:38 ` john stultz
  2010-09-23 20:36 ` Alan Cox
  10 siblings, 3 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 17:53 UTC (permalink / raw)
  To: Richard Cochran
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, netdev,
	Thomas Gleixner, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 23 Sep 2010, Richard Cochran wrote:

>   Support for obtaining timestamps from a PHC already exists via the
>   SO_TIMESTAMPING socket option, integrated in kernel version 2.6.30.
>   This patch set completes the picture by allow user space programs to
>   adjust the PHC and to control its ancillary features.

Is there a way to use the PHC as a system clock? I think the main benefit
of PTP is to have syncronized time on multiple machines in a cluster. That
may mean getting rid of ntp and using an in kernel PHC based way to sync time.

>    So as far as the POSIX standard is concerned, offering a clock id
>    to represent the PHC would be acceptable.

Sure but what would you do with it? HPET timer support has no such need.

> 3.2.1 Using the POSIX Clock API
> --------------------------------
>
>     Looking at the mapping from PHC operation to the POSIX clock API,
>     we see that two of the basic clock operations, marked with *, have
>     no POSIX equivalent. The items marked NA are peculiar to PHCs and
>     will be discussed separately, below.
>
>       Clock Operation               POSIX function
>      -----------------------------+-----------------------------
>       Set time                      clock_gettime
>       Get time                      clock_settime
>       Shift the clock               *
>       Adjust clock frequency        *
>      -----------------------------+-----------------------------
>       Time stamp external events    NA
>       Enable PPS events             NA
>       Periodic output signals       NA
>       One shot or periodic alarms   timer_create, timer_settime
>
>     In contrast to the standard Linux system clock, a PHC is
>     adjustable in hardware, for example using frequency compensation
>     registers or a VCO. The ability to directly tune the PHC is
>     essential to reap the benefit of hardware timestamping.

There is a reason for not being able to shift posix clocks: The system has
one time base. The various clocks are contributing to maintaining that
sytem wide time.

I do not understand why you want to maintain different clocks running at
different speeds. Certainly interesting for some uses I guess that I
do not have the energy to imagine right now. But can we get the PTP killer
feature of synchronized accurate system time first?

> 3.3 Synchronizing the Linux System Time
> ========================================
>
>    One could offer a PHC as a combined clock source and clock event
>    device. The advantage of this approach would be that it obviates
>    the need for synchronization when the PHC is selected as the system
>    timer. However, some PHCs, namely the PHY based clocks, cannot be
>    used in this way.

Why not? Do PHY based clock not at least provide a counter that increments
in synchronized intervals throughout the network?

>    Instead, the patch set provides a way to offer a Pulse Per Second
>    (PPS) event from the PHC to the Linux PPS subsystem. A user space
>    application can read the PPS events and tune the system clock, just
>    like when using other external time sources like radio clocks or
>    GPS.

User space is subject to various latencies created by the OS etc. I would
that in order to have fine grained (read microsecond) accurary we would
have to run the portions that are relevant to obtaining the desired
accuracy in the kernel.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
@ 2010-09-23 18:21   ` Jacob Keller
  2010-09-23 18:36     ` Christoph Lameter
  2010-09-23 18:59   ` john stultz
  2010-09-24  8:33   ` Richard Cochran
  2 siblings, 1 reply; 53+ messages in thread
From: Jacob Keller @ 2010-09-23 18:21 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

[-- Attachment #1: Type: text/plain, Size: 2646 bytes --]

>
> >     In contrast to the standard Linux system clock, a PHC is
> >     adjustable in hardware, for example using frequency compensation
> >     registers or a VCO. The ability to directly tune the PHC is
> >     essential to reap the benefit of hardware timestamping.
>
> There is a reason for not being able to shift posix clocks: The system has
> one time base. The various clocks are contributing to maintaining that
> sytem wide time.
>
> Adjusting clocks is absolutely essential for proper functioning of the PTP
protocol. The slave obtains and calculates the offset from master and uses
that in order to adjust the clock properly, The problem is that the
timestamps are done via the hardware. We need a method to expose that
hardware so that the ptp software can properly adjust those clocks.


> I do not understand why you want to maintain different clocks running at
> different speeds. Certainly interesting for some uses I guess that I
> do not have the energy to imagine right now. But can we get the PTP killer
> feature of synchronized accurate system time first?
>

The problem is maintaining a hardware clock at the correct speed/frequency
and time. The timestamping is done via hardware, and that hardware clock
needs to be accurate. We need to be able to modify that clock. Yes, having
the system time be the same value would be nice, but the problem comes
because we don't want to jump through hoops to keep that hardware clock
accurate to the ptp protocol running on the network.



>
> >    Instead, the patch set provides a way to offer a Pulse Per Second
> >    (PPS) event from the PHC to the Linux PPS subsystem. A user space
> >    application can read the PPS events and tune the system clock, just
> >    like when using other external time sources like radio clocks or
> >    GPS.
>
> User space is subject to various latencies created by the OS etc. I would
> that in order to have fine grained (read microsecond) accurary we would
> have to run the portions that are relevant to obtaining the desired
> accuracy in the kernel.
>

All of the necessary features for microsecond or better accuracy are done
via the hardware. You can get accuracy to within <10 mircoseconds while only
sending sync packets and such once per second. The reason is because the
hardware timestamps are very accurate. But if we can't properly adjust the
clocks time and frequency, we cannot maintain the accuracy of the
timestamps.

>
>
 --
> 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
>

[-- Attachment #2: Type: text/html, Size: 3837 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 18:21   ` Jacob Keller
@ 2010-09-23 18:36     ` Christoph Lameter
  0 siblings, 0 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 18:36 UTC (permalink / raw)
  To: Jacob Keller
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

On Thu, 23 Sep 2010, Jacob Keller wrote:

> > There is a reason for not being able to shift posix clocks: The system has
> > one time base. The various clocks are contributing to maintaining that
> > sytem wide time.
> >
> > Adjusting clocks is absolutely essential for proper functioning of the PTP
> protocol. The slave obtains and calculates the offset from master and uses
> that in order to adjust the clock properly, The problem is that the
> timestamps are done via the hardware. We need a method to expose that
> hardware so that the ptp software can properly adjust those clocks.

There is no way to use that clock directly to avoid all the user space
tuning etc? There are already tuning mechanisms in the kernel that do this
with system time based on periodic clocks. If you calculate the
nanoseconds since the epoch then you should be able to use that to tune
system time.

> > I do not understand why you want to maintain different clocks running at
> > different speeds. Certainly interesting for some uses I guess that I
> > do not have the energy to imagine right now. But can we get the PTP killer
> > feature of synchronized accurate system time first?
> >
>
> The problem is maintaining a hardware clock at the correct speed/frequency
> and time. The timestamping is done via hardware, and that hardware clock
> needs to be accurate. We need to be able to modify that clock. Yes, having
> the system time be the same value would be nice, but the problem comes
> because we don't want to jump through hoops to keep that hardware clock
> accurate to the ptp protocol running on the network.

Then allow system time == hardware clock?

> All of the necessary features for microsecond or better accuracy are done
> via the hardware. You can get accuracy to within <10 mircoseconds while only
> sending sync packets and such once per second. The reason is because the
> hardware timestamps are very accurate. But if we can't properly adjust the
> clocks time and frequency, we cannot maintain the accuracy of the
> timestamps.

You can already adjust the system time with the existing APIs. Tuning
hardware clocks is currently done using device specific controls. But I
would think that you do not need to expose this to user space if you can
do it all in kernel.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
  2010-09-23 18:21   ` Jacob Keller
@ 2010-09-23 18:59   ` john stultz
  2010-09-23 19:15     ` Christoph Lameter
  2010-09-24  8:33   ` Richard Cochran
  2 siblings, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 18:59 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 12:53 -0500, Christoph Lameter wrote:
> On Thu, 23 Sep 2010, Richard Cochran wrote:
> >     In contrast to the standard Linux system clock, a PHC is
> >     adjustable in hardware, for example using frequency compensation
> >     registers or a VCO. The ability to directly tune the PHC is
> >     essential to reap the benefit of hardware timestamping.
> 
> There is a reason for not being able to shift posix clocks: The system has
> one time base. The various clocks are contributing to maintaining that
> sytem wide time.
> 
> I do not understand why you want to maintain different clocks running at
> different speeds. Certainly interesting for some uses I guess that I
> do not have the energy to imagine right now. But can we get the PTP killer
> feature of synchronized accurate system time first?

This was my initial gut reaction as well, but in the end, I agree with
Richard that in the case of one or multiple PTP hardware clocks, we
really can't abstract over the different time domains.



> > 3.3 Synchronizing the Linux System Time
> > ========================================
> >
> >    One could offer a PHC as a combined clock source and clock event
> >    device. The advantage of this approach would be that it obviates
> >    the need for synchronization when the PHC is selected as the system
> >    timer. However, some PHCs, namely the PHY based clocks, cannot be
> >    used in this way.
> 
> Why not? Do PHY based clock not at least provide a counter that increments
> in synchronized intervals throughout the network?

I really don't think the PTP clock can be used as a clocksource sanely.

First, the hardware access is much to slow for system timekeeping.

Second, there is the problem that the system time is a software clock,
and adjustments made (like freq) are made in the layer that interprets
the underlying hardware cycle counter. Adjustments made in PTP (in order
to sync the network timestamps) are made at the hardware level. 

This would cause a disconnect between the hardware freq understood by
the system time management code and the actual hardware freq.

Richard, I'd actually strike this paragraph from the rational, as I feel
it has the tendency to confuse as it suggests having the PHC as a
clocksource is feasible when really it isn't. Or alternatively, maybe
express more clearly why its not feasible, so it doesn't just seem like
a minor design choice.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 18:59   ` john stultz
@ 2010-09-23 19:15     ` Christoph Lameter
  2010-09-23 20:28       ` john stultz
  0 siblings, 1 reply; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 19:15 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 23 Sep 2010, john stultz wrote:

> This was my initial gut reaction as well, but in the end, I agree with
> Richard that in the case of one or multiple PTP hardware clocks, we
> really can't abstract over the different time domains.

My (arguably still superficial) review of the source does not show
anything that would make me reach that conclusion.

> I really don't think the PTP clock can be used as a clocksource sanely.
>
> First, the hardware access is much to slow for system timekeeping.

The HPET or pit timesource are also quite slow these days. You only need
access periodically to essentially tune the TSC ratio.

> Second, there is the problem that the system time is a software clock,
> and adjustments made (like freq) are made in the layer that interprets
> the underlying hardware cycle counter. Adjustments made in PTP (in order
> to sync the network timestamps) are made at the hardware level.

>From what I can see the PTP clocks are periodic hardware cycle counters
like any other clock that we currently support. If its configurable enough
then setup a hardware cycle counter that mimics nanoseconds since the
epoch as closely as possible and use that to sync the TSC rate to. Makes
it very easy.

> This would cause a disconnect between the hardware freq understood by
> the system time management code and the actual hardware freq.

We can switch underlying clocks for system time already. We can adapt to a
different hw frequency. But then I do not know why adjust the freq? I
thought the point was that the periodic clock was network synchronized and
can be used as "the" master clock for multiple machines?

> Richard, I'd actually strike this paragraph from the rational, as I feel
> it has the tendency to confuse as it suggests having the PHC as a
> clocksource is feasible when really it isn't. Or alternatively, maybe
> express more clearly why its not feasible, so it doesn't just seem like
> a minor design choice.

Sorry but I still feel that this is pretty much a misguided approach that
creates unnecessary layers in the kernel. The trivial easy approach was
not done (copy a driver from drivers/clocksource, modify so that it
programs access to a centralized periodic ptp signal and uses it for
system sync).

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 17:33 ` [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx Richard Cochran
@ 2010-09-23 19:17   ` Christoph Lameter
  2010-09-23 20:43     ` Alan Cox
  2010-09-24  8:49     ` Richard Cochran
  0 siblings, 2 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 19:17 UTC (permalink / raw)
  To: Richard Cochran
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, netdev,
	Thomas Gleixner, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 23 Sep 2010, Richard Cochran wrote:

> +* Gianfar PTP clock nodes
> +
> +General Properties:
> +
> +  - compatible   Should be "fsl,etsec-ptp"
> +  - reg          Offset and length of the register set for the device
> +  - interrupts   There should be at least two interrupts. Some devices
> +                 have as many as four PTP related interrupts.
> +
> +Clock Properties:
> +
> +  - tclk-period  Timer reference clock period in nanoseconds.
> +  - tmr-prsc     Prescaler, divides the output clock.
> +  - tmr-add      Frequency compensation value.
> +  - cksel        0= external clock, 1= eTSEC system clock, 3= RTC clock input.
> +                 Currently the driver only supports choice "1".
> +  - tmr-fiper1   Fixed interval period pulse generator.
> +  - tmr-fiper2   Fixed interval period pulse generator.
> +  - max-adj      Maximum frequency adjustment in parts per billion.
> +
> +  These properties set the operational parameters for the PTP
> +  clock. You must choose these carefully for the clock to work right.
> +  Here is how to figure good values:
> +
> +  TimerOsc     = system clock               MHz
> +  tclk_period  = desired clock period       nanoseconds
> +  NominalFreq  = 1000 / tclk_period         MHz
> +  FreqDivRatio = TimerOsc / NominalFreq     (must be greater that 1.0)
> +  tmr_add      = ceil(2^32 / FreqDivRatio)
> +  OutputClock  = NominalFreq / tmr_prsc     MHz
> +  PulseWidth   = 1 / OutputClock            microseconds
> +  FiperFreq1   = desired frequency in Hz
> +  FiperDiv1    = 1000000 * OutputClock / FiperFreq1
> +  tmr_fiper1   = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
> +  max_adj      = 1000000000 * (FreqDivRatio - 1.0) - 1

Great stuff for clock synchronization...

> +  The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
> +  driver expects that tmr_fiper1 will be correctly set to produce a 1
> +  Pulse Per Second (PPS) signal, since this will be offered to the PPS
> +  subsystem to synchronize the Linux clock.

Argh. And conceptually completely screwed up. Why go through the PPS
subsystem if you can directly tune the system clock based on a number of
the cool periodic clock features that you have above? See how the other
clocks do that easily? Look into drivers/clocksource. Add it there.

Please do not introduce useless additional layers for clock sync. Load
these ptp clocks like the other regular clock modules and make them sync
system time like any other clock.

Really guys: I want a PTP solution! Now! And not some idiotic additional
kernel layers that just pass bits around because its so much fun and
screws up clock accurary in due to the latency noise introduced while
having so much fun with the bits.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (8 preceding siblings ...)
  2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
@ 2010-09-23 19:38 ` john stultz
  2010-09-24 13:50   ` Richard Cochran
  2010-09-23 20:36 ` Alan Cox
  10 siblings, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 19:38 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 19:30 +0200, Richard Cochran wrote:
> Here is the sixth version of my patch set adding PTP hardware clock
> support to the Linux kernel. The main difference to v5 is that the
> character device interface has been replaced with one based on the
> posix clock system calls.
> 
> The first three patches add necessary background support in the posix
> clock code. The last five add the new PTP hardware clock features.
> Previously, I had tried to present the posix clock changes all by
> themselves, but commentators asked to see the whole context.

Richard,
	Its great to see this work continue and the patch set is shaping up
nicely! There's still a few details to work out, but I think the
remaining issues are relatively small.


> 3.2.3 Dynamic POSIX Clock IDs 
> ------------------------------
> 
>     The reaction on the list to having a static id like CLOCK_PTP was
>     mostly negative. However, the idea of generating a clock id
>     dynamically seems to have gained acceptance. The general idea is
>     to advertise the available clock ids to user space via sysfs. This
>     patch set implements two different ways:
> 
>     /sys/class/timesource/<name>/id
>     /sys/class/ptp/ptp_clock_X/id
> 
>     Note: I am not too sure that this is exactly what people imagined,
>           but it is my best understanding so far. I gleaned two
>           different ideas about where to offer the clock id. In order
>           to keep just one way, I will be happy to remove the less
>           popular one.

So yea, I'm not a fan of the "timesource" sysfs interface. One, I think
the name is poor (posix_clocks or something a little more specific would
be an improvement), and second, I don't like the dictionary interface,
where one looks up the clock by name.

Instead, I think having the id hanging off the class driver is much
better, as it allows mapping the actual hardware to the id more clearly.

So I'd drop the "timesource" listing. And maybe change "id" to
"clock_id" so its a little more clear what the id is for.



> 3.3 Synchronizing the Linux System Time 
> ========================================
> 
>    One could offer a PHC as a combined clock source and clock event
>    device. The advantage of this approach would be that it obviates
>    the need for synchronization when the PHC is selected as the system
>    timer. However, some PHCs, namely the PHY based clocks, cannot be
>    used in this way.

Again, I'd scratch this. 

What I think you might want to mention is that an application like NTP
could use the PTP clockid much like NTP currently can be configured to
use the RTC to steer the system time.

Possibly the PTPd could just do this, reducing the number of deamons and
avoiding mixing NTP up in what is really a different sync algorithm.

>    Instead, the patch set provides a way to offer a Pulse Per Second
>    (PPS) event from the PHC to the Linux PPS subsystem. A user space
>    application can read the PPS events and tune the system clock, just
>    like when using other external time sources like radio clocks or
>    GPS.

Forgive me for a bit of a tangent here:
	So while I think this PPS method is a neat idea, I'm a little curious
how much of a difference the PPS method for syncing the clock would be
over just a simple reading of the two clocks and correcting the offset.

It seems much of it depends on the read latency of the PTP hardware vs
the interrupt latency. Also the PTP clock granularity would effect the
read accuracy (like on the RTC, you don't really know how close to the
second boundary you are).

Have you done any such measurements between the two methods? I just
wonder if it would actually be something noticeable, and if its not, how
much lighter this patch-set would be without the PPS connection.

Again, this isn't super critical, just trying to make sure we don't end
up adding a bunch of code that doesn't end up being used. Also PPS
interrupts are awfully frequent, so systems concerned with power-saving
and deep idles probably would like something that could be done at a
more coarse interval.


> 3.5 User timers 
> ================
> 
>    Using the POSIX clock API gived user space the possibility to
>    create and use timers with timer_create and timer_settime. In the
>    current patch set the kernel functionality is not implemented,
>    since there are some issues to consider first. I see two ways to do
>    about this.
> 
>    1. Implement the functionality anew. This approach might end up
>       duplicating similar code that already exists. Also, looking at
>       the hrtimer code, getting user timers right seems to have a
>       number of gotchas and thorny issues.
> 
>    2. Reuse the hrtimer code. Since the hrtimer code uses a clock
>       event device under the hood, it might be possible (in theory) to
>       offer capable PHCs as clock event devices. However, the current
>       hrtimers are hard-coded to the event device via a per-cpu
>       global. Perhaps one could associate an event device with a
>       hrtimer via the timer itself.
> 
>    At this point I am not optimistic about either approach, and I
>    would vote for postponing the timer issue indefinitely. The
>    implementation effort would be high, but the utility low.

So I'm working on this! I've pulled out the basic rbtree based timer
list manipulation out of the hrtimer code and into its own lib code.

I'll send those patches out for RFC shortly.


>    If the Linux system time is synchronized to the PHC via the PPS
>    method, then using standard hrtimers would be good enough for most
>    purposes. Consider the time scales involved. The PHC can be
>    synchronized to within 100 nanoseconds of an external time source,
>    while timer wakeup latency (even with rt kernels) is tens of
>    microseconds.

Hmm. I think this point touches on my earlier tangent.



> 4 Drivers 
> ~~~~~~~~~~
> 
> 4.1 Supported Hardware Clocks 
> ==============================
> 
>    + Standard Linux system timer
>      This driver exports the standard Linux timer as a PTP clock.
>      Although this duplicates CLOCK_REALTIME, the code serves as a
>      simple example for driver development and lets people who without
>      special hardware try the new API.

Still not a fan of this one, figure the app should handle the special
case where there are no PTP clocks and just use CLOCK_REALTIME rather
then funneling CLOCK_REALTIME through the PTP interface.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
@ 2010-09-23 19:48   ` john stultz
  2010-09-24  7:29     ` Richard Cochran
  2010-09-23 22:03   ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 19:48 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 19:31 +0200, Richard Cochran wrote:
> A new syscall is introduced that allows tuning of a POSIX clock. The
> syscall is implemented for four architectures: arm, blackfin, powerpc,
> and x86.
> 
> The new syscall, clock_adjtime, takes two parameters, the clock ID,
> and a pointer to a struct timex. The semantics of the timex struct
> have been expanded by one additional mode flag, which allows an
> absolute offset correction. When specificied, the clock offset is
> immediately corrected by adding the given time value to the current
> time value.


So I'd still split this patch up a little bit more.

1) Patch that implements the ADJ_SETOFFSET  (*and its implementation*)
in do_adjtimex.

2) Patch that adds the new syscall and clock_id multiplexing.

3) Patches that wire it up to the rest of the architectures (there's
still a bunch missing here).



And one little nit in the code:

> diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
> index 9ca4973..446b566 100644
> --- a/kernel/posix-timers.c
> +++ b/kernel/posix-timers.c
> @@ -197,6 +197,14 @@ static int common_timer_create(struct k_itimer *new_timer)
>  	return 0;
>  }
> 
> +static inline int common_clock_adj(const clockid_t which_clock, struct timex *t)
> +{
> +	if (CLOCK_REALTIME == which_clock)
> +		return do_adjtimex(t);
> +	else
> +		return -EOPNOTSUPP;
> +}


Would it make sense to point to the do_adjtimex() in the k_clock
definition for CLOCK_REALTIME rather then conditionalizing it here?



thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 19:15     ` Christoph Lameter
@ 2010-09-23 20:28       ` john stultz
  2010-09-23 20:49         ` Christoph Lameter
  0 siblings, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 20:28 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 14:15 -0500, Christoph Lameter wrote:
> On Thu, 23 Sep 2010, john stultz wrote:
> 
> > This was my initial gut reaction as well, but in the end, I agree with
> > Richard that in the case of one or multiple PTP hardware clocks, we
> > really can't abstract over the different time domains.
> 
> My (arguably still superficial) review of the source does not show
> anything that would make me reach that conclusion.
> 
> > I really don't think the PTP clock can be used as a clocksource sanely.
> >
> > First, the hardware access is much to slow for system timekeeping.
> 
> The HPET or pit timesource are also quite slow these days. You only need
> access periodically to essentially tune the TSC ratio.

If we're using the TSC, then we're not using the PTP clock as you
suggest. Further the HPET and PIT aren't used to steer the system time
when we are using the TSC as a clocksource. Its only used to calibrate
the initial constant freq used by the timekeeping code (and if its
non-constant, we throw it out).

> > Second, there is the problem that the system time is a software clock,
> > and adjustments made (like freq) are made in the layer that interprets
> > the underlying hardware cycle counter. Adjustments made in PTP (in order
> > to sync the network timestamps) are made at the hardware level.
> 
> From what I can see the PTP clocks are periodic hardware cycle counters
> like any other clock that we currently support. If its configurable enough
> then setup a hardware cycle counter that mimics nanoseconds since the
> epoch as closely as possible and use that to sync the TSC rate to. Makes
> it very easy.

I guess I'm confused by what you're suggesting.
If we're using the TSC, then that's the clocksource timekeeping uses.
The original issue seemed to be around the suggestion of using the PTP
clock as a clocksource, which I don't think is really feasible.

Again, that's because
1) The PTP access latency is slow (so is the PIT, true enough, but no
one should be using the PIT as a clocksource unless they really have no
better hardware - its really only useful for 486s and old freq scaling
laptops that have no other stable clocksource).

2) The way PTP clocks are steered to sync with network time causes their
hardware freq to actually change. Since these adjustments are done on
the hardware clock level, and not on the system time level, the
adjustments to sync the system time/freq would then be made incorrect by
PTP hardware adjustments. 

3) Further, the PTP hardware counter can be simply set to a new offset
to put it in line with the network time. This could cause trouble with
timekeeping much like unsynced TSCs do.


Now, what you seem to be suggesting is to use the TSC (or whatever
clocksource the system time is using) but to steer the system time using
the PTP clock. This is actually what is being proposed, however, the
steering is done in userland. This is due to the fact that there are two
components to the steering, 1) adjusting the PTP clock hardware to
network time and 2) adjusting the system time to the PTP hardware. By
exposing the PTP clock to userland via the posix clocks interface, we
allow this to easily be done.


> > This would cause a disconnect between the hardware freq understood by
> > the system time management code and the actual hardware freq.
> 
> We can switch underlying clocks for system time already. We can adapt to a
> different hw frequency.

Actually no. The timekeeping code requires a fixed freq counter. Dealing
with hardware freq changes is difficult, because error is introduced by
the latency between when the freq changes and when the timekeeping code
is notified of it. So the system treats the hardware counters as fixed
freq. Now, hardware does vary freq ever so slightly as thermal
conditions change, but this is addressed in userland and corrected via
adjtimex.

>  But then I do not know why adjust the freq? I
> thought the point was that the periodic clock was network synchronized and
> can be used as "the" master clock for multiple machines?

Not parsing that. What do you mean by periodic clock?

> > Richard, I'd actually strike this paragraph from the rational, as I feel
> > it has the tendency to confuse as it suggests having the PHC as a
> > clocksource is feasible when really it isn't. Or alternatively, maybe
> > express more clearly why its not feasible, so it doesn't just seem like
> > a minor design choice.
> 
> Sorry but I still feel that this is pretty much a misguided approach that
> creates unnecessary layers in the kernel.

Unnecessary layers? Where? This approach has less in-kernel layers, as
it exposes the PTP clock to userland, instead of trying to layer things
on top of it and stretching the system time abstraction to cover it.

>  The trivial easy approach was
> not done (copy a driver from drivers/clocksource, modify so that it
> programs access to a centralized periodic ptp signal and uses it for
> system sync).

I disagree.

I've argued through the approach trying to keep it all internal to the
kernel, but to do so would be anything but trivial. Further, there's the
case of master-clocks, where the PTP hardware must be synced to system
time, instead of the other way around. And then there's the case of
boundary-clocks, which may have multiple PTP hardware clocks that have
to be synced.

I think exposing this through the posix clock interface is really the
best approach. Its not a static clockid, so its not something most apps
will ever have to deal with, but it allows the few apps that really need
to have access to the PTP clock hardware can do so in a clean way.


And credits to Richard for having to slowly explain this to me (and
others) many times over, before I got it.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 20:43     ` Alan Cox
@ 2010-09-23 20:32       ` Christoph Lameter
  2010-09-23 21:26       ` Christian Riesch
  1 sibling, 0 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 20:32 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

On Thu, 23 Sep 2010, Alan Cox wrote:

> > Please do not introduce useless additional layers for clock sync. Load
> > these ptp clocks like the other regular clock modules and make them sync
> > system time like any other clock.
>
> I don't think you understand PTP. PTP has masters, a system can need to
> be honouring multiple conflicting masters at once.

The upshot of it all has to be some synchronized notion of time regardless
of how many other things are going on under the hood. And the spec here
suggests a hardware able to generate periodic accurate events that can be
used to sync system time.

> > Really guys: I want a PTP solution! Now! And not some idiotic additional
> > kernel layers that just pass bits around because its so much fun and
> > screws up clock accurary in due to the latency noise introduced while
> > having so much fun with the bits.
>
> There are some interesting complications in putting a PTP sync
> interface in kernel.

If the PTP logic internally has to juggle multiple clocks then that is a
complication for the driver ok. In any case the driver ultimately has to
provide *one* source of time for the system to sync to.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
                   ` (9 preceding siblings ...)
  2010-09-23 19:38 ` john stultz
@ 2010-09-23 20:36 ` Alan Cox
  2010-09-23 20:49   ` john stultz
  2010-09-24 13:14   ` Richard Cochran
  10 siblings, 2 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-23 20:36 UTC (permalink / raw)
  To: Richard Cochran
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, David Miller,
	linux-arm-kernel, Krzysztof Halasa

>    So as far as the POSIX standard is concerned, offering a clock id
>    to represent the PHC would be acceptable.

But completely useless as you may have more than one entirely different
time managed by PTP and in which you are not master but must work with
the timebases provided.


>     /sys/class/timesource/<name>/id
>     /sys/class/ptp/ptp_clock_X/id
> 
>     Note: I am not too sure that this is exactly what people imagined,
>           but it is my best understanding so far. I gleaned two
>           different ideas about where to offer the clock id. In order
>           to keep just one way, I will be happy to remove the less
>           popular one.

I see no fix proposed for the race condition I pointed out. This doesn't
work.


>    If the Linux system time is synchronized to the PHC via the PPS

To which PHC we can have several

>    + Intel IXP465
>      - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
>      - Target Time (optional interrupt)

And about 40 already supported by char driver interface clocks and rtcs
in the kernel...


I'd say the inability to have multiple clocks and the race condition
because of the clockid stuff leaves the proposal dead in the water.

It also ignores the existing APIs we have floating around attached to
devices.

You need to make one small important change. You need to take the POSIX
crap about enumerating things out and shoot it, bury it at a crossroads
and sprinkle holy water on it.

Drop the clockid_t and swap it for a file handle like a proper Unix or
Linux interface. The rest is much the same

	fd = open /sys/class/timesource/[whatever]

	various queries you may want to do to check the name etc

	fclock_adjtime(fd, ...)
	

The posix interface is fundamentally flawed. It only works for staticly
enumerable objects. Unix avoided that forty years ago by making the
identifier a handle which immediately cures all your object lifetime
problems in one swoop.

Namespace -> file handle translations are dynamic, but once you have it
open you hold on to the same object, which means you can check what you
have.



Alan

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 19:17   ` Christoph Lameter
@ 2010-09-23 20:43     ` Alan Cox
  2010-09-23 20:32       ` Christoph Lameter
  2010-09-23 21:26       ` Christian Riesch
  2010-09-24  8:49     ` Richard Cochran
  1 sibling, 2 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-23 20:43 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

> Please do not introduce useless additional layers for clock sync. Load
> these ptp clocks like the other regular clock modules and make them sync
> system time like any other clock.

I don't think you understand PTP. PTP has masters, a system can need to
be honouring multiple conflicting masters at once.

> Really guys: I want a PTP solution! Now! And not some idiotic additional
> kernel layers that just pass bits around because its so much fun and
> screws up clock accurary in due to the latency noise introduced while
> having so much fun with the bits.

There are some interesting complications in putting a PTP sync
interface in kernel.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:36 ` Alan Cox
@ 2010-09-23 20:49   ` john stultz
  2010-09-23 21:30     ` Alan Cox
  2010-09-24 13:14   ` Richard Cochran
  1 sibling, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 20:49 UTC (permalink / raw)
  To: Alan Cox
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

On Thu, 2010-09-23 at 21:36 +0100, Alan Cox wrote:
> >    So as far as the POSIX standard is concerned, offering a clock id
> >    to represent the PHC would be acceptable.
> 
> But completely useless as you may have more than one entirely different
> time managed by PTP and in which you are not master but must work with
> the timebases provided.

I don't see how this is a problem, as it exposes the multiple hardware
clocks via different posix clock ids. So in the boundary clock case, you
can configure which side is the client and which side is the master in a
config file and the PTPd will appropriately steer them individually.


> 
> >     /sys/class/timesource/<name>/id
> >     /sys/class/ptp/ptp_clock_X/id
> > 
> >     Note: I am not too sure that this is exactly what people imagined,
> >           but it is my best understanding so far. I gleaned two
> >           different ideas about where to offer the clock id. In order
> >           to keep just one way, I will be happy to remove the less
> >           popular one.
> 
> I see no fix proposed for the race condition I pointed out. This doesn't
> work.

So, if I recall this was: "How do you keep the module from unloading
while its being used?" 

There may need to be proper locking for unregistering the posix clock_id
on module unload, but I don't think we need a use-count to prevent the
module from being unloaded.

My question would be: How do we handle a USB network device ($14.99 now
with PTP!) being unplugged? We can't say "Sorry! That's in use!". So we
note the hardware is gone, and return the proper error code.

Or am I missing something else?


> >    If the Linux system time is synchronized to the PHC via the PPS
> 
> To which PHC we can have several
> 
> >    + Intel IXP465
> >      - Auxiliary Slave/Master Mode Snapshot (optional interrupt)
> >      - Target Time (optional interrupt)
> 
> And about 40 already supported by char driver interface clocks and rtcs
> in the kernel...

And those char driver interfaces are all subtly different.

I actually recently submitted an RFC to expose the RTC devices via the
posix clock/timer interface, because working with the RTC hardware
device directly is terrible for managing alarm interrupts. 

For instance, you easily run into the case where your TV recording
application programs an alarm to record your favorite show at 8pm. Then
your backup script programs an alarm to wake up at 2am to do your
nightly backups. Your box suspends and the next morning, you're missing
your favorite show!


> I'd say the inability to have multiple clocks and the race condition
> because of the clockid stuff leaves the proposal dead in the water.
> 
> It also ignores the existing APIs we have floating around attached to
> devices.
> 
> You need to make one small important change. You need to take the POSIX
> crap about enumerating things out and shoot it, bury it at a crossroads
> and sprinkle holy water on it.

We agree the list-by-name stuff isn't the way to go. :)


> Drop the clockid_t and swap it for a file handle like a proper Unix or
> Linux interface. The rest is much the same
> 
> 	fd = open /sys/class/timesource/[whatever]
> 
> 	various queries you may want to do to check the name etc
> 
> 	fclock_adjtime(fd, ...)
> 	
> 
> The posix interface is fundamentally flawed. It only works for staticly
> enumerable objects. Unix avoided that forty years ago by making the
> identifier a handle which immediately cures all your object lifetime
> problems in one swoop.

So, I don't really see how that's so different from what is being
proposed. The clock_id is dynamically assigned per registered clock, and
exposed via the sysfs interface from ptp hardware entry.

The only difference is the open/close reference counting, which I don't
think is necessary here (since we can't always keep the hardware from
going away).

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:28       ` john stultz
@ 2010-09-23 20:49         ` Christoph Lameter
  2010-09-23 21:34           ` Alan Cox
  2010-09-23 21:42           ` john stultz
  0 siblings, 2 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-23 20:49 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 23 Sep 2010, john stultz wrote:

> > The HPET or pit timesource are also quite slow these days. You only need
> > access periodically to essentially tune the TSC ratio.
>
> If we're using the TSC, then we're not using the PTP clock as you
> suggest. Further the HPET and PIT aren't used to steer the system time
> when we are using the TSC as a clocksource. Its only used to calibrate
> the initial constant freq used by the timekeeping code (and if its
> non-constant, we throw it out).

There is no other scalable time source available for fast timer access
than the time stamp counter in the cpu. Other time source require
memory accesses which is inherently slower.

An accurate other time source is used to adjust this clock. NTP does that
via the clock interfaces from user space which has its problems with
accuracy. PTP can provide the network synced time access
that would a more accurate calibration of the time.

> 2) The way PTP clocks are steered to sync with network time causes their
> hardware freq to actually change. Since these adjustments are done on
> the hardware clock level, and not on the system time level, the
> adjustments to sync the system time/freq would then be made incorrect by
> PTP hardware adjustments.

Right. So use these as a way to fine tune the TSC clock (and thereby the
system time).

> 3) Further, the PTP hardware counter can be simply set to a new offset
> to put it in line with the network time. This could cause trouble with
> timekeeping much like unsynced TSCs do.

You can do the same for system time.

> Now, what you seem to be suggesting is to use the TSC (or whatever
> clocksource the system time is using) but to steer the system time using
> the PTP clock. This is actually what is being proposed, however, the
> steering is done in userland. This is due to the fact that there are two
> components to the steering, 1) adjusting the PTP clock hardware to
> network time and 2) adjusting the system time to the PTP hardware. By
> exposing the PTP clock to userland via the posix clocks interface, we
> allow this to easily be done.

Userland code would introduce latencies that would make sub microsecond
time sync very difficult.

> > We can switch underlying clocks for system time already. We can adapt to a
> > different hw frequency.
>
> Actually no. The timekeeping code requires a fixed freq counter. Dealing
> with hardware freq changes is difficult, because error is introduced by
> the latency between when the freq changes and when the timekeeping code
> is notified of it. So the system treats the hardware counters as fixed
> freq. Now, hardware does vary freq ever so slightly as thermal
> conditions change, but this is addressed in userland and corrected via
> adjtimex.

Acadmic hair splitting? I have repeatedly switched between different
clocks on various systems. So its difficult but we do it?

> Unnecessary layers? Where? This approach has less in-kernel layers, as
> it exposes the PTP clock to userland, instead of trying to layer things
> on top of it and stretching the system time abstraction to cover it.

You dont need the user APIs if you directly use the PTP time source to
steer the system clock. In fact I think you have to do it in kernel space
since user space latencies will degrade accuracy otherwise.

> I've argued through the approach trying to keep it all internal to the
> kernel, but to do so would be anything but trivial. Further, there's the
> case of master-clocks, where the PTP hardware must be synced to system
> time, instead of the other way around. And then there's the case of
> boundary-clocks, which may have multiple PTP hardware clocks that have
> to be synced.

Ok maybe we need some sort of control interface to manage the clock like
the others have.

> I think exposing this through the posix clock interface is really the
> best approach. Its not a static clockid, so its not something most apps
> will ever have to deal with, but it allows the few apps that really need
> to have access to the PTP clock hardware can do so in a clean way.

It implies clock tuning in userspace for a potential sub microsecond
accurate clock. The clock accuracy will be limited by user space
latencies and noise. You wont be able to discipline the system clock
accurately.

The posix clocks today assumes one notion of real "time" in the kernel.
All clocks increase in lockstep (aside from offset updates). This approach
here result in multiple notions of "time" increasing at various speeds.
And it implies that someone is user space is trying to tinker around with
extremely low latencies using system call APIs that take much longer than
these intervals to process the data.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 20:43     ` Alan Cox
  2010-09-23 20:32       ` Christoph Lameter
@ 2010-09-23 21:26       ` Christian Riesch
  2010-09-24 11:52         ` Alan Cox
  1 sibling, 1 reply; 53+ messages in thread
From: Christian Riesch @ 2010-09-23 21:26 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	Thomas Gleixner, netdev, Christoph Lameter, linuxppc-dev,
	Richard Cochran, linux-arm-kernel, Krzysztof Halasa

Alan Cox wrote:
>> Please do not introduce useless additional layers for clock sync. Load
>> these ptp clocks like the other regular clock modules and make them sync
>> system time like any other clock.
> 
> I don't think you understand PTP. PTP has masters, a system can need to
> be honouring multiple conflicting masters at once.

AFAIK the master's should not be conflicting. The Best Master Clock 
algorithm (BMC) defined in IEEE1588 selects the best master clock. This 
clock distributes its notion of time on the network while the other 
masters, that is the other clocks/nodes that are configured to 
potentially become a master, keep quiet. So usually we will only have 
one source of time (the master clock selected by the BMC) and we will 
steer our single PHC (PTP hardware clock) to follow this master (Of 
course there may be use-cases that require more than one PTP clock, 
e.g., for research purposes).

However, if the clock selected by the BMC is switched off, loses its 
network connection..., the second best clock is selected by the BMC and 
becomes master. This clock may be less accurate and thus our slave clock 
has to switch from one notion of time to another. Is that the conflict 
you mentioned?

Christian

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:49   ` john stultz
@ 2010-09-23 21:30     ` Alan Cox
  2010-09-23 22:03       ` john stultz
  0 siblings, 1 reply; 53+ messages in thread
From: Alan Cox @ 2010-09-23 21:30 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

O> I don't see how this is a problem, as it exposes the multiple hardware
> clocks via different posix clock ids. So in the boundary clock case, you
> can configure which side is the client and which side is the master in a
> config file and the PTPd will appropriately steer them individually.

They may all be slaves - that means you can't treat them as part of
system time.
> 

> on module unload, but I don't think we need a use-count to prevent the
> module from being unloaded.
> 
> My question would be: How do we handle a USB network device ($14.99 now
> with PTP!) being unplugged? We can't say "Sorry! That's in use!". So we
> note the hardware is gone, and return the proper error code.
> 
> Or am I missing something else?

Open list
Oh number 31 appears to be the device I want
Close list

	USB unplugged
	Random other device plugged

clock_op(31, ....)

Oh bugger I've just reprogrammed the wrong time source.

We don't have stop the device being removed, instead of a disaster you get

	clock_op(fd, blah)
	-ENODEV

which btw is how just about everything else USB works when you pull the
hardware.

> > And about 40 already supported by char driver interface clocks and rtcs
> > in the kernel...
> 
> And those char driver interfaces are all subtly different.
> 
> I actually recently submitted an RFC to expose the RTC devices via the
> posix clock/timer interface, because working with the RTC hardware
> device directly is terrible for managing alarm interrupts. 

Given that driver interfaces are sane and posix clock/timer interfaces
have totally broken enumeration maybe you have it backwards. But if you
follow through to my proposal maybe there is a saner answer still
 
> For instance, you easily run into the case where your TV recording
> application programs an alarm to record your favorite show at 8pm. Then
> your backup script programs an alarm to wake up at 2am to do your
> nightly backups. Your box suspends and the next morning, you're missing
> your favorite show!

Poor resource management, and yes I'd agree you want a sensible interface.


> > Drop the clockid_t and swap it for a file handle like a proper Unix or
> > Linux interface. The rest is much the same
> > 
> > 	fd = open /sys/class/timesource/[whatever]
> > 
> > 	various queries you may want to do to check the name etc
> > 
> > 	fclock_adjtime(fd, ...)
> > 	
> > 
> > The posix interface is fundamentally flawed. It only works for staticly
> > enumerable objects. Unix avoided that forty years ago by making the
> > identifier a handle which immediately cures all your object lifetime
> > problems in one swoop.
> 
> So, I don't really see how that's so different from what is being
> proposed. The clock_id is dynamically assigned per registered clock, and
> exposed via the sysfs interface from ptp hardware entry.
> 
> The only difference is the open/close reference counting, which I don't
> think is necessary here (since we can't always keep the hardware from
> going away).

It is absolutely neccessary in order that you can be sure that two calls
actually relate to the *same* device. It's as fundamental as the
difference betweeh chmod and fchmod although with the added ugliness of
some random numeric identifier stuck in the middle.

It also btw makes it much easier to fix up the existing random collection
of /dev/rtc devices - because you can open them and issue fclock_adjtime
if we are careful how we do it and it makes sense.

Alan

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 21:34           ` Alan Cox
@ 2010-09-23 21:34             ` Christian Riesch
  2010-09-27 15:37               ` Christoph Lameter
  0 siblings, 1 reply; 53+ messages in thread
From: Christian Riesch @ 2010-09-23 21:34 UTC (permalink / raw)
  To: Alan Cox
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linux-api, Christoph Lameter, linuxppc-dev,
	Richard Cochran, linux-arm-kernel, Krzysztof Halasa

Alan Cox wrote:
>> It implies clock tuning in userspace for a potential sub microsecond
>> accurate clock. The clock accuracy will be limited by user space
>> latencies and noise. You wont be able to discipline the system clock
>> accurately.
> 
> Noise matters, latency doesn't. 

Well put! That's why we need hardware support for PTP timestamping to 
reduce the noise, but get along well with the clock servo that is 
steering the PHC in user space.

Christian

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:49         ` Christoph Lameter
@ 2010-09-23 21:34           ` Alan Cox
  2010-09-23 21:34             ` Christian Riesch
  2010-09-23 21:42           ` john stultz
  1 sibling, 1 reply; 53+ messages in thread
From: Alan Cox @ 2010-09-23 21:34 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	linux-api, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

> There is no other scalable time source available for fast timer access
> than the time stamp counter in the cpu. Other time source require
> memory accesses which is inherently slower.

On what hardware ?

> An accurate other time source is used to adjust this clock. NTP does that
> via the clock interfaces from user space which has its problems with
> accuracy. PTP can provide the network synced time access
> that would a more accurate calibration of the time.

Accuracy does not require speed of access. Accuracy requires predictible
latency of access.

> Userland code would introduce latencies that would make sub microsecond
> time sync very difficult.

You can take a multiple micro-second I/O stall or SMI trap on a PC so you
already lost the battle on the platform you seem to be discussing.

> You dont need the user APIs if you directly use the PTP time source to
> steer the system clock. In fact I think you have to do it in kernel space
> since user space latencies will degrade accuracy otherwise.

PTP is not a 'time source' it is one or more source of time. The
distinction is rather important.

> It implies clock tuning in userspace for a potential sub microsecond
> accurate clock. The clock accuracy will be limited by user space
> latencies and noise. You wont be able to discipline the system clock
> accurately.

Noise matters, latency doesn't. And the kernel is getting more and more
real time support all the time.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:49         ` Christoph Lameter
  2010-09-23 21:34           ` Alan Cox
@ 2010-09-23 21:42           ` john stultz
  2010-09-27 15:52             ` Christoph Lameter
  1 sibling, 1 reply; 53+ messages in thread
From: john stultz @ 2010-09-23 21:42 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 15:49 -0500, Christoph Lameter wrote:
> On Thu, 23 Sep 2010, john stultz wrote:
> 
> > > The HPET or pit timesource are also quite slow these days. You only need
> > > access periodically to essentially tune the TSC ratio.
> >
> > If we're using the TSC, then we're not using the PTP clock as you
> > suggest. Further the HPET and PIT aren't used to steer the system time
> > when we are using the TSC as a clocksource. Its only used to calibrate
> > the initial constant freq used by the timekeeping code (and if its
> > non-constant, we throw it out).
> 
> There is no other scalable time source available for fast timer access
> than the time stamp counter in the cpu. Other time source require
> memory accesses which is inherently slower.

Right, but no one likes the HPET or ACPI PM for a clocksource, its just
the TSC isn't usable in some cases, so they have to be used.

We don't want to force folks to decide between closely sycned time and
fast time reads. So that is part of the reason why PTP as a clocksource
isn't a good idea.

> An accurate other time source is used to adjust this clock. NTP does that
> via the clock interfaces from user space which has its problems with
> accuracy. PTP can provide the network synced time access
> that would a more accurate calibration of the time.

Calibration isn't whats needed here (it is an issue, but a separate one
- and I've got some patches if you're interested!) as its a one-time
source of error and can be corrected by ntp today without trouble.
Adjustments to the system time is something that has to be done
continuously to handle for variable thermal drift over time.

> > 2) The way PTP clocks are steered to sync with network time causes their
> > hardware freq to actually change. Since these adjustments are done on
> > the hardware clock level, and not on the system time level, the
> > adjustments to sync the system time/freq would then be made incorrect by
> > PTP hardware adjustments.
> 
> Right. So use these as a way to fine tune the TSC clock (and thereby the
> system time).

So you're then not suggesting to "use the PTP as a clocksource".

Using the PTP hardware to adjust the system time freq is exactly whats
being proposed.

> > 3) Further, the PTP hardware counter can be simply set to a new offset
> > to put it in line with the network time. This could cause trouble with
> > timekeeping much like unsynced TSCs do.
> 
> You can do the same for system time.

Settimeofday does allow CLOCK_REALTIME to jump, but the CLOCK_MONOTONIC
time cannot jump around. Having a clocksource that is non-monotonic
would break this.

> > Now, what you seem to be suggesting is to use the TSC (or whatever
> > clocksource the system time is using) but to steer the system time using
> > the PTP clock. This is actually what is being proposed, however, the
> > steering is done in userland. This is due to the fact that there are two
> > components to the steering, 1) adjusting the PTP clock hardware to
> > network time and 2) adjusting the system time to the PTP hardware. By
> > exposing the PTP clock to userland via the posix clocks interface, we
> > allow this to easily be done.
> 
> Userland code would introduce latencies that would make sub microsecond
> time sync very difficult.

The design actually avoids most userland induced latency.

1) On the PTP hardware syncing point, the reference packet gets
timestamped with the PTP hardware time on arrival. This allows the
offset calculation to be done in userland without introducing latency.

2) On the system syncing side, the proposal for the PPS interrupt allows
the PTP hardware to trigger an interrupt on the second boundary that
would take a timestamp of the system time. Then the pps interface allows
for the timestamp to be read from userland allowing the offset to be
calculated without introducing additional latency.


> > > We can switch underlying clocks for system time already. We can adapt to a
> > > different hw frequency.
> >
> > Actually no. The timekeeping code requires a fixed freq counter. Dealing
> > with hardware freq changes is difficult, because error is introduced by
> > the latency between when the freq changes and when the timekeeping code
> > is notified of it. So the system treats the hardware counters as fixed
> > freq. Now, hardware does vary freq ever so slightly as thermal
> > conditions change, but this is addressed in userland and corrected via
> > adjtimex.
> 
> Acadmic hair splitting? I have repeatedly switched between different
> clocks on various systems. So its difficult but we do it?

Sure, we handle the fairly-rare case of switching clocksources. And that
introduces a bit of error each time. But one doesn't expect to be
switching clock-sources every second and still keep synced time.


> > Unnecessary layers? Where? This approach has less in-kernel layers, as
> > it exposes the PTP clock to userland, instead of trying to layer things
> > on top of it and stretching the system time abstraction to cover it.
> 
> You dont need the user APIs if you directly use the PTP time source to
> steer the system clock. In fact I think you have to do it in kernel space
> since user space latencies will degrade accuracy otherwise.

Via the PPS interface, this can be done easily without large latency.

Additionally, even just in userland, it would be easy to bracket two
reads of the system time around one read of the PTP clock to bound any
userland latency fairly well. It may not be as good as the PPS interface
(although that depends on the interrupt latency), but if the accesses
are all local, it probably could get fairly close.

> > I've argued through the approach trying to keep it all internal to the
> > kernel, but to do so would be anything but trivial. Further, there's the
> > case of master-clocks, where the PTP hardware must be synced to system
> > time, instead of the other way around. And then there's the case of
> > boundary-clocks, which may have multiple PTP hardware clocks that have
> > to be synced.
> 
> Ok maybe we need some sort of control interface to manage the clock like
> the others have.

That's what the clock_adjtime call provides.


> > I think exposing this through the posix clock interface is really the
> > best approach. Its not a static clockid, so its not something most apps
> > will ever have to deal with, but it allows the few apps that really need
> > to have access to the PTP clock hardware can do so in a clean way.
> 
> It implies clock tuning in userspace for a potential sub microsecond
> accurate clock. The clock accuracy will be limited by user space
> latencies and noise. You wont be able to discipline the system clock
> accurately.

I think you'll find that the userspace latency issue is fairly well
addressed by the existing design.


> The posix clocks today assumes one notion of real "time" in the kernel.
> All clocks increase in lockstep (aside from offset updates).

Not true. The cputime clockids do not increment at the same rate (as the
apps don't always run). Further CLOCK_MONOTONIC_RAW provides a non-freq
corrected view of CLOCK_MONOTONIC, so it increments at a slightly
different rate.

>  This approach
> here result in multiple notions of "time" increasing at various speeds.

Yes. I found this initially to be distasteful as well. But the more I
realized its just a reality of the hardware, and that as long as its not
advertised along side of CLOCK_REALTIME and CLOCK_MONOTONIC (the ptp
clock ids are dynamic and somewhat hidden in the sysfs tree), so
userland developers don't get confused, its not so bad. 

Especially since driver writers will just expose the same stuff via a
chardev ioctl in a less standard way and its likely no one will notice.
Re-using the fairly nice (Alan of course disagrees :) posix interface
seems at least a little better for application developers who actually
have to use the hardware.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 21:30     ` Alan Cox
@ 2010-09-23 22:03       ` john stultz
  0 siblings, 0 replies; 53+ messages in thread
From: john stultz @ 2010-09-23 22:03 UTC (permalink / raw)
  To: Alan Cox
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

On Thu, 2010-09-23 at 22:30 +0100, Alan Cox wrote:
> O> I don't see how this is a problem, as it exposes the multiple hardware
> > clocks via different posix clock ids. So in the boundary clock case, you
> > can configure which side is the client and which side is the master in a
> > config file and the PTPd will appropriately steer them individually.
> 
> They may all be slaves - that means you can't treat them as part of
> system time.

Sure, and that's something one would configure. So I'm not sure I see
how exposing the different hardware bits via a clock_id is problematic.
They're just clocks that are being exposed. The steering of system time
to PTP or PTP to system time  (or just PTP to other PTP clocks).


> > on module unload, but I don't think we need a use-count to prevent the
> > module from being unloaded.
> > 
> > My question would be: How do we handle a USB network device ($14.99 now
> > with PTP!) being unplugged? We can't say "Sorry! That's in use!". So we
> > note the hardware is gone, and return the proper error code.
> > 
> > Or am I missing something else?
> 
> Open list
> Oh number 31 appears to be the device I want
> Close list
> 
> 	USB unplugged
> 	Random other device plugged
> 
> clock_op(31, ....)
> 
> Oh bugger I've just reprogrammed the wrong time source.

Ok. So its just the issue of clock_id reuse. I was confusing it with
some sort of module use counting issue.  And yea, I can see how it might
be  easier to re-use the file descriptor then re-implementing the reuse
logic in the posix-clock registration.


> We don't have stop the device being removed, instead of a disaster you get
> 
> 	clock_op(fd, blah)
> 	-ENODEV
> 
> which btw is how just about everything else USB works when you pull the
> hardware.

Right, which was what I was thinking as well, but assuming we didn't
re-use clockids quickly.
 
> > So, I don't really see how that's so different from what is being
> > proposed. The clock_id is dynamically assigned per registered clock, and
> > exposed via the sysfs interface from ptp hardware entry.
> > 
> > The only difference is the open/close reference counting, which I don't
> > think is necessary here (since we can't always keep the hardware from
> > going away).
> 
> It is absolutely neccessary in order that you can be sure that two calls
> actually relate to the *same* device. It's as fundamental as the
> difference betweeh chmod and fchmod although with the added ugliness of
> some random numeric identifier stuck in the middle.
> 
> It also btw makes it much easier to fix up the existing random collection
> of /dev/rtc devices - because you can open them and issue fclock_adjtime
> if we are careful how we do it and it makes sense.

Wait, you're suggesting we add new fclock_* calls that duplicate the
posix interface? That doesn't sound great to me.

What did you think of Kyle Moffett's suggestion of utilizing the fd to
map to the clock_id which could then be used by the posix clocks
interface?

Although I'm still not sure if it wouldn't be so hard to just simply
increment the id on each registration and index to a clock through a
reasonably small hash table. I suspect that would solve the
enumeration/reuse issue without much trouble (but again, I'm open to
being corrected if I'm missing something larger).

But yes, in summary, this is an issue to be addressed one way or
another.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
  2010-09-23 19:48   ` john stultz
@ 2010-09-23 22:03   ` Benjamin Herrenschmidt
  2010-09-23 22:12     ` Thomas Gleixner
  2010-09-24  7:55     ` Richard Cochran
  1 sibling, 2 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2010-09-23 22:03 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Peter Zijlstra, John Stultz, devicetree-discuss, linuxppc-dev,
	linux-kernel, David Miller, netdev, linux-api, Thomas Gleixner,
	Rodolfo Giometti, Christoph Lameter, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 2010-09-23 at 19:31 +0200, Richard Cochran wrote:
> A new syscall is introduced that allows tuning of a POSIX clock. The
> syscall is implemented for four architectures: arm, blackfin, powerpc,
> and x86.
> 
> The new syscall, clock_adjtime, takes two parameters, the clock ID,
> and a pointer to a struct timex. The semantics of the timex struct
> have been expanded by one additional mode flag, which allows an
> absolute offset correction. When specificied, the clock offset is
> immediately corrected by adding the given time value to the current
> time value.

Any reason why you CC'ed device-tree discuss ?

This list is getting way too much unrelated stuff, which I find
annoying, it would be nice if we were all a bit more careful here with
our CC lists.

Cheers,
Ben.

> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
> ---
>  arch/arm/include/asm/unistd.h      |    1 +
>  arch/arm/kernel/calls.S            |    1 +
>  arch/blackfin/include/asm/unistd.h |    3 +-
>  arch/blackfin/mach-common/entry.S  |    1 +
>  arch/powerpc/include/asm/systbl.h  |    1 +
>  arch/powerpc/include/asm/unistd.h  |    3 +-
>  arch/x86/ia32/ia32entry.S          |    1 +
>  arch/x86/include/asm/unistd_32.h   |    3 +-
>  arch/x86/include/asm/unistd_64.h   |    2 +
>  arch/x86/kernel/syscall_table_32.S |    1 +
>  include/linux/posix-timers.h       |    3 +
>  include/linux/syscalls.h           |    2 +
>  include/linux/timex.h              |    3 +-
>  kernel/compat.c                    |  136 +++++++++++++++++++++++-------------
>  kernel/posix-cpu-timers.c          |    4 +
>  kernel/posix-timers.c              |   17 +++++
>  16 files changed, 130 insertions(+), 52 deletions(-)
> 
> diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
> index c891eb7..f58d881 100644
> --- a/arch/arm/include/asm/unistd.h
> +++ b/arch/arm/include/asm/unistd.h
> @@ -396,6 +396,7 @@
>  #define __NR_fanotify_init		(__NR_SYSCALL_BASE+367)
>  #define __NR_fanotify_mark		(__NR_SYSCALL_BASE+368)
>  #define __NR_prlimit64			(__NR_SYSCALL_BASE+369)
> +#define __NR_clock_adjtime		(__NR_SYSCALL_BASE+370)
>  
>  /*
>   * The following SWIs are ARM private.
> diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
> index 5c26ecc..430de4c 100644
> --- a/arch/arm/kernel/calls.S
> +++ b/arch/arm/kernel/calls.S
> @@ -379,6 +379,7 @@
>  		CALL(sys_fanotify_init)
>  		CALL(sys_fanotify_mark)
>  		CALL(sys_prlimit64)
> +/* 370 */	CALL(sys_clock_adjtime)
>  #ifndef syscalls_counted
>  .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
>  #define syscalls_counted
> diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h
> index 14fcd25..79ad99b 100644
> --- a/arch/blackfin/include/asm/unistd.h
> +++ b/arch/blackfin/include/asm/unistd.h
> @@ -392,8 +392,9 @@
>  #define __NR_fanotify_init	371
>  #define __NR_fanotify_mark	372
>  #define __NR_prlimit64		373
> +#define __NR_clock_adjtime	374
>  
> -#define __NR_syscall		374
> +#define __NR_syscall		375
>  #define NR_syscalls		__NR_syscall
>  
>  /* Old optional stuff no one actually uses */
> diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
> index af1bffa..ee68730 100644
> --- a/arch/blackfin/mach-common/entry.S
> +++ b/arch/blackfin/mach-common/entry.S
> @@ -1631,6 +1631,7 @@ ENTRY(_sys_call_table)
>  	.long _sys_fanotify_init
>  	.long _sys_fanotify_mark
>  	.long _sys_prlimit64
> +	.long _sys_clock_adjtime
>  
>  	.rept NR_syscalls-(.-_sys_call_table)/4
>  	.long _sys_ni_syscall
> diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
> index 3d21266..2485d8f 100644
> --- a/arch/powerpc/include/asm/systbl.h
> +++ b/arch/powerpc/include/asm/systbl.h
> @@ -329,3 +329,4 @@ COMPAT_SYS(rt_tgsigqueueinfo)
>  SYSCALL(fanotify_init)
>  COMPAT_SYS(fanotify_mark)
>  SYSCALL_SPU(prlimit64)
> +COMPAT_SYS_SPU(clock_adjtime)
> diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
> index 597e6f9..85d5067 100644
> --- a/arch/powerpc/include/asm/unistd.h
> +++ b/arch/powerpc/include/asm/unistd.h
> @@ -348,10 +348,11 @@
>  #define __NR_fanotify_init	323
>  #define __NR_fanotify_mark	324
>  #define __NR_prlimit64		325
> +#define __NR_clock_adjtime	326
>  
>  #ifdef __KERNEL__
>  
> -#define __NR_syscalls		326
> +#define __NR_syscalls		327
>  
>  #define __NR__exit __NR_exit
>  #define NR_syscalls	__NR_syscalls
> diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
> index 518bb99..0ed7896 100644
> --- a/arch/x86/ia32/ia32entry.S
> +++ b/arch/x86/ia32/ia32entry.S
> @@ -851,4 +851,5 @@ ia32_sys_call_table:
>  	.quad sys_fanotify_init
>  	.quad sys32_fanotify_mark
>  	.quad sys_prlimit64		/* 340 */
> +	.quad compat_sys_clock_adjtime
>  ia32_syscall_end:
> diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h
> index b766a5e..b6f73f1 100644
> --- a/arch/x86/include/asm/unistd_32.h
> +++ b/arch/x86/include/asm/unistd_32.h
> @@ -346,10 +346,11 @@
>  #define __NR_fanotify_init	338
>  #define __NR_fanotify_mark	339
>  #define __NR_prlimit64		340
> +#define __NR_clock_adjtime	341
>  
>  #ifdef __KERNEL__
>  
> -#define NR_syscalls 341
> +#define NR_syscalls 342
>  
>  #define __ARCH_WANT_IPC_PARSE_VERSION
>  #define __ARCH_WANT_OLD_READDIR
> diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h
> index 363e9b8..5ee3085 100644
> --- a/arch/x86/include/asm/unistd_64.h
> +++ b/arch/x86/include/asm/unistd_64.h
> @@ -669,6 +669,8 @@ __SYSCALL(__NR_fanotify_init, sys_fanotify_init)
>  __SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
>  #define __NR_prlimit64				302
>  __SYSCALL(__NR_prlimit64, sys_prlimit64)
> +#define __NR_clock_adjtime			303
> +__SYSCALL(__NR_clock_adjtime, sys_clock_adjtime)
>  
>  #ifndef __NO_STUBS
>  #define __ARCH_WANT_OLD_READDIR
> diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
> index b35786d..68c7b9a 100644
> --- a/arch/x86/kernel/syscall_table_32.S
> +++ b/arch/x86/kernel/syscall_table_32.S
> @@ -340,3 +340,4 @@ ENTRY(sys_call_table)
>  	.long sys_fanotify_init
>  	.long sys_fanotify_mark
>  	.long sys_prlimit64		/* 340 */
> +	.long sys_clock_adjtime
> diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
> index 3e23844..abf61cc 100644
> --- a/include/linux/posix-timers.h
> +++ b/include/linux/posix-timers.h
> @@ -4,6 +4,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/list.h>
>  #include <linux/sched.h>
> +#include <linux/timex.h>
>  
>  union cpu_time_count {
>  	cputime_t cpu;
> @@ -71,6 +72,7 @@ struct k_clock {
>  	int (*clock_getres) (const clockid_t which_clock, struct timespec *tp);
>  	int (*clock_set) (const clockid_t which_clock, struct timespec * tp);
>  	int (*clock_get) (const clockid_t which_clock, struct timespec * tp);
> +	int (*clock_adj) (const clockid_t which_clock, struct timex *tx);
>  	int (*timer_create) (struct k_itimer *timer);
>  	int (*nsleep) (const clockid_t which_clock, int flags,
>  		       struct timespec *, struct timespec __user *);
> @@ -97,6 +99,7 @@ int posix_timer_event(struct k_itimer *timr, int si_private);
>  int posix_cpu_clock_getres(const clockid_t which_clock, struct timespec *ts);
>  int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *ts);
>  int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *ts);
> +int posix_cpu_clock_adj(const clockid_t which_clock, struct timex *tx);
>  int posix_cpu_timer_create(struct k_itimer *timer);
>  int posix_cpu_nsleep(const clockid_t which_clock, int flags,
>  		     struct timespec *rqtp, struct timespec __user *rmtp);
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index e6319d1..0b24775 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -313,6 +313,8 @@ asmlinkage long sys_clock_settime(clockid_t which_clock,
>  				const struct timespec __user *tp);
>  asmlinkage long sys_clock_gettime(clockid_t which_clock,
>  				struct timespec __user *tp);
> +asmlinkage long sys_clock_adjtime(clockid_t which_clock,
> +				struct timex __user *tx);
>  asmlinkage long sys_clock_getres(clockid_t which_clock,
>  				struct timespec __user *tp);
>  asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags,
> diff --git a/include/linux/timex.h b/include/linux/timex.h
> index 32d852f..82d4b24 100644
> --- a/include/linux/timex.h
> +++ b/include/linux/timex.h
> @@ -73,7 +73,7 @@ struct timex {
>  	long tolerance;		/* clock frequency tolerance (ppm)
>  				 * (read only)
>  				 */
> -	struct timeval time;	/* (read only) */
> +	struct timeval time;	/* (read only, except for ADJ_SETOFFSET) */
>  	long tick;		/* (modified) usecs between clock ticks */
>  
>  	long ppsfreq;           /* pps frequency (scaled ppm) (ro) */
> @@ -101,6 +101,7 @@ struct timex {
>  #define ADJ_ESTERROR		0x0008	/* estimated time error */
>  #define ADJ_STATUS		0x0010	/* clock status */
>  #define ADJ_TIMECONST		0x0020	/* pll time constant */
> +#define ADJ_SETOFFSET		0x0040  /* add 'time' to current time */
>  #define ADJ_TAI			0x0080	/* set TAI offset */
>  #define ADJ_MICRO		0x1000	/* select microsecond resolution */
>  #define ADJ_NANO		0x2000	/* select nanosecond resolution */
> diff --git a/kernel/compat.c b/kernel/compat.c
> index c9e2ec0..38b1d2c 100644
> --- a/kernel/compat.c
> +++ b/kernel/compat.c
> @@ -52,6 +52,64 @@ static int compat_put_timeval(struct compat_timeval __user *o,
>  		put_user(i->tv_usec, &o->tv_usec)) ? -EFAULT : 0;
>  }
>  
> +static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp)
> +{
> +	memset(txc, 0, sizeof(struct timex));
> +
> +	if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
> +			__get_user(txc->modes, &utp->modes) ||
> +			__get_user(txc->offset, &utp->offset) ||
> +			__get_user(txc->freq, &utp->freq) ||
> +			__get_user(txc->maxerror, &utp->maxerror) ||
> +			__get_user(txc->esterror, &utp->esterror) ||
> +			__get_user(txc->status, &utp->status) ||
> +			__get_user(txc->constant, &utp->constant) ||
> +			__get_user(txc->precision, &utp->precision) ||
> +			__get_user(txc->tolerance, &utp->tolerance) ||
> +			__get_user(txc->time.tv_sec, &utp->time.tv_sec) ||
> +			__get_user(txc->time.tv_usec, &utp->time.tv_usec) ||
> +			__get_user(txc->tick, &utp->tick) ||
> +			__get_user(txc->ppsfreq, &utp->ppsfreq) ||
> +			__get_user(txc->jitter, &utp->jitter) ||
> +			__get_user(txc->shift, &utp->shift) ||
> +			__get_user(txc->stabil, &utp->stabil) ||
> +			__get_user(txc->jitcnt, &utp->jitcnt) ||
> +			__get_user(txc->calcnt, &utp->calcnt) ||
> +			__get_user(txc->errcnt, &utp->errcnt) ||
> +			__get_user(txc->stbcnt, &utp->stbcnt))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int compat_put_timex(struct compat_timex __user *utp, struct timex *txc)
> +{
> +	if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
> +			__put_user(txc->modes, &utp->modes) ||
> +			__put_user(txc->offset, &utp->offset) ||
> +			__put_user(txc->freq, &utp->freq) ||
> +			__put_user(txc->maxerror, &utp->maxerror) ||
> +			__put_user(txc->esterror, &utp->esterror) ||
> +			__put_user(txc->status, &utp->status) ||
> +			__put_user(txc->constant, &utp->constant) ||
> +			__put_user(txc->precision, &utp->precision) ||
> +			__put_user(txc->tolerance, &utp->tolerance) ||
> +			__put_user(txc->time.tv_sec, &utp->time.tv_sec) ||
> +			__put_user(txc->time.tv_usec, &utp->time.tv_usec) ||
> +			__put_user(txc->tick, &utp->tick) ||
> +			__put_user(txc->ppsfreq, &utp->ppsfreq) ||
> +			__put_user(txc->jitter, &utp->jitter) ||
> +			__put_user(txc->shift, &utp->shift) ||
> +			__put_user(txc->stabil, &utp->stabil) ||
> +			__put_user(txc->jitcnt, &utp->jitcnt) ||
> +			__put_user(txc->calcnt, &utp->calcnt) ||
> +			__put_user(txc->errcnt, &utp->errcnt) ||
> +			__put_user(txc->stbcnt, &utp->stbcnt) ||
> +			__put_user(txc->tai, &utp->tai))
> +		return -EFAULT;
> +	return 0;
> +}
> +
>  asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv,
>  		struct timezone __user *tz)
>  {
> @@ -617,6 +675,29 @@ long compat_sys_clock_gettime(clockid_t which_clock,
>  	return err;
>  }
>  
> +long compat_sys_clock_adjtime(clockid_t which_clock,
> +		struct compat_timex __user *utp)
> +{
> +	struct timex txc;
> +	mm_segment_t oldfs;
> +	int err, ret;
> +
> +	err = compat_get_timex(&txc, utp);
> +	if (err)
> +		return err;
> +
> +	oldfs = get_fs();
> +	set_fs(KERNEL_DS);
> +	ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc);
> +	set_fs(oldfs);
> +
> +	err = compat_put_timex(utp, &txc);
> +	if (err)
> +		return err;
> +
> +	return ret;
> +}
> +
>  long compat_sys_clock_getres(clockid_t which_clock,
>  		struct compat_timespec __user *tp)
>  {
> @@ -951,58 +1032,17 @@ asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat
>  asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp)
>  {
>  	struct timex txc;
> -	int ret;
> -
> -	memset(&txc, 0, sizeof(struct timex));
> +	int err, ret;
>  
> -	if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) ||
> -			__get_user(txc.modes, &utp->modes) ||
> -			__get_user(txc.offset, &utp->offset) ||
> -			__get_user(txc.freq, &utp->freq) ||
> -			__get_user(txc.maxerror, &utp->maxerror) ||
> -			__get_user(txc.esterror, &utp->esterror) ||
> -			__get_user(txc.status, &utp->status) ||
> -			__get_user(txc.constant, &utp->constant) ||
> -			__get_user(txc.precision, &utp->precision) ||
> -			__get_user(txc.tolerance, &utp->tolerance) ||
> -			__get_user(txc.time.tv_sec, &utp->time.tv_sec) ||
> -			__get_user(txc.time.tv_usec, &utp->time.tv_usec) ||
> -			__get_user(txc.tick, &utp->tick) ||
> -			__get_user(txc.ppsfreq, &utp->ppsfreq) ||
> -			__get_user(txc.jitter, &utp->jitter) ||
> -			__get_user(txc.shift, &utp->shift) ||
> -			__get_user(txc.stabil, &utp->stabil) ||
> -			__get_user(txc.jitcnt, &utp->jitcnt) ||
> -			__get_user(txc.calcnt, &utp->calcnt) ||
> -			__get_user(txc.errcnt, &utp->errcnt) ||
> -			__get_user(txc.stbcnt, &utp->stbcnt))
> -		return -EFAULT;
> +	err = compat_get_timex(&txc, utp);
> +	if (err)
> +		return err;
>  
>  	ret = do_adjtimex(&txc);
>  
> -	if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) ||
> -			__put_user(txc.modes, &utp->modes) ||
> -			__put_user(txc.offset, &utp->offset) ||
> -			__put_user(txc.freq, &utp->freq) ||
> -			__put_user(txc.maxerror, &utp->maxerror) ||
> -			__put_user(txc.esterror, &utp->esterror) ||
> -			__put_user(txc.status, &utp->status) ||
> -			__put_user(txc.constant, &utp->constant) ||
> -			__put_user(txc.precision, &utp->precision) ||
> -			__put_user(txc.tolerance, &utp->tolerance) ||
> -			__put_user(txc.time.tv_sec, &utp->time.tv_sec) ||
> -			__put_user(txc.time.tv_usec, &utp->time.tv_usec) ||
> -			__put_user(txc.tick, &utp->tick) ||
> -			__put_user(txc.ppsfreq, &utp->ppsfreq) ||
> -			__put_user(txc.jitter, &utp->jitter) ||
> -			__put_user(txc.shift, &utp->shift) ||
> -			__put_user(txc.stabil, &utp->stabil) ||
> -			__put_user(txc.jitcnt, &utp->jitcnt) ||
> -			__put_user(txc.calcnt, &utp->calcnt) ||
> -			__put_user(txc.errcnt, &utp->errcnt) ||
> -			__put_user(txc.stbcnt, &utp->stbcnt) ||
> -			__put_user(txc.tai, &utp->tai))
> -		ret = -EFAULT;
> +	err = compat_put_timex(utp, &txc);
> +	if (err)
> +		return err;
>  
>  	return ret;
>  }
> diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
> index 6842eeb..e1c2e7b 100644
> --- a/kernel/posix-cpu-timers.c
> +++ b/kernel/posix-cpu-timers.c
> @@ -207,6 +207,10 @@ int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp)
>  	return error;
>  }
>  
> +int posix_cpu_clock_adj(const clockid_t which_clock, struct timex *tx)
> +{
> +	return -EOPNOTSUPP;
> +}
>  
>  /*
>   * Sample a per-thread clock for the given task.
> diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
> index 9ca4973..446b566 100644
> --- a/kernel/posix-timers.c
> +++ b/kernel/posix-timers.c
> @@ -197,6 +197,14 @@ static int common_timer_create(struct k_itimer *new_timer)
>  	return 0;
>  }
>  
> +static inline int common_clock_adj(const clockid_t which_clock, struct timex *t)
> +{
> +	if (CLOCK_REALTIME == which_clock)
> +		return do_adjtimex(t);
> +	else
> +		return -EOPNOTSUPP;
> +}
> +
>  static int no_timer_create(struct k_itimer *new_timer)
>  {
>  	return -EOPNOTSUPP;
> @@ -969,6 +977,15 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
>  
>  }
>  
> +SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
> +		struct timex __user *, tx)
> +{
> +	if (invalid_clockid(which_clock))
> +		return -EINVAL;
> +
> +	return CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, tx));
> +}
> +
>  SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
>  		struct timespec __user *, tp)
>  {

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 22:03   ` Benjamin Herrenschmidt
@ 2010-09-23 22:12     ` Thomas Gleixner
  2010-09-24  1:20       ` Benjamin Herrenschmidt
  2010-09-24  7:55     ` Richard Cochran
  1 sibling, 1 reply; 53+ messages in thread
From: Thomas Gleixner @ 2010-09-23 22:12 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Peter Zijlstra, John Stultz, Richard Cochran, linuxppc-dev, LKML,
	David Miller, netdev, linux-api, Christoph Lameter,
	Rodolfo Giometti, devicetree-discuss, linux-arm-kernel,
	Krzysztof Halasa

On Fri, 24 Sep 2010, Benjamin Herrenschmidt wrote:
> On Thu, 2010-09-23 at 19:31 +0200, Richard Cochran wrote:
> > The new syscall, clock_adjtime, takes two parameters, the clock ID,
> > and a pointer to a struct timex. The semantics of the timex struct
> > have been expanded by one additional mode flag, which allows an
> > absolute offset correction. When specificied, the clock offset is
> > immediately corrected by adding the given time value to the current
> > time value.
> 
> Any reason why you CC'ed device-tree discuss ?
> 
> This list is getting way too much unrelated stuff, which I find
> annoying, it would be nice if we were all a bit more careful here with
> our CC lists.

Says the guy who missed to trim the useless context of the original
mail, which made me scroll down all the way just to find out that
there is nothing to see.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 22:12     ` Thomas Gleixner
@ 2010-09-24  1:20       ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2010-09-24  1:20 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Peter Zijlstra, John Stultz, Richard Cochran, linuxppc-dev, LKML,
	David Miller, netdev, linux-api, Christoph Lameter,
	Rodolfo Giometti, devicetree-discuss, linux-arm-kernel,
	Krzysztof Halasa

On Fri, 2010-09-24 at 00:12 +0200, Thomas Gleixner wrote:
> > This list is getting way too much unrelated stuff, which I find
> > annoying, it would be nice if we were all a bit more careful here
> with
> > our CC lists.
> 
> Says the guy who missed to trim the useless context of the original
> mail, which made me scroll down all the way just to find out that
> there is nothing to see. 

Heh, you can usually ignore what's after my signature :-) At least I
didn't put my reply all the way down the bottom !

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 19:48   ` john stultz
@ 2010-09-24  7:29     ` Richard Cochran
  2010-09-24 17:55       ` john stultz
  0 siblings, 1 reply; 53+ messages in thread
From: Richard Cochran @ 2010-09-24  7:29 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, Sep 23, 2010 at 12:48:51PM -0700, john stultz wrote:
> On Thu, 2010-09-23 at 19:31 +0200, Richard Cochran wrote:
> > A new syscall is introduced that allows tuning of a POSIX clock. The
> > syscall is implemented for four architectures: arm, blackfin, powerpc,
> > and x86.
> > 
> > The new syscall, clock_adjtime, takes two parameters, the clock ID,
> > and a pointer to a struct timex. The semantics of the timex struct
> > have been expanded by one additional mode flag, which allows an
> > absolute offset correction. When specificied, the clock offset is
> > immediately corrected by adding the given time value to the current
> > time value.
> 
> 
> So I'd still split this patch up a little bit more.
> 
> 1) Patch that implements the ADJ_SETOFFSET  (*and its implementation*)
> in do_adjtimex.
> 
> 2) Patch that adds the new syscall and clock_id multiplexing.
> 
> 3) Patches that wire it up to the rest of the architectures (there's
> still a bunch missing here).

I was not sure what the policy is about adding syscalls. Is it the
syscall author's responsibility to add it into every arch?

The last time (see a2e2725541fad7) the commit only added half of some
archs, and ignored others. In my patch, the syscall *really* works on
the archs that are present in the patch.

(Actually, I did not test blackfin, since I don't have one, but I
included it since I know they have a PTP hardware clock.)

> > +static inline int common_clock_adj(const clockid_t which_clock, struct timex *t)
> > +{
> > +	if (CLOCK_REALTIME == which_clock)
> > +		return do_adjtimex(t);
> > +	else
> > +		return -EOPNOTSUPP;
> > +}
> 
> 
> Would it make sense to point to the do_adjtimex() in the k_clock
> definition for CLOCK_REALTIME rather then conditionalizing it here?

But what about CLOCK_MONOTONIC_RAW, for example?

Does it make sense to allow it to be adjusted?

Thanks,
Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-23 22:03   ` Benjamin Herrenschmidt
  2010-09-23 22:12     ` Thomas Gleixner
@ 2010-09-24  7:55     ` Richard Cochran
  2010-09-24 22:12       ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 53+ messages in thread
From: Richard Cochran @ 2010-09-24  7:55 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Peter Zijlstra, John Stultz, devicetree-discuss, linuxppc-dev,
	linux-kernel, David Miller, netdev, linux-api, Thomas Gleixner,
	Rodolfo Giometti, Christoph Lameter, linux-arm-kernel,
	Krzysztof Halasa

On Fri, Sep 24, 2010 at 08:03:43AM +1000, Benjamin Herrenschmidt wrote:
> On Thu, 2010-09-23 at 19:31 +0200, Richard Cochran wrote:
> > A new syscall is introduced that allows tuning of a POSIX clock. The
> > syscall is implemented for four architectures: arm, blackfin, powerpc,
> > and x86.
> > 
> > The new syscall, clock_adjtime, takes two parameters, the clock ID,
> > and a pointer to a struct timex. The semantics of the timex struct
> > have been expanded by one additional mode flag, which allows an
> > absolute offset correction. When specificied, the clock offset is
> > immediately corrected by adding the given time value to the current
> > time value.
> 
> Any reason why you CC'ed device-tree discuss ?
> 
> This list is getting way too much unrelated stuff, which I find
> annoying, it would be nice if we were all a bit more careful here with
> our CC lists.

Sorry, I only added device-tree because some one asked me to do so.

    http://marc.info/?l=linux-netdev&m=127273157912358

I'll leave it off next time.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
  2010-09-23 18:21   ` Jacob Keller
  2010-09-23 18:59   ` john stultz
@ 2010-09-24  8:33   ` Richard Cochran
  2 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-24  8:33 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, netdev,
	Thomas Gleixner, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, Sep 23, 2010 at 12:53:20PM -0500, Christoph Lameter wrote:
> On Thu, 23 Sep 2010, Richard Cochran wrote:
> > 3.3 Synchronizing the Linux System Time
> > ========================================
> >
> >    One could offer a PHC as a combined clock source and clock event
> >    device. The advantage of this approach would be that it obviates
> >    the need for synchronization when the PHC is selected as the system
> >    timer. However, some PHCs, namely the PHY based clocks, cannot be
> >    used in this way.
> 
> Why not? Do PHY based clock not at least provide a counter that increments
> in synchronized intervals throughout the network?

The counter in the PHY is accessed via the MDIO bus. One 16 bit read
takes anywhere from 25 to 40 microseconds. Reading the 64 bit time
value requires four reads, so we're talking about 100 to 160
microseconds, just for a single time reading.

In addition to that, reading MDIO bus can sleep.  So, we can't (in
general) to offer PHCs as clock sources.

> >    Instead, the patch set provides a way to offer a Pulse Per Second
> >    (PPS) event from the PHC to the Linux PPS subsystem. A user space
> >    application can read the PPS events and tune the system clock, just
> >    like when using other external time sources like radio clocks or
> >    GPS.
> 
> User space is subject to various latencies created by the OS etc. I would
> that in order to have fine grained (read microsecond) accurary we would
> have to run the portions that are relevant to obtaining the desired
> accuracy in the kernel.

The time-critical operations are all performed in hardware (packet
timestamp), or in kernel space (input PPS timestamp). User space only
runs the servo (using hardware or kernel timestamps as input) and
performs the clock correction. With a sample rate of 1 PPS, the small
user space induced delay (a few dozen microseconds) between sample
time and clock correction is not an issue.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 19:17   ` Christoph Lameter
  2010-09-23 20:43     ` Alan Cox
@ 2010-09-24  8:49     ` Richard Cochran
  1 sibling, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-24  8:49 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, netdev,
	Thomas Gleixner, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, Sep 23, 2010 at 02:17:36PM -0500, Christoph Lameter wrote:
> On Thu, 23 Sep 2010, Richard Cochran wrote:
> > +  These properties set the operational parameters for the PTP
> > +  clock. You must choose these carefully for the clock to work right.
> > +  Here is how to figure good values:
> > +
> > +  TimerOsc     = system clock               MHz
> > +  tclk_period  = desired clock period       nanoseconds
> > +  NominalFreq  = 1000 / tclk_period         MHz
> > +  FreqDivRatio = TimerOsc / NominalFreq     (must be greater that 1.0)
> > +  tmr_add      = ceil(2^32 / FreqDivRatio)
> > +  OutputClock  = NominalFreq / tmr_prsc     MHz
> > +  PulseWidth   = 1 / OutputClock            microseconds
> > +  FiperFreq1   = desired frequency in Hz
> > +  FiperDiv1    = 1000000 * OutputClock / FiperFreq1
> > +  tmr_fiper1   = tmr_prsc * tclk_period * FiperDiv1 - tclk_period
> > +  max_adj      = 1000000000 * (FreqDivRatio - 1.0) - 1
> 
> Great stuff for clock synchronization...
> 
> > +  The calculation for tmr_fiper2 is the same as for tmr_fiper1. The
> > +  driver expects that tmr_fiper1 will be correctly set to produce a 1
> > +  Pulse Per Second (PPS) signal, since this will be offered to the PPS
> > +  subsystem to synchronize the Linux clock.
> 
> Argh. And conceptually completely screwed up. Why go through the PPS
> subsystem if you can directly tune the system clock based on a number of
> the cool periodic clock features that you have above? See how the other
> clocks do that easily? Look into drivers/clocksource. Add it there.
> 
> Please do not introduce useless additional layers for clock sync. Load
> these ptp clocks like the other regular clock modules and make them sync
> system time like any other clock.
> 
> Really guys: I want a PTP solution! Now! And not some idiotic additional
> kernel layers that just pass bits around because its so much fun and
> screws up clock accurary in due to the latency noise introduced while
> having so much fun with the bits.

(Sorry if this message comes twice. Mutt/Gmail flaked out again.)

I think you misunderstood this particular patch. The device tree
parameters are really just internal driver stuff. When you use the
eTSEC, you must make some design choices at the same time as you plan
your board. The proper values for some of the eTSEC registers are
based on these design choices. Since the Freescale documentation is a
bit thin on this, I added a few notes to help my fellow board
designers.

Because these values are closely related to the board itself, I think
that it is nicer to configure them via the device tree than using
either CONFIG_ variables or platform data.

Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx.
  2010-09-23 21:26       ` Christian Riesch
@ 2010-09-24 11:52         ` Alan Cox
  0 siblings, 0 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-24 11:52 UTC (permalink / raw)
  To: Christian Riesch
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	Thomas Gleixner, netdev, Christoph Lameter, linuxppc-dev,
	Richard Cochran, linux-arm-kernel, Krzysztof Halasa

> However, if the clock selected by the BMC is switched off, loses its 
> network connection..., the second best clock is selected by the BMC and 
> becomes master. This clock may be less accurate and thus our slave clock 
> has to switch from one notion of time to another. Is that the conflict 
> you mentioned?

No you get situations where you have policy reasons for trusting
particular clocks for particular things.

So you may have a PTP or NTP clock providing basic system time but also
have other PTP clocks that are actually being used for synchronization
work.

With NTP it's not so far been a big issue - NTP isn't used for industrial
high precision control and the cases we end up with multiple NTP clocks
it's on a virtualised systems where it is isolated.

With high precision clocks you sometimes want to honour a specific PTP
time source and use it rather than try and merge it with your other time
sources (which may differ from the equipment elsewhere). What matters is
things like all the parts of a several mile long conveyor belt of hot
steel slab stopping at the same moment [1].

In lots of control applications you've got assorted different time planes
which wish to talk their own time and you have to accept it, so we need
to support that kind of use.

I agree entirely the normal boring 'I installed my distro and..' case for
PTP or for NTP is merging all the sources, running the algorithm and using
the system time for it. Likewise almost all "normal" application code
will be watching system time.

Alan
[1] Which was my first encounter with writing Vax/VMS assembly language

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 20:36 ` Alan Cox
  2010-09-23 20:49   ` john stultz
@ 2010-09-24 13:14   ` Richard Cochran
  2010-09-24 14:02     ` Alan Cox
  1 sibling, 1 reply; 53+ messages in thread
From: Richard Cochran @ 2010-09-24 13:14 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, David Miller,
	linux-arm-kernel, Krzysztof Halasa

On Thu, Sep 23, 2010 at 09:36:54PM +0100, Alan Cox wrote:
> Drop the clockid_t and swap it for a file handle like a proper Unix or
> Linux interface. The rest is much the same
> 
> 	fd = open /sys/class/timesource/[whatever]
> 
> 	various queries you may want to do to check the name etc
> 
> 	fclock_adjtime(fd, ...)

Okay, but lets extend the story:

	clock_getttime(fd, ...);

	clock_settime(fd, ...);

	timer_create(fd, ...);

Can you agree to that as well?

(We would need to ensure that 'fd' avoids the range 0 to MAX_CLOCKS).

Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 19:38 ` john stultz
@ 2010-09-24 13:50   ` Richard Cochran
  2010-09-24 14:57     ` Alan Cox
  0 siblings, 1 reply; 53+ messages in thread
From: Richard Cochran @ 2010-09-24 13:50 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Thu, Sep 23, 2010 at 12:38:53PM -0700, john stultz wrote:
> On Thu, 2010-09-23 at 19:30 +0200, Richard Cochran wrote:
> >     /sys/class/timesource/<name>/id
> >     /sys/class/ptp/ptp_clock_X/id
> > 
> So yea, I'm not a fan of the "timesource" sysfs interface. One, I think
> the name is poor (posix_clocks or something a little more specific would
> be an improvement), and second, I don't like the dictionary interface,
> where one looks up the clock by name.
> 
> Instead, I think having the id hanging off the class driver is much
> better, as it allows mapping the actual hardware to the id more clearly.
> 
> So I'd drop the "timesource" listing. And maybe change "id" to
> "clock_id" so its a little more clear what the id is for.

Okay, I will drop /sys/class/timesource (hope Alan Cox agrees :)

I threw it out there mostly for the sake of discussion. I imagined
that there could be other properties in that directory, like time
scale (TAI, UTC, etc). But it seems like we don't really need anything
in that direction.

> > 3.3 Synchronizing the Linux System Time 
> > ========================================
> > 
> >    One could offer a PHC as a combined clock source and clock event
> >    device. The advantage of this approach would be that it obviates
> >    the need for synchronization when the PHC is selected as the system
> >    timer. However, some PHCs, namely the PHY based clocks, cannot be
> >    used in this way.
> 
> Again, I'd scratch this. 

Okay, I only wanted to preempt the question which people are asking
all the time: why can't it work with the system clock transparently?

> >    Instead, the patch set provides a way to offer a Pulse Per Second
> >    (PPS) event from the PHC to the Linux PPS subsystem. A user space
> >    application can read the PPS events and tune the system clock, just
> >    like when using other external time sources like radio clocks or
> >    GPS.
> 
> Forgive me for a bit of a tangent here:
> 	So while I think this PPS method is a neat idea, I'm a little curious
> how much of a difference the PPS method for syncing the clock would be
> over just a simple reading of the two clocks and correcting the offset.
> 
> It seems much of it depends on the read latency of the PTP hardware vs
> the interrupt latency. Also the PTP clock granularity would effect the
> read accuracy (like on the RTC, you don't really know how close to the
> second boundary you are).
> 
> Have you done any such measurements between the two methods?

I have not yet tested how well the PPS method works, but I expect at
least as good results as when using a GPS.

> I just
> wonder if it would actually be something noticeable, and if its not, how
> much lighter this patch-set would be without the PPS connection.

As you say, the problem with just reading two clocks at nearly the
same time is that you have two uncertain operations. If you use a PPS,
then there is only one clock to read, and that clock is the system
clock, which hopefully is not too slow to read!

In addition, PHY reads can sleep, and that surely won't work. Even with
MAC PHCs, reading outside of interrupt context makes you vulnerable to
other interrupts.

> Again, this isn't super critical, just trying to make sure we don't end
> up adding a bunch of code that doesn't end up being used.

The PPS hooks are really only just a few lines of code.

The great advantage of a PPS approach over and ad-hoc "read two clocks
and compare", is that, with a steady, known sample rate, you can
analyze and predict your control loop behavior. There is lots of
literature available on how to do it. IMHO, that is the big weakness
of the timecompare.c stuff used in the current IGB driver.

> Also PPS
> interrupts are awfully frequent, so systems concerned with power-saving
> and deep idles probably would like something that could be done at a
> more coarse interval.

We could always make the pulse rate programmable, for power-saving
applications.

> > 4.1 Supported Hardware Clocks 
> > ==============================
> > 
> >    + Standard Linux system timer
> >      This driver exports the standard Linux timer as a PTP clock.
> >      Although this duplicates CLOCK_REALTIME, the code serves as a
> >      simple example for driver development and lets people who without
> >      special hardware try the new API.
> 
> Still not a fan of this one, figure the app should handle the special
> case where there are no PTP clocks and just use CLOCK_REALTIME rather
> then funneling CLOCK_REALTIME through the PTP interface.

It is really just as an example and for people who want to test driver
the API. It can surely be removed before the final version...

Thanks for your comments,

Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-24 13:14   ` Richard Cochran
@ 2010-09-24 14:02     ` Alan Cox
  2010-09-24 14:07       ` Alan Cox
  2010-09-27 15:56       ` Christoph Lameter
  0 siblings, 2 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-24 14:02 UTC (permalink / raw)
  To: Richard Cochran
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, David Miller,
	linux-arm-kernel, Krzysztof Halasa

On Fri, 24 Sep 2010 15:14:07 +0200
Richard Cochran <richardcochran@gmail.com> wrote:

> On Thu, Sep 23, 2010 at 09:36:54PM +0100, Alan Cox wrote:
> > Drop the clockid_t and swap it for a file handle like a proper Unix or
> > Linux interface. The rest is much the same
> > 
> > 	fd = open /sys/class/timesource/[whatever]
> > 
> > 	various queries you may want to do to check the name etc
> > 
> > 	fclock_adjtime(fd, ...)
> 
> Okay, but lets extend the story:
> 
> 	clock_getttime(fd, ...);
> 
> 	clock_settime(fd, ...);
> 
> 	timer_create(fd, ...);
> 
> Can you agree to that as well?
> 
> (We would need to ensure that 'fd' avoids the range 0 to MAX_CLOCKS).

You can't do that avoiding as you might like because the behaviour of
file handle numbering is defined by the standards. Hence the "f*"
versions of the calls (and of lots of other stuff)

Whether you add new syscalls or do the fd passing using flags and hide
the ugly bits in glibc is another question.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-24 14:02     ` Alan Cox
@ 2010-09-24 14:07       ` Alan Cox
  2010-09-27 15:56       ` Christoph Lameter
  1 sibling, 0 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-24 14:07 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	Thomas Gleixner, netdev, Christoph Lameter, linuxppc-dev,
	Richard Cochran, linux-arm-kernel, Krzysztof Halasa

> You can't do that avoiding as you might like because the behaviour of
> file handle numbering is defined by the standards. Hence the "f*"
> versions of the calls (and of lots of other stuff)
> 
> Whether you add new syscalls or do the fd passing using flags and hide
> the ugly bits in glibc is another question.

To add an example of what I mean you might end up defining "CLOCK_FD" to
indicate to use the fd in the struct, but given syscalls are trivial
codewise and would end up as

	fclock_foo(int fd, blah)
	{
		clock = fd_to_clock(fd);
		if (error)
			return error
		clock_do_foo(clock, blah);
		clock_put(clock);
	}

and

	clock_foo(int posixid, blah)
	{
		clock = posix_to_clock(posixid)
		...
		rest same
	}

as wrappers it seems hardly worth adding ugly hacks

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-24 13:50   ` Richard Cochran
@ 2010-09-24 14:57     ` Alan Cox
  0 siblings, 0 replies; 53+ messages in thread
From: Alan Cox @ 2010-09-24 14:57 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, netdev, Thomas Gleixner,
	linux-api, Christoph Lameter, linuxppc-dev, David Miller,
	linux-arm-kernel, Krzysztof Halasa

> > Instead, I think having the id hanging off the class driver is much
> > better, as it allows mapping the actual hardware to the id more clearly.
> > 
> > So I'd drop the "timesource" listing. And maybe change "id" to
> > "clock_id" so its a little more clear what the id is for.
> 
> Okay, I will drop /sys/class/timesource (hope Alan Cox agrees :)

It makes sense to hang anything off the physical id

> I threw it out there mostly for the sake of discussion. I imagined
> that there could be other properties in that directory, like time
> scale (TAI, UTC, etc). But it seems like we don't really need anything
> in that direction.

They can still hang off the physical device. Thats really a detail

> > interrupts are awfully frequent, so systems concerned with power-saving
> > and deep idles probably would like something that could be done at a
> > more coarse interval.
> 
> We could always make the pulse rate programmable, for power-saving
> applications.

I would expect the kernel drivers to be responsible for
- Turning off when they can
- Picking rates that are power optimal for the requirement

The latter is a bit interesting as I don't see anything in any of the
timer APIs to express accuracy (a problem we have in kernel too).
Historically it simply hasn't mattered.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-24  7:29     ` Richard Cochran
@ 2010-09-24 17:55       ` john stultz
  0 siblings, 0 replies; 53+ messages in thread
From: john stultz @ 2010-09-24 17:55 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, Thomas Gleixner, netdev,
	Christoph Lameter, linuxppc-dev, David Miller, linux-arm-kernel,
	Krzysztof Halasa

On Fri, 2010-09-24 at 09:29 +0200, Richard Cochran wrote:
> On Thu, Sep 23, 2010 at 12:48:51PM -0700, john stultz wrote:
> > So I'd still split this patch up a little bit more.
> > 
> > 1) Patch that implements the ADJ_SETOFFSET  (*and its implementation*)
> > in do_adjtimex.
> > 
> > 2) Patch that adds the new syscall and clock_id multiplexing.
> > 
> > 3) Patches that wire it up to the rest of the architectures (there's
> > still a bunch missing here).
> 
> I was not sure what the policy is about adding syscalls. Is it the
> syscall author's responsibility to add it into every arch?
> 
> The last time (see a2e2725541fad7) the commit only added half of some
> archs, and ignored others. In my patch, the syscall *really* works on
> the archs that are present in the patch.
> 
> (Actually, I did not test blackfin, since I don't have one, but I
> included it since I know they have a PTP hardware clock.)

I'm not sure about policy, but I think for completeness sake you should
make sure every arch supports a new syscall. You're not expected to be
able to test every one, but getting the basic support patch sent to
maintainers should be done.

> > > +static inline int common_clock_adj(const clockid_t which_clock, struct timex *t)
> > > +{
> > > +	if (CLOCK_REALTIME == which_clock)
> > > +		return do_adjtimex(t);
> > > +	else
> > > +		return -EOPNOTSUPP;
> > > +}
> > 
> > 
> > Would it make sense to point to the do_adjtimex() in the k_clock
> > definition for CLOCK_REALTIME rather then conditionalizing it here?
> 
> But what about CLOCK_MONOTONIC_RAW, for example?

-EOPNOTSUPP

> Does it make sense to allow it to be adjusted?

No. I think only CLOCK_REALTIME would make sense of the existing clocks.

I'm just suggesting you conditionalize it from the function pointer,
rather then in the common function.

thanks
-john

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/8] posix clocks: introduce a syscall for clock tuning.
  2010-09-24  7:55     ` Richard Cochran
@ 2010-09-24 22:12       ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 53+ messages in thread
From: Benjamin Herrenschmidt @ 2010-09-24 22:12 UTC (permalink / raw)
  To: Richard Cochran
  Cc: Peter Zijlstra, John Stultz, devicetree-discuss, linuxppc-dev,
	linux-kernel, David Miller, netdev, linux-api, Thomas Gleixner,
	Rodolfo Giometti, Christoph Lameter, linux-arm-kernel,
	Krzysztof Halasa


> > This list is getting way too much unrelated stuff, which I find
> > annoying, it would be nice if we were all a bit more careful here with
> > our CC lists.
> 
> Sorry, I only added device-tree because some one asked me to do so.
> 
>     http://marc.info/?l=linux-netdev&m=127273157912358
> 
> I'll leave it off next time.

That's allright. I'd rather you just post the bindings there than the
whole patch least but no big deal.

I was just fixing my email filters and notice a lot of seemingly
unrelated stuff landing there :-)

Cheers,
Ben.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 21:34             ` Christian Riesch
@ 2010-09-27 15:37               ` Christoph Lameter
  2010-09-30  3:50                 ` Christian Riesch
  2010-10-02  1:44                 ` M. Warner Losh
  0 siblings, 2 replies; 53+ messages in thread
From: Christoph Lameter @ 2010-09-27 15:37 UTC (permalink / raw)
  To: Christian Riesch
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	linux-arm-kernel, linux-api, Thomas Gleixner, linuxppc-dev,
	Richard Cochran, Alan Cox, Krzysztof Halasa

On Thu, 23 Sep 2010, Christian Riesch wrote:

> > > It implies clock tuning in userspace for a potential sub microsecond
> > > accurate clock. The clock accuracy will be limited by user space
> > > latencies and noise. You wont be able to discipline the system clock
> > > accurately.
> >
> > Noise matters, latency doesn't.
>
> Well put! That's why we need hardware support for PTP timestamping to reduce
> the noise, but get along well with the clock servo that is steering the PHC in
> user space.

Even if I buy into the catch phrase above: User space is subject to noise
that the in kernel code is not. If you do the tuning over long intervals
then it hopefully averages out but it still causes jitter effects that
affects the degree of accuracy (or sync) that you can reach. And the noise
varies with the load on the system.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-23 21:42           ` john stultz
@ 2010-09-27 15:52             ` Christoph Lameter
  2010-09-27 16:14               ` M. Warner Losh
  0 siblings, 1 reply; 53+ messages in thread
From: Christoph Lameter @ 2010-09-27 15:52 UTC (permalink / raw)
  To: john stultz
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, linux-api,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	Thomas Gleixner, linuxppc-dev, Richard Cochran, linux-arm-kernel,
	Krzysztof Halasa

On Thu, 23 Sep 2010, john stultz wrote:

> > > 3) Further, the PTP hardware counter can be simply set to a new offset
> > > to put it in line with the network time. This could cause trouble with
> > > timekeeping much like unsynced TSCs do.
> >
> > You can do the same for system time.
>
> Settimeofday does allow CLOCK_REALTIME to jump, but the CLOCK_MONOTONIC
> time cannot jump around. Having a clocksource that is non-monotonic
> would break this.

Currently time runs at the same speed. CLOCK_MONOTONIC runs at a offset
to CLOCK_REALTIME. We are creating APIs here that allow time to run at
different speeds.

> The design actually avoids most userland induced latency.
>
> 1) On the PTP hardware syncing point, the reference packet gets
> timestamped with the PTP hardware time on arrival. This allows the
> offset calculation to be done in userland without introducing latency.

The timestamps allows the calculation of the network transmission time I
guess and therefore its more accurate to calculate that effect out. Ok but
then the overhead of getting to code in user space (that does the proper
clock adjustments) is resulting in the addition of a relatively long time
that is subject to OS scheduling latencies and noises.

> 2) On the system syncing side, the proposal for the PPS interrupt allows
> the PTP hardware to trigger an interrupt on the second boundary that
> would take a timestamp of the system time. Then the pps interface allows
> for the timestamp to be read from userland allowing the offset to be
> calculated without introducing additional latency.

Sorry dont really get the whole picture here it seems. Sounds like one is
going through additional unnecessary layers. Why would the PTP hardware
triggger an interrupt? I thought the PTP messages came in via
timestamping and are then processed by software. Then the software is
issuing a hardware interrupt that then triggers the PPS subsystem. And
that is supposed to be better than directly interfacing with the PTP?


> Additionally, even just in userland, it would be easy to bracket two
> reads of the system time around one read of the PTP clock to bound any
> userland latency fairly well. It may not be as good as the PPS interface
> (although that depends on the interrupt latency), but if the accesses
> are all local, it probably could get fairly close.

That sounds hacky.

> > Ok maybe we need some sort of control interface to manage the clock like
> > the others have.
>
> That's what the clock_adjtime call provides.

Ummm... You are managing a hardware device with hardware (driver) specific
settings. That is currently being done via ioctls. Why generalize it?

> > The posix clocks today assumes one notion of real "time" in the kernel.
> > All clocks increase in lockstep (aside from offset updates).
>
> Not true. The cputime clockids do not increment at the same rate (as the
> apps don't always run). Further CLOCK_MONOTONIC_RAW provides a non-freq
> corrected view of CLOCK_MONOTONIC, so it increments at a slightly
> different rate.

cputime clockids are not tracking time but cpu resource use.

> Re-using the fairly nice (Alan of course disagrees :) posix interface
> seems at least a little better for application developers who actually
> have to use the hardware.

Well it may also be confusing for others. The application developers also
will have a hard time using a generic clock interface to control PTP
device specific things like frequencies, rates etc etc. So you always need
to ioctl/device specific control interface regardless.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-24 14:02     ` Alan Cox
  2010-09-24 14:07       ` Alan Cox
@ 2010-09-27 15:56       ` Christoph Lameter
  2010-09-27 17:05         ` Alan Cox
  1 sibling, 1 reply; 53+ messages in thread
From: Christoph Lameter @ 2010-09-27 15:56 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa


On Fri, 24 Sep 2010, Alan Cox wrote:

> Whether you add new syscalls or do the fd passing using flags and hide
> the ugly bits in glibc is another question.

Use device specific ioctls instead of syscalls?

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 15:52             ` Christoph Lameter
@ 2010-09-27 16:14               ` M. Warner Losh
  2010-09-28  6:34                 ` Richard Cochran
  0 siblings, 1 reply; 53+ messages in thread
From: M. Warner Losh @ 2010-09-27 16:14 UTC (permalink / raw)
  To: cl
  Cc: richardcochran, peterz, johnstul, devicetree-discuss,
	linuxppc-dev, linux-kernel, netdev, linux-api, tglx, giometti,
	davem, linux-arm-kernel, khc

In message: <alpine.DEB.2.00.1009271038150.9258@router.home>
            Christoph Lameter <cl@linux.com> writes:
: On Thu, 23 Sep 2010, john stultz wrote:
: > The design actually avoids most userland induced latency.
: >
: > 1) On the PTP hardware syncing point, the reference packet gets
: > timestamped with the PTP hardware time on arrival. This allows the
: > offset calculation to be done in userland without introducing latency.
: 
: The timestamps allows the calculation of the network transmission time I
: guess and therefore its more accurate to calculate that effect out. Ok but
: then the overhead of getting to code in user space (that does the proper
: clock adjustments) is resulting in the addition of a relatively long time
: that is subject to OS scheduling latencies and noises.

The timestamps at the hardware level allow you to factor out variation
caused by OS Scheduling, OS network stack delay and internal buffering
on the NIC.  Variation in measurements is what kills accuracy.

When steering a clock by making an error measurement of the phase and
frequency of it, the latency induced by OS scheduling tends to be
unimportant.  It is far more important to know when you steered the
clock (called adjtime or friends) than to steer it at any fixed
latency to when the data for the measurements was made.  Measuring the
time of steer can tolerate errors in the range of OS scheduling
latencies easily, since that tends to produce a very small effect.  It
introduces an error in your expected phase for the next measurement on
the order of the product of the time of steer error times the change
in fractional frequency (abs( 1 - (nu_new / nu_old))).  Even if the
estimate is really bad at 100ms, most steers are on the order about
one part per million.  This leads to a sub-nanosecond phase error
estimate in the next measurement cycle (a non-accumulating error).  A
1ms error leads to maybe tens of picoseconds of estimate error.

This is a common error that I've seen repeated in this thread.  The
only reason that it has historically been important is because when
you are doing timestamping in software based on an interrupt, that
stuff does matter.

Warner

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 15:56       ` Christoph Lameter
@ 2010-09-27 17:05         ` Alan Cox
  2010-09-28  6:47           ` Richard Cochran
  0 siblings, 1 reply; 53+ messages in thread
From: Alan Cox @ 2010-09-27 17:05 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, David Miller,
	netdev, Thomas Gleixner, linuxppc-dev, Richard Cochran,
	linux-arm-kernel, Krzysztof Halasa

On Mon, 27 Sep 2010 10:56:09 -0500 (CDT)
Christoph Lameter <cl@linux.com> wrote:

> 
> On Fri, 24 Sep 2010, Alan Cox wrote:
> 
> > Whether you add new syscalls or do the fd passing using flags and hide
> > the ugly bits in glibc is another question.
> 
> Use device specific ioctls instead of syscalls?

Some of the ioctls are probably not device specific, the job of the OS in
part is to present a unified interface. We already have a mess of HPET
and RTC driver ioctls.

Some of it undoubtedly is device specific.

Alan

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 16:14               ` M. Warner Losh
@ 2010-09-28  6:34                 ` Richard Cochran
  0 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-28  6:34 UTC (permalink / raw)
  To: M. Warner Losh
  Cc: peterz, johnstul, devicetree-discuss, linuxppc-dev, linux-kernel,
	netdev, tglx, linux-api, cl, giometti, davem, linux-arm-kernel,
	khc

On Mon, Sep 27, 2010 at 10:14:23AM -0600, M. Warner Losh wrote:
> 
> This is a common error that I've seen repeated in this thread.  The
> only reason that it has historically been important is because when
> you are doing timestamping in software based on an interrupt, that
> stuff does matter.

Yes, thanks for your helpful explanation.

To further illustrate how small the effect of running the servo in
user space is, consider the following.

It is true that delays and jitter in the time to process the received
packet negatively affect the clock servo loop. However, the effect in
our case will be quite small.

John Eidson's book [1] presents a rigorous servo model that includes
an analysis of the delay from the time the samples (timestamps) are
taken until the corrective action is performed by the PTP software.

For PTP, the sample time is at the sender (the remote host, the master
clock). The master sends *two* packets, where the second one (the so
called follow-up packet) contains the hardware time stamp of the first
one. So, the computational delay includes the time spent by the sender
in its PTP stack preparing the second packet, the time the packet
spends in transit, and the time in the receiver's PTP stack and servo.

According to Eidson's analysis, a delay of up to 10 milliseconds would
be acceptable, even with a sample rate of 10 Hz. Therefore, saving a
few dozen microseconds by placing the servo (and PTP stack) into the
kernel is not worth the effort.

Richard


[1] title =     {Measurement, control, and communication using IEEE 1588},
    author =    {J. C. Eidson},
    year =      2006,
    publisher = {Springer-Verlag},

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 17:05         ` Alan Cox
@ 2010-09-28  6:47           ` Richard Cochran
  0 siblings, 0 replies; 53+ messages in thread
From: Richard Cochran @ 2010-09-28  6:47 UTC (permalink / raw)
  To: Alan Cox
  Cc: John Stultz, Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra,
	linux-api, devicetree-discuss, linux-kernel, Thomas Gleixner,
	netdev, Christoph Lameter, linuxppc-dev, David Miller,
	linux-arm-kernel, Krzysztof Halasa

On Mon, Sep 27, 2010 at 06:05:58PM +0100, Alan Cox wrote:
> On Mon, 27 Sep 2010 10:56:09 -0500 (CDT)
> Christoph Lameter <cl@linux.com> wrote:
> 
> > 
> > On Fri, 24 Sep 2010, Alan Cox wrote:
> > 
> > > Whether you add new syscalls or do the fd passing using flags and hide
> > > the ugly bits in glibc is another question.
> > 
> > Use device specific ioctls instead of syscalls?
> 
> Some of the ioctls are probably not device specific, the job of the OS in
> part is to present a unified interface. We already have a mess of HPET
> and RTC driver ioctls.

Yes, and the whole point of introducing a PTP hardare clock API was to
avoid each new clock driver introducing yet another ioctl interface.

I had proposed a standard ioctl interface for PTP hardware clocks, but
that interface was rightly criticized for duplicating the posix clock
API. It does not make sense to have multiple interfaces with the exact
same functionality.

It is impossible to support every last feature of every possible
hardware clock with a generic interface, so there will always need to
be special ioctls for such features.

However, some clock functions *are* completely generic and apply to
every clock:

- set time
- get time
- adjust the frequency by N ppb
- shift the time by a given offset

The first two are provided by the posix clock interface, the third by
the NTP adjtimex call, which also can support (by a single mode
extension) the last item.

Richard

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 15:37               ` Christoph Lameter
@ 2010-09-30  3:50                 ` Christian Riesch
  2010-10-02  1:44                 ` M. Warner Losh
  1 sibling, 0 replies; 53+ messages in thread
From: Christian Riesch @ 2010-09-30  3:50 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
	devicetree-discuss, linux-kernel, David Miller, netdev,
	linux-arm-kernel, linux-api, Thomas Gleixner, linuxppc-dev,
	Richard Cochran, Alan Cox, Krzysztof Halasa

Quoting Christoph Lameter <cl@linux.com>:
> On Thu, 23 Sep 2010, Christian Riesch wrote:
>
>> > > It implies clock tuning in userspace for a potential sub microsecond
>> > > accurate clock. The clock accuracy will be limited by user space
>> > > latencies and noise. You wont be able to discipline the system clock
>> > > accurately.
>> >
>> > Noise matters, latency doesn't.
>>
>> Well put! That's why we need hardware support for PTP timestamping to reduce
>> the noise, but get along well with the clock servo that is steering  
>> the PHC in
>> user space.
>
> Even if I buy into the catch phrase above: User space is subject to noise
> that the in kernel code is not. If you do the tuning over long intervals
> then it hopefully averages out but it still causes jitter effects that
> affects the degree of accuracy (or sync) that you can reach. And the noise
> varies with the load on the system.

Yes and no. If you regard it as a control system: The latencies of the  
operating system are a dead time in the control system. The sampling  
time is quite large, one second, maybe around 100ms or 10ms in  
telecommunication applications, but that is still large compared to  
the latencies you expect to have in the operating system. Hence, this  
latencies (=dead time) can be neglected and the important thing that  
remains is the noise that you introduce in the measurements of the  
time stamps, which is therefore done in hardware.
I admit that my short statement above is not completely correct, I  
should have mentioned the rather large sampling time we are dealing  
with here.

Christian

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
  2010-09-27 15:37               ` Christoph Lameter
  2010-09-30  3:50                 ` Christian Riesch
@ 2010-10-02  1:44                 ` M. Warner Losh
  1 sibling, 0 replies; 53+ messages in thread
From: M. Warner Losh @ 2010-10-02  1:44 UTC (permalink / raw)
  To: cl
  Cc: richardcochran, alan, peterz, johnstul, devicetree-discuss,
	linuxppc-dev, linux-kernel, christian, netdev, tglx, linux-api,
	giometti, davem, linux-arm-kernel, khc

In message: <alpine.DEB.2.00.1009271035110.9258@router.home>
            Christoph Lameter <cl@linux.com> writes:
: On Thu, 23 Sep 2010, Christian Riesch wrote:
: 
: > > > It implies clock tuning in userspace for a potential sub microsecond
: > > > accurate clock. The clock accuracy will be limited by user space
: > > > latencies and noise. You wont be able to discipline the system clock
: > > > accurately.
: > >
: > > Noise matters, latency doesn't.
: >
: > Well put! That's why we need hardware support for PTP timestamping to reduce
: > the noise, but get along well with the clock servo that is steering the PHC in
: > user space.
: 
: Even if I buy into the catch phrase above: User space is subject to noise
: that the in kernel code is not. If you do the tuning over long intervals
: then it hopefully averages out but it still causes jitter effects that
: affects the degree of accuracy (or sync) that you can reach. And the noise
: varies with the load on the system.

Please see the earlier posts in this thread about why this doesn't
matter as much as you might think.  What matters is the measurements
(which are done in hardware and the results buffered), not the latency
in processing those messages through your servo.  This is due to the
fact that the errors that even long latencies introduce are
proportional to the change in fractional frequency[*] of the clock being
steered.  This change is usually on the order of a part per million.
Even with 10ms of latency would mean that you're introducing on the
order of sub-nanoseconds of phase error that will be measured in the
next cycle and steered out.

That's why latency doesn't matter.  Do you have other math to show
that it does?

Warner

[*] abs(1 - (clock_freq_old / clock_freq_new)) where clock_freq_old is
the old estimate of the clock and clock_freq_new is the new frequency
estimate of the clock.  Second to second, these change on the order of
a part per million or less...

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2010-10-02  1:44 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-23 17:30 [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
2010-09-23 17:31 ` [PATCH 1/8] posix clocks: introduce a syscall for clock tuning Richard Cochran
2010-09-23 19:48   ` john stultz
2010-09-24  7:29     ` Richard Cochran
2010-09-24 17:55       ` john stultz
2010-09-23 22:03   ` Benjamin Herrenschmidt
2010-09-23 22:12     ` Thomas Gleixner
2010-09-24  1:20       ` Benjamin Herrenschmidt
2010-09-24  7:55     ` Richard Cochran
2010-09-24 22:12       ` Benjamin Herrenschmidt
2010-09-23 17:31 ` [PATCH 2/8] posix clocks: dynamic clock ids Richard Cochran
2010-09-23 17:31 ` [PATCH 3/8] posix clocks: introduce a sysfs presence Richard Cochran
2010-09-23 17:32 ` [PATCH 4/8] ptp: Added a brand new class driver for ptp clocks Richard Cochran
2010-09-23 17:32 ` [PATCH 5/8] ptp: Added a simulated PTP hardware clock Richard Cochran
2010-09-23 17:33 ` [PATCH 6/8] ptp: Added a clock that uses the eTSEC found on the MPC85xx Richard Cochran
2010-09-23 19:17   ` Christoph Lameter
2010-09-23 20:43     ` Alan Cox
2010-09-23 20:32       ` Christoph Lameter
2010-09-23 21:26       ` Christian Riesch
2010-09-24 11:52         ` Alan Cox
2010-09-24  8:49     ` Richard Cochran
2010-09-23 17:33 ` [PATCH 7/8] ptp: Added a clock driver for the IXP46x Richard Cochran
2010-09-23 17:34 ` [PATCH 8/8] ptp: Added a clock driver for the National Semiconductor PHYTER Richard Cochran
2010-09-23 17:53 ` [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support Christoph Lameter
2010-09-23 18:21   ` Jacob Keller
2010-09-23 18:36     ` Christoph Lameter
2010-09-23 18:59   ` john stultz
2010-09-23 19:15     ` Christoph Lameter
2010-09-23 20:28       ` john stultz
2010-09-23 20:49         ` Christoph Lameter
2010-09-23 21:34           ` Alan Cox
2010-09-23 21:34             ` Christian Riesch
2010-09-27 15:37               ` Christoph Lameter
2010-09-30  3:50                 ` Christian Riesch
2010-10-02  1:44                 ` M. Warner Losh
2010-09-23 21:42           ` john stultz
2010-09-27 15:52             ` Christoph Lameter
2010-09-27 16:14               ` M. Warner Losh
2010-09-28  6:34                 ` Richard Cochran
2010-09-24  8:33   ` Richard Cochran
2010-09-23 19:38 ` john stultz
2010-09-24 13:50   ` Richard Cochran
2010-09-24 14:57     ` Alan Cox
2010-09-23 20:36 ` Alan Cox
2010-09-23 20:49   ` john stultz
2010-09-23 21:30     ` Alan Cox
2010-09-23 22:03       ` john stultz
2010-09-24 13:14   ` Richard Cochran
2010-09-24 14:02     ` Alan Cox
2010-09-24 14:07       ` Alan Cox
2010-09-27 15:56       ` Christoph Lameter
2010-09-27 17:05         ` Alan Cox
2010-09-28  6:47           ` Richard Cochran

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).