diff options
Diffstat (limited to 'kernel/time/ntp.c')
| -rw-r--r-- | kernel/time/ntp.c | 675 | 
1 files changed, 528 insertions, 147 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index d2321891538..33db43a3951 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -14,22 +14,28 @@  #include <linux/timex.h>  #include <linux/time.h>  #include <linux/mm.h> +#include <linux/module.h> +#include <linux/rtc.h> + +#include "tick-internal.h" +#include "ntp_internal.h"  /*   * NTP timekeeping variables: + * + * Note: All of the NTP state is protected by the timekeeping locks.   */ +  /* USER_HZ period (usecs): */  unsigned long			tick_usec = TICK_USEC; -/* ACTHZ period (nsecs): */ +/* SHIFTED_HZ period (nsecs): */  unsigned long			tick_nsec; -u64				tick_length; +static u64			tick_length;  static u64			tick_length_base; -static struct hrtimer		leap_timer; -  #define MAX_TICKADJ		500LL		/* usecs */  #define MAX_TICKADJ_SCALED \  	(((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ) @@ -46,10 +52,7 @@ static struct hrtimer		leap_timer;  static int			time_state = TIME_OK;  /* clock status bits:							*/ -int				time_status = STA_UNSYNC; - -/* TAI offset (secs):							*/ -static long			time_tai; +static int			time_status = STA_UNSYNC;  /* time adjustment (nsecs):						*/  static s64			time_offset; @@ -74,6 +77,169 @@ static long			time_adjust;  /* constant (boot-param configurable) NTP tick adjustment (upscaled)	*/  static s64			ntp_tick_adj; +#ifdef CONFIG_NTP_PPS + +/* + * The following variables are used when a pulse-per-second (PPS) signal + * is available. They establish the engineering parameters of the clock + * discipline loop when controlled by the PPS signal. + */ +#define PPS_VALID	10	/* PPS signal watchdog max (s) */ +#define PPS_POPCORN	4	/* popcorn spike threshold (shift) */ +#define PPS_INTMIN	2	/* min freq interval (s) (shift) */ +#define PPS_INTMAX	8	/* max freq interval (s) (shift) */ +#define PPS_INTCOUNT	4	/* number of consecutive good intervals to +				   increase pps_shift or consecutive bad +				   intervals to decrease it */ +#define PPS_MAXWANDER	100000	/* max PPS freq wander (ns/s) */ + +static int pps_valid;		/* signal watchdog counter */ +static long pps_tf[3];		/* phase median filter */ +static long pps_jitter;		/* current jitter (ns) */ +static struct timespec pps_fbase; /* beginning of the last freq interval */ +static int pps_shift;		/* current interval duration (s) (shift) */ +static int pps_intcnt;		/* interval counter */ +static s64 pps_freq;		/* frequency offset (scaled ns/s) */ +static long pps_stabil;		/* current stability (scaled ns/s) */ + +/* + * PPS signal quality monitors + */ +static long pps_calcnt;		/* calibration intervals */ +static long pps_jitcnt;		/* jitter limit exceeded */ +static long pps_stbcnt;		/* stability limit exceeded */ +static long pps_errcnt;		/* calibration errors */ + + +/* PPS kernel consumer compensates the whole phase error immediately. + * Otherwise, reduce the offset by a fixed factor times the time constant. + */ +static inline s64 ntp_offset_chunk(s64 offset) +{ +	if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL) +		return offset; +	else +		return shift_right(offset, SHIFT_PLL + time_constant); +} + +static inline void pps_reset_freq_interval(void) +{ +	/* the PPS calibration interval may end +	   surprisingly early */ +	pps_shift = PPS_INTMIN; +	pps_intcnt = 0; +} + +/** + * pps_clear - Clears the PPS state variables + */ +static inline void pps_clear(void) +{ +	pps_reset_freq_interval(); +	pps_tf[0] = 0; +	pps_tf[1] = 0; +	pps_tf[2] = 0; +	pps_fbase.tv_sec = pps_fbase.tv_nsec = 0; +	pps_freq = 0; +} + +/* Decrease pps_valid to indicate that another second has passed since + * the last PPS signal. When it reaches 0, indicate that PPS signal is + * missing. + */ +static inline void pps_dec_valid(void) +{ +	if (pps_valid > 0) +		pps_valid--; +	else { +		time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER | +				 STA_PPSWANDER | STA_PPSERROR); +		pps_clear(); +	} +} + +static inline void pps_set_freq(s64 freq) +{ +	pps_freq = freq; +} + +static inline int is_error_status(int status) +{ +	return (status & (STA_UNSYNC|STA_CLOCKERR)) +		/* PPS signal lost when either PPS time or +		 * PPS frequency synchronization requested +		 */ +		|| ((status & (STA_PPSFREQ|STA_PPSTIME)) +			&& !(status & STA_PPSSIGNAL)) +		/* PPS jitter exceeded when +		 * PPS time synchronization requested */ +		|| ((status & (STA_PPSTIME|STA_PPSJITTER)) +			== (STA_PPSTIME|STA_PPSJITTER)) +		/* PPS wander exceeded or calibration error when +		 * PPS frequency synchronization requested +		 */ +		|| ((status & STA_PPSFREQ) +			&& (status & (STA_PPSWANDER|STA_PPSERROR))); +} + +static inline void pps_fill_timex(struct timex *txc) +{ +	txc->ppsfreq	   = shift_right((pps_freq >> PPM_SCALE_INV_SHIFT) * +					 PPM_SCALE_INV, NTP_SCALE_SHIFT); +	txc->jitter	   = pps_jitter; +	if (!(time_status & STA_NANO)) +		txc->jitter /= NSEC_PER_USEC; +	txc->shift	   = pps_shift; +	txc->stabil	   = pps_stabil; +	txc->jitcnt	   = pps_jitcnt; +	txc->calcnt	   = pps_calcnt; +	txc->errcnt	   = pps_errcnt; +	txc->stbcnt	   = pps_stbcnt; +} + +#else /* !CONFIG_NTP_PPS */ + +static inline s64 ntp_offset_chunk(s64 offset) +{ +	return shift_right(offset, SHIFT_PLL + time_constant); +} + +static inline void pps_reset_freq_interval(void) {} +static inline void pps_clear(void) {} +static inline void pps_dec_valid(void) {} +static inline void pps_set_freq(s64 freq) {} + +static inline int is_error_status(int status) +{ +	return status & (STA_UNSYNC|STA_CLOCKERR); +} + +static inline void pps_fill_timex(struct timex *txc) +{ +	/* PPS is not implemented, so these are zero */ +	txc->ppsfreq	   = 0; +	txc->jitter	   = 0; +	txc->shift	   = 0; +	txc->stabil	   = 0; +	txc->jitcnt	   = 0; +	txc->calcnt	   = 0; +	txc->errcnt	   = 0; +	txc->stbcnt	   = 0; +} + +#endif /* CONFIG_NTP_PPS */ + + +/** + * ntp_synced - Returns 1 if the NTP status is not UNSYNC + * + */ +static inline int ntp_synced(void) +{ +	return !(time_status & STA_UNSYNC); +} + +  /*   * NTP methods:   */ @@ -116,7 +282,7 @@ static inline s64 ntp_update_offset_fll(s64 offset64, long secs)  	time_status |= STA_MODE; -	return div_s64(offset64 << (NTP_SCALE_SHIFT - SHIFT_FLL), secs); +	return div64_long(offset64 << (NTP_SCALE_SHIFT - SHIFT_FLL), secs);  }  static void ntp_update_offset(long offset) @@ -171,8 +337,6 @@ static void ntp_update_offset(long offset)  /**   * ntp_clear - Clears the NTP state variables - * - * Must be called while holding a write on the xtime_lock   */  void ntp_clear(void)  { @@ -185,63 +349,75 @@ void ntp_clear(void)  	tick_length	= tick_length_base;  	time_offset	= 0; + +	/* Clear PPS state variables */ +	pps_clear(); +} + + +u64 ntp_tick_length(void) +{ +	return tick_length;  } +  /* - * Leap second processing. If in leap-insert state at the end of the - * day, the system clock is set back one second; if in leap-delete - * state, the system clock is set ahead one second. + * this routine handles the overflow of the microsecond field + * + * The tricky bits of code to handle the accurate clock support + * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. + * They were originally developed for SUN and DEC kernels. + * All the kudos should go to Dave for this stuff. + * + * Also handles leap second processing, and returns leap offset   */ -static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer) +int second_overflow(unsigned long secs)  { -	enum hrtimer_restart res = HRTIMER_NORESTART; - -	write_seqlock(&xtime_lock); +	s64 delta; +	int leap = 0; +	/* +	 * Leap second processing. If in leap-insert state at the end of the +	 * day, the system clock is set back one second; if in leap-delete +	 * state, the system clock is set ahead one second. +	 */  	switch (time_state) {  	case TIME_OK: +		if (time_status & STA_INS) +			time_state = TIME_INS; +		else if (time_status & STA_DEL) +			time_state = TIME_DEL;  		break;  	case TIME_INS: -		timekeeping_leap_insert(-1); -		time_state = TIME_OOP; -		printk(KERN_NOTICE -			"Clock: inserting leap second 23:59:60 UTC\n"); -		hrtimer_add_expires_ns(&leap_timer, NSEC_PER_SEC); -		res = HRTIMER_RESTART; +		if (!(time_status & STA_INS)) +			time_state = TIME_OK; +		else if (secs % 86400 == 0) { +			leap = -1; +			time_state = TIME_OOP; +			printk(KERN_NOTICE +				"Clock: inserting leap second 23:59:60 UTC\n"); +		}  		break;  	case TIME_DEL: -		timekeeping_leap_insert(1); -		time_tai--; -		time_state = TIME_WAIT; -		printk(KERN_NOTICE -			"Clock: deleting leap second 23:59:59 UTC\n"); +		if (!(time_status & STA_DEL)) +			time_state = TIME_OK; +		else if ((secs + 1) % 86400 == 0) { +			leap = 1; +			time_state = TIME_WAIT; +			printk(KERN_NOTICE +				"Clock: deleting leap second 23:59:59 UTC\n"); +		}  		break;  	case TIME_OOP: -		time_tai++;  		time_state = TIME_WAIT; -		/* fall through */ +		break; +  	case TIME_WAIT:  		if (!(time_status & (STA_INS | STA_DEL)))  			time_state = TIME_OK;  		break;  	} -	write_sequnlock(&xtime_lock); - -	return res; -} - -/* - * this routine handles the overflow of the microsecond field - * - * The tricky bits of code to handle the accurate clock support - * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. - * They were originally developed for SUN and DEC kernels. - * All the kudos should go to Dave for this stuff. - */ -void second_overflow(void) -{ -	s64 delta;  	/* Bump the maxerror field */  	time_maxerror += MAXFREQ / NSEC_PER_USEC; @@ -250,41 +426,40 @@ void second_overflow(void)  		time_status |= STA_UNSYNC;  	} -	/* -	 * Compute the phase adjustment for the next second. The offset is -	 * reduced by a fixed factor times the time constant. -	 */ +	/* Compute the phase adjustment for the next second */  	tick_length	 = tick_length_base; -	delta		 = shift_right(time_offset, SHIFT_PLL + time_constant); +	delta		 = ntp_offset_chunk(time_offset);  	time_offset	-= delta;  	tick_length	+= delta; +	/* Check PPS signal */ +	pps_dec_valid(); +  	if (!time_adjust) -		return; +		goto out;  	if (time_adjust > MAX_TICKADJ) {  		time_adjust -= MAX_TICKADJ;  		tick_length += MAX_TICKADJ_SCALED; -		return; +		goto out;  	}  	if (time_adjust < -MAX_TICKADJ) {  		time_adjust += MAX_TICKADJ;  		tick_length -= MAX_TICKADJ_SCALED; -		return; +		goto out;  	}  	tick_length += (s64)(time_adjust * NSEC_PER_USEC / NTP_INTERVAL_FREQ)  							 << NTP_SCALE_SHIFT;  	time_adjust = 0; -} - -#ifdef CONFIG_GENERIC_CMOS_UPDATE -/* Disable the cmos update - used by virtualization and embedded */ -int no_sync_cmos_clock  __read_mostly; +out: +	return leap; +} +#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)  static void sync_cmos_clock(struct work_struct *work);  static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock); @@ -300,6 +475,7 @@ static void sync_cmos_clock(struct work_struct *work)  	 * called as close as possible to 500 ms before the new second starts.  	 * This code is run on a timer.  If the clock is set, that timer  	 * may not expire at the correct time.  Thus, we adjust... +	 * We want the clock to be within a couple of ticks from the target.  	 */  	if (!ntp_synced()) {  		/* @@ -310,14 +486,26 @@ static void sync_cmos_clock(struct work_struct *work)  	}  	getnstimeofday(&now); -	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) -		fail = update_persistent_clock(now); +	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) { +		struct timespec adjust = now; + +		fail = -ENODEV; +		if (persistent_clock_is_local) +			adjust.tv_sec -= (sys_tz.tz_minuteswest * 60); +#ifdef CONFIG_GENERIC_CMOS_UPDATE +		fail = update_persistent_clock(adjust); +#endif +#ifdef CONFIG_RTC_SYSTOHC +		if (fail == -ENODEV) +			fail = rtc_set_ntp_time(adjust); +#endif +	}  	next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);  	if (next.tv_nsec <= 0)  		next.tv_nsec += NSEC_PER_SEC; -	if (!fail) +	if (!fail || fail == -ENODEV)  		next.tv_sec = 659;  	else  		next.tv_sec = 0; @@ -326,40 +514,19 @@ static void sync_cmos_clock(struct work_struct *work)  		next.tv_sec++;  		next.tv_nsec -= NSEC_PER_SEC;  	} -	schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next)); +	queue_delayed_work(system_power_efficient_wq, +			   &sync_cmos_work, timespec_to_jiffies(&next));  } -static void notify_cmos_timer(void) +void ntp_notify_cmos_timer(void)  { -	if (!no_sync_cmos_clock) -		schedule_delayed_work(&sync_cmos_work, 0); +	queue_delayed_work(system_power_efficient_wq, &sync_cmos_work, 0);  }  #else -static inline void notify_cmos_timer(void) { } +void ntp_notify_cmos_timer(void) { }  #endif -/* - * Start the leap seconds timer: - */ -static inline void ntp_start_leap_timer(struct timespec *ts) -{ -	long now = ts->tv_sec; - -	if (time_status & STA_INS) { -		time_state = TIME_INS; -		now += 86400 - now % 86400; -		hrtimer_start(&leap_timer, ktime_set(now, 0), HRTIMER_MODE_ABS); - -		return; -	} - -	if (time_status & STA_DEL) { -		time_state = TIME_DEL; -		now += 86400 - (now + 1) % 86400; -		hrtimer_start(&leap_timer, ktime_set(now, 0), HRTIMER_MODE_ABS); -	} -}  /*   * Propagate a new txc->status value into the NTP state: @@ -369,6 +536,8 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)  	if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {  		time_state = TIME_OK;  		time_status = STA_UNSYNC; +		/* restart PPS frequency calibration */ +		pps_reset_freq_interval();  	}  	/* @@ -381,29 +550,12 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts)  	/* only set allowed bits */  	time_status &= STA_RONLY;  	time_status |= txc->status & ~STA_RONLY; - -	switch (time_state) { -	case TIME_OK: -		ntp_start_leap_timer(ts); -		break; -	case TIME_INS: -	case TIME_DEL: -		time_state = TIME_OK; -		ntp_start_leap_timer(ts); -	case TIME_WAIT: -		if (!(time_status & (STA_INS | STA_DEL))) -			time_state = TIME_OK; -		break; -	case TIME_OOP: -		hrtimer_restart(&leap_timer); -		break; -	}  } -/* - * Called with the xtime lock held, so we can access and modify - * all the global NTP state: - */ -static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts) + + +static inline void process_adjtimex_modes(struct timex *txc, +						struct timespec *ts, +						s32 *time_tai)  {  	if (txc->modes & ADJ_STATUS)  		process_adj_status(txc, ts); @@ -418,6 +570,8 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts  		time_freq = txc->freq * PPM_SCALE;  		time_freq = min(time_freq, MAXFREQ_SCALED);  		time_freq = max(time_freq, -MAXFREQ_SCALED); +		/* update pps_freq */ +		pps_set_freq(time_freq);  	}  	if (txc->modes & ADJ_MAXERROR) @@ -435,7 +589,7 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts  	}  	if (txc->modes & ADJ_TAI && txc->constant > 0) -		time_tai = txc->constant; +		*time_tai = txc->constant;  	if (txc->modes & ADJ_OFFSET)  		ntp_update_offset(txc->offset); @@ -447,16 +601,13 @@ static inline void process_adjtimex_modes(struct timex *txc, struct timespec *ts  		ntp_update_frequency();  } -/* - * adjtimex mainly allows reading (and writing, if superuser) of - * kernel time-keeping variables. used by xntpd. + + +/** + * ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex   */ -int do_adjtimex(struct timex *txc) +int ntp_validate_timex(struct timex *txc)  { -	struct timespec ts; -	int result; - -	/* Validate the data before disabling interrupts */  	if (txc->modes & ADJ_ADJTIME) {  		/* singleshot must not be used with any other mode bits */  		if (!(txc->modes & ADJ_OFFSET_SINGLESHOT)) @@ -468,7 +619,6 @@ int do_adjtimex(struct timex *txc)  		/* In order to modify anything, you gotta be super-user! */  		 if (txc->modes && !capable(CAP_SYS_TIME))  			return -EPERM; -  		/*  		 * if the quartz is off by more than 10% then  		 * something is VERY wrong! @@ -477,14 +627,22 @@ int do_adjtimex(struct timex *txc)  		    (txc->tick <  900000/USER_HZ ||  		     txc->tick > 1100000/USER_HZ))  			return -EINVAL; - -		if (txc->modes & ADJ_STATUS && time_state != TIME_OK) -			hrtimer_cancel(&leap_timer);  	} -	getnstimeofday(&ts); +	if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) +		return -EPERM; + +	return 0; +} + -	write_seqlock_irq(&xtime_lock); +/* + * adjtimex mainly allows reading (and writing, if superuser) of + * kernel time-keeping variables. used by xntpd. + */ +int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai) +{ +	int result;  	if (txc->modes & ADJ_ADJTIME) {  		long save_adjust = time_adjust; @@ -499,7 +657,7 @@ int do_adjtimex(struct timex *txc)  		/* If there are input parameters, then process them: */  		if (txc->modes) -			process_adjtimex_modes(txc, &ts); +			process_adjtimex_modes(txc, ts, time_tai);  		txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,  				  NTP_SCALE_SHIFT); @@ -508,7 +666,8 @@ int do_adjtimex(struct timex *txc)  	}  	result = time_state;	/* mostly `TIME_OK' */ -	if (time_status & (STA_UNSYNC|STA_CLOCKERR)) +	/* check for errors */ +	if (is_error_status(time_status))  		result = TIME_ERROR;  	txc->freq	   = shift_right((time_freq >> PPM_SCALE_INV_SHIFT) * @@ -520,33 +679,257 @@ int do_adjtimex(struct timex *txc)  	txc->precision	   = 1;  	txc->tolerance	   = MAXFREQ_SCALED / PPM_SCALE;  	txc->tick	   = tick_usec; -	txc->tai	   = time_tai; - -	/* PPS is not implemented, so these are zero */ -	txc->ppsfreq	   = 0; -	txc->jitter	   = 0; -	txc->shift	   = 0; -	txc->stabil	   = 0; -	txc->jitcnt	   = 0; -	txc->calcnt	   = 0; -	txc->errcnt	   = 0; -	txc->stbcnt	   = 0; +	txc->tai	   = *time_tai; -	write_sequnlock_irq(&xtime_lock); +	/* fill PPS status fields */ +	pps_fill_timex(txc); -	txc->time.tv_sec = ts.tv_sec; -	txc->time.tv_usec = ts.tv_nsec; +	txc->time.tv_sec = ts->tv_sec; +	txc->time.tv_usec = ts->tv_nsec;  	if (!(time_status & STA_NANO))  		txc->time.tv_usec /= NSEC_PER_USEC; -	notify_cmos_timer(); -  	return result;  } +#ifdef	CONFIG_NTP_PPS + +/* actually struct pps_normtime is good old struct timespec, but it is + * semantically different (and it is the reason why it was invented): + * pps_normtime.nsec has a range of ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ] + * while timespec.tv_nsec has a range of [0, NSEC_PER_SEC) */ +struct pps_normtime { +	__kernel_time_t	sec;	/* seconds */ +	long		nsec;	/* nanoseconds */ +}; + +/* normalize the timestamp so that nsec is in the +   ( -NSEC_PER_SEC / 2, NSEC_PER_SEC / 2 ] interval */ +static inline struct pps_normtime pps_normalize_ts(struct timespec ts) +{ +	struct pps_normtime norm = { +		.sec = ts.tv_sec, +		.nsec = ts.tv_nsec +	}; + +	if (norm.nsec > (NSEC_PER_SEC >> 1)) { +		norm.nsec -= NSEC_PER_SEC; +		norm.sec++; +	} + +	return norm; +} + +/* get current phase correction and jitter */ +static inline long pps_phase_filter_get(long *jitter) +{ +	*jitter = pps_tf[0] - pps_tf[1]; +	if (*jitter < 0) +		*jitter = -*jitter; + +	/* TODO: test various filters */ +	return pps_tf[0]; +} + +/* add the sample to the phase filter */ +static inline void pps_phase_filter_add(long err) +{ +	pps_tf[2] = pps_tf[1]; +	pps_tf[1] = pps_tf[0]; +	pps_tf[0] = err; +} + +/* decrease frequency calibration interval length. + * It is halved after four consecutive unstable intervals. + */ +static inline void pps_dec_freq_interval(void) +{ +	if (--pps_intcnt <= -PPS_INTCOUNT) { +		pps_intcnt = -PPS_INTCOUNT; +		if (pps_shift > PPS_INTMIN) { +			pps_shift--; +			pps_intcnt = 0; +		} +	} +} + +/* increase frequency calibration interval length. + * It is doubled after four consecutive stable intervals. + */ +static inline void pps_inc_freq_interval(void) +{ +	if (++pps_intcnt >= PPS_INTCOUNT) { +		pps_intcnt = PPS_INTCOUNT; +		if (pps_shift < PPS_INTMAX) { +			pps_shift++; +			pps_intcnt = 0; +		} +	} +} + +/* update clock frequency based on MONOTONIC_RAW clock PPS signal + * timestamps + * + * At the end of the calibration interval the difference between the + * first and last MONOTONIC_RAW clock timestamps divided by the length + * of the interval becomes the frequency update. If the interval was + * too long, the data are discarded. + * Returns the difference between old and new frequency values. + */ +static long hardpps_update_freq(struct pps_normtime freq_norm) +{ +	long delta, delta_mod; +	s64 ftemp; + +	/* check if the frequency interval was too long */ +	if (freq_norm.sec > (2 << pps_shift)) { +		time_status |= STA_PPSERROR; +		pps_errcnt++; +		pps_dec_freq_interval(); +		printk_deferred(KERN_ERR +			"hardpps: PPSERROR: interval too long - %ld s\n", +			freq_norm.sec); +		return 0; +	} + +	/* here the raw frequency offset and wander (stability) is +	 * calculated. If the wander is less than the wander threshold +	 * the interval is increased; otherwise it is decreased. +	 */ +	ftemp = div_s64(((s64)(-freq_norm.nsec)) << NTP_SCALE_SHIFT, +			freq_norm.sec); +	delta = shift_right(ftemp - pps_freq, NTP_SCALE_SHIFT); +	pps_freq = ftemp; +	if (delta > PPS_MAXWANDER || delta < -PPS_MAXWANDER) { +		printk_deferred(KERN_WARNING +				"hardpps: PPSWANDER: change=%ld\n", delta); +		time_status |= STA_PPSWANDER; +		pps_stbcnt++; +		pps_dec_freq_interval(); +	} else {	/* good sample */ +		pps_inc_freq_interval(); +	} + +	/* the stability metric is calculated as the average of recent +	 * frequency changes, but is used only for performance +	 * monitoring +	 */ +	delta_mod = delta; +	if (delta_mod < 0) +		delta_mod = -delta_mod; +	pps_stabil += (div_s64(((s64)delta_mod) << +				(NTP_SCALE_SHIFT - SHIFT_USEC), +				NSEC_PER_USEC) - pps_stabil) >> PPS_INTMIN; + +	/* if enabled, the system clock frequency is updated */ +	if ((time_status & STA_PPSFREQ) != 0 && +	    (time_status & STA_FREQHOLD) == 0) { +		time_freq = pps_freq; +		ntp_update_frequency(); +	} + +	return delta; +} + +/* correct REALTIME clock phase error against PPS signal */ +static void hardpps_update_phase(long error) +{ +	long correction = -error; +	long jitter; + +	/* add the sample to the median filter */ +	pps_phase_filter_add(correction); +	correction = pps_phase_filter_get(&jitter); + +	/* Nominal jitter is due to PPS signal noise. If it exceeds the +	 * threshold, the sample is discarded; otherwise, if so enabled, +	 * the time offset is updated. +	 */ +	if (jitter > (pps_jitter << PPS_POPCORN)) { +		printk_deferred(KERN_WARNING +				"hardpps: PPSJITTER: jitter=%ld, limit=%ld\n", +				jitter, (pps_jitter << PPS_POPCORN)); +		time_status |= STA_PPSJITTER; +		pps_jitcnt++; +	} else if (time_status & STA_PPSTIME) { +		/* correct the time using the phase offset */ +		time_offset = div_s64(((s64)correction) << NTP_SCALE_SHIFT, +				NTP_INTERVAL_FREQ); +		/* cancel running adjtime() */ +		time_adjust = 0; +	} +	/* update jitter */ +	pps_jitter += (jitter - pps_jitter) >> PPS_INTMIN; +} + +/* + * __hardpps() - discipline CPU clock oscillator to external PPS signal + * + * This routine is called at each PPS signal arrival in order to + * discipline the CPU clock oscillator to the PPS signal. It takes two + * parameters: REALTIME and MONOTONIC_RAW clock timestamps. The former + * is used to correct clock phase error and the latter is used to + * correct the frequency. + * + * This code is based on David Mills's reference nanokernel + * implementation. It was mostly rewritten but keeps the same idea. + */ +void __hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) +{ +	struct pps_normtime pts_norm, freq_norm; + +	pts_norm = pps_normalize_ts(*phase_ts); + +	/* clear the error bits, they will be set again if needed */ +	time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR); + +	/* indicate signal presence */ +	time_status |= STA_PPSSIGNAL; +	pps_valid = PPS_VALID; + +	/* when called for the first time, +	 * just start the frequency interval */ +	if (unlikely(pps_fbase.tv_sec == 0)) { +		pps_fbase = *raw_ts; +		return; +	} + +	/* ok, now we have a base for frequency calculation */ +	freq_norm = pps_normalize_ts(timespec_sub(*raw_ts, pps_fbase)); + +	/* check that the signal is in the range +	 * [1s - MAXFREQ us, 1s + MAXFREQ us], otherwise reject it */ +	if ((freq_norm.sec == 0) || +			(freq_norm.nsec > MAXFREQ * freq_norm.sec) || +			(freq_norm.nsec < -MAXFREQ * freq_norm.sec)) { +		time_status |= STA_PPSJITTER; +		/* restart the frequency calibration interval */ +		pps_fbase = *raw_ts; +		printk_deferred(KERN_ERR "hardpps: PPSJITTER: bad pulse\n"); +		return; +	} + +	/* signal is ok */ + +	/* check if the current frequency interval is finished */ +	if (freq_norm.sec >= (1 << pps_shift)) { +		pps_calcnt++; +		/* restart the frequency calibration interval */ +		pps_fbase = *raw_ts; +		hardpps_update_freq(freq_norm); +	} + +	hardpps_update_phase(pts_norm.nsec); + +} +#endif	/* CONFIG_NTP_PPS */ +  static int __init ntp_tick_adj_setup(char *str)  { -	ntp_tick_adj = simple_strtol(str, NULL, 0); +	int rc = kstrtol(str, 0, (long *)&ntp_tick_adj); + +	if (rc) +		return rc;  	ntp_tick_adj <<= NTP_SCALE_SHIFT;  	return 1; @@ -557,6 +940,4 @@ __setup("ntp_tick_adj=", ntp_tick_adj_setup);  void __init ntp_init(void)  {  	ntp_clear(); -	hrtimer_init(&leap_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); -	leap_timer.function = ntp_leap_second;  }  | 
