* [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-16 15:41 [PATCH V7 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-12-16 15:41 ` Richard Cochran
[not found] ` <cover.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
` (2 subsequent siblings)
3 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:41 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
This patch adds a new mode bit into the timex structure. When set, the bit
instructs the kernel to add the given time value to the current time.
Signed-off-by: Richard Cochran <richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
---
include/linux/timex.h | 3 ++-
kernel/time/ntp.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletions(-)
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/time/ntp.c b/kernel/time/ntp.c
index d232189..e9e3915 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -454,6 +454,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
int do_adjtimex(struct timex *txc)
{
struct timespec ts;
+ ktime_t delta, kt;
int result;
/* Validate the data before disabling interrupts */
@@ -482,8 +483,33 @@ int do_adjtimex(struct timex *txc)
hrtimer_cancel(&leap_timer);
}
+ if (txc->modes & ADJ_SETOFFSET) {
+ /* Validate the delta value. */
+ if (txc->time.tv_sec && txc->time.tv_usec < 0)
+ return -EINVAL;
+
+ if (txc->modes & ADJ_NANO) {
+ struct timespec tmp;
+ tmp.tv_sec = txc->time.tv_sec;
+ tmp.tv_nsec = txc->time.tv_usec;
+ delta = timespec_to_ktime(tmp);
+ } else
+ delta = timeval_to_ktime(txc->time);
+
+ /* Adding the delta should be an "atomic" operation. */
+ local_irq_disable();
+ }
+
getnstimeofday(&ts);
+ if (txc->modes & ADJ_SETOFFSET) {
+ kt = timespec_to_ktime(ts);
+ kt = ktime_add(kt, delta);
+ ts = ktime_to_timespec(kt);
+ do_settimeofday(&ts);
+ local_irq_enable();
+ }
+
write_seqlock_irq(&xtime_lock);
if (txc->modes & ADJ_ADJTIME) {
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread* [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-16 15:41 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:41 UTC (permalink / raw)
To: linux-kernel
Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
This patch adds a new mode bit into the timex structure. When set, the bit
instructs the kernel to add the given time value to the current time.
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
include/linux/timex.h | 3 ++-
kernel/time/ntp.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletions(-)
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/time/ntp.c b/kernel/time/ntp.c
index d232189..e9e3915 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -454,6 +454,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts
int do_adjtimex(struct timex *txc)
{
struct timespec ts;
+ ktime_t delta, kt;
int result;
/* Validate the data before disabling interrupts */
@@ -482,8 +483,33 @@ int do_adjtimex(struct timex *txc)
hrtimer_cancel(&leap_timer);
}
+ if (txc->modes & ADJ_SETOFFSET) {
+ /* Validate the delta value. */
+ if (txc->time.tv_sec && txc->time.tv_usec < 0)
+ return -EINVAL;
+
+ if (txc->modes & ADJ_NANO) {
+ struct timespec tmp;
+ tmp.tv_sec = txc->time.tv_sec;
+ tmp.tv_nsec = txc->time.tv_usec;
+ delta = timespec_to_ktime(tmp);
+ } else
+ delta = timeval_to_ktime(txc->time);
+
+ /* Adding the delta should be an "atomic" operation. */
+ local_irq_disable();
+ }
+
getnstimeofday(&ts);
+ if (txc->modes & ADJ_SETOFFSET) {
+ kt = timespec_to_ktime(ts);
+ kt = ktime_add(kt, delta);
+ ts = ktime_to_timespec(kt);
+ do_settimeofday(&ts);
+ local_irq_enable();
+ }
+
write_seqlock_irq(&xtime_lock);
if (txc->modes & ADJ_ADJTIME) {
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread[parent not found: <880d82bb8120f73973db27e0c48e949014b1a106.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>]
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-16 15:41 ` Richard Cochran
@ 2010-12-16 17:48 ` Thomas Gleixner
-1 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 17:48 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, Richard Cochran wrote:
> + if (txc->modes & ADJ_SETOFFSET) {
> + /* Validate the delta value. */
> + if (txc->time.tv_sec && txc->time.tv_usec < 0)
> + return -EINVAL;
> +
> + if (txc->modes & ADJ_NANO) {
> + struct timespec tmp;
> + tmp.tv_sec = txc->time.tv_sec;
> + tmp.tv_nsec = txc->time.tv_usec;
> + delta = timespec_to_ktime(tmp);
> + } else
> + delta = timeval_to_ktime(txc->time);
> +
> + /* Adding the delta should be an "atomic" operation. */
> + local_irq_disable();
I really do not like that conditional irq_disable(), especially as we
disable them further down again and we only safe the getnstimeofday()
call in the non ADJSETOFFSET code path.
So we really better do that unconditionally before getnstimeofday()
with an appropriate comment and change the write_seqlock_irq() to
write_seqlock().
> + }
> +
> getnstimeofday(&ts);
>
> + if (txc->modes & ADJ_SETOFFSET) {
> + kt = timespec_to_ktime(ts);
> + kt = ktime_add(kt, delta);
> + ts = ktime_to_timespec(kt);
> + do_settimeofday(&ts);
> + local_irq_enable();
> + }
> +
> write_seqlock_irq(&xtime_lock);
Thanks,
tglx
>
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-16 17:48 ` Thomas Gleixner
0 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 17:48 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, Richard Cochran wrote:
> + if (txc->modes & ADJ_SETOFFSET) {
> + /* Validate the delta value. */
> + if (txc->time.tv_sec && txc->time.tv_usec < 0)
> + return -EINVAL;
> +
> + if (txc->modes & ADJ_NANO) {
> + struct timespec tmp;
> + tmp.tv_sec = txc->time.tv_sec;
> + tmp.tv_nsec = txc->time.tv_usec;
> + delta = timespec_to_ktime(tmp);
> + } else
> + delta = timeval_to_ktime(txc->time);
> +
> + /* Adding the delta should be an "atomic" operation. */
> + local_irq_disable();
I really do not like that conditional irq_disable(), especially as we
disable them further down again and we only safe the getnstimeofday()
call in the non ADJSETOFFSET code path.
So we really better do that unconditionally before getnstimeofday()
with an appropriate comment and change the write_seqlock_irq() to
write_seqlock().
> + }
> +
> getnstimeofday(&ts);
>
> + if (txc->modes & ADJ_SETOFFSET) {
> + kt = timespec_to_ktime(ts);
> + kt = ktime_add(kt, delta);
> + ts = ktime_to_timespec(kt);
> + do_settimeofday(&ts);
> + local_irq_enable();
> + }
> +
> write_seqlock_irq(&xtime_lock);
Thanks,
tglx
>
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-16 15:41 ` Richard Cochran
@ 2010-12-17 20:16 ` Kuwahara,T.
-1 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-17 20:16 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On 12/17/10, Richard Cochran <richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> This patch adds a new mode bit into the timex structure. When set, the bit
> instructs the kernel to add the given time value to the current time.
>
The proposed new control mode, ADJ_SETOFFSET, is logically the same as
ADJ_OFFSET with timex.constant == -INFINITY. So it is possible to do
the same thing without risking forward compatibility. (I mean by "risking
forward compatibility" that the mode bit 0x0040 may be defined differently
by the upstream maintainer anytime in the future.)
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-17 20:16 ` Kuwahara,T.
0 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-17 20:16 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On 12/17/10, Richard Cochran <richardcochran@gmail.com> wrote:
> This patch adds a new mode bit into the timex structure. When set, the bit
> instructs the kernel to add the given time value to the current time.
>
The proposed new control mode, ADJ_SETOFFSET, is logically the same as
ADJ_OFFSET with timex.constant == -INFINITY. So it is possible to do
the same thing without risking forward compatibility. (I mean by "risking
forward compatibility" that the mode bit 0x0040 may be defined differently
by the upstream maintainer anytime in the future.)
^ permalink raw reply [flat|nested] 68+ messages in thread
[parent not found: <AANLkTi=yGoFwYt4p_LeHtAQyYgmURspO-p57UdL0sUEZ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-17 20:16 ` Kuwahara,T.
@ 2010-12-21 7:56 ` Richard Cochran
-1 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-21 7:56 UTC (permalink / raw)
To: Kuwahara,T.
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Sat, Dec 18, 2010 at 05:16:52AM +0900, Kuwahara,T. wrote:
> On 12/17/10, Richard Cochran <richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> > This patch adds a new mode bit into the timex structure. When set, the bit
> > instructs the kernel to add the given time value to the current time.
> >
>
> The proposed new control mode, ADJ_SETOFFSET, is logically the same as
> ADJ_OFFSET with timex.constant == -INFINITY. So it is possible to do
> the same thing without risking forward compatibility. (I mean by "risking
> forward compatibility" that the mode bit 0x0040 may be defined differently
> by the upstream maintainer anytime in the future.)
Can you please elaborate?
I don't see any way to use timex.constant with ADJ_OFFSET in order to
correct a time offset. The 'time_constant' in kernel/time/ntp.c is
restricted to the interval [0..MAXTC], and MAXTC is 10 in timex.h.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-21 7:56 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-21 7:56 UTC (permalink / raw)
To: Kuwahara,T.
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sat, Dec 18, 2010 at 05:16:52AM +0900, Kuwahara,T. wrote:
> On 12/17/10, Richard Cochran <richardcochran@gmail.com> wrote:
> > This patch adds a new mode bit into the timex structure. When set, the bit
> > instructs the kernel to add the given time value to the current time.
> >
>
> The proposed new control mode, ADJ_SETOFFSET, is logically the same as
> ADJ_OFFSET with timex.constant == -INFINITY. So it is possible to do
> the same thing without risking forward compatibility. (I mean by "risking
> forward compatibility" that the mode bit 0x0040 may be defined differently
> by the upstream maintainer anytime in the future.)
Can you please elaborate?
I don't see any way to use timex.constant with ADJ_OFFSET in order to
correct a time offset. The 'time_constant' in kernel/time/ntp.c is
restricted to the interval [0..MAXTC], and MAXTC is 10 in timex.h.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
[parent not found: <20101221075612.GA13626-7KxsofuKt4IfAd9E5cN8NEzG7cXyKsk/@public.gmane.org>]
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-21 7:56 ` Richard Cochran
@ 2010-12-21 20:57 ` Kuwahara,T.
-1 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-21 20:57 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Tue, Dec 21, 2010 at 4:56 PM, Richard Cochran
<richardcochran-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Can you please elaborate?
The timex.constant is defined as equal to the binary logarithm of the
reciprocal of
the natural frequency minus SHIFT_PLL. In other words, the following
equation holds:
log2(natural frequency) + time_constant + SHIFT_PLL = 0,
which means that decreasing time_constant increases natural frequency
exponentially.
And since a larger natural frequency gives a smaller settling time, a
sufficiently
large negative time_constant results in immediate time step, at least in theory.
> I don't see any way to use timex.constant with ADJ_OFFSET in order to
> correct a time offset.
How about this?
if (txc->modes & ADJ_OFFSET) {
if (txc->constant == INT32_MIN) {
/* step time */
} else {
/* slew time */
}
}
> The 'time_constant' in kernel/time/ntp.c is
> restricted to the interval [0..MAXTC], and MAXTC is 10 in timex.h.
Then let's just ignore the restriction. (It's possible by setting the
timex.constant
without setting the ADJ_TIMECONST flag.)
That said, I'm somehow against the idea of using the adjtimex syscall
for that purpose.
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-21 20:57 ` Kuwahara,T.
0 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-21 20:57 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Tue, Dec 21, 2010 at 4:56 PM, Richard Cochran
<richardcochran@gmail.com> wrote:
> Can you please elaborate?
The timex.constant is defined as equal to the binary logarithm of the
reciprocal of
the natural frequency minus SHIFT_PLL. In other words, the following
equation holds:
log2(natural frequency) + time_constant + SHIFT_PLL = 0,
which means that decreasing time_constant increases natural frequency
exponentially.
And since a larger natural frequency gives a smaller settling time, a
sufficiently
large negative time_constant results in immediate time step, at least in theory.
> I don't see any way to use timex.constant with ADJ_OFFSET in order to
> correct a time offset.
How about this?
if (txc->modes & ADJ_OFFSET) {
if (txc->constant == INT32_MIN) {
/* step time */
} else {
/* slew time */
}
}
> The 'time_constant' in kernel/time/ntp.c is
> restricted to the interval [0..MAXTC], and MAXTC is 10 in timex.h.
Then let's just ignore the restriction. (It's possible by setting the
timex.constant
without setting the ADJ_TIMECONST flag.)
That said, I'm somehow against the idea of using the adjtimex syscall
for that purpose.
^ permalink raw reply [flat|nested] 68+ messages in thread[parent not found: <AANLkTimJd5pScKTiDxuYd-h+NevYbCusyvUAqmUDXe8h-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-21 20:57 ` Kuwahara,T.
@ 2010-12-21 22:25 ` john stultz
-1 siblings, 0 replies; 68+ messages in thread
From: john stultz @ 2010-12-21 22:25 UTC (permalink / raw)
To: Kuwahara,T.
Cc: Richard Cochran, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Wed, 2010-12-22 at 05:57 +0900, Kuwahara,T. wrote:
> How about this?
>
> if (txc->modes & ADJ_OFFSET) {
> if (txc->constant == INT32_MIN) {
> /* step time */
> } else {
> /* slew time */
> }
> }
This looks like magic behavior. Sort of a "knock twice and then say the
password" interface. I don't see why that would be better then adding a
clear new mode flag?
> That said, I'm somehow against the idea of using the adjtimex syscall
> for that purpose.
Could you expand on why you don't like this?
thanks
-john
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-21 22:25 ` john stultz
0 siblings, 0 replies; 68+ messages in thread
From: john stultz @ 2010-12-21 22:25 UTC (permalink / raw)
To: Kuwahara,T.
Cc: Richard Cochran, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Wed, 2010-12-22 at 05:57 +0900, Kuwahara,T. wrote:
> How about this?
>
> if (txc->modes & ADJ_OFFSET) {
> if (txc->constant == INT32_MIN) {
> /* step time */
> } else {
> /* slew time */
> }
> }
This looks like magic behavior. Sort of a "knock twice and then say the
password" interface. I don't see why that would be better then adding a
clear new mode flag?
> That said, I'm somehow against the idea of using the adjtimex syscall
> for that purpose.
Could you expand on why you don't like this?
thanks
-john
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-21 22:25 ` john stultz
(?)
@ 2010-12-22 7:13 ` Richard Cochran
-1 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-22 7:13 UTC (permalink / raw)
To: john stultz
Cc: Kuwahara,T., linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Tue, Dec 21, 2010 at 02:25:55PM -0800, john stultz wrote:
> On Wed, 2010-12-22 at 05:57 +0900, Kuwahara,T. wrote:
>
> > How about this?
> >
> > if (txc->modes & ADJ_OFFSET) {
> > if (txc->constant == INT32_MIN) {
> > /* step time */
> > } else {
> > /* slew time */
> > }
> > }
>
> This looks like magic behavior. Sort of a "knock twice and then say the
> password" interface. I don't see why that would be better then adding a
> clear new mode flag?
I have to agree with John on this one. Looks very hacky to me.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-21 22:25 ` john stultz
@ 2010-12-22 20:27 ` Kuwahara,T.
-1 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-22 20:27 UTC (permalink / raw)
To: john stultz
Cc: Richard Cochran, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> wrote:
> I don't see why that would be better then adding a
> clear new mode flag?
In short, time step is a special case of time slew. Those are the same,
only different in one parameter, as is shown in my previous post.
That's why I said there's no need for adding a new mode.
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-22 20:27 ` Kuwahara,T.
0 siblings, 0 replies; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-22 20:27 UTC (permalink / raw)
To: john stultz
Cc: Richard Cochran, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul@us.ibm.com> wrote:
> I don't see why that would be better then adding a
> clear new mode flag?
In short, time step is a special case of time slew. Those are the same,
only different in one parameter, as is shown in my previous post.
That's why I said there's no need for adding a new mode.
^ permalink raw reply [flat|nested] 68+ messages in thread
[parent not found: <AANLkTimmTzH8+fSYmbajqZ+hU5Ps-UZaTp_1TYzjHB6P-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-22 20:27 ` Kuwahara,T.
@ 2010-12-23 0:00 ` john stultz
-1 siblings, 0 replies; 68+ messages in thread
From: john stultz @ 2010-12-23 0:00 UTC (permalink / raw)
To: Kuwahara,T.
Cc: Richard Cochran, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Thu, 2010-12-23 at 05:27 +0900, Kuwahara,T. wrote:
> On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> wrote:
> > I don't see why that would be better then adding a
> > clear new mode flag?
>
> In short, time step is a special case of time slew. Those are the same,
> only different in one parameter, as is shown in my previous post.
> That's why I said there's no need for adding a new mode.
Sure, I get that a instantaneous offset adjustment could be represented
as a instantaneous slew of the same amount. But what is the benefit of
doing this? The ADJ_OFFSET isn't really a continuous function like you
describe. For instance, you can't slew time backwards. The amount you
can slow or speed up the clock is limited, so time will always increase.
So to have a magic value way outside the bound of normal operation that
does something special seems a bit obfuscated.
ADJ_SETOFFSET isn't slewing the clock. So I think its much clearer to
add a new mode, rather then to try to wedge the functionality into a
corner case of ADJ_OFFSET slewing just because one could mathematically
represent it the same way.
I see the cost of adding it as a special case as adding extra complexity
to an already complex subsystem. Doing things that make the code easier
to understand and read makes it more maintainable. And I don't see the
gain (other then saving a bit in the bit flag) to offset the complexity
of trying to squeeze this functionality into the ADJ_OFFSET mode.
thanks
-john
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-23 0:00 ` john stultz
0 siblings, 0 replies; 68+ messages in thread
From: john stultz @ 2010-12-23 0:00 UTC (permalink / raw)
To: Kuwahara,T.
Cc: Richard Cochran, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Thu, 2010-12-23 at 05:27 +0900, Kuwahara,T. wrote:
> On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul@us.ibm.com> wrote:
> > I don't see why that would be better then adding a
> > clear new mode flag?
>
> In short, time step is a special case of time slew. Those are the same,
> only different in one parameter, as is shown in my previous post.
> That's why I said there's no need for adding a new mode.
Sure, I get that a instantaneous offset adjustment could be represented
as a instantaneous slew of the same amount. But what is the benefit of
doing this? The ADJ_OFFSET isn't really a continuous function like you
describe. For instance, you can't slew time backwards. The amount you
can slow or speed up the clock is limited, so time will always increase.
So to have a magic value way outside the bound of normal operation that
does something special seems a bit obfuscated.
ADJ_SETOFFSET isn't slewing the clock. So I think its much clearer to
add a new mode, rather then to try to wedge the functionality into a
corner case of ADJ_OFFSET slewing just because one could mathematically
represent it the same way.
I see the cost of adding it as a special case as adding extra complexity
to an already complex subsystem. Doing things that make the code easier
to understand and read makes it more maintainable. And I don't see the
gain (other then saving a bit in the bit flag) to offset the complexity
of trying to squeeze this functionality into the ADJ_OFFSET mode.
thanks
-john
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-22 20:27 ` Kuwahara,T.
@ 2010-12-23 6:13 ` Richard Cochran
-1 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-23 6:13 UTC (permalink / raw)
To: Kuwahara,T.
Cc: john stultz, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Thu, Dec 23, 2010 at 05:27:58AM +0900, Kuwahara,T. wrote:
> On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org> wrote:
> > I don't see why that would be better then adding a
> > clear new mode flag?
>
> In short, time step is a special case of time slew. Those are the same,
> only different in one parameter, as is shown in my previous post.
> That's why I said there's no need for adding a new mode.
Well, in addition to the objections raised by John, your suggested
implementation is also shortsighted. The field timex.constant is
copied into time_constant in some code paths. Obviously, this would be
a bad thing when timex.constant==-huge.
So, you need to clarify the interaction between ADJ_OFFSET,
ADJ_TIMECONST, ADJ_TAI, timex.constant, time_constant, and MAXTC.
If you would fully implement your idea, I expect it would become
obvious that it a bit of a hack, both in the kernel code and in the
user space interface. But, if you disagree, please just post a patch
with the complete implementation...
Thanks,
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
@ 2010-12-23 6:13 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-23 6:13 UTC (permalink / raw)
To: Kuwahara,T.
Cc: john stultz, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Thu, Dec 23, 2010 at 05:27:58AM +0900, Kuwahara,T. wrote:
> On Wed, Dec 22, 2010 at 7:25 AM, john stultz <johnstul@us.ibm.com> wrote:
> > I don't see why that would be better then adding a
> > clear new mode flag?
>
> In short, time step is a special case of time slew. Those are the same,
> only different in one parameter, as is shown in my previous post.
> That's why I said there's no need for adding a new mode.
Well, in addition to the objections raised by John, your suggested
implementation is also shortsighted. The field timex.constant is
copied into time_constant in some code paths. Obviously, this would be
a bad thing when timex.constant==-huge.
So, you need to clarify the interaction between ADJ_OFFSET,
ADJ_TIMECONST, ADJ_TAI, timex.constant, time_constant, and MAXTC.
If you would fully implement your idea, I expect it would become
obvious that it a bit of a hack, both in the kernel code and in the
user space interface. But, if you disagree, please just post a patch
with the complete implementation...
Thanks,
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-23 6:13 ` Richard Cochran
(?)
@ 2010-12-25 20:38 ` Kuwahara,T.
2010-12-26 14:14 ` Richard Cochran
-1 siblings, 1 reply; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-25 20:38 UTC (permalink / raw)
To: Richard Cochran
Cc: john stultz, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
After all, I'd prefer your earlier patchset. Leaving aside the
compatibility issue, there's no particular reason we have to re-use
the struct timex, which requires otherwise unnecessary conditional
branches as well as unit conversions. Don't you agree?
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-25 20:38 ` Kuwahara,T.
@ 2010-12-26 14:14 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-26 14:14 UTC (permalink / raw)
To: Kuwahara,T.
Cc: john stultz, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sun, Dec 26, 2010 at 05:38:57AM +0900, Kuwahara,T. wrote:
> After all, I'd prefer your earlier patchset. Leaving aside the
> compatibility issue, there's no particular reason we have to re-use
> the struct timex, which requires otherwise unnecessary conditional
> branches as well as unit conversions. Don't you agree?
Well, from my point of view of wanting to allow a user space clock
servo to be able to adjust a hardware clock, it would be sufficient to
offer a way to jump the clock and to adjust the frequency via one or
perhaps two new system calls.
That is indeed what I first suggested, and I still think it would be a
clean and simple interface. The NTP call is quite gross, since it
multiplexes a whole bunch of different functions through the timex
structure.
However, several reviewers on the lkml prefered to keep with the NTP
interface. It offers the needed functionality and is already well
established, despite its ugliness.
To me, the proposed ADJ_SETOFFSET is a simple and logical extenstion
of the NTP interface. Also, the implementation is straightforward. In
contrast, using a special -INF value seems a bit obtuse to me.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-17 20:16 ` Kuwahara,T.
(?)
(?)
@ 2010-12-21 19:37 ` john stultz
2010-12-21 21:13 ` Kuwahara,T.
-1 siblings, 1 reply; 68+ messages in thread
From: john stultz @ 2010-12-21 19:37 UTC (permalink / raw)
To: Kuwahara,T.
Cc: Richard Cochran, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sat, 2010-12-18 at 05:16 +0900, Kuwahara,T. wrote:
> On 12/17/10, Richard Cochran <richardcochran@gmail.com> wrote:
> > This patch adds a new mode bit into the timex structure. When set, the bit
> > instructs the kernel to add the given time value to the current time.
> >
>
> The proposed new control mode, ADJ_SETOFFSET, is logically the same as
> ADJ_OFFSET with timex.constant == -INFINITY.
I'm not sure if this is correct. Its more like settimeofday, only giving
a relative offset to jump the clock, rather then an absolute time. It
does not slew the clock over time like ADJ_OFFSET does.
> So it is possible to do
> the same thing without risking forward compatibility. (I mean by "risking
> forward compatibility" that the mode bit 0x0040 may be defined differently
> by the upstream maintainer anytime in the future.)
adjtimex is a linux specific interface, which is compatible but not
identical to the ntp specified interfaces. The ntp client code already
has Linux specific modifications, so I don't think we have to worry
about 0x40 specifically being reserved by the NTP client.
thanks
-john
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 1/8] ntp: add ADJ_SETOFFSET mode bit
2010-12-21 19:37 ` john stultz
@ 2010-12-21 21:13 ` Kuwahara,T.
[not found] ` <AANLkTikHV-qN73Zvvvxn76vtFFKG_VNTQhF3qpqTnOrE-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
0 siblings, 1 reply; 68+ messages in thread
From: Kuwahara,T. @ 2010-12-21 21:13 UTC (permalink / raw)
To: john stultz
Cc: Richard Cochran, linux-kernel, linux-api, netdev, Alan Cox,
Arnd Bergmann, Christoph Lameter, David Miller, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Wed, Dec 22, 2010 at 4:37 AM, john stultz <johnstul@us.ibm.com> wrote:
> adjtimex is a linux specific interface, which is compatible but not
> identical to the ntp specified interfaces. The ntp client code already
> has Linux specific modifications, so I don't think we have to worry
> about 0x40 specifically being reserved by the NTP client.
But struct timex is not linux-specific...
^ permalink raw reply [flat|nested] 68+ messages in thread
* [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
2010-12-16 15:41 [PATCH V7 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-12-16 15:42 ` Richard Cochran
[not found] ` <cover.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
` (2 subsequent siblings)
3 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:42 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
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. Any ADJTIMEX(2) operation may be
requested via this system call, but various POSIX clocks may or may
not support tuning.
Signed-off-by: Richard Cochran <richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
---
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 +
drivers/char/mmtimer.c | 1 +
include/linux/posix-timers.h | 4 +
include/linux/syscalls.h | 2 +
kernel/compat.c | 136 +++++++++++++++++++++++-------------
kernel/posix-cpu-timers.c | 6 ++
kernel/posix-timers.c | 35 +++++++++
16 files changed, 150 insertions(+), 51 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 928ae97..e640c51 100644
--- a/arch/blackfin/include/asm/unistd.h
+++ b/arch/blackfin/include/asm/unistd.h
@@ -393,8 +393,9 @@
#define __NR_fanotify_mark 372
#define __NR_prlimit64 373
#define __NR_cacheflush 374
+#define __NR_clock_adjtime 375
-#define __NR_syscall 375
+#define __NR_syscall 376
#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 2ca915e..cb6cc1e 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1738,6 +1738,7 @@ ENTRY(_sys_call_table)
.long _sys_fanotify_mark
.long _sys_prlimit64
.long _sys_cacheflush
+ .long _sys_clock_adjtime /* 375 */
.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 aa0f1eb..6a1152c 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -348,3 +348,4 @@ COMPAT_SYS_SPU(sendmsg)
COMPAT_SYS_SPU(recvmsg)
COMPAT_SYS_SPU(recvmmsg)
SYSCALL_SPU(accept4)
+COMPAT_SYS_SPU(clock_adjtime)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index 6151937..386de07 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -367,10 +367,11 @@
#define __NR_recvmsg 342
#define __NR_recvmmsg 343
#define __NR_accept4 344
+#define __NR_clock_adjtime 345
#ifdef __KERNEL__
-#define __NR_syscalls 345
+#define __NR_syscalls 346
#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/drivers/char/mmtimer.c b/drivers/char/mmtimer.c
index e6d7562..98f2488 100644
--- a/drivers/char/mmtimer.c
+++ b/drivers/char/mmtimer.c
@@ -767,6 +767,7 @@ static struct k_clock sgi_clock = {
.res = 0,
.clock_set = sgi_clock_set,
.clock_get = sgi_clock_get,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = sgi_timer_create,
.nsleep = do_posix_clock_nonanosleep,
.timer_set = sgi_timer_set,
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 3e23844..b05d9b8 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 *);
@@ -90,6 +92,7 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock);
int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *,
struct timespec __user *);
int do_posix_clock_nosettime(const clockid_t, struct timespec *tp);
+int do_posix_clock_noadjtime(const clockid_t, struct timex *tx);
/* function to call to trigger timer event */
int posix_timer_event(struct k_itimer *timr, int si_private);
@@ -97,6 +100,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 cacc27a..c76cefb 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/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 05bb717..0206116 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.
@@ -1610,6 +1614,7 @@ static __init int init_posix_cpu_timers(void)
.clock_getres = process_cpu_clock_getres,
.clock_get = process_cpu_clock_get,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = process_cpu_timer_create,
.nsleep = process_cpu_nsleep,
.nsleep_restart = process_cpu_nsleep_restart,
@@ -1618,6 +1623,7 @@ static __init int init_posix_cpu_timers(void)
.clock_getres = thread_cpu_clock_getres,
.clock_get = thread_cpu_clock_get,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = thread_cpu_timer_create,
.nsleep = thread_cpu_nsleep,
.nsleep_restart = thread_cpu_nsleep_restart,
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 9ca4973..502bde4 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -197,6 +197,11 @@ 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)
+{
+ return do_adjtimex(t);
+}
+
static int no_timer_create(struct k_itimer *new_timer)
{
return -EOPNOTSUPP;
@@ -273,11 +278,13 @@ static __init int init_posix_timers(void)
.clock_getres = hrtimer_get_res,
.clock_get = posix_ktime_get_ts,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
};
struct k_clock clock_monotonic_raw = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_monotonic_raw,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -285,6 +292,7 @@ static __init int init_posix_timers(void)
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_realtime_coarse,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -292,6 +300,7 @@ static __init int init_posix_timers(void)
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -928,6 +937,12 @@ int do_posix_clock_nosettime(const clockid_t clockid, struct timespec *tp)
}
EXPORT_SYMBOL_GPL(do_posix_clock_nosettime);
+int do_posix_clock_noadjtime(const clockid_t which_clock, struct timex *t)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(do_posix_clock_noadjtime);
+
int do_posix_clock_nonanosleep(const clockid_t clock, int flags,
struct timespec *t, struct timespec __user *r)
{
@@ -969,6 +984,26 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
}
+SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
+ struct timex __user *, utx)
+{
+ struct timex ktx;
+ int err;
+
+ if (copy_from_user(&ktx, utx, sizeof(ktx)))
+ return -EFAULT;
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ err = CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, &ktx));
+
+ if (copy_to_user(utx, &ktx, sizeof(ktx)))
+ return -EFAULT;
+
+ return err;
+}
+
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
struct timespec __user *, tp)
{
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread* [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
@ 2010-12-16 15:42 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:42 UTC (permalink / raw)
To: linux-kernel
Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
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. Any ADJTIMEX(2) operation may be
requested via this system call, but various POSIX clocks may or may
not support tuning.
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 +
drivers/char/mmtimer.c | 1 +
include/linux/posix-timers.h | 4 +
include/linux/syscalls.h | 2 +
kernel/compat.c | 136 +++++++++++++++++++++++-------------
kernel/posix-cpu-timers.c | 6 ++
kernel/posix-timers.c | 35 +++++++++
16 files changed, 150 insertions(+), 51 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 928ae97..e640c51 100644
--- a/arch/blackfin/include/asm/unistd.h
+++ b/arch/blackfin/include/asm/unistd.h
@@ -393,8 +393,9 @@
#define __NR_fanotify_mark 372
#define __NR_prlimit64 373
#define __NR_cacheflush 374
+#define __NR_clock_adjtime 375
-#define __NR_syscall 375
+#define __NR_syscall 376
#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 2ca915e..cb6cc1e 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1738,6 +1738,7 @@ ENTRY(_sys_call_table)
.long _sys_fanotify_mark
.long _sys_prlimit64
.long _sys_cacheflush
+ .long _sys_clock_adjtime /* 375 */
.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 aa0f1eb..6a1152c 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -348,3 +348,4 @@ COMPAT_SYS_SPU(sendmsg)
COMPAT_SYS_SPU(recvmsg)
COMPAT_SYS_SPU(recvmmsg)
SYSCALL_SPU(accept4)
+COMPAT_SYS_SPU(clock_adjtime)
diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h
index 6151937..386de07 100644
--- a/arch/powerpc/include/asm/unistd.h
+++ b/arch/powerpc/include/asm/unistd.h
@@ -367,10 +367,11 @@
#define __NR_recvmsg 342
#define __NR_recvmmsg 343
#define __NR_accept4 344
+#define __NR_clock_adjtime 345
#ifdef __KERNEL__
-#define __NR_syscalls 345
+#define __NR_syscalls 346
#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/drivers/char/mmtimer.c b/drivers/char/mmtimer.c
index e6d7562..98f2488 100644
--- a/drivers/char/mmtimer.c
+++ b/drivers/char/mmtimer.c
@@ -767,6 +767,7 @@ static struct k_clock sgi_clock = {
.res = 0,
.clock_set = sgi_clock_set,
.clock_get = sgi_clock_get,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = sgi_timer_create,
.nsleep = do_posix_clock_nonanosleep,
.timer_set = sgi_timer_set,
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 3e23844..b05d9b8 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 *);
@@ -90,6 +92,7 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock);
int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *,
struct timespec __user *);
int do_posix_clock_nosettime(const clockid_t, struct timespec *tp);
+int do_posix_clock_noadjtime(const clockid_t, struct timex *tx);
/* function to call to trigger timer event */
int posix_timer_event(struct k_itimer *timr, int si_private);
@@ -97,6 +100,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 cacc27a..c76cefb 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/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 05bb717..0206116 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.
@@ -1610,6 +1614,7 @@ static __init int init_posix_cpu_timers(void)
.clock_getres = process_cpu_clock_getres,
.clock_get = process_cpu_clock_get,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = process_cpu_timer_create,
.nsleep = process_cpu_nsleep,
.nsleep_restart = process_cpu_nsleep_restart,
@@ -1618,6 +1623,7 @@ static __init int init_posix_cpu_timers(void)
.clock_getres = thread_cpu_clock_getres,
.clock_get = thread_cpu_clock_get,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = thread_cpu_timer_create,
.nsleep = thread_cpu_nsleep,
.nsleep_restart = thread_cpu_nsleep_restart,
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 9ca4973..502bde4 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -197,6 +197,11 @@ 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)
+{
+ return do_adjtimex(t);
+}
+
static int no_timer_create(struct k_itimer *new_timer)
{
return -EOPNOTSUPP;
@@ -273,11 +278,13 @@ static __init int init_posix_timers(void)
.clock_getres = hrtimer_get_res,
.clock_get = posix_ktime_get_ts,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
};
struct k_clock clock_monotonic_raw = {
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_monotonic_raw,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -285,6 +292,7 @@ static __init int init_posix_timers(void)
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_realtime_coarse,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -292,6 +300,7 @@ static __init int init_posix_timers(void)
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
.clock_set = do_posix_clock_nosettime,
+ .clock_adj = do_posix_clock_noadjtime,
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
@@ -928,6 +937,12 @@ int do_posix_clock_nosettime(const clockid_t clockid, struct timespec *tp)
}
EXPORT_SYMBOL_GPL(do_posix_clock_nosettime);
+int do_posix_clock_noadjtime(const clockid_t which_clock, struct timex *t)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(do_posix_clock_noadjtime);
+
int do_posix_clock_nonanosleep(const clockid_t clock, int flags,
struct timespec *t, struct timespec __user *r)
{
@@ -969,6 +984,26 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
}
+SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
+ struct timex __user *, utx)
+{
+ struct timex ktx;
+ int err;
+
+ if (copy_from_user(&ktx, utx, sizeof(ktx)))
+ return -EFAULT;
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ err = CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, &ktx));
+
+ if (copy_to_user(utx, &ktx, sizeof(ktx)))
+ return -EFAULT;
+
+ return err;
+}
+
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
struct timespec __user *, tp)
{
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread[parent not found: <a93ce18c2987f7e102bc2ff5b12130211c222ea7.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>]
* Re: [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
2010-12-16 15:42 ` Richard Cochran
@ 2010-12-16 15:51 ` Arnd Bergmann
-1 siblings, 0 replies; 68+ messages in thread
From: Arnd Bergmann @ 2010-12-16 15:51 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Christoph Lameter, David Miller, John Stultz,
Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Thursday 16 December 2010, 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. Any ADJTIMEX(2) operation may be
> requested via this system call, but various POSIX clocks may or may
> not support tuning.
>
> Signed-off-by: Richard Cochran <richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
The syscall implementation looks good to me.
Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
It would be good to add it to include/asm-generic/unistd.h
as well, so that tile and future architectures get it, too.
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
@ 2010-12-16 15:51 ` Arnd Bergmann
0 siblings, 0 replies; 68+ messages in thread
From: Arnd Bergmann @ 2010-12-16 15:51 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
On Thursday 16 December 2010, 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. Any ADJTIMEX(2) operation may be
> requested via this system call, but various POSIX clocks may or may
> not support tuning.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
The syscall implementation looks good to me.
Acked-by: Arnd Bergmann <arnd@arndb.de>
It would be good to add it to include/asm-generic/unistd.h
as well, so that tile and future architectures get it, too.
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
2010-12-16 15:42 ` Richard Cochran
@ 2010-12-16 17:55 ` Thomas Gleixner
-1 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 17:55 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, 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.
Can you please split that patch into one providing the syscall itself
and for each architecture a separate one ?
Looks good otherwise.
Reviewed-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 2/8] posix clocks: introduce a syscall for clock tuning.
@ 2010-12-16 17:55 ` Thomas Gleixner
0 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 17:55 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, 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.
Can you please split that patch into one providing the syscall itself
and for each architecture a separate one ?
Looks good otherwise.
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
^ permalink raw reply [flat|nested] 68+ messages in thread
* [PATCH V7 4/8] posix clocks: hook dynamic clocks into system calls
2010-12-16 15:41 [PATCH V7 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-12-16 15:43 ` Richard Cochran
[not found] ` <cover.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
` (2 subsequent siblings)
3 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
This patch lets the various posix clock and timer system calls use
dynamic posix clocks, as well as the traditional static clocks.
For the performance critical calls (eg clock_gettime) the code path
from the traditional static clocks is preserved.
The following system calls are affected:
- clock_adjtime (brand new syscall)
- clock_gettime
- clock_getres
- clock_settime
- timer_create
- timer_delete
- timer_gettime
- timer_settime
Signed-off-by: Richard Cochran <richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
---
include/linux/posix-clock.h | 25 +++++++
include/linux/posix-timers.h | 28 +++++++-
include/linux/time.h | 2 +
kernel/posix-timers.c | 122 +++++++++++++++++++++++++++-----
kernel/time/posix-clock.c | 159 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 316 insertions(+), 20 deletions(-)
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 1ce7fb7..7ea94f5 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -108,4 +108,29 @@ struct posix_clock *posix_clock_create(struct posix_clock_operations *cops,
*/
void posix_clock_destroy(struct posix_clock *clk);
+/**
+ * clockid_to_posix_clock() - obtain clock device pointer from a clock id
+ * @id: user space clock id
+ *
+ * Returns a pointer to the clock device, or NULL if the id is not a
+ * dynamic clock id.
+ */
+struct posix_clock *clockid_to_posix_clock(clockid_t id);
+
+/*
+ * The following functions are only to be called from posix-timers.c
+ */
+int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx);
+int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts);
+int pc_clock_getres(struct posix_clock *clk, struct timespec *ts);
+int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts);
+
+int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit);
+int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit);
+
+void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
+ struct itimerspec *ts);
+int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
+ int flags, struct itimerspec *ts, struct itimerspec *old);
+
#endif
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index b05d9b8..62ae296 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -18,10 +18,22 @@ struct cpu_timer_list {
int firing;
};
+/* Bit fields within a clockid:
+ *
+ * The most significant 29 bits hold either a pid or a file descriptor.
+ *
+ * Bit 2 indicates whether a cpu clock refers to a thread or a process.
+ *
+ * Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or FD=3.
+ *
+ * A clockid is invalid if bits 2, 1, and 0 all set (see also CLOCK_INVALID
+ * in include/linux/time.h)
+ */
+
#define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3))
#define CPUCLOCK_PERTHREAD(clock) \
(((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0)
-#define CPUCLOCK_PID_MASK 7
+
#define CPUCLOCK_PERTHREAD_MASK 4
#define CPUCLOCK_WHICH(clock) ((clock) & (clockid_t) CPUCLOCK_CLOCK_MASK)
#define CPUCLOCK_CLOCK_MASK 3
@@ -29,12 +41,26 @@ struct cpu_timer_list {
#define CPUCLOCK_VIRT 1
#define CPUCLOCK_SCHED 2
#define CPUCLOCK_MAX 3
+#define CLOCKFD CPUCLOCK_MAX
+#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define MAKE_PROCESS_CPUCLOCK(pid, clock) \
((~(clockid_t) (pid) << 3) | (clockid_t) (clock))
#define MAKE_THREAD_CPUCLOCK(tid, clock) \
MAKE_PROCESS_CPUCLOCK((tid), (clock) | CPUCLOCK_PERTHREAD_MASK)
+#define FD_TO_CLOCKID(fd) ((clockid_t) (fd << 3) | CLOCKFD)
+#define CLOCKID_TO_FD(clk) (((unsigned int) clk) >> 3)
+
+static inline bool clock_is_static(const clockid_t id)
+{
+ if (0 == (id & ~CLOCKFD_MASK))
+ return true;
+ if (CLOCKFD == (id & CLOCKFD_MASK))
+ return false;
+ return true;
+}
+
/* POSIX.1b interval timer structure. */
struct k_itimer {
struct list_head list; /* free/ allocate list */
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 502bde4..cca3bd5 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -41,6 +41,7 @@
#include <linux/init.h>
#include <linux/compiler.h>
#include <linux/idr.h>
+#include <linux/posix-clock.h>
#include <linux/posix-timers.h>
#include <linux/syscalls.h>
#include <linux/wait.h>
@@ -532,12 +533,15 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
struct sigevent __user *, timer_event_spec,
timer_t __user *, created_timer_id)
{
+ struct posix_clock *clk_dev;
struct k_itimer *new_timer;
int error, new_timer_id;
sigevent_t event;
int it_id_set = IT_ID_NOT_SET;
- if (invalid_clockid(which_clock))
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev && invalid_clockid(which_clock))
return -EINVAL;
new_timer = alloc_posix_timer();
@@ -600,7 +604,11 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
goto out;
}
- error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
+ if (unlikely(clk_dev))
+ error = pc_timer_create(clk_dev, new_timer);
+ else {
+ error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
+ }
if (error)
goto out;
@@ -712,6 +720,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
struct itimerspec __user *, setting)
{
+ struct posix_clock *clk_dev;
struct k_itimer *timr;
struct itimerspec cur_setting;
unsigned long flags;
@@ -720,7 +729,15 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
if (!timr)
return -EINVAL;
- CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
+ if (likely(clock_is_static(timr->it_clock))) {
+
+ CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
+
+ } else {
+ clk_dev = clockid_to_posix_clock(timr->it_clock);
+ if (clk_dev)
+ pc_timer_gettime(clk_dev, timr, &cur_setting);
+ }
unlock_timer(timr, flags);
@@ -811,6 +828,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
const struct itimerspec __user *, new_setting,
struct itimerspec __user *, old_setting)
{
+ struct posix_clock *clk_dev;
struct k_itimer *timr;
struct itimerspec new_spec, old_spec;
int error = 0;
@@ -831,8 +849,19 @@ retry:
if (!timr)
return -EINVAL;
- error = CLOCK_DISPATCH(timr->it_clock, timer_set,
- (timr, flags, &new_spec, rtn));
+ if (likely(clock_is_static(timr->it_clock))) {
+
+ error = CLOCK_DISPATCH(timr->it_clock, timer_set,
+ (timr, flags, &new_spec, rtn));
+
+ } else {
+ clk_dev = clockid_to_posix_clock(timr->it_clock);
+ if (clk_dev)
+ error = pc_timer_settime(clk_dev, timr,
+ flags, &new_spec, rtn);
+ else
+ error = -EINVAL;
+ }
unlock_timer(timr, flag);
if (error == TIMER_RETRY) {
@@ -858,6 +887,13 @@ static inline int common_timer_del(struct k_itimer *timer)
static inline int timer_delete_hook(struct k_itimer *timer)
{
+ struct posix_clock *clk_dev;
+
+ clk_dev = clockid_to_posix_clock(timer->it_clock);
+
+ if (clk_dev)
+ return pc_timer_delete(clk_dev, timer);
+
return CLOCK_DISPATCH(timer->it_clock, timer_del, (timer));
}
@@ -957,26 +993,51 @@ EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
const struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev = NULL;
struct timespec new_tp;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+ if (!clk_dev)
+ return -EINVAL;
+ }
+
if (copy_from_user(&new_tp, tp, sizeof (*tp)))
return -EFAULT;
+ if (unlikely(clk_dev))
+ return pc_clock_settime(clk_dev, &new_tp);
+
return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
}
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
- struct timespec __user *,tp)
+ struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev;
struct timespec kernel_tp;
int error;
- if (invalid_clockid(which_clock))
- return -EINVAL;
- error = CLOCK_DISPATCH(which_clock, clock_get,
- (which_clock, &kernel_tp));
+ if (likely(clock_is_static(which_clock))) {
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ error = CLOCK_DISPATCH(which_clock, clock_get,
+ (which_clock, &kernel_tp));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ error = pc_clock_gettime(clk_dev, &kernel_tp);
+ }
+
if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp)))
error = -EFAULT;
@@ -987,16 +1048,28 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
struct timex __user *, utx)
{
+ struct posix_clock *clk_dev;
struct timex ktx;
int err;
if (copy_from_user(&ktx, utx, sizeof(ktx)))
return -EFAULT;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
- err = CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, &ktx));
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ err = CLOCK_DISPATCH(which_clock, clock_adj,
+ (which_clock, &ktx));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ err = pc_clock_adjtime(clk_dev, &ktx);
+ }
if (copy_to_user(utx, &ktx, sizeof(ktx)))
return -EFAULT;
@@ -1007,14 +1080,25 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev;
struct timespec rtn_tp;
int error;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
- error = CLOCK_DISPATCH(which_clock, clock_getres,
- (which_clock, &rtn_tp));
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ error = CLOCK_DISPATCH(which_clock, clock_getres,
+ (which_clock, &rtn_tp));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ error = pc_clock_getres(clk_dev, &rtn_tp);
+ }
if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) {
error = -EFAULT;
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index e5924c9..a707c10 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -19,9 +19,12 @@
*/
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/file.h>
#include <linux/mutex.h>
#include <linux/posix-clock.h>
#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
#define MAX_CLKDEV BITS_PER_LONG
static DECLARE_BITMAP(clocks_map, MAX_CLKDEV);
@@ -215,3 +218,159 @@ void posix_clock_destroy(struct posix_clock *clk)
kref_put(&clk->kref, delete_clock);
}
EXPORT_SYMBOL_GPL(posix_clock_destroy);
+
+struct posix_clock *clockid_to_posix_clock(const clockid_t id)
+{
+ struct posix_clock *clk = NULL;
+ struct file *fp;
+
+ if (clock_is_static(id))
+ return NULL;
+
+ fp = fget(CLOCKID_TO_FD(id));
+ if (!fp)
+ return NULL;
+
+ if (fp->f_op->open == posix_clock_open)
+ clk = fp->private_data;
+
+ fput(fp);
+ return clk;
+}
+
+int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_adjtime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_adjtime(clk->priv, tx);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_gettime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_gettime(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_getres(struct posix_clock *clk, struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_getres)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_getres(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_settime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_settime(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_create)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_create(clk->priv, kit);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_delete)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_delete(clk->priv, kit);
+
+ mutex_unlock(&clk->mux);
+
+ return err;
+}
+
+void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
+ struct itimerspec *ts)
+{
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ goto out;
+ else if (!clk->ops->timer_gettime)
+ goto out;
+ else
+ clk->ops->timer_gettime(clk->priv, kit, ts);
+out:
+ mutex_unlock(&clk->mux);
+}
+
+int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
+ int flags, struct itimerspec *ts, struct itimerspec *old)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_settime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_settime(clk->priv, kit, flags, ts, old);
+
+ mutex_unlock(&clk->mux);
+
+ return err;
+}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread* [PATCH V7 4/8] posix clocks: hook dynamic clocks into system calls
@ 2010-12-16 15:43 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:43 UTC (permalink / raw)
To: linux-kernel
Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
This patch lets the various posix clock and timer system calls use
dynamic posix clocks, as well as the traditional static clocks.
For the performance critical calls (eg clock_gettime) the code path
from the traditional static clocks is preserved.
The following system calls are affected:
- clock_adjtime (brand new syscall)
- clock_gettime
- clock_getres
- clock_settime
- timer_create
- timer_delete
- timer_gettime
- timer_settime
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
---
include/linux/posix-clock.h | 25 +++++++
include/linux/posix-timers.h | 28 +++++++-
include/linux/time.h | 2 +
kernel/posix-timers.c | 122 +++++++++++++++++++++++++++-----
kernel/time/posix-clock.c | 159 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 316 insertions(+), 20 deletions(-)
diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
index 1ce7fb7..7ea94f5 100644
--- a/include/linux/posix-clock.h
+++ b/include/linux/posix-clock.h
@@ -108,4 +108,29 @@ struct posix_clock *posix_clock_create(struct posix_clock_operations *cops,
*/
void posix_clock_destroy(struct posix_clock *clk);
+/**
+ * clockid_to_posix_clock() - obtain clock device pointer from a clock id
+ * @id: user space clock id
+ *
+ * Returns a pointer to the clock device, or NULL if the id is not a
+ * dynamic clock id.
+ */
+struct posix_clock *clockid_to_posix_clock(clockid_t id);
+
+/*
+ * The following functions are only to be called from posix-timers.c
+ */
+int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx);
+int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts);
+int pc_clock_getres(struct posix_clock *clk, struct timespec *ts);
+int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts);
+
+int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit);
+int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit);
+
+void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
+ struct itimerspec *ts);
+int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
+ int flags, struct itimerspec *ts, struct itimerspec *old);
+
#endif
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index b05d9b8..62ae296 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -18,10 +18,22 @@ struct cpu_timer_list {
int firing;
};
+/* Bit fields within a clockid:
+ *
+ * The most significant 29 bits hold either a pid or a file descriptor.
+ *
+ * Bit 2 indicates whether a cpu clock refers to a thread or a process.
+ *
+ * Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or FD=3.
+ *
+ * A clockid is invalid if bits 2, 1, and 0 all set (see also CLOCK_INVALID
+ * in include/linux/time.h)
+ */
+
#define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3))
#define CPUCLOCK_PERTHREAD(clock) \
(((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0)
-#define CPUCLOCK_PID_MASK 7
+
#define CPUCLOCK_PERTHREAD_MASK 4
#define CPUCLOCK_WHICH(clock) ((clock) & (clockid_t) CPUCLOCK_CLOCK_MASK)
#define CPUCLOCK_CLOCK_MASK 3
@@ -29,12 +41,26 @@ struct cpu_timer_list {
#define CPUCLOCK_VIRT 1
#define CPUCLOCK_SCHED 2
#define CPUCLOCK_MAX 3
+#define CLOCKFD CPUCLOCK_MAX
+#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define MAKE_PROCESS_CPUCLOCK(pid, clock) \
((~(clockid_t) (pid) << 3) | (clockid_t) (clock))
#define MAKE_THREAD_CPUCLOCK(tid, clock) \
MAKE_PROCESS_CPUCLOCK((tid), (clock) | CPUCLOCK_PERTHREAD_MASK)
+#define FD_TO_CLOCKID(fd) ((clockid_t) (fd << 3) | CLOCKFD)
+#define CLOCKID_TO_FD(clk) (((unsigned int) clk) >> 3)
+
+static inline bool clock_is_static(const clockid_t id)
+{
+ if (0 == (id & ~CLOCKFD_MASK))
+ return true;
+ if (CLOCKFD == (id & CLOCKFD_MASK))
+ return false;
+ return true;
+}
+
/* POSIX.1b interval timer structure. */
struct k_itimer {
struct list_head list; /* free/ allocate list */
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 502bde4..cca3bd5 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -41,6 +41,7 @@
#include <linux/init.h>
#include <linux/compiler.h>
#include <linux/idr.h>
+#include <linux/posix-clock.h>
#include <linux/posix-timers.h>
#include <linux/syscalls.h>
#include <linux/wait.h>
@@ -532,12 +533,15 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
struct sigevent __user *, timer_event_spec,
timer_t __user *, created_timer_id)
{
+ struct posix_clock *clk_dev;
struct k_itimer *new_timer;
int error, new_timer_id;
sigevent_t event;
int it_id_set = IT_ID_NOT_SET;
- if (invalid_clockid(which_clock))
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev && invalid_clockid(which_clock))
return -EINVAL;
new_timer = alloc_posix_timer();
@@ -600,7 +604,11 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
goto out;
}
- error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
+ if (unlikely(clk_dev))
+ error = pc_timer_create(clk_dev, new_timer);
+ else {
+ error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
+ }
if (error)
goto out;
@@ -712,6 +720,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
struct itimerspec __user *, setting)
{
+ struct posix_clock *clk_dev;
struct k_itimer *timr;
struct itimerspec cur_setting;
unsigned long flags;
@@ -720,7 +729,15 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
if (!timr)
return -EINVAL;
- CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
+ if (likely(clock_is_static(timr->it_clock))) {
+
+ CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
+
+ } else {
+ clk_dev = clockid_to_posix_clock(timr->it_clock);
+ if (clk_dev)
+ pc_timer_gettime(clk_dev, timr, &cur_setting);
+ }
unlock_timer(timr, flags);
@@ -811,6 +828,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
const struct itimerspec __user *, new_setting,
struct itimerspec __user *, old_setting)
{
+ struct posix_clock *clk_dev;
struct k_itimer *timr;
struct itimerspec new_spec, old_spec;
int error = 0;
@@ -831,8 +849,19 @@ retry:
if (!timr)
return -EINVAL;
- error = CLOCK_DISPATCH(timr->it_clock, timer_set,
- (timr, flags, &new_spec, rtn));
+ if (likely(clock_is_static(timr->it_clock))) {
+
+ error = CLOCK_DISPATCH(timr->it_clock, timer_set,
+ (timr, flags, &new_spec, rtn));
+
+ } else {
+ clk_dev = clockid_to_posix_clock(timr->it_clock);
+ if (clk_dev)
+ error = pc_timer_settime(clk_dev, timr,
+ flags, &new_spec, rtn);
+ else
+ error = -EINVAL;
+ }
unlock_timer(timr, flag);
if (error == TIMER_RETRY) {
@@ -858,6 +887,13 @@ static inline int common_timer_del(struct k_itimer *timer)
static inline int timer_delete_hook(struct k_itimer *timer)
{
+ struct posix_clock *clk_dev;
+
+ clk_dev = clockid_to_posix_clock(timer->it_clock);
+
+ if (clk_dev)
+ return pc_timer_delete(clk_dev, timer);
+
return CLOCK_DISPATCH(timer->it_clock, timer_del, (timer));
}
@@ -957,26 +993,51 @@ EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
const struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev = NULL;
struct timespec new_tp;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+ if (!clk_dev)
+ return -EINVAL;
+ }
+
if (copy_from_user(&new_tp, tp, sizeof (*tp)))
return -EFAULT;
+ if (unlikely(clk_dev))
+ return pc_clock_settime(clk_dev, &new_tp);
+
return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
}
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
- struct timespec __user *,tp)
+ struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev;
struct timespec kernel_tp;
int error;
- if (invalid_clockid(which_clock))
- return -EINVAL;
- error = CLOCK_DISPATCH(which_clock, clock_get,
- (which_clock, &kernel_tp));
+ if (likely(clock_is_static(which_clock))) {
+
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ error = CLOCK_DISPATCH(which_clock, clock_get,
+ (which_clock, &kernel_tp));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ error = pc_clock_gettime(clk_dev, &kernel_tp);
+ }
+
if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp)))
error = -EFAULT;
@@ -987,16 +1048,28 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
struct timex __user *, utx)
{
+ struct posix_clock *clk_dev;
struct timex ktx;
int err;
if (copy_from_user(&ktx, utx, sizeof(ktx)))
return -EFAULT;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
- err = CLOCK_DISPATCH(which_clock, clock_adj, (which_clock, &ktx));
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ err = CLOCK_DISPATCH(which_clock, clock_adj,
+ (which_clock, &ktx));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ err = pc_clock_adjtime(clk_dev, &ktx);
+ }
if (copy_to_user(utx, &ktx, sizeof(ktx)))
return -EFAULT;
@@ -1007,14 +1080,25 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
struct timespec __user *, tp)
{
+ struct posix_clock *clk_dev;
struct timespec rtn_tp;
int error;
- if (invalid_clockid(which_clock))
- return -EINVAL;
+ if (likely(clock_is_static(which_clock))) {
- error = CLOCK_DISPATCH(which_clock, clock_getres,
- (which_clock, &rtn_tp));
+ if (invalid_clockid(which_clock))
+ return -EINVAL;
+
+ error = CLOCK_DISPATCH(which_clock, clock_getres,
+ (which_clock, &rtn_tp));
+ } else {
+ clk_dev = clockid_to_posix_clock(which_clock);
+
+ if (!clk_dev)
+ return -EINVAL;
+
+ error = pc_clock_getres(clk_dev, &rtn_tp);
+ }
if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) {
error = -EFAULT;
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index e5924c9..a707c10 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -19,9 +19,12 @@
*/
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/file.h>
#include <linux/mutex.h>
#include <linux/posix-clock.h>
#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
#define MAX_CLKDEV BITS_PER_LONG
static DECLARE_BITMAP(clocks_map, MAX_CLKDEV);
@@ -215,3 +218,159 @@ void posix_clock_destroy(struct posix_clock *clk)
kref_put(&clk->kref, delete_clock);
}
EXPORT_SYMBOL_GPL(posix_clock_destroy);
+
+struct posix_clock *clockid_to_posix_clock(const clockid_t id)
+{
+ struct posix_clock *clk = NULL;
+ struct file *fp;
+
+ if (clock_is_static(id))
+ return NULL;
+
+ fp = fget(CLOCKID_TO_FD(id));
+ if (!fp)
+ return NULL;
+
+ if (fp->f_op->open == posix_clock_open)
+ clk = fp->private_data;
+
+ fput(fp);
+ return clk;
+}
+
+int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_adjtime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_adjtime(clk->priv, tx);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_gettime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_gettime(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_getres(struct posix_clock *clk, struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_getres)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_getres(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->clock_settime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->clock_settime(clk->priv, ts);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_create)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_create(clk->priv, kit);
+
+ mutex_unlock(&clk->mux);
+ return err;
+}
+
+int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_delete)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_delete(clk->priv, kit);
+
+ mutex_unlock(&clk->mux);
+
+ return err;
+}
+
+void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
+ struct itimerspec *ts)
+{
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ goto out;
+ else if (!clk->ops->timer_gettime)
+ goto out;
+ else
+ clk->ops->timer_gettime(clk->priv, kit, ts);
+out:
+ mutex_unlock(&clk->mux);
+}
+
+int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
+ int flags, struct itimerspec *ts, struct itimerspec *old)
+{
+ int err;
+
+ mutex_lock(&clk->mux);
+
+ if (clk->zombie)
+ err = -ENODEV;
+ else if (!clk->ops->timer_settime)
+ err = -EOPNOTSUPP;
+ else
+ err = clk->ops->timer_settime(clk->priv, kit, flags, ts, old);
+
+ mutex_unlock(&clk->mux);
+
+ return err;
+}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread[parent not found: <6241238a1df55033e50b151ec9d35ba957e43d53.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>]
* Re: [PATCH V7 4/8] posix clocks: hook dynamic clocks into system calls
2010-12-16 15:43 ` Richard Cochran
@ 2010-12-16 23:20 ` Thomas Gleixner
-1 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 23:20 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, Richard Cochran wrote:
Can you please split this into infrastructure and conversion of
posix-timer.c ?
> diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
> index 1ce7fb7..7ea94f5 100644
> --- a/include/linux/posix-clock.h
> +++ b/include/linux/posix-clock.h
> @@ -108,4 +108,29 @@ struct posix_clock *posix_clock_create(struct posix_clock_operations *cops,
> */
> void posix_clock_destroy(struct posix_clock *clk);
>
> +/**
> + * clockid_to_posix_clock() - obtain clock device pointer from a clock id
> + * @id: user space clock id
> + *
> + * Returns a pointer to the clock device, or NULL if the id is not a
> + * dynamic clock id.
> + */
> +struct posix_clock *clockid_to_posix_clock(clockid_t id);
> +
> +/*
> + * The following functions are only to be called from posix-timers.c
> + */
Then they should be in a header file which is not consumable by the
general public. e.g. kernel/time/posix-clock.h
> +int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx);
> +int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts);
> +int pc_clock_getres(struct posix_clock *clk, struct timespec *ts);
> +int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts);
> +
> +int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit);
> +int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit);
> +
> +void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
> + struct itimerspec *ts);
> +int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
> + int flags, struct itimerspec *ts, struct itimerspec *old);
> +
> #endif
> +
> +static inline bool clock_is_static(const clockid_t id)
> +{
> + if (0 == (id & ~CLOCKFD_MASK))
> + return true;
> + if (CLOCKFD == (id & CLOCKFD_MASK))
> + return false;
Please use the usual kernel notation.
> + return true;
> +}
> +
> /* POSIX.1b interval timer structure. */
> struct k_itimer {
> struct list_head list; /* free/ allocate list */
> 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
> +
> @@ -712,6 +720,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
> SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
> struct itimerspec __user *, setting)
> {
> + struct posix_clock *clk_dev;
> struct k_itimer *timr;
> struct itimerspec cur_setting;
> unsigned long flags;
> @@ -720,7 +729,15 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
> if (!timr)
> return -EINVAL;
>
> - CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
> + if (likely(clock_is_static(timr->it_clock))) {
> +
> + CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
> +
> + } else {
> + clk_dev = clockid_to_posix_clock(timr->it_clock);
> + if (clk_dev)
> + pc_timer_gettime(clk_dev, timr, &cur_setting);
Why this extra step ? Why can't you call
pc_timer_gettime(timr, &cur_setting);
You already established that the timer is fd based, so let the
pc_timer_* functions deal with it.
> + }
>
> unlock_timer(timr, flags);
>
> @@ -811,6 +828,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
> const struct itimerspec __user *, new_setting,
> struct itimerspec __user *, old_setting)
> {
> + struct posix_clock *clk_dev;
> struct k_itimer *timr;
> struct itimerspec new_spec, old_spec;
> int error = 0;
> @@ -831,8 +849,19 @@ retry:
> if (!timr)
> return -EINVAL;
>
> - error = CLOCK_DISPATCH(timr->it_clock, timer_set,
> - (timr, flags, &new_spec, rtn));
> + if (likely(clock_is_static(timr->it_clock))) {
> +
> + error = CLOCK_DISPATCH(timr->it_clock, timer_set,
> + (timr, flags, &new_spec, rtn));
> +
> + } else {
> + clk_dev = clockid_to_posix_clock(timr->it_clock);
> + if (clk_dev)
> + error = pc_timer_settime(clk_dev, timr,
> + flags, &new_spec, rtn);
> + else
> + error = -EINVAL;
Ditto. pc_timer_settime() can return -EINVAL when there is no valid fd.
> @@ -957,26 +993,51 @@ EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
> SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
> const struct timespec __user *, tp)
> {
> + struct posix_clock *clk_dev = NULL;
> struct timespec new_tp;
>
> - if (invalid_clockid(which_clock))
> - return -EINVAL;
> + if (likely(clock_is_static(which_clock))) {
> +
> + if (invalid_clockid(which_clock))
> + return -EINVAL;
> + } else {
> + clk_dev = clockid_to_posix_clock(which_clock);
> + if (!clk_dev)
> + return -EINVAL;
> + }
It's not a problem to check the validity of that clock fd after the
copy_from_user. That's an error case and we don't care about whether
we return EINVAL here or later. And with your current code this can
happen anyway as you don't hold a reference to the fd. And we do the
same thing for posix_cpu_timers as well. See invalid_clockid()
> if (copy_from_user(&new_tp, tp, sizeof (*tp)))
> return -EFAULT;
>
> + if (unlikely(clk_dev))
> + return pc_clock_settime(clk_dev, &new_tp);
> +
> return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
I really start to wonder whether we should cleanup that whole
CLOCK_DISPATCH macro magic and provide real inline functions for
each of the clock functions instead.
static inline int dispatch_clock_settime(const clockid_t id, struct timespec *tp)
{
if (id >= 0) {
return posix_clocks[id].clock_set ?
posic_clocks[id].clock_set(id, tp) : -EINVAL;
}
if (posix_cpu_clock(id))
return -EINVAL;
return pc_timers_clock_set(id, tp);
}
That is a bit of work, but the code will be simpler and we just do the
normal thing of function pointer structures. Stuff which is not
implemented does not magically become called via some common
function. There is no point in doing that. We just have to fill in the
various k_clock structs with the correct pointers in the first place
and let the NULL case return a sensible error value. The data
structure does not become larger that way. It's a little bit more init
code, but that's fine if we make the code better in general. In that
case it's not even more init code, it's just filling the data
structures which we register.
That needs to be done in two steps:
1) cleanup CLOCK_DISPATCH
2) add the pc_timer_* extras
That will make the thing way more palatable than working around the
restrictions of CLOCK_DISPATCH and adding the hell to each syscall.
The second step would be a patch consisting of exactly nine lines:
{
if (id >= 0)
return .....
if (posix_cpu_clock_id(id))
return ....
- return -EINVAL;
+ return pc_timer_xxx(...);
}
Please don't tell me now that we could even hack this into
CLOCK_DISPATCH. *shudder*
> +
> +struct posix_clock *clockid_to_posix_clock(const clockid_t id)
> +{
> + struct posix_clock *clk = NULL;
> + struct file *fp;
> +
> + if (clock_is_static(id))
> + return NULL;
> +
> + fp = fget(CLOCKID_TO_FD(id));
> + if (!fp)
> + return NULL;
> +
> + if (fp->f_op->open == posix_clock_open)
> + clk = fp->private_data;
> +
> + fput(fp);
> + return clk;
> +}
> +
> +int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx)
> +{
> + int err;
> +
> + mutex_lock(&clk->mux);
> +
> + if (clk->zombie)
Uuurgh. That explains the zombie thing. That's really horrible and we
can be more clever. When we leave the file lookup to the pc_timer_*
functions, then we can simply do:
struct posix_clock_descr {
struct file *fp;
struct posix_clock *clk;
};
static int get_posix_clock(const clockid_t id, struct posix_clock_descr *cd)
{
struct file *fp = fget(CLOCKID_TO_FD(id));
if (!fp || fp->f_op->open != posix_clock_open || !fp->private_data)
return -ENODEV;
cd->fp = fp;
cd->clk = fp->private_data;
return 0;
}
static void put_posix_clock(struct posix_clock_descr *cd)
{
fput(cd->fp);
}
int pc_timer_****(....)
{
struct posix_clock_descr cd;
ret = -EOPNOTSUPP;
if (get_posix_clock(id, &cd))
return -ENODEV;
if (cd.clk->ops->whatever)
ret = cd.clk->ops->whatever(....);
put_posix_clock(&cd);
return ret;
}
That get's rid of your life time problems, of clk->mutex, clock->zombie
and makes the code simpler and more robust. And it avoids the whole
mess in posix-timers.c as well.
> + err = -ENODEV;
> + else if (!clk->ops->clock_adjtime)
> + err = -EOPNOTSUPP;
> + else
> + err = clk->ops->clock_adjtime(clk->priv, tx);
> +
> + mutex_unlock(&clk->mux);
> + return err;
> +}
Though all this feels still a bit backwards.
The whole point of this exercise is to provide dynamic clock ids and
make them available via the standard posix timer interface and aside
of that have standard file operations for these clocks to provide
special purpose ioctls, mmap and whatever. And to make it a bit more
complex these must be removable modules.
I need to read back the previous discussions, but maybe someone can
fill me in why we can't make these dynamic things not just live in
posix_clocks[MAX_CLOCKS ... MAX_DYNAMIC_CLOCKS] (there is no
requirement to have an unlimited number of those) and just get a
mapping from the fd to the clock_id? That creates a different set of
life time problems, but no real horrible ones.
Thanks,
tglx
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 4/8] posix clocks: hook dynamic clocks into system calls
@ 2010-12-16 23:20 ` Thomas Gleixner
0 siblings, 0 replies; 68+ messages in thread
From: Thomas Gleixner @ 2010-12-16 23:20 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti
On Thu, 16 Dec 2010, Richard Cochran wrote:
Can you please split this into infrastructure and conversion of
posix-timer.c ?
> diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h
> index 1ce7fb7..7ea94f5 100644
> --- a/include/linux/posix-clock.h
> +++ b/include/linux/posix-clock.h
> @@ -108,4 +108,29 @@ struct posix_clock *posix_clock_create(struct posix_clock_operations *cops,
> */
> void posix_clock_destroy(struct posix_clock *clk);
>
> +/**
> + * clockid_to_posix_clock() - obtain clock device pointer from a clock id
> + * @id: user space clock id
> + *
> + * Returns a pointer to the clock device, or NULL if the id is not a
> + * dynamic clock id.
> + */
> +struct posix_clock *clockid_to_posix_clock(clockid_t id);
> +
> +/*
> + * The following functions are only to be called from posix-timers.c
> + */
Then they should be in a header file which is not consumable by the
general public. e.g. kernel/time/posix-clock.h
> +int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx);
> +int pc_clock_gettime(struct posix_clock *clk, struct timespec *ts);
> +int pc_clock_getres(struct posix_clock *clk, struct timespec *ts);
> +int pc_clock_settime(struct posix_clock *clk, const struct timespec *ts);
> +
> +int pc_timer_create(struct posix_clock *clk, struct k_itimer *kit);
> +int pc_timer_delete(struct posix_clock *clk, struct k_itimer *kit);
> +
> +void pc_timer_gettime(struct posix_clock *clk, struct k_itimer *kit,
> + struct itimerspec *ts);
> +int pc_timer_settime(struct posix_clock *clk, struct k_itimer *kit,
> + int flags, struct itimerspec *ts, struct itimerspec *old);
> +
> #endif
> +
> +static inline bool clock_is_static(const clockid_t id)
> +{
> + if (0 == (id & ~CLOCKFD_MASK))
> + return true;
> + if (CLOCKFD == (id & CLOCKFD_MASK))
> + return false;
Please use the usual kernel notation.
> + return true;
> +}
> +
> /* POSIX.1b interval timer structure. */
> struct k_itimer {
> struct list_head list; /* free/ allocate list */
> 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
> +
> @@ -712,6 +720,7 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
> SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
> struct itimerspec __user *, setting)
> {
> + struct posix_clock *clk_dev;
> struct k_itimer *timr;
> struct itimerspec cur_setting;
> unsigned long flags;
> @@ -720,7 +729,15 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id,
> if (!timr)
> return -EINVAL;
>
> - CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
> + if (likely(clock_is_static(timr->it_clock))) {
> +
> + CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting));
> +
> + } else {
> + clk_dev = clockid_to_posix_clock(timr->it_clock);
> + if (clk_dev)
> + pc_timer_gettime(clk_dev, timr, &cur_setting);
Why this extra step ? Why can't you call
pc_timer_gettime(timr, &cur_setting);
You already established that the timer is fd based, so let the
pc_timer_* functions deal with it.
> + }
>
> unlock_timer(timr, flags);
>
> @@ -811,6 +828,7 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags,
> const struct itimerspec __user *, new_setting,
> struct itimerspec __user *, old_setting)
> {
> + struct posix_clock *clk_dev;
> struct k_itimer *timr;
> struct itimerspec new_spec, old_spec;
> int error = 0;
> @@ -831,8 +849,19 @@ retry:
> if (!timr)
> return -EINVAL;
>
> - error = CLOCK_DISPATCH(timr->it_clock, timer_set,
> - (timr, flags, &new_spec, rtn));
> + if (likely(clock_is_static(timr->it_clock))) {
> +
> + error = CLOCK_DISPATCH(timr->it_clock, timer_set,
> + (timr, flags, &new_spec, rtn));
> +
> + } else {
> + clk_dev = clockid_to_posix_clock(timr->it_clock);
> + if (clk_dev)
> + error = pc_timer_settime(clk_dev, timr,
> + flags, &new_spec, rtn);
> + else
> + error = -EINVAL;
Ditto. pc_timer_settime() can return -EINVAL when there is no valid fd.
> @@ -957,26 +993,51 @@ EXPORT_SYMBOL_GPL(do_posix_clock_nonanosleep);
> SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
> const struct timespec __user *, tp)
> {
> + struct posix_clock *clk_dev = NULL;
> struct timespec new_tp;
>
> - if (invalid_clockid(which_clock))
> - return -EINVAL;
> + if (likely(clock_is_static(which_clock))) {
> +
> + if (invalid_clockid(which_clock))
> + return -EINVAL;
> + } else {
> + clk_dev = clockid_to_posix_clock(which_clock);
> + if (!clk_dev)
> + return -EINVAL;
> + }
It's not a problem to check the validity of that clock fd after the
copy_from_user. That's an error case and we don't care about whether
we return EINVAL here or later. And with your current code this can
happen anyway as you don't hold a reference to the fd. And we do the
same thing for posix_cpu_timers as well. See invalid_clockid()
> if (copy_from_user(&new_tp, tp, sizeof (*tp)))
> return -EFAULT;
>
> + if (unlikely(clk_dev))
> + return pc_clock_settime(clk_dev, &new_tp);
> +
> return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
I really start to wonder whether we should cleanup that whole
CLOCK_DISPATCH macro magic and provide real inline functions for
each of the clock functions instead.
static inline int dispatch_clock_settime(const clockid_t id, struct timespec *tp)
{
if (id >= 0) {
return posix_clocks[id].clock_set ?
posic_clocks[id].clock_set(id, tp) : -EINVAL;
}
if (posix_cpu_clock(id))
return -EINVAL;
return pc_timers_clock_set(id, tp);
}
That is a bit of work, but the code will be simpler and we just do the
normal thing of function pointer structures. Stuff which is not
implemented does not magically become called via some common
function. There is no point in doing that. We just have to fill in the
various k_clock structs with the correct pointers in the first place
and let the NULL case return a sensible error value. The data
structure does not become larger that way. It's a little bit more init
code, but that's fine if we make the code better in general. In that
case it's not even more init code, it's just filling the data
structures which we register.
That needs to be done in two steps:
1) cleanup CLOCK_DISPATCH
2) add the pc_timer_* extras
That will make the thing way more palatable than working around the
restrictions of CLOCK_DISPATCH and adding the hell to each syscall.
The second step would be a patch consisting of exactly nine lines:
{
if (id >= 0)
return .....
if (posix_cpu_clock_id(id))
return ....
- return -EINVAL;
+ return pc_timer_xxx(...);
}
Please don't tell me now that we could even hack this into
CLOCK_DISPATCH. *shudder*
> +
> +struct posix_clock *clockid_to_posix_clock(const clockid_t id)
> +{
> + struct posix_clock *clk = NULL;
> + struct file *fp;
> +
> + if (clock_is_static(id))
> + return NULL;
> +
> + fp = fget(CLOCKID_TO_FD(id));
> + if (!fp)
> + return NULL;
> +
> + if (fp->f_op->open == posix_clock_open)
> + clk = fp->private_data;
> +
> + fput(fp);
> + return clk;
> +}
> +
> +int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx)
> +{
> + int err;
> +
> + mutex_lock(&clk->mux);
> +
> + if (clk->zombie)
Uuurgh. That explains the zombie thing. That's really horrible and we
can be more clever. When we leave the file lookup to the pc_timer_*
functions, then we can simply do:
struct posix_clock_descr {
struct file *fp;
struct posix_clock *clk;
};
static int get_posix_clock(const clockid_t id, struct posix_clock_descr *cd)
{
struct file *fp = fget(CLOCKID_TO_FD(id));
if (!fp || fp->f_op->open != posix_clock_open || !fp->private_data)
return -ENODEV;
cd->fp = fp;
cd->clk = fp->private_data;
return 0;
}
static void put_posix_clock(struct posix_clock_descr *cd)
{
fput(cd->fp);
}
int pc_timer_****(....)
{
struct posix_clock_descr cd;
ret = -EOPNOTSUPP;
if (get_posix_clock(id, &cd))
return -ENODEV;
if (cd.clk->ops->whatever)
ret = cd.clk->ops->whatever(....);
put_posix_clock(&cd);
return ret;
}
That get's rid of your life time problems, of clk->mutex, clock->zombie
and makes the code simpler and more robust. And it avoids the whole
mess in posix-timers.c as well.
> + err = -ENODEV;
> + else if (!clk->ops->clock_adjtime)
> + err = -EOPNOTSUPP;
> + else
> + err = clk->ops->clock_adjtime(clk->priv, tx);
> +
> + mutex_unlock(&clk->mux);
> + return err;
> +}
Though all this feels still a bit backwards.
The whole point of this exercise is to provide dynamic clock ids and
make them available via the standard posix timer interface and aside
of that have standard file operations for these clocks to provide
special purpose ioctls, mmap and whatever. And to make it a bit more
complex these must be removable modules.
I need to read back the previous discussions, but maybe someone can
fill me in why we can't make these dynamic things not just live in
posix_clocks[MAX_CLOCKS ... MAX_DYNAMIC_CLOCKS] (there is no
requirement to have an unlimited number of those) and just get a
mapping from the fd to the clock_id? That creates a different set of
life time problems, but no real horrible ones.
Thanks,
tglx
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 4/8] posix clocks: hook dynamic clocks into system calls
2010-12-16 23:20 ` Thomas Gleixner
(?)
@ 2010-12-17 7:04 ` Richard Cochran
[not found] ` <20101217070450.GB2982-7KxsofuKt4IfAd9E5cN8NEzG7cXyKsk/@public.gmane.org>
-1 siblings, 1 reply; 68+ messages in thread
From: Richard Cochran @ 2010-12-17 7:04 UTC (permalink / raw)
To: Thomas Gleixner
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti
On Fri, Dec 17, 2010 at 12:20:44AM +0100, Thomas Gleixner wrote:
> On Thu, 16 Dec 2010, Richard Cochran wrote:
> > +
> > +static inline bool clock_is_static(const clockid_t id)
> > +{
> > + if (0 == (id & ~CLOCKFD_MASK))
> > + return true;
> > + if (CLOCKFD == (id & CLOCKFD_MASK))
> > + return false;
>
> Please use the usual kernel notation.
Sorry, what do you mean?
> > return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp));
>
> I really start to wonder whether we should cleanup that whole
> CLOCK_DISPATCH macro magic and provide real inline functions for
> each of the clock functions instead.
I'm glad you suggested that.
IMHO, the CLOCK_DISPATCH thing is calling out to be eliminated, but I
didn't dare to take that upon myself.
> Please don't tell me now that we could even hack this into
> CLOCK_DISPATCH. *shudder*
;^)
> > +int pc_clock_adjtime(struct posix_clock *clk, struct timex *tx)
> > +{
> > + int err;
> > +
> > + mutex_lock(&clk->mux);
> > +
> > + if (clk->zombie)
>
> Uuurgh. That explains the zombie thing. That's really horrible and we
> can be more clever. When we leave the file lookup to the pc_timer_*
> functions, then we can simply do:
...
> That get's rid of your life time problems, of clk->mutex, clock->zombie
> and makes the code simpler and more robust. And it avoids the whole
> mess in posix-timers.c as well.
The code in the patch set is modeled after a USB driver, namely
drivers/usb/class/usbtmc.c. Alan Cox had brought up the example of a
PTP Hardware Clock appearing as a USB device. So the device might
suddenly disappear, and the zombie field is supposed to cover the case
where the hardware no longer exists, but the file pointer is still
valid.
I'll take a closer look at your suggestion...
> The whole point of this exercise is to provide dynamic clock ids and
> make them available via the standard posix timer interface and aside
> of that have standard file operations for these clocks to provide
> special purpose ioctls, mmap and whatever. And to make it a bit more
> complex these must be removable modules.
Yes, and hot pluggable, too.
> I need to read back the previous discussions, but maybe someone can
> fill me in why we can't make these dynamic things not just live in
> posix_clocks[MAX_CLOCKS ... MAX_DYNAMIC_CLOCKS] (there is no
> requirement to have an unlimited number of those) and just get a
> mapping from the fd to the clock_id? That creates a different set of
> life time problems, but no real horrible ones.
I would summarize the discussion like this:
Alan Cox was strongly in favor of using a regular file descriptor as
the reference to the dynamic clock.
John Stultz thought it wouldn't be too bad to cycle though a number of
static ids, being careful not to every assign the same static id (in
series) to two different clocks.
I implemented Alan's idea, since it seemed like maintaining the
mapping between clocks and static ids would be extra work, but without
any real benefit.
Thanks again for your review,
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* [PATCH V7 5/8] ptp: Added a brand new class driver for ptp clocks.
2010-12-16 15:41 [PATCH V7 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-12-16 15:44 ` Richard Cochran
[not found] ` <cover.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
` (2 subsequent siblings)
3 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:44 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
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-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
---
Documentation/ABI/testing/sysfs-ptp | 107 +++++++++++
Documentation/ptp/ptp.txt | 94 ++++++++++
Documentation/ptp/testptp.c | 352 +++++++++++++++++++++++++++++++++++
Documentation/ptp/testptp.mk | 33 ++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ptp/Kconfig | 27 +++
drivers/ptp/Makefile | 6 +
drivers/ptp/ptp_chardev.c | 144 ++++++++++++++
drivers/ptp/ptp_clock.c | 317 +++++++++++++++++++++++++++++++
drivers/ptp/ptp_private.h | 68 +++++++
drivers/ptp/ptp_sysfs.c | 230 +++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 79 ++++++++
include/linux/ptp_clock_kernel.h | 139 ++++++++++++++
15 files changed, 1600 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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+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..9e389a4
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,352 @@
+/*
+ * 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"
+
+#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(int fd)
+{
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd) ((clockid_t) (fd << 3) | CLOCKFD)
+
+ return FD_TO_CLOCKID(fd);
+}
+
+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;
+ }
+ }
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s: %s", device, strerror(errno));
+ return -1;
+ }
+
+ clkid = get_clockid(fd);
+ if (CLOCK_INVALID == clkid) {
+ fprintf(stderr, "failed to read clock id\n");
+ 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 f3ebb30..8e28b9b 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..6e90e49
--- /dev/null
+++ b/drivers/ptp/ptp_chardev.c
@@ -0,0 +1,144 @@
+/*
+ * 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/posix-clock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+
+#include <ptp_private.h>
+
+int ptp_open(void *priv, fmode_t fmode)
+{
+ return 0;
+}
+
+long ptp_ioctl(void *pc_private, unsigned int cmd, unsigned long arg)
+{
+ struct ptp_clock_caps caps;
+ struct ptp_clock_request req;
+ struct ptp_clock *ptp = pc_private;
+ 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;
+}
+
+unsigned int ptp_poll(void *priv, struct file *fp, poll_table *wait)
+{
+ struct ptp_clock *ptp = priv;
+
+ poll_wait(fp, &ptp->tsev_wq, wait);
+
+ return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
+}
+
+ssize_t ptp_read(void *priv, uint flags, char __user *buf, size_t cnt)
+{
+ struct ptp_clock *ptp = priv;
+ 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);
+}
+
+int ptp_release(void *priv)
+{
+ return 0;
+}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..f5c9355
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+#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-clock.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 DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS);
+static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */
+
+/* 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(void *priv, struct timespec *tp)
+{
+ return 1; /* always round timer functions to one nanosecond */
+}
+
+static int ptp_clock_settime(void *priv, const struct timespec *tp)
+{
+ struct ptp_clock *ptp = priv;
+ return ptp->info->settime(ptp->info->priv, tp);
+}
+
+static int ptp_clock_gettime(void *priv, struct timespec *tp)
+{
+ struct ptp_clock *ptp = priv;
+ return ptp->info->gettime(ptp->info->priv, tp);
+}
+
+static int ptp_clock_adjtime(void *priv, struct timex *tx)
+{
+ struct ptp_clock *ptp = priv;
+ struct ptp_clock_info *ops;
+ int err = -EOPNOTSUPP;
+
+ ops = ptp->info;
+
+ if (tx->modes & ADJ_SETOFFSET) {
+ struct timespec ts;
+ ktime_t kt;
+ s64 delta;
+
+ /* Validate the delta value. */
+ if (tx->time.tv_sec && tx->time.tv_usec < 0)
+ return -EINVAL;
+
+ if (tx->modes & ADJ_NANO) {
+ ts.tv_sec = tx->time.tv_sec;
+ ts.tv_nsec = tx->time.tv_usec;
+ kt = timespec_to_ktime(ts);
+ } else
+ kt = timeval_to_ktime(tx->time);
+
+ delta = ktime_to_ns(kt);
+
+ err = ops->adjtime(ops->priv, delta);
+
+ } else if (tx->modes & ADJ_FREQUENCY) {
+ s64 ppb = 1 + tx->freq;
+ ppb *= 125;
+ ppb >>= 13;
+ err = ops->adjfreq(ops->priv, (s32)ppb);
+ }
+
+ return err;
+}
+
+static struct posix_clock_operations ptp_cops = {
+ .owner = THIS_MODULE,
+ .clock_adjtime = ptp_clock_adjtime,
+ .clock_gettime = ptp_clock_gettime,
+ .clock_getres = ptp_clock_getres,
+ .clock_settime = ptp_clock_settime,
+ .fops = {
+ .open = ptp_open,
+ .release = ptp_release,
+ .ioctl = ptp_ioctl,
+ .read = ptp_read,
+ .poll = ptp_poll,
+ },
+};
+
+/* public interface */
+
+struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
+{
+ struct ptp_clock *ptp;
+ int err = 0, index, major = MAJOR(ptp_devt);
+
+ if (info->n_alarm > PTP_MAX_ALARMS)
+ return ERR_PTR(-EINVAL);
+
+ /* Find a free clock slot and reserve it. */
+ err = -EBUSY;
+ mutex_lock(&ptp_clocks_mutex);
+ index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS);
+ if (index < PTP_MAX_CLOCKS)
+ set_bit(index, ptp_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_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. */
+ ptp->clk = posix_clock_create(&ptp_cops, ptp->devid, ptp);
+ if (IS_ERR(ptp->clk)) {
+ pr_err("failed to create posix clock\n");
+ err = PTR_ERR(ptp->clk);
+ goto no_clock;
+ }
+
+ mutex_unlock(&ptp_clocks_mutex);
+ return ptp;
+
+no_clock:
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+no_pps:
+ ptp_cleanup_sysfs(ptp);
+no_sysfs:
+ device_destroy(ptp_class, ptp->devid);
+no_device:
+ mutex_destroy(&ptp->tsevq_mux);
+ kfree(ptp);
+no_memory:
+ clear_bit(index, ptp_clocks_map);
+no_slot:
+ mutex_unlock(&ptp_clocks_mutex);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+ /* Release the clock's resources. */
+ posix_clock_destroy(ptp->clk);
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+ ptp_cleanup_sysfs(ptp);
+ device_destroy(ptp_class, ptp->devid);
+ mutex_destroy(&ptp->tsevq_mux);
+
+ /* Remove the clock from the bit map. */
+ mutex_lock(&ptp_clocks_mutex);
+ clear_bit(ptp->index, ptp_clocks_map);
+ mutex_unlock(&ptp_clocks_mutex);
+
+ 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;
+
+ 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-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>");
+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..6408655
--- /dev/null
+++ b/drivers/ptp/ptp_private.h
@@ -0,0 +1,68 @@
+/*
+ * 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/posix-clock.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 posix_clock *clk;
+ struct device *dev;
+ struct ptp_clock_info *info;
+ 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_open(void *priv, fmode_t fmode);
+int ptp_release(void *priv);
+long ptp_ioctl(void *pc_private, unsigned int cmd, unsigned long arg);
+ssize_t ptp_read(void *priv, uint flags, char __user *buf, size_t cnt);
+unsigned int ptp_poll(void *priv, struct file *fp, poll_table *wait);
+
+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..b1d1c56
--- /dev/null
+++ b/drivers/ptp/ptp_sysfs.c
@@ -0,0 +1,230 @@
+/*
+ * 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_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_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:
+ if (info->n_per_out)
+ device_remove_file(dev, &dev_attr_period);
+out3:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_fifo);
+out2:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_extts_enable);
+out1:
+ return err;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 97319a8..c9dfbc7 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..165e391
--- /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 delta: Desired change in 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, s64 delta);
+ int (*gettime)(void *priv, struct timespec *ts);
+ int (*settime)(void *priv, const 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] 68+ messages in thread* [PATCH V7 5/8] ptp: Added a brand new class driver for ptp clocks.
@ 2010-12-16 15:44 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:44 UTC (permalink / raw)
To: linux-kernel
Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
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 | 352 +++++++++++++++++++++++++++++++++++
Documentation/ptp/testptp.mk | 33 ++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ptp/Kconfig | 27 +++
drivers/ptp/Makefile | 6 +
drivers/ptp/ptp_chardev.c | 144 ++++++++++++++
drivers/ptp/ptp_clock.c | 317 +++++++++++++++++++++++++++++++
drivers/ptp/ptp_private.h | 68 +++++++
drivers/ptp/ptp_sysfs.c | 230 +++++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/ptp_clock.h | 79 ++++++++
include/linux/ptp_clock_kernel.h | 139 ++++++++++++++
15 files changed, 1600 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..9e389a4
--- /dev/null
+++ b/Documentation/ptp/testptp.c
@@ -0,0 +1,352 @@
+/*
+ * 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"
+
+#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(int fd)
+{
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd) ((clockid_t) (fd << 3) | CLOCKFD)
+
+ return FD_TO_CLOCKID(fd);
+}
+
+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;
+ }
+ }
+
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open %s: %s", device, strerror(errno));
+ return -1;
+ }
+
+ clkid = get_clockid(fd);
+ if (CLOCK_INVALID == clkid) {
+ fprintf(stderr, "failed to read clock id\n");
+ 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 f3ebb30..8e28b9b 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..6e90e49
--- /dev/null
+++ b/drivers/ptp/ptp_chardev.c
@@ -0,0 +1,144 @@
+/*
+ * 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/posix-clock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+
+#include <ptp_private.h>
+
+int ptp_open(void *priv, fmode_t fmode)
+{
+ return 0;
+}
+
+long ptp_ioctl(void *pc_private, unsigned int cmd, unsigned long arg)
+{
+ struct ptp_clock_caps caps;
+ struct ptp_clock_request req;
+ struct ptp_clock *ptp = pc_private;
+ 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;
+}
+
+unsigned int ptp_poll(void *priv, struct file *fp, poll_table *wait)
+{
+ struct ptp_clock *ptp = priv;
+
+ poll_wait(fp, &ptp->tsev_wq, wait);
+
+ return queue_cnt(&ptp->tsevq) ? POLLIN : 0;
+}
+
+ssize_t ptp_read(void *priv, uint flags, char __user *buf, size_t cnt)
+{
+ struct ptp_clock *ptp = priv;
+ 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);
+}
+
+int ptp_release(void *priv)
+{
+ return 0;
+}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
new file mode 100644
index 0000000..f5c9355
--- /dev/null
+++ b/drivers/ptp/ptp_clock.c
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+#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-clock.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 DECLARE_BITMAP(ptp_clocks_map, PTP_MAX_CLOCKS);
+static DEFINE_MUTEX(ptp_clocks_mutex); /* protects 'ptp_clocks_map' */
+
+/* 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(void *priv, struct timespec *tp)
+{
+ return 1; /* always round timer functions to one nanosecond */
+}
+
+static int ptp_clock_settime(void *priv, const struct timespec *tp)
+{
+ struct ptp_clock *ptp = priv;
+ return ptp->info->settime(ptp->info->priv, tp);
+}
+
+static int ptp_clock_gettime(void *priv, struct timespec *tp)
+{
+ struct ptp_clock *ptp = priv;
+ return ptp->info->gettime(ptp->info->priv, tp);
+}
+
+static int ptp_clock_adjtime(void *priv, struct timex *tx)
+{
+ struct ptp_clock *ptp = priv;
+ struct ptp_clock_info *ops;
+ int err = -EOPNOTSUPP;
+
+ ops = ptp->info;
+
+ if (tx->modes & ADJ_SETOFFSET) {
+ struct timespec ts;
+ ktime_t kt;
+ s64 delta;
+
+ /* Validate the delta value. */
+ if (tx->time.tv_sec && tx->time.tv_usec < 0)
+ return -EINVAL;
+
+ if (tx->modes & ADJ_NANO) {
+ ts.tv_sec = tx->time.tv_sec;
+ ts.tv_nsec = tx->time.tv_usec;
+ kt = timespec_to_ktime(ts);
+ } else
+ kt = timeval_to_ktime(tx->time);
+
+ delta = ktime_to_ns(kt);
+
+ err = ops->adjtime(ops->priv, delta);
+
+ } else if (tx->modes & ADJ_FREQUENCY) {
+ s64 ppb = 1 + tx->freq;
+ ppb *= 125;
+ ppb >>= 13;
+ err = ops->adjfreq(ops->priv, (s32)ppb);
+ }
+
+ return err;
+}
+
+static struct posix_clock_operations ptp_cops = {
+ .owner = THIS_MODULE,
+ .clock_adjtime = ptp_clock_adjtime,
+ .clock_gettime = ptp_clock_gettime,
+ .clock_getres = ptp_clock_getres,
+ .clock_settime = ptp_clock_settime,
+ .fops = {
+ .open = ptp_open,
+ .release = ptp_release,
+ .ioctl = ptp_ioctl,
+ .read = ptp_read,
+ .poll = ptp_poll,
+ },
+};
+
+/* public interface */
+
+struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info)
+{
+ struct ptp_clock *ptp;
+ int err = 0, index, major = MAJOR(ptp_devt);
+
+ if (info->n_alarm > PTP_MAX_ALARMS)
+ return ERR_PTR(-EINVAL);
+
+ /* Find a free clock slot and reserve it. */
+ err = -EBUSY;
+ mutex_lock(&ptp_clocks_mutex);
+ index = find_first_zero_bit(ptp_clocks_map, PTP_MAX_CLOCKS);
+ if (index < PTP_MAX_CLOCKS)
+ set_bit(index, ptp_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_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. */
+ ptp->clk = posix_clock_create(&ptp_cops, ptp->devid, ptp);
+ if (IS_ERR(ptp->clk)) {
+ pr_err("failed to create posix clock\n");
+ err = PTR_ERR(ptp->clk);
+ goto no_clock;
+ }
+
+ mutex_unlock(&ptp_clocks_mutex);
+ return ptp;
+
+no_clock:
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+no_pps:
+ ptp_cleanup_sysfs(ptp);
+no_sysfs:
+ device_destroy(ptp_class, ptp->devid);
+no_device:
+ mutex_destroy(&ptp->tsevq_mux);
+ kfree(ptp);
+no_memory:
+ clear_bit(index, ptp_clocks_map);
+no_slot:
+ mutex_unlock(&ptp_clocks_mutex);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL(ptp_clock_register);
+
+int ptp_clock_unregister(struct ptp_clock *ptp)
+{
+ /* Release the clock's resources. */
+ posix_clock_destroy(ptp->clk);
+ if (ptp->info->pps)
+ pps_unregister_source(ptp->pps_source);
+ ptp_cleanup_sysfs(ptp);
+ device_destroy(ptp_class, ptp->devid);
+ mutex_destroy(&ptp->tsevq_mux);
+
+ /* Remove the clock from the bit map. */
+ mutex_lock(&ptp_clocks_mutex);
+ clear_bit(ptp->index, ptp_clocks_map);
+ mutex_unlock(&ptp_clocks_mutex);
+
+ 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;
+
+ 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..6408655
--- /dev/null
+++ b/drivers/ptp/ptp_private.h
@@ -0,0 +1,68 @@
+/*
+ * 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/posix-clock.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 posix_clock *clk;
+ struct device *dev;
+ struct ptp_clock_info *info;
+ 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_open(void *priv, fmode_t fmode);
+int ptp_release(void *priv);
+long ptp_ioctl(void *pc_private, unsigned int cmd, unsigned long arg);
+ssize_t ptp_read(void *priv, uint flags, char __user *buf, size_t cnt);
+unsigned int ptp_poll(void *priv, struct file *fp, poll_table *wait);
+
+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..b1d1c56
--- /dev/null
+++ b/drivers/ptp/ptp_sysfs.c
@@ -0,0 +1,230 @@
+/*
+ * 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_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_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:
+ if (info->n_per_out)
+ device_remove_file(dev, &dev_attr_period);
+out3:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_fifo);
+out2:
+ if (info->n_ext_ts)
+ device_remove_file(dev, &dev_attr_extts_enable);
+out1:
+ return err;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 97319a8..c9dfbc7 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..165e391
--- /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 delta: Desired change in 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, s64 delta);
+ int (*gettime)(void *priv, struct timespec *ts);
+ int (*settime)(void *priv, const 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] 68+ messages in thread* Re: [PATCH V7 5/8] ptp: Added a brand new class driver for ptp clocks.
2010-12-16 15:44 ` Richard Cochran
(?)
@ 2010-12-16 15:57 ` Arnd Bergmann
-1 siblings, 0 replies; 68+ messages in thread
From: Arnd Bergmann @ 2010-12-16 15:57 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
On Thursday 16 December 2010, Richard Cochran wrote:
> This patch adds an infrastructure for hardware clocks that implement
> IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a
> registration method to particular hardware clock drivers. Each clock is
> 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>
Looks fine to me, with one tiny comment:
> +
> +/* private globals */
> +
> +extern struct device_attribute ptp_dev_attrs[]; /* see ptp_sysfs.c */
The declaration is in a C file, better move it into a header in order
to make sure the definition matches the declaration.
Arnd
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 5/8] ptp: Added a brand new class driver for ptp clocks.
2010-12-16 15:44 ` Richard Cochran
(?)
(?)
@ 2010-12-16 16:08 ` Rodolfo Giometti
-1 siblings, 0 replies; 68+ messages in thread
From: Rodolfo Giometti @ 2010-12-16 16:08 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Thomas Gleixner
On Thu, Dec 16, 2010 at 04:44:24PM +0100, Richard Cochran wrote:
> This patch adds an infrastructure for hardware clocks that implement
> IEEE 1588, the Precision Time Protocol (PTP). A class driver offers a
> registration method to particular hardware clock drivers. Each clock is
> 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>
Regarding the PPS subsystem it looks ok for me.
Acked-by: Rodolfo Giometti <giometti@linux.it>
Ciao,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@linux.it
Embedded Systems phone: +39 349 2432127
UNIX programming skype: rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it
^ permalink raw reply [flat|nested] 68+ messages in thread
* [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2010-12-16 15:41 [PATCH V7 0/8] ptp: IEEE 1588 hardware clock support Richard Cochran
@ 2010-12-16 15:44 ` Richard Cochran
[not found] ` <cover.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
` (2 subsequent siblings)
3 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:44 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
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-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>
---
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 | 342 +++++++++++++++++++++++++
5 files changed, 625 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..61cf4b4 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(®s->channel[ch].Ch_Event);
+
+ if (!(val & RX_SNAPSHOT_LOCKED))
+ return;
+
+ lo = __raw_readl(®s->channel[ch].SrcUUIDLo);
+ hi = __raw_readl(®s->channel[ch].SrcUUIDHi);
+
+ uid = hi & 0xffff;
+ seq = (hi >> 16) & 0xffff;
+
+ if (!match(skb, htons(uid), htonl(lo), htons(seq)))
+ goto out;
+
+ lo = __raw_readl(®s->channel[ch].RxSnapLo);
+ hi = __raw_readl(®s->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, ®s->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;
+ struct skb_shared_info *shtx;
+ u64 ns;
+ u32 ch, cnt, hi, lo, val;
+
+ shtx = skb_shinfo(skb);
+ if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && port->hwts_tx_en))
+ shtx->tx_flags |= SKBTX_IN_PROGRESS;
+ 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(®s->channel[ch].Ch_Event);
+ if (val & TX_SNAPSHOT_LOCKED)
+ break;
+ udelay(1);
+ }
+ if (!(val & TX_SNAPSHOT_LOCKED)) {
+ shtx->tx_flags &= ~SKBTX_IN_PROGRESS;
+ return;
+ }
+
+ lo = __raw_readl(®s->channel[ch].TxSnapLo);
+ hi = __raw_readl(®s->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, ®s->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, ®s->channel[ch].Ch_Control);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ port->hwts_rx_en = PTP_MASTER_MODE;
+ __raw_writel(MASTER_MODE, ®s->channel[ch].Ch_Control);
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* Clear out any old time stamps. */
+ __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
+ ®s->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 a4df298..6f1dcbf 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -37,4 +37,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 480e2af..f6933e8 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_IXP46X) += ptp_ixp46x.o
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
new file mode 100644
index 0000000..387bd0f
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,342 @@
+/*
+ * 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(®s->SysTimeLo);
+ hi = ixp_read(®s->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(®s->SysTimeLo, lo);
+ ixp_write(®s->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(®s->Event);
+
+ if (val & TSER_SNS) {
+ ack |= TSER_SNS;
+ if (ixp_clock->exts0_enabled) {
+ hi = ixp_read(®s->ASMSHi);
+ lo = ixp_read(®s->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(®s->AMMSHi);
+ lo = ixp_read(®s->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(®s->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(®s->Addend, addend);
+
+ return 0;
+}
+
+static int ptp_ixp_adjtime(void *priv, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct ixp_clock *ixp_clock = priv;
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ now = sys_time_read(regs);
+ now += delta;
+ sys_time_write(regs, now);
+
+ spin_unlock_irqrestore(®ister_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(®ister_lock, flags);
+
+ ns = sys_time_read(regs);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_ixp_settime(void *priv, const 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(®ister_lock, flags);
+
+ sys_time_write(regs, ns);
+
+ spin_unlock_irqrestore(®ister_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-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>");
+MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
+MODULE_LICENSE("GPL");
--
1.7.0.4
^ permalink raw reply related [flat|nested] 68+ messages in thread* [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
@ 2010-12-16 15:44 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2010-12-16 15:44 UTC (permalink / raw)
To: linux-kernel
Cc: linux-api, netdev, Alan Cox, Arnd Bergmann, Christoph Lameter,
David Miller, John Stultz, Krzysztof Halasa, Peter Zijlstra,
Rodolfo Giometti, Thomas Gleixner
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 | 342 +++++++++++++++++++++++++
5 files changed, 625 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..61cf4b4 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(®s->channel[ch].Ch_Event);
+
+ if (!(val & RX_SNAPSHOT_LOCKED))
+ return;
+
+ lo = __raw_readl(®s->channel[ch].SrcUUIDLo);
+ hi = __raw_readl(®s->channel[ch].SrcUUIDHi);
+
+ uid = hi & 0xffff;
+ seq = (hi >> 16) & 0xffff;
+
+ if (!match(skb, htons(uid), htonl(lo), htons(seq)))
+ goto out;
+
+ lo = __raw_readl(®s->channel[ch].RxSnapLo);
+ hi = __raw_readl(®s->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, ®s->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;
+ struct skb_shared_info *shtx;
+ u64 ns;
+ u32 ch, cnt, hi, lo, val;
+
+ shtx = skb_shinfo(skb);
+ if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && port->hwts_tx_en))
+ shtx->tx_flags |= SKBTX_IN_PROGRESS;
+ 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(®s->channel[ch].Ch_Event);
+ if (val & TX_SNAPSHOT_LOCKED)
+ break;
+ udelay(1);
+ }
+ if (!(val & TX_SNAPSHOT_LOCKED)) {
+ shtx->tx_flags &= ~SKBTX_IN_PROGRESS;
+ return;
+ }
+
+ lo = __raw_readl(®s->channel[ch].TxSnapLo);
+ hi = __raw_readl(®s->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, ®s->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, ®s->channel[ch].Ch_Control);
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ port->hwts_rx_en = PTP_MASTER_MODE;
+ __raw_writel(MASTER_MODE, ®s->channel[ch].Ch_Control);
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* Clear out any old time stamps. */
+ __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED,
+ ®s->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 a4df298..6f1dcbf 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -37,4 +37,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 480e2af..f6933e8 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_IXP46X) += ptp_ixp46x.o
diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c
new file mode 100644
index 0000000..387bd0f
--- /dev/null
+++ b/drivers/ptp/ptp_ixp46x.c
@@ -0,0 +1,342 @@
+/*
+ * 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(®s->SysTimeLo);
+ hi = ixp_read(®s->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(®s->SysTimeLo, lo);
+ ixp_write(®s->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(®s->Event);
+
+ if (val & TSER_SNS) {
+ ack |= TSER_SNS;
+ if (ixp_clock->exts0_enabled) {
+ hi = ixp_read(®s->ASMSHi);
+ lo = ixp_read(®s->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(®s->AMMSHi);
+ lo = ixp_read(®s->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(®s->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(®s->Addend, addend);
+
+ return 0;
+}
+
+static int ptp_ixp_adjtime(void *priv, s64 delta)
+{
+ s64 now;
+ unsigned long flags;
+ struct ixp_clock *ixp_clock = priv;
+ struct ixp46x_ts_regs *regs = ixp_clock->regs;
+
+ spin_lock_irqsave(®ister_lock, flags);
+
+ now = sys_time_read(regs);
+ now += delta;
+ sys_time_write(regs, now);
+
+ spin_unlock_irqrestore(®ister_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(®ister_lock, flags);
+
+ ns = sys_time_read(regs);
+
+ spin_unlock_irqrestore(®ister_lock, flags);
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_ixp_settime(void *priv, const 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(®ister_lock, flags);
+
+ sys_time_write(regs, ns);
+
+ spin_unlock_irqrestore(®ister_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] 68+ messages in thread[parent not found: <d2e0a5ac5eb51e9e4c7fcc94723b67c72c2f57c1.1292512461.git.richard.cochran-3mrvs1K0uXizZXS1Dc/lvw@public.gmane.org>]
* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2010-12-16 15:44 ` Richard Cochran
@ 2011-01-02 8:45 ` Pavel Machek
-1 siblings, 0 replies; 68+ messages in thread
From: Pavel Machek @ 2011-01-02 8:45 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
Hi!!
> +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 */
CouldWeGetRidOfCamelCase?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
@ 2011-01-02 8:45 ` Pavel Machek
0 siblings, 0 replies; 68+ messages in thread
From: Pavel Machek @ 2011-01-02 8:45 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
Hi!!
> +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 */
CouldWeGetRidOfCamelCase?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 68+ messages in thread[parent not found: <20110102084505.GA3980-+ZI9xUNit7I@public.gmane.org>]
* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2011-01-02 8:45 ` Pavel Machek
@ 2011-01-02 9:12 ` Richard Cochran
-1 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2011-01-02 9:12 UTC (permalink / raw)
To: Pavel Machek
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Sun, Jan 02, 2011 at 09:45:05AM +0100, Pavel Machek wrote:
> Hi!!
>
> > +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 */
>
> CouldWeGetRidOfCamelCase?
I agree that CamelCase is ugly and in bad taste. However, I make an
exception when the register level programmer's manual uses this style.
IMHO, it is better to use the exact same mnemonics as in the
manual. That way, when the next person comes along to make a change to
the code, with manual in hand, he will immediately know what is what.
Thanks,
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
@ 2011-01-02 9:12 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2011-01-02 9:12 UTC (permalink / raw)
To: Pavel Machek
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sun, Jan 02, 2011 at 09:45:05AM +0100, Pavel Machek wrote:
> Hi!!
>
> > +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 */
>
> CouldWeGetRidOfCamelCase?
I agree that CamelCase is ugly and in bad taste. However, I make an
exception when the register level programmer's manual uses this style.
IMHO, it is better to use the exact same mnemonics as in the
manual. That way, when the next person comes along to make a change to
the code, with manual in hand, he will immediately know what is what.
Thanks,
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread[parent not found: <20110102091233.GA2847-7KxsofuKt4IfAd9E5cN8NEzG7cXyKsk/@public.gmane.org>]
* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2011-01-02 9:12 ` Richard Cochran
@ 2011-01-02 9:20 ` Pavel Machek
-1 siblings, 0 replies; 68+ messages in thread
From: Pavel Machek @ 2011-01-02 9:20 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
On Sun 2011-01-02 10:12:33, Richard Cochran wrote:
> On Sun, Jan 02, 2011 at 09:45:05AM +0100, Pavel Machek wrote:
> > Hi!!
> >
> > > +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 */
> >
> > CouldWeGetRidOfCamelCase?
>
> I agree that CamelCase is ugly and in bad taste. However, I make an
> exception when the register level programmer's manual uses this style.
>
> IMHO, it is better to use the exact same mnemonics as in the
> manual. That way, when the next person comes along to make a change to
Given the comments -- does manual really use camelCase crap?
And... the identifiers actually combine camelCase with _. Better fix
it.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
@ 2011-01-02 9:20 ` Pavel Machek
0 siblings, 0 replies; 68+ messages in thread
From: Pavel Machek @ 2011-01-02 9:20 UTC (permalink / raw)
To: Richard Cochran
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sun 2011-01-02 10:12:33, Richard Cochran wrote:
> On Sun, Jan 02, 2011 at 09:45:05AM +0100, Pavel Machek wrote:
> > Hi!!
> >
> > > +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 */
> >
> > CouldWeGetRidOfCamelCase?
>
> I agree that CamelCase is ugly and in bad taste. However, I make an
> exception when the register level programmer's manual uses this style.
>
> IMHO, it is better to use the exact same mnemonics as in the
> manual. That way, when the next person comes along to make a change to
Given the comments -- does manual really use camelCase crap?
And... the identifiers actually combine camelCase with _. Better fix
it.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 68+ messages in thread* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2011-01-02 9:20 ` Pavel Machek
(?)
@ 2011-01-03 17:07 ` Richard Cochran
[not found] ` <20110103170732.GA3700-7KxsofuKt4IfAd9E5cN8NEzG7cXyKsk/@public.gmane.org>
-1 siblings, 1 reply; 68+ messages in thread
From: Richard Cochran @ 2011-01-03 17:07 UTC (permalink / raw)
To: Pavel Machek
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
On Sun, Jan 02, 2011 at 10:20:42AM +0100, Pavel Machek wrote:
> Given the comments -- does manual really use camelCase crap?
> And... the identifiers actually combine camelCase with _. Better fix
> it.
Thats the way Intel likes it:
http://download.intel.com/design/network/manuals/30626204.pdf
Page 837
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
2011-01-02 8:45 ` Pavel Machek
@ 2011-01-02 9:19 ` Richard Cochran
-1 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2011-01-02 9:19 UTC (permalink / raw)
To: Pavel Machek
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, netdev-u79uwXL29TY76Z2rM5mHXA,
Alan Cox, Arnd Bergmann, Christoph Lameter, David Miller,
John Stultz, Krzysztof Halasa, Peter Zijlstra, Rodolfo Giometti,
Thomas Gleixner
BTW, I posted an updated V8 of this patch series on December 31.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread
* Re: [PATCH V7 7/8] ptp: Added a clock driver for the IXP46x.
@ 2011-01-02 9:19 ` Richard Cochran
0 siblings, 0 replies; 68+ messages in thread
From: Richard Cochran @ 2011-01-02 9:19 UTC (permalink / raw)
To: Pavel Machek
Cc: linux-kernel, linux-api, netdev, Alan Cox, Arnd Bergmann,
Christoph Lameter, David Miller, John Stultz, Krzysztof Halasa,
Peter Zijlstra, Rodolfo Giometti, Thomas Gleixner
BTW, I posted an updated V8 of this patch series on December 31.
Richard
^ permalink raw reply [flat|nested] 68+ messages in thread