diff options
| author | Alexander Gordeev <lasaine@lvk.cs.msu.su> | 2011-01-12 20:00:56 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 11:03:20 -0500 |
| commit | 025b40abe715d638e60516a657d354e8560c1a85 (patch) | |
| tree | 9320d3993c65c4eb56d3ab30337d590eb8c892fd /kernel/time | |
| parent | 12f9b1f9c11700893a7b453705d95b260d78f268 (diff) | |
ntp: add hardpps implementation
This commit adds hardpps() implementation based upon the original one from
the NTPv4 reference kernel code from David Mills. However, it is highly
optimized towards very fast syncronization and maximum stickness to PPS
signal. The typical error is less then a microsecond.
To make it sync faster I had to throw away exponential phase filter so
that the full phase offset is corrected immediately. Then I also had to
throw away median phase filter because it gives a bigger error itself if
used without exponential filter.
Maybe we will find an appropriate filtering scheme in the future but it's
not necessary if the signal quality is ok.
Signed-off-by: Alexander Gordeev <lasaine@lvk.cs.msu.su>
Acked-by: John Stultz <johnstul@us.ibm.com>
Cc: Rodolfo Giometti <giometti@enneenne.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/time')
| -rw-r--r-- | kernel/time/ntp.c | 425 |
1 files changed, 410 insertions, 15 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index d2321891538f..5c00242fa921 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/timex.h> | 14 | #include <linux/timex.h> |
| 15 | #include <linux/time.h> | 15 | #include <linux/time.h> |
| 16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
| 17 | #include <linux/module.h> | ||
| 17 | 18 | ||
| 18 | /* | 19 | /* |
| 19 | * NTP timekeeping variables: | 20 | * NTP timekeeping variables: |
| @@ -74,6 +75,162 @@ static long time_adjust; | |||
| 74 | /* constant (boot-param configurable) NTP tick adjustment (upscaled) */ | 75 | /* constant (boot-param configurable) NTP tick adjustment (upscaled) */ |
| 75 | static s64 ntp_tick_adj; | 76 | static s64 ntp_tick_adj; |
| 76 | 77 | ||
| 78 | #ifdef CONFIG_NTP_PPS | ||
| 79 | |||
| 80 | /* | ||
| 81 | * The following variables are used when a pulse-per-second (PPS) signal | ||
| 82 | * is available. They establish the engineering parameters of the clock | ||
| 83 | * discipline loop when controlled by the PPS signal. | ||
| 84 | */ | ||
| 85 | #define PPS_VALID 10 /* PPS signal watchdog max (s) */ | ||
| 86 | #define PPS_POPCORN 4 /* popcorn spike threshold (shift) */ | ||
| 87 | #define PPS_INTMIN 2 /* min freq interval (s) (shift) */ | ||
| 88 | #define PPS_INTMAX 8 /* max freq interval (s) (shift) */ | ||
| 89 | #define PPS_INTCOUNT 4 /* number of consecutive good intervals to | ||
| 90 | increase pps_shift or consecutive bad | ||
| 91 | intervals to decrease it */ | ||
| 92 | #define PPS_MAXWANDER 100000 /* max PPS freq wander (ns/s) */ | ||
| 93 | |||
| 94 | static int pps_valid; /* signal watchdog counter */ | ||
| 95 | static long pps_tf[3]; /* phase median filter */ | ||
| 96 | static long pps_jitter; /* current jitter (ns) */ | ||
| 97 | static struct timespec pps_fbase; /* beginning of the last freq interval */ | ||
| 98 | static int pps_shift; /* current interval duration (s) (shift) */ | ||
| 99 | static int pps_intcnt; /* interval counter */ | ||
| 100 | static s64 pps_freq; /* frequency offset (scaled ns/s) */ | ||
| 101 | static long pps_stabil; /* current stability (scaled ns/s) */ | ||
| 102 | |||
| 103 | /* | ||
| 104 | * PPS signal quality monitors | ||
| 105 | */ | ||
| 106 | static long pps_calcnt; /* calibration intervals */ | ||
| 107 | static long pps_jitcnt; /* jitter limit exceeded */ | ||
| 108 | static long pps_stbcnt; /* stability limit exceeded */ | ||
| 109 | static long pps_errcnt; /* calibration errors */ | ||
| 110 | |||
| 111 | |||
| 112 | /* PPS kernel consumer compensates the whole phase error immediately. | ||
| 113 | * Otherwise, reduce the offset by a fixed factor times the time constant. | ||
| 114 | */ | ||
| 115 | static inline s64 ntp_offset_chunk(s64 offset) | ||
| 116 | { | ||
| 117 | if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL) | ||
| 118 | return offset; | ||
| 119 | else | ||
| 120 | return shift_right(offset, SHIFT_PLL + time_constant); | ||
| 121 | } | ||
| 122 | |||
| 123 | static inline void pps_reset_freq_interval(void) | ||
| 124 | { | ||
| 125 | /* the PPS calibration interval may end | ||
| 126 | surprisingly early */ | ||
| 127 | pps_shift = PPS_INTMIN; | ||
| 128 | pps_intcnt = 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * pps_clear - Clears the PPS state variables | ||
| 133 | * | ||
| 134 | * Must be called while holding a write on the xtime_lock | ||
| 135 | */ | ||
| 136 | static inline void pps_clear(void) | ||
| 137 | { | ||
| 138 | pps_reset_freq_interval(); | ||
| 139 | pps_tf[0] = 0; | ||
| 140 | pps_tf[1] = 0; | ||
| 141 | pps_tf[2] = 0; | ||
| 142 | pps_fbase.tv_sec = pps_fbase.tv_nsec = 0; | ||
| 143 | pps_freq = 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* Decrease pps_valid to indicate that another second has passed since | ||
| 147 | * the last PPS signal. When it reaches 0, indicate that PPS signal is | ||
| 148 | * missing. | ||
| 149 | * | ||
| 150 | * Must be called while holding a write on the xtime_lock | ||
| 151 | */ | ||
| 152 | static inline void pps_dec_valid(void) | ||
| 153 | { | ||
| 154 | if (pps_valid > 0) | ||
| 155 | pps_valid--; | ||
| 156 | else { | ||
| 157 | time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER | | ||
| 158 | STA_PPSWANDER | STA_PPSERROR); | ||
| 159 | pps_clear(); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | static inline void pps_set_freq(s64 freq) | ||
| 164 | { | ||
| 165 | pps_freq = freq; | ||
| 166 | } | ||
| 167 | |||
| 168 | static inline int is_error_status(int status) | ||
| 169 | { | ||
| 170 | return (time_status & (STA_UNSYNC|STA_CLOCKERR)) | ||
| 171 | /* PPS signal lost when either PPS time or | ||
| 172 | * PPS frequency synchronization requested | ||
| 173 | */ | ||
| 174 | || ((time_status & (STA_PPSFREQ|STA_PPSTIME)) | ||
| 175 | && !(time_status & STA_PPSSIGNAL)) | ||
| 176 | /* PPS jitter exceeded when | ||
| 177 | * PPS time synchronization requested */ | ||
| 178 | || ((time_status & (STA_PPSTIME|STA_PPSJITTER)) | ||
| 179 | == (STA_PPSTIME|STA_PPSJITTER)) | ||
| 180 | /* PPS wander exceeded or calibration error when | ||
| 181 | * PPS frequency synchronization requested | ||
| 182 | */ | ||
| 183 | || ((time_status & STA_PPSFREQ) | ||
| 184 | && (time_status & (STA_PPSWANDER|STA_PPSERROR))); | ||
| 185 | } | ||
| 186 | |||
| 187 | static inline void pps_fill_timex(struct timex *txc) | ||
| 188 | { | ||
| 189 | txc->ppsfreq = shift_right((pps_freq >> PPM_SCALE_INV_SHIFT) * | ||
| 190 | PPM_SCALE_INV, NTP_SCALE_SHIFT); | ||
| 191 | txc->jitter = pps_jitter; | ||
| 192 | if (!(time_status & STA_NANO)) | ||
| 193 | txc->jitter /= NSEC_PER_USEC; | ||
| 194 | txc->shift = pps_shift; | ||
| 195 | txc->stabil = pps_stabil; | ||
| 196 | txc->jitcnt = pps_jitcnt; | ||
| 197 | txc->calcnt = pps_calcnt; | ||
| 198 | txc->errcnt = pps_errcnt; | ||
| 199 | txc->stbcnt = pps_stbcnt; | ||
| 200 | } | ||
| 201 | |||
| 202 | #else /* !CONFIG_NTP_PPS */ | ||
| 203 | |||
| 204 | static inline s64 ntp_offset_chunk(s64 offset) | ||
| 205 | { | ||
| 206 | return shift_right(offset, SHIFT_PLL + time_constant); | ||
| 207 | } | ||
| 208 | |||
| 209 | static inline void pps_reset_freq_interval(void) {} | ||
| 210 | static inline void pps_clear(void) {} | ||
| 211 | static inline void pps_dec_valid(void) {} | ||
| 212 | static inline void pps_set_freq(s64 freq) {} | ||
| 213 | |||
| 214 | static inline int is_error_status(int status) | ||
| 215 | { | ||
| 216 | return status & (STA_UNSYNC|STA_CLOCKERR); | ||
| 217 | } | ||
| 218 | |||
| 219 | static inline void pps_fill_timex(struct timex *txc) | ||
| 220 | { | ||
| 221 | /* PPS is not implemented, so these are zero */ | ||
| 222 | txc->ppsfreq = 0; | ||
| 223 | txc->jitter = 0; | ||
| 224 | txc->shift = 0; | ||
| 225 | txc->stabil = 0; | ||
| 226 | txc->jitcnt = 0; | ||
| 227 | txc->calcnt = 0; | ||
| 228 | txc->errcnt = 0; | ||
| 229 | txc->stbcnt = 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | #endif /* CONFIG_NTP_PPS */ | ||
| 233 | |||
| 77 | /* | 234 | /* |
| 78 | * NTP methods: | 235 | * NTP methods: |
| 79 | */ | 236 | */ |
| @@ -185,6 +342,9 @@ void ntp_clear(void) | |||
| 185 | 342 | ||
| 186 | tick_length = tick_length_base; | 343 | tick_length = tick_length_base; |
| 187 | time_offset = 0; | 344 | time_offset = 0; |
| 345 | |||
| 346 | /* Clear PPS state variables */ | ||
| 347 | pps_clear(); | ||
| 188 | } | 348 | } |
| 189 | 349 | ||
| 190 | /* | 350 | /* |
| @@ -250,16 +410,16 @@ void second_overflow(void) | |||
| 250 | time_status |= STA_UNSYNC; | 410 | time_status |= STA_UNSYNC; |
| 251 | } | 411 | } |
| 252 | 412 | ||
| 253 | /* | 413 | /* Compute the phase adjustment for the next second */ |
| 254 | * Compute the phase adjustment for the next second. The offset is | ||
| 255 | * reduced by a fixed factor times the time constant. | ||
| 256 | */ | ||
| 257 | tick_length = tick_length_base; | 414 | tick_length = tick_length_base; |
| 258 | 415 | ||
