[PATCH] ntp: convert time_freq to nsec value
authorRoman Zippel <zippel@linux-m68k.org>
Sun, 1 Oct 2006 06:28:27 +0000 (23:28 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 1 Oct 2006 07:39:27 +0000 (00:39 -0700)
This converts time_freq to a scaled nsec value and adds around 6bit of extra
resolution.  This pushes the time_freq to its 32bit limits so the calculatons
have to be done with 64bit.

Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Cc: john stultz <johnstul@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
include/linux/timex.h
kernel/time/ntp.c

index 7715b4c0caf9e738e27a38dca86722ff71f1ae15..671609ee1a3d9b9b4dbea784c6de56edd6d6c6a8 100644 (file)
 #define SHIFT_SCALE 22         /* phase scale (shift) */
 #define SHIFT_UPDATE (SHIFT_HZ + 1) /* time offset scale (shift) */
 #define SHIFT_USEC 16          /* frequency offset scale (shift) */
+#define SHIFT_NSEC 12          /* kernel frequency offset scale */
 #define FINENSEC (1L << (SHIFT_SCALE - 10)) /* ~1 ns in phase units */
 
 #define MAXPHASE 512000L        /* max phase error (us) */
 #define MAXFREQ (512L << SHIFT_USEC)  /* max frequency error (ppm) */
+#define MAXFREQ_NSEC (512000L << SHIFT_NSEC) /* max frequency error (ppb) */
 #define MINSEC 16L              /* min interval between updates (s) */
 #define MAXSEC 1200L            /* max interval between updates (s) */
 #define        NTP_PHASE_LIMIT (MAXPHASE << 5) /* beyond max. dispersion */
index af7563f5d4e2b3eb06da60cbd4d3a8bad297fda2..9137b54613e0b9c2871c63b016bac4c81d046b68 100644 (file)
@@ -66,7 +66,7 @@ void ntp_update_frequency(void)
 {
        tick_length_base = (u64)(tick_usec * NSEC_PER_USEC * USER_HZ) << TICK_LENGTH_SHIFT;
        tick_length_base += (s64)CLOCK_TICK_ADJUST << TICK_LENGTH_SHIFT;
-       tick_length_base += ((s64)time_freq * NSEC_PER_USEC) << (TICK_LENGTH_SHIFT - SHIFT_USEC);
+       tick_length_base += (s64)time_freq << (TICK_LENGTH_SHIFT - SHIFT_NSEC);
 
        do_div(tick_length_base, HZ);
 
@@ -200,6 +200,7 @@ void __attribute__ ((weak)) notify_arch_cmos_timer(void)
 int do_adjtimex(struct timex *txc)
 {
        long ltemp, mtemp, save_adjust;
+       s64 freq_adj;
        int result;
 
        /* In order to modify anything, you gotta be super-user! */
@@ -245,7 +246,7 @@ int do_adjtimex(struct timex *txc)
                    result = -EINVAL;
                    goto leave;
                }
-               time_freq = txc->freq;
+               time_freq = ((s64)txc->freq * NSEC_PER_USEC) >> (SHIFT_USEC - SHIFT_NSEC);
            }
 
            if (txc->modes & ADJ_MAXERROR) {
@@ -278,14 +279,14 @@ int do_adjtimex(struct timex *txc)
                    time_adjust = txc->offset;
                }
                else if (time_status & STA_PLL) {
-                   ltemp = txc->offset;
+                   ltemp = txc->offset * NSEC_PER_USEC;
 
                    /*
                     * Scale the phase adjustment and
                     * clamp to the operating range.
                     */
-                   time_offset = min(ltemp, MAXPHASE);
-                   time_offset = max(time_offset, -MAXPHASE);
+                   time_offset = min(ltemp, MAXPHASE * NSEC_PER_USEC);
+                   time_offset = max(time_offset, -MAXPHASE * NSEC_PER_USEC);
 
                    /*
                     * Select whether the frequency is to be controlled
@@ -297,24 +298,31 @@ int do_adjtimex(struct timex *txc)
                        time_reftime = xtime.tv_sec;
                    mtemp = xtime.tv_sec - time_reftime;
                    time_reftime = xtime.tv_sec;
+                   freq_adj = 0;
                    if (time_status & STA_FLL) {
                        if (mtemp >= MINSEC) {
-                           ltemp = ((time_offset << 12) / mtemp) << (SHIFT_USEC - 12);
-                           time_freq += shift_right(ltemp, SHIFT_KH);
+                           freq_adj = (s64)time_offset << (SHIFT_NSEC - SHIFT_KH);
+                           if (time_offset < 0) {
+                               freq_adj = -freq_adj;
+                               do_div(freq_adj, mtemp);
+                               freq_adj = -freq_adj;
+                           } else
+                               do_div(freq_adj, mtemp);
                        } else /* calibration interval too short (p. 12) */
                                result = TIME_ERROR;
                    } else {    /* PLL mode */
                        if (mtemp < MAXSEC) {
-                           ltemp *= mtemp;
-                           time_freq += shift_right(ltemp,(time_constant +
+                           freq_adj = (s64)ltemp * mtemp;
+                           freq_adj = shift_right(freq_adj,(time_constant +
                                                       time_constant +
-                                                      SHIFT_KF - SHIFT_USEC));
+                                                      SHIFT_KF - SHIFT_NSEC));
                        } else /* calibration interval too long (p. 12) */
                                result = TIME_ERROR;
                    }
-                   time_freq = min(time_freq, MAXFREQ);
-                   time_freq = max(time_freq, -MAXFREQ);
-                   time_offset = (time_offset * NSEC_PER_USEC / HZ) << SHIFT_UPDATE;
+                   freq_adj += time_freq;
+                   freq_adj = min(freq_adj, (s64)MAXFREQ_NSEC);
+                   time_freq = max(freq_adj, (s64)-MAXFREQ_NSEC);
+                   time_offset = (time_offset / HZ) << SHIFT_UPDATE;
                } /* STA_PLL */
            } /* txc->modes & ADJ_OFFSET */
            if (txc->modes & ADJ_TICK)
@@ -330,7 +338,7 @@ leave:      if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0)
            txc->offset    = save_adjust;
        else
            txc->offset    = shift_right(time_offset, SHIFT_UPDATE) * HZ / 1000;
-       txc->freq          = time_freq;
+       txc->freq          = (time_freq / NSEC_PER_USEC) << (SHIFT_USEC - SHIFT_NSEC);
        txc->maxerror      = time_maxerror;
        txc->esterror      = time_esterror;
        txc->status        = time_status;