[LinuxPPS] [PATCH 2/3] pps: capture MONOTONIC_RAW timestamps as well
Alexander Gordeev
lasaine at lvk.cs.msu.su
Thu Oct 1 22:37:02 CEST 2009
MONOTONIC_RAW clock timestamps are ideally suited for frequency
calculation and also fit well into the original NTP hardpps design. Now
phase and frequency can be adjusted separately: the former based on
REALTIME clock and the latter based on MONOTONIC_RAW clock. This way
phase can be adjusted using an old good adjtime which makes its job much
faster then full PLL/FLL.
A new function getnstime_raw_and_real is added to timekeeping subsystem
to capture both timestamps at the same time and atomically..
Signed-off-by: Alexander Gordeev <lasaine at lvk.cs.msu.su>
---
arch/x86/kernel/irq_32.c | 13 +++++++----
arch/x86/kernel/irq_64.c | 13 +++++++----
drivers/pps/clients/ktimer.c | 6 ++--
drivers/pps/clients/pps-ldisc.c | 11 +++++----
drivers/pps/clients/pps_parport.c | 34 +++++++++++++++++---------------
drivers/pps/kapi.c | 15 ++++++++++++-
include/linux/pps.h | 7 ++++-
include/linux/serial_core.h | 9 ++++---
include/linux/time.h | 1 +
include/linux/tty_ldisc.h | 6 ++--
kernel/time/timekeeping.c | 39 +++++++++++++++++++++++++++++++++++++
11 files changed, 109 insertions(+), 45 deletions(-)
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index e57d4f5..a4ccb3f 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -28,8 +28,10 @@ DEFINE_PER_CPU(struct pt_regs *, irq_regs);
EXPORT_PER_CPU_SYMBOL(irq_regs);
#ifdef CONFIG_PPS_IRQ_EVENTS
-struct timespec pps_irq_ts[NR_IRQS];
-EXPORT_SYMBOL(pps_irq_ts);
+struct timespec pps_irq_ts_real[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_real);
+struct timespec pps_irq_ts_mono_raw[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_mono_raw);
#endif
#ifdef CONFIG_DEBUG_STACKOVERFLOW
@@ -204,10 +206,10 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
int overflow;
#ifdef CONFIG_PPS_IRQ_EVENTS
- struct timespec ts;
+ struct timespec ts_mono_raw, ts_real;
/* Get IRQ timestamps as soon as possible for the PPS layer */
- getnstimeofday(&ts);
+ getnstime_raw_and_real(&ts_mono_raw, &ts_real);
#endif
overflow = check_stack_overflow();
@@ -218,7 +220,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
#ifdef CONFIG_PPS_IRQ_EVENTS
/* Then, after sanity check, store the IRQ timestamp */
- pps_irq_ts[irq] = ts;
+ pps_irq_ts_mono_raw[irq] = ts_mono_raw;
+ pps_irq_ts_real[irq] = ts_real;
#endif
if (!execute_on_irq_stack(overflow, desc, irq)) {
diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c
index aeabd9e..c217e01 100644
--- a/arch/x86/kernel/irq_64.c
+++ b/arch/x86/kernel/irq_64.c
@@ -28,8 +28,10 @@ DEFINE_PER_CPU(struct pt_regs *, irq_regs);
EXPORT_PER_CPU_SYMBOL(irq_regs);
#ifdef CONFIG_PPS_IRQ_EVENTS
-struct timespec pps_irq_ts[NR_IRQS];
-EXPORT_SYMBOL(pps_irq_ts);
+struct timespec pps_irq_ts_real[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_real);
+struct timespec pps_irq_ts_mono_raw[NR_IRQS];
+EXPORT_SYMBOL(pps_irq_ts_mono_raw);
#endif
/*
@@ -58,10 +60,10 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
{
struct irq_desc *desc;
#ifdef CONFIG_PPS_IRQ_EVENTS
- struct timespec ts;
+ struct timespec ts_mono_raw, ts_real;
/* Get IRQ timestamps as soon as possible for the PPS layer */
- getnstimeofday(&ts);
+ getnstime_raw_and_real(&ts_mono_raw, &ts_real);
#endif
stack_overflow_check(regs);
@@ -72,7 +74,8 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
#ifdef CONFIG_PPS_IRQ_EVENTS
/* Then, after sanity check, store the IRQ timestamp */
- pps_irq_ts[irq] = ts;
+ pps_irq_ts_mono_raw[irq] = ts_mono_raw;
+ pps_irq_ts_real[irq] = ts_real;
#endif
generic_handle_irq_desc(irq, desc);
diff --git a/drivers/pps/clients/ktimer.c b/drivers/pps/clients/ktimer.c
index 259baa7..439a5c1 100644
--- a/drivers/pps/clients/ktimer.c
+++ b/drivers/pps/clients/ktimer.c
@@ -41,11 +41,11 @@ static struct timer_list ktimer;
static void pps_ktimer_event(unsigned long ptr)
{
- struct timespec __ts;
+ struct timespec __ts, __ts_raw;
struct pps_ktime ts;
/* First of all we get the time stamp... */
- getnstimeofday(&__ts);
+ getnstime_raw_and_real(&__ts_raw, &__ts);
pr_info("PPS event at %lu\n", jiffies);
@@ -53,7 +53,7 @@ static void pps_ktimer_event(unsigned long ptr)
ts.sec = __ts.tv_sec;
ts.nsec = __ts.tv_nsec;
- pps_event(source, &ts, PPS_CAPTUREASSERT, NULL);
+ pps_event(source, &ts, &__ts_raw, PPS_CAPTUREASSERT, NULL);
mod_timer(&ktimer, jiffies + HZ);
}
diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c
index f16396b..f35b317 100644
--- a/drivers/pps/clients/pps-ldisc.c
+++ b/drivers/pps/clients/pps-ldisc.c
@@ -27,26 +27,27 @@
#define PPS_TTY_MAGIC 0x0001
static void pps_tty_dcd_change(struct tty_struct *tty, unsigned int status,
- struct timespec *ts)
+ struct timespec *ts, struct timespec *ts_raw)
{
long id = (long) tty->disc_data;
- struct timespec __ts;
+ struct timespec __ts, __ts_raw;
struct pps_ktime pps_ts;
/* First of all we get the time stamp... */
- getnstimeofday(&__ts);
+ getnstime_raw_and_real(&__ts_raw, &__ts);
/* Does caller give us a timestamp? */
- if (ts) { /* Yes. Let's use it! */
+ if (ts && ts_raw) { /* Yes. Let's use it! */
pps_ts.sec = ts->tv_sec;
pps_ts.nsec = ts->tv_nsec;
+ __ts_raw = *ts_raw;
} else { /* No. Do it ourself! */
pps_ts.sec = __ts.tv_sec;
pps_ts.nsec = __ts.tv_nsec;
}
/* Now do the PPS event report */
- pps_event(id, &pps_ts, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
+ pps_event(id, &pps_ts, &__ts_raw, status ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR,
NULL);
pr_debug("PPS %s at %lu on source #%d\n",
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 8ade87c..ae38168 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -131,7 +131,7 @@ static struct pps_client_pp device = {
}
};
-/* calibrated getnstimeofday time */
+/* calibrated getnstime_raw_and_real time */
unsigned long gntd_time;
/* calibrate port write time */
@@ -149,14 +149,14 @@ static void calibrate_port(struct pps_client_pp *dev)
for (i = 0; i < PORT_NTESTS; i++)
{
- struct timespec a, b;
+ struct timespec a, b, c;
unsigned long d;
unsigned long irq_flags;
local_irq_save(irq_flags);
- getnstimeofday(&a);
+ getnstime_raw_and_real(&a, &c);
port->ops->read_status(port);
- getnstimeofday(&b);
+ getnstime_raw_and_real(&b, &c);
local_irq_restore(irq_flags);
b = timespec_sub(b, a);
@@ -185,7 +185,7 @@ static inline void timespec_to_pps_ktime(struct pps_ktime *kt, struct timespec t
/* parport interrupt handler */
static void parport_irq(void *handle)
{
- struct timespec ts;
+ struct timespec ts_mono_raw, ts_real;
struct pps_ktime kt;
struct pps_client_pp *dev = handle;
struct parport *port = dev->pardev->port;
@@ -193,13 +193,14 @@ static void parport_irq(void *handle)
/* first of all we get the time stamp ... */
#ifdef CONFIG_PPS_IRQ_EVENTS
- ts = pps_irq_ts[port->irq];
+ ts_mono_raw = pps_irq_ts_mono_raw[port->irq];
+ ts_real = pps_irq_ts_real[port->irq];
#else
- getnstimeofday(&ts);
+ getnstime_raw_and_real(&ts_mono_raw, &ts_real);
#endif
/* ... and translate it to PPS time data struct */
- timespec_to_pps_ktime(&kt, ts);
+ timespec_to_pps_ktime(&kt, ts_real);
/* check the signal (no signal means the pulse is lost this time) */
if (!SIGNAL_IS_SET(port)) {
@@ -208,16 +209,16 @@ static void parport_irq(void *handle)
}
/* fire assert event */
- pps_event(dev->source, &kt, PPS_CAPTUREASSERT, NULL);
+ pps_event(dev->source, &kt, &ts_mono_raw, PPS_CAPTUREASSERT, NULL);
/* poll the port until the signal is unset */
for (i = RECEIVE_TIMEOUT; i; i--)
if (!SIGNAL_IS_SET(port)) {
- getnstimeofday(&ts);
- set_normalized_timespec(&ts, ts.tv_sec, ts.tv_nsec - gntd_time);
- timespec_to_pps_ktime(&kt, ts);
+ getnstime_raw_and_real(&ts_mono_raw, &ts_real);
+ set_normalized_timespec(&ts_real, ts_real.tv_sec, ts_real.tv_nsec - gntd_time);
+ timespec_to_pps_ktime(&kt, ts_real);
/* fire clear event */
- pps_event(dev->source, &kt, PPS_CAPTURECLEAR, NULL);
+ pps_event(dev->source, &kt, &ts_mono_raw, PPS_CAPTURECLEAR, NULL);
return;
}
@@ -271,11 +272,12 @@ static struct parport_driver pps_parport_driver = {
.detach = parport_detach,
};
-/* calibrate getnstimeofday time */
+/* calibrate getnstime_raw_and_real time */
#define GNTD_NTESTS 30
static void __init calibrate_gntd(void)
{
static struct timespec gntd[GNTD_NTESTS + 1];
+ struct timespec t;
int i;
unsigned long irq_flags;
struct time_stat gntd_stat = {
@@ -288,7 +290,7 @@ static void __init calibrate_gntd(void)
local_irq_save(irq_flags);
for (i = 0; i <= GNTD_NTESTS; i++)
{
- getnstimeofday(gntd + i);
+ getnstime_raw_and_real(gntd + i, &t);
}
local_irq_restore(irq_flags);
@@ -302,7 +304,7 @@ static void __init calibrate_gntd(void)
}
gntd_time = ts_getavg(&gntd_stat);
gntd_disp = ts_getdisp(&gntd_stat);
- pr_info(DRVNAME ": getnstimeofday takes %ldns with %ldns dispersion\n", gntd_time, gntd_disp);
+ pr_info(DRVNAME ": getnstime_raw_and_real takes %ldns with %ldns dispersion\n", gntd_time, gntd_disp);
}
/* module staff */
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index edb5315..8e0bcec 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -298,10 +298,11 @@ void hardpps(const struct timespec *p_ts, long nsec)
* pps->info.echo(source, event, data);
*/
-void pps_event(int source, struct pps_ktime *ts, int event, void *data)
+void pps_event(int source, struct pps_ktime *ts, struct timespec *ts_raw, int event, void *data)
{
struct pps_device *pps;
unsigned long flags;
+ long nsec = 0;
if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0) {
printk(KERN_ERR "pps: unknown event (%x) for source %d\n",
@@ -331,6 +332,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
/* Save the time stamp */
pps->assert_tu = *ts;
+ if (likely(pps->assert_tu_raw.tv_sec != 0)) {
+ struct timespec d = timespec_sub(*ts_raw, pps->assert_tu_raw);
+ nsec = timespec_to_ns(&d);
+ }
+ pps->assert_tu_raw = *ts_raw;
pps->assert_sequence++;
pr_debug("capture assert seq #%u for source %d\n",
pps->assert_sequence, source);
@@ -342,6 +348,11 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
/* Save the time stamp */
pps->clear_tu = *ts;
+ if (likely(pps->clear_tu_raw.tv_sec != 0)) {
+ struct timespec d = timespec_sub(*ts_raw, pps->clear_tu_raw);
+ nsec = timespec_to_ns(&d);
+ }
+ pps->clear_tu_raw = *ts_raw;
pps->clear_sequence++;
pr_debug("capture clear seq #%u for source %d\n",
pps->clear_sequence, source);
@@ -349,7 +360,7 @@ void pps_event(int source, struct pps_ktime *ts, int event, void *data)
spin_lock(&pps_kc_hardpps_lock);
if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) {
struct timespec p_ts = { .tv_sec = ts->sec, .tv_nsec = ts->nsec };
- hardpps(&p_ts, 0);
+ hardpps(&p_ts, nsec);
}
spin_unlock(&pps_kc_hardpps_lock);
diff --git a/include/linux/pps.h b/include/linux/pps.h
index 558d679..a103ab0 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -159,6 +159,8 @@ struct pps_device {
__u32 clear_sequence; /* PPS' clear event seq # */
struct pps_ktime assert_tu;
struct pps_ktime clear_tu;
+ struct timespec assert_tu_raw;
+ struct timespec clear_tu_raw;
int current_mode; /* PPS mode at event time */
int go; /* PPS event is arrived? */
@@ -180,7 +182,8 @@ struct pps_device {
extern spinlock_t pps_idr_lock;
extern struct idr pps_idr;
-extern struct timespec pps_irq_ts[];
+extern struct timespec pps_irq_ts_real[];
+extern struct timespec pps_irq_ts_mono_raw[];
extern struct device_attribute pps_attrs[];
@@ -201,7 +204,7 @@ extern int pps_register_source(struct pps_source_info *info,
extern void pps_unregister_source(int source);
extern int pps_register_cdev(struct pps_device *pps);
extern void pps_unregister_cdev(struct pps_device *pps);
-extern void pps_event(int source, struct pps_ktime *ts, int event, void *data);
+extern void pps_event(int source, struct pps_ktime *ts, struct timespec *ts_raw, int event, void *data);
#endif /* __KERNEL__ */
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index bbe91b4..98856e9 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -510,13 +510,14 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status)
{
struct uart_info *info = port->info;
struct tty_ldisc *ld = tty_ldisc_ref(info->port.tty);
- struct timespec ts;
+ struct timespec ts, ts_raw;
if (ld && ld->ops->dcd_change)
#ifdef CONFIG_PPS_IRQ_EVENTS
- ts = pps_irq_ts[port->irq];
+ ts = pps_irq_ts_real[port->irq];
+ ts_raw = pps_irq_ts_mono_raw[port->irq];
#else
- getnstimeofday(&ts);
+ getnstime_raw_and_real(&ts_raw, &ts);
#endif
port->icount.dcd++;
@@ -533,7 +534,7 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status)
}
if (ld && ld->ops->dcd_change)
- ld->ops->dcd_change(info->port.tty, status, &ts);
+ ld->ops->dcd_change(info->port.tty, status, &ts, &ts_raw);
if (ld)
tty_ldisc_deref(ld);
}
diff --git a/include/linux/time.h b/include/linux/time.h
index 74568fe..6308d65 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -125,6 +125,7 @@ extern unsigned int alarm_setitimer(unsigned int seconds);
extern int do_getitimer(int which, struct itimerval *value);
extern void getnstimeofday(struct timespec *tv);
extern void getrawmonotonic(struct timespec *ts);
+extern void getnstime_raw_and_real(struct timespec *ts_mono_raw, struct timespec *ts_real);
extern void getboottime(struct timespec *ts);
extern void monotonic_to_bootbased(struct timespec *ts);
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 526fbf4..73e81ac 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -101,10 +101,10 @@
* any pending driver I/O is completed.
*
* void (*dcd_change)(struct tty_struct *tty, unsigned int status,
- * struct timespec *ts)
+ * struct timespec *ts, struct timespec *ts_raw)
*
* Tells the discipline that the DCD pin has changed its status and
- * the relative timestamp. Pointer ts can be NULL.
+ * the relative timestamps. Pointers ts and ts_raw can be NULL.
*/
#include <linux/fs.h>
@@ -143,7 +143,7 @@ struct tty_ldisc_ops {
char *fp, int count);
void (*write_wakeup)(struct tty_struct *);
void (*dcd_change)(struct tty_struct *, unsigned int,
- struct timespec *);
+ struct timespec *, struct timespec *);
struct module *owner;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 00c02c4..3c7b5be 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -118,6 +118,45 @@ void getnstimeofday(struct timespec *ts)
EXPORT_SYMBOL(getnstimeofday);
+/**
+ * getnstime_raw_and_real - Returns both the time of day an raw
+ * monotonic time in a timespec format
+ * @ts_mono_raw: pointer to the timespec to be set to raw
+ * monotonic time
+ * @ts_real: pointer to the timespec to be set to the time
+ * of day
+ */
+void getnstime_raw_and_real(struct timespec *ts_mono_raw, struct timespec *ts_real)
+{
+ cycle_t cycle_now, cycle_delta;
+ unsigned long seq;
+ s64 nsecs_mono_raw, nsecs_real;
+
+ WARN_ON(timekeeping_suspended);
+
+ do {
+ seq = read_seqbegin(&xtime_lock);
+
+ *ts_real = xtime;
+
+ /* read clocksource: */
+ cycle_now = clocksource_read(clock);
+ /* calculate the delta since the last update_wall_time: */
+ cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
+ /* convert to nanoseconds: */
+ nsecs_mono_raw = ((s64)cycle_delta * clock->mult_orig) >> clock->shift;
+ nsecs_real = cyc2ns(clock, cycle_delta);
+
+ *ts_mono_raw = clock->raw_time;
+
+ } while (read_seqretry(&xtime_lock, seq));
+
+ timespec_add_ns(ts_mono_raw, nsecs_mono_raw);
+ timespec_add_ns(ts_real, nsecs_real);
+}
+
+EXPORT_SYMBOL(getnstime_raw_and_real);
+
ktime_t ktime_get(void)
{
cycle_t cycle_now, cycle_delta;
--
1.6.3.3
More information about the LinuxPPS
mailing list