/*
 * Linux
 */
#if defined(__linux__)

#include "config.h"

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/sched.h>
#ifndef __ia64__
#include <asm/msr.h>
#endif
#include "dagreg.h"
#include "dagdrv.h"
#include "dagmon.h"
//include <inttypes.h>

#ifndef PRId64
#define PRId64 "lld"
#endif 

/* Debugging macros. */
#undef DD
#ifdef DEBUG
#  define	DD(...)	printk(KERN_DEBUG __VA_ARGS__)
#else
#  define	DD(...) if(0){printk(KERN_DEBUG __VA_ARGS__);}
#endif
# define	DA(...)	printk(KERN_INFO __VA_ARGS__)

/* Writing DN() and DD() as if(0)... (as opposed to defining them to be nothing)
 * means we can catch mismatched format strings and arguments.
 */
#define	DN(...) if(0){printk(__VA_ARGS__);}

/* Utility macros. */
# define	DUCKR(X,R)		(readl((u_char *)X->mmio + X->duck->base + R))
# define	DUCKW(X,R,Y)	(writel(Y,(u_char *)X->mmio + X->duck->base + R))
# define	dag_gettime(X)		do_gettimeofday(X)

# ifdef HAVE_GETNSTIMEOFDAY
#   define	dag_getnstime(X)	getnstimeofday(X)
# else
#   define	dag_getnstime(X)	do_gettimeofday((struct timeval*)(X)); (X)->tv_nsec*=1000;
# endif

# define	pci_get_device(X)	(X->device)

/*
 * FreeBSD
 */
#elif defined (__FreeBSD__)
#ifndef PRId64
#define PRId64 "ld"
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/cpufunc.h>
#include <machine/resource.h>
#include <machine/stdarg.h>
#include <sys/bus.h>
#define __RMAN_RESOURCE_VISIBLE
#include <sys/rman.h>

#if (__FreeBSD_version >= 504000)

/* FreeBSD 5.4 and higher. */
#include <dev/pci/pcivar.h>
#include "dagdrv.h"

#else

/* FreeBSD prior to 5.4. */
#include <pci/pcivar.h>
#include "dagdrv.h"

#endif /* FreeBSD version-specific code. */

#include "dagreg.h"
#include "dagmon.h"

/* Debugging routines. */
static void DA(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
static void DD(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
static void DN(const char* fmt, ...) __attribute__((format (printf, 1, 2)));

static void
DA(const char* fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
}

static void
DD(const char* fmt, ...)
{
#ifdef DEBUG
	va_list ap;

	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
#endif /* DEBUG */
}

static void
DN(const char* fmt, ...)
{
	/* Do nothing. */
}

/* Utility macros. */
#if (__FreeBSD_version < 504000)
#define DUCKR(X,R)  (*(volatile uint32_t *)((u_char *)X->iomres->r_virtual + X->duck->base + R))
#define DUCKW(X,R,Y)    (*(volatile uint32_t *)((u_char *)X->iomres->r_virtual + X->duck->base + R) = Y)
#else
#define DUCKR(X,R)  (*(volatile uint32_t *)((u_char *)rman_get_virtual(X->iomres) + X->duck->base + R))
#define DUCKW(X,R,Y)    (*(volatile uint32_t *)((u_char *)rman_get_virtual(X->iomres) + X->duck->base + R) = Y)
#endif

# define	dag_gettime(X)	microtime(X)
# define	dag_getnstime(X)	nanotime(X)
# define	HZ hz

/*
 * Solaris
 */
#elif defined(__SVR4) && defined(__sun)

#include	"dagreg.h"
#include	"dagdrv.h"

/* Debugging macros. */
#undef DD
#define DEBUG aaa
#ifdef DEBUG
#  define	DD(...) cmn_err(CE_NOTE, __VA_ARGS__)
#else
#  define	DD(...)
#endif
#define		DA(...) cmn_err(CE_NOTE, __VA_ARGS__)

#define DN(...) cmn_err(CE_NOTE, __VA_ARGS__)

/* Utility macros. */
#define	DUCKR(X,R)	dag_ioread(X->unit, X->duck->base + R)
#define	DUCKW(X,R,Y)	dag_iowrite(X->unit, X->duck->base + R, Y)

/* no way to obtain nanoseconds related to gettimeofday from kernel context */
#define	dag_gettime(X)	(X)->tv_usec=1000000;(X)->tv_sec=0
#define HZ	drv_usectohz(1000000)

/*
 * Other platforms
 */
#else
#error You are compiling on an unsupported platform. Please contact <support@endace.com> for assistance.
#endif

#include "dagduck.h"
#define ABS(X)((uint64_t)(((X)<0ll)?-(X):(X)))
#define cshift 3

/*
 * The following code tries to move initialization of the DUCK time close
 * to the beginning of a new second within the system. Since the timer only
 * has a granularity of 1/HZ seconds (typically HZ==100 and resolution=10ms)
 * we are still to far off the beginning of the second and the duck control
 * code would make repeated reset attempts to get things going properly.
 * We now keep waiting in a spin loop for the system time second to wrap,
 * which typically brings us close in the order of 50 microseconds (tested
 * with a NTP-synced host) to the PPS, which will not result in resets.
 * The close syncronization is necessary to avoid aliasing (off-by-one second)
 * when using a dual-DAG system with cable synchronization. Also tremendously
 * helpful when using GPS, as there is basically no extra code to be added:
 * all you need to do is to set the  system time to UTC right before loading
 * new Xilinx image(s).
 * We would love to use the "fetch and start DUCK on next PPS pulse" feature,
 * but this would require the PPS to be present to operate the DUCK. Instead,
 * by requiring the system time to be closely synchronized we achieve the same
 * effect without depending on the PPS to be present at all times.
 */

static void
duck_clear_stats(dag_duck_t *duck)
{
	struct timeval tv;

	DN("duck_clear_stats\n");
	/* Stats */
	duck->bad_pulses = 0;
	duck->worst_freq = 0;
	duck->worst_phase = 0;
	duck->pulses = 0;
	duck->single_misses = 0;
	duck->longest_miss = 0;
	duck->sickness = 0;
	duck->resyncs = 0;
	dag_gettime(&tv);
	duck->stat_start = tv.tv_sec;
	duck->stat_end = tv.tv_sec;
}

static int
duck_set(dag_softc_t *sc, int reset)
{
	dag_duck_t * duck = sc->duck;
	dag_reg_t result[DAG_REG_MAX_ENTRIES];
	
#if defined (__FreeBSD__)

#if (__FreeBSD_version < 504000)
	uint8_t * p_mmio = sc->iomres->r_virtual;
#else
	uint8_t * p_mmio = rman_get_virtual(sc->iomres); 
#endif
 
#endif /* FreeBSD */

	/* This will configure the duck registers and struct */
	
	DN("duck_set\n");

#if defined (__FreeBSD__)
	if (dag_reg_find(p_mmio, DAG_REG_DUCK, result) < 1)
#elif defined(__linux__)
	if (dag_reg_find(sc->mmio, DAG_REG_DUCK, result) < 1)
#endif
	{
		DD("dag%u: could not find DAG_REG_DUCK\n", sc->unit);
		return -1;
	}

	switch(result[0].version) {
	case 1: /* 100MHz emulated
		 * The 4.1 has a duck where communication is
		 * emulated by the ARM processor.
		 * Don't start the duck unless the ARM is busy, as
		 * we (naively) expect it must then be running the
		 * communication code.
		 */
#if defined(__FreeBSD__) || defined(__linux__)

		if(!dagmon_ready(sc))
			return -1;

#endif /* Platform-specific code. */

		/* else fall through */

		/*
		 * Notes on determining appropriate settings:
		 *
		 *   sfreq = 2^width 
		 *   cfreq = DUCK crystal frequency
		 *   DDS = (sfreq * 2^32) / cfreq)
		 *
		 * (DDS = Direct Digital Synthesis Rate)
		 */

	case 0: /* 100MHz */
		duck->cfreq = 100000000 << cshift;
		duck->dds = 0xabcc7712;
		duck->sfreq = 67108864;
		duck->width = 26;
		break;
	case 2: /* 50MHz */
		duck->cfreq = 50000000 << cshift;
		duck->dds = 0x55e63b89;
		duck->sfreq = 16777216;
		duck->width = 24;
		break;
	case 3: /* 33MHz */
		duck->cfreq = 32000000 << cshift;
		duck->dds = 0x8637bd06;
		duck->sfreq = 16777216;
		duck->width = 24;
		break;
	case 4: /* 62.5MHz */
		duck->cfreq = 62500000 << cshift;
		duck->dds = 0x44b82fa1;
		duck->sfreq = 16777216;
		duck->width = 24;
		break;
	case 5: /* 200MHz */
		duck->cfreq = 200000000 << cshift;
		duck->dds = 0xabcc7712;
		duck->sfreq = 134217728;
		duck->width = 27;
		break;
	case 6: /* 125MHz */
		duck->cfreq = 125000000 << cshift;
		duck->dds = 0x89705F41;
		duck->sfreq = 67108864;
		duck->width = 26;
		break;
	case 7: /* 250MHz */
		duck->cfreq = 250000000 << cshift;
		duck->dds = 0x89705F41;			/* (2^width * 2^32) / (cfreq >> cshift) */
		duck->sfreq = 134217728;		/* (2^width) */
		duck->width = 27;
		break;
	default:
		return -1;
	}
	
	duck->base = result[0].addr;
	DD("dag%u: duck->base = 0x%08x\n", sc->unit, (uint32_t) duck->base);

	/* set the dds on the actual card */
	DUCKW(sc, dds, duck->dds);
	DUCKW(sc, cmd, Duck_Cmd_Stop);
	if (reset) {
		DUCKW(sc, conf, Duck_Config_Muxin_RS422 | Duck_Config_Terminate_En);
		duck->health_threshold = 0xa00; /* health threshold, +/- 596ns */
		duck->phase_correction = 0;
	}
	duck->started=0;

	return 0;
}

static void
duck_start(dag_softc_t *sc)
{
	struct timeval tv;
	uint32_t start;

	DN("duck_start entry...\n");
	dag_gettime(&tv);
	start = tv.tv_sec;
	if(tv.tv_usec > 500000)
		start += 1;
	DA("dag%u: starting timestamp clock at %ld.%06lu (%u)\n", sc->unit, tv.tv_sec, tv.tv_usec, start);
	DUCKW(sc, hload, start );
	
	/* Load immediately - note this is relatively coarse */
	DUCKW(sc, cmd, (Duck_Cmd_Load_Immed|Duck_Cmd_Go));
	sc->duck->started=1;
}

static void
duck_health_timeout(unsigned long data)
{
	dag_softc_t *sc = (dag_softc_t *)data;
	
	if(sc->duck->health) {
		DA("dag%u: timestamp clock lost synchronisation - no input for over 10 seconds\n", sc->unit);
		sc->duck->sickness++;
	}
	sc->duck->health = 0;
	sc->duck->good_secs = 0;
}

static void
duck_init_timeout(unsigned long data)
{
	dag_softc_t *sc = (dag_softc_t *)data;
	dag_duck_t *duck = sc->duck;
	struct timeval tv;
	
	DN("dag: duck_timeout\n");
	dag_gettime(&tv);
	if(tv.tv_usec >= (1000*1000-1000*1000/HZ)) {
		/*
		 * The next tick will be at or beyond the second
		 * wrap. Keep spin waiting rather than being late
		 * by waiting for another timer expiry.
		 */
#if defined(__FreeBSD__) || defined(__linux__)

		while(tv.tv_usec >= (1000*1000-1000*1000/HZ))
			dag_gettime(&tv); /* spin lock until second wraps */

#endif

		/* next second is now */
		duck_start(sc);
		/* from now on this slot is occupied for sickness monitoring */

#if defined(__linux__)

		duck->timer->function = duck_health_timeout;
		wake_up_interruptible(&duck->wait);
	} else {
		/* keep waiting */
		duck->timer->expires = jiffies + 1;
		add_timer(duck->timer);

#elif defined (__FreeBSD__)

		wakeup(duck);		
	} else {
		/* keep waiting */
		duck->timer=timeout((void *)duck_init_timeout, sc, 1);

#elif defined (__SVR4) && defined (__sun)

		/* driver is awaking itself in a second */
		DN("duck_init_timeout: duck programmed.\n");
	} else {
		/* wake up again in one tick */
		DN("duck_init_timeout: reprogrammed in one tick.\n");
		duck->timer = timeout ((void *)duck_init_timeout, sc, 1);

#endif /* Platform-specific code. */
	}
}

int
duck_init(dag_softc_t *sc)
{
	dag_duck_t *duck = sc->duck;
#if defined (__FreeBSD__)
	int loop, error;
#endif

	/* This one is to set a card after module insert, xilinx load, or reset */
	
	DN("dag: init_duck duck: %p timer: %p\n", (void*) duck, &duck->timer);

	/* Program in config */
	if(duck_set(sc, 1))
		return -1;

	duck_clear_stats(duck);
	duck->last=0;
	duck->health=0;
	duck->good_secs=0;

	/*
	 * Synchronize DUCK init as close as possible to next second "pulse".
	 */
#if defined(__linux__)

	del_timer_sync(duck->timer);	/* the health watchdog might be running */
	
	duck->timer->data = (unsigned long)sc;
	duck->timer->expires = jiffies + 1;		/* soon */
	duck->timer->function = duck_init_timeout;	/* function */
	add_timer(duck->timer);

	/*
	 * The actual initialization is called from timeout to make
	 * it as precise as possible. Just wait here until finished.
	 */
	wait_event_interruptible(duck->wait, duck->started);
	//interruptible_sleep_on(&duck->wait);
	return 0;

#elif defined (__FreeBSD__)

	untimeout((void *)duck_init_timeout, sc, duck->timer);
	duck->timer=timeout((void *)duck_init_timeout, sc, 1);
	/*
	 * The actual initialization is called from timeout to make
	 * it as precise as possible. Just wait here until finished.
	 */
	for( loop = 0 ; loop < 10*100 ; loop++ ) {		
		error = tsleep(duck, (PUSER|PCATCH), "duck_i", HZ/100);
		switch(error) {
		case EAGAIN:
			break;
		default:
			return error;
		}
	}
	return -1;
	
#elif defined (__SVR4) && defined (__sun)

	untimeout (duck->timer);
	duck->timer = timeout((void *)duck_init_timeout, sc, 1);
	
	/*
	 * The initalization is done in duck_init_timeout and would take
	 * at most one second. Sleep during this time (1 sec).
	 */
	delay(drv_usectohz(1000000));
	return 0;

#endif /* Platform-specific code. */
}

void
duck_intr(dag_softc_t *sc)
{
	dag_duck_t * duck = sc->duck;
	int64_t now, hostnow, delta, err=0, offset=0, temp_freq=0, want;
	int pulses;
	uint32_t high, low;
	uint64_t tsc_now = 0;
       
	struct timespec ts;
	
	/* Retrieve the value of the TSC. */
#if defined(__i386__)
#if defined(__linux__)

	rdtscll(tsc_now);
	
#elif defined(__FreeBSD__)

	tsc_now = rdtsc();

#endif /* Platform-specific code. */
#endif /* x86 detection code. */

	dag_getnstime(&ts);
	if(!duck)
		return;
	duck->last_ts_sec = ts.tv_sec;
	duck->last_ts_nsec = ts.tv_nsec;

	DN("dag%d: duck_intr: system time: %ld.%09ld sec\n", sc->unit, (long) ts.tv_sec, (long) ts.tv_nsec);

	hostnow = ((int64_t)ts.tv_sec << 32) + ((int64_t)ts.tv_nsec << 32) / 1000 / 1000 / 1000;

	if(ts.tv_nsec > 500000000)
		ts.tv_sec++;

	if(!duck->base)
		return;

	high = DUCKR(sc, hread);
	low = DUCKR(sc, lread);
	now = ((uint64_t)high << 32) + low;

	DN("dag%d: duck_intr: real duck now: %d.%09u\n",
		  sc->unit, (uint32_t)(now>>32), (uint32_t)(((now&0xffffffff)*1000*1000*1000)>>32));

	duck->stat_end = ts.tv_sec;
	duck->pulses++;

	/* check to see if host and DAG agree on current second */
	delta = now-hostnow;
	if ((delta > 0xffffffffll) || (delta < -0xffffffffll)) {
		DA("dag%d: timestamp clock phase error > 1s, resetting DAG clock.\n", sc->unit);
		DA("dag%d: clock system %d.%09u dag %d.%09u delta %d.%09ds\n",
		   sc->unit,
		   (uint32_t)(hostnow>>32), (uint32_t)(((hostnow&0xffffffff)*1000*1000*1000)>>32),
		   (uint32_t)(now>>32), (uint32_t)(((now&0xffffffff)*1000*1000*1000)>>32),
		   (int32_t)(delta>>32), (int32_t)(((delta&0xffffffff)*1000*1000*1000)>>32) );

		duck->resyncs++;
		duck->sickness++;
		duck->health = 0;
		duck->good_secs = 0;
		/* reset duck config, but don't change mux settings or clear stats */
		duck_set(sc, 0);
		/* Start duck immediately.
		 * This is coarse as we may not be near a second boundary,
		 * but we can't wait for the next boundary here.
		 * The standard sync algorithm should get us back after this. */
		duck_start(sc);
		return;
	}


	/* For overflow mode, synchronise dag to host PC clock. */
	if(DUCKR(sc, conf) & Duck_Config_Muxin_Ovrflow) {
		now = hostnow;
		DN("dag%d: duck_intr: fake duck now: %d.%09u\n",
		     sc->unit,
		     (uint32_t)(now>>32),
		     (uint32_t)(((now&0xffffffff)*1000*1000*1000)>>32));
		/* duck->health_threshold = 0xc800; +/- 11.92us, 20 times higher than normal */
	}
	
	/* reuse delta, now the time since last pulse (2^-32 sec units) */
	delta = (now - duck->last);

	if ((duck->last != 0) && (delta != 0)) {
		/* received at least one pulse before */
		
		/* test delta for reasonableness, this is a frequency filter */
		if ( (delta < 0x101000000ll) && (delta > 0xff000000ll) ) {
			/* delta (wavelength) within ~1% */

			/* For overflow mode, synchronise dag to host PC clock. */
			if(DUCKR(sc, conf) & Duck_Config_Muxin_Ovrflow)
				delta = 0xffffffffffffffffll / delta;

			/* err is wavelength error */
			err = delta - 0x100000000ll;
			duck->freq_error = (int32_t)err;
			if (duck->worst_freq < ABS(err))
				duck->worst_freq = ABS(err);

			/* offset is phase error from second boundary (2^-32 sec units) */
			if ( (now & 0xffffffffll) < 0x80000000ll)
				offset = now & 0xffffffffll;
			else
				offset = 0 - (0x100000000ll - (now & 0xffffffffll));
			
			/* subtract user defined phase offset in fixed point format to allow for long cables */
			offset -= duck->phase_correction;

			/* If we are synchronising to the host, invert the offset */
			if(DUCKR(sc, conf) & Duck_Config_Muxin_Ovrflow)
				offset = -offset;

			/* store offset for stats reporting */
			duck->phase_error = (int32_t)offset;

			/* store worst offset for stats reporting */
			if (duck->worst_phase < ABS(offset))
				duck->worst_phase = ABS(offset);

			/* temp_freq is the crystal freq over the last second */
			temp_freq = ( (delta << duck->width) / duck->dds );
			
			/* if phase error very low ( < +/- health_threshold ns) , then input considered good */
			if ( (offset < duck->health_threshold) && (offset > -duck->health_threshold) ) {
				if(duck->health == 0) {
					/* good input receved when unsynced */
					duck->good_secs++;
					if(duck->good_secs >= 10) {
						/* After 10 sequential good inputs we are synced */
						DA("dag%u: timestamp clock synchronised\n", sc->unit);
						duck->health = 1;
						duck->good_secs = 0;
					}
				}

				if(duck->health == 1) { /* separate if as health is modified above */
					/* good input received when synced */
					/* Reset health timer expiry */
#if defined(__linux__)
					mod_timer(duck->timer, jiffies + 10*HZ + HZ/2);
					
#elif defined (__FreeBSD__)
					untimeout((void *)duck_health_timeout, sc, duck->timer);
					duck->timer=timeout((void *)duck_health_timeout, sc, hz*10 + hz/2);
					
#elif defined (__SVR4) && defined (__sun)
					untimeout(duck->timer);
					duck->timer=timeout((void *)duck_health_timeout, sc, 10*HZ + HZ/2);
					
#endif /* Platform-specific code. */
				}

				/* Prepare for next call */
				duck->last = now;
				
			} else {
				/* Input exceeded thresholds, considered bad. */
				duck->good_secs = 0;

				/* If healthy/synchronised, ignore bad inputs.
				 * Don't pollute crystal estimate or DDS with bad input.
				 * After 10 sequential bad inputs the health timer will expire.
				 */
				if(duck->health) {
					DD("dag%u: timestamp bad input ignored - phase error %"PRId64"ns exceeds threshold +/-%"PRId64"ns\n",
					   sc->unit,
					   (offset*1000*1000*1000)>>32,
					   (((int64_t)duck->health_threshold)*1000*1000*1000)>>32);
					duck->bad_pulses++;
					offset = 0;
					temp_freq = duck->cfreq >> cshift;
					duck->last += 0x100000000ll;
				} else {
					/* Prepare for next call */
					duck->last = now;
				}

				/* Otherwise if unhealthy/unsynchronised then
				 * accept any input for synchronisation.
				 */
			}
			
			/*duck->cfreq = temp_freq;*/
			duck->cfreq = (duck->cfreq - (duck->cfreq >> cshift)) + (temp_freq);

			want = 0x100000000ll - (offset >> 1);

		} else {
			/* unreasonable delta, could mean a pulse or more was missed */
			duck->good_secs = 0;
			duck->bad_pulses++;
			pulses = (int)((delta * ((1<<8)+1)) >> (32+8)) - 1; /* rounding up by 1.004 */
			switch (pulses) {
			case -1:
			case 0:
				/* Less than one missing pulse or negative. */
				if (delta > 0x01000000) {
					/* >1/256 seconds, debug  print*/
					DD("dag%d: timestamp clock input - Bad delta %u.%09u\n",
					       sc->unit, (uint32_t)(delta>>32),
					       (uint32_t)(((delta&0xffffffff)*1000*1000*1000)>>32) );
				}
				/* Otherwise too high rate, don't debug print */
				break;
			case 1:
				DD("dag%d: timestamp clock input - missing one pulse\n", sc->unit);
				duck->single_misses++;
				if (duck->longest_miss < 1)
					duck->longest_miss = 1;
				break;
			default:
				DD("dag%d: timestamp clock input - missing %d pulses\n", sc->unit, pulses);
				if (duck->longest_miss < pulses)
					duck->longest_miss = pulses;
				break;
			}
			want = 0x100000000ll;
			/* Prepare for next call */
			duck->last = now;
		}
		/* calculate desired dds rate given desired freq and crystal freq */
		duck->dds = (uint32_t)( ((want << (duck->width+cshift)) / duck->cfreq) );
		DUCKW(sc, dds, duck->dds);

		/* final debug outputs now we have all the info */
		if ( !((offset < duck->health_threshold) && (offset > -duck->health_threshold)) ) {
			if(duck->health) {
				DD("dag%u: timestamp bad input - phase error %"PRId64"ns exceeds threshold +/-%"PRId64"ns\n",
				   sc->unit,
				   (offset*1000*1000*1000)>>32,
				   (((int64_t)duck->health_threshold)*1000*1000*1000)>>32);
			}
			
			DD("dag%d: clock freq err: %"PRId64"ppb phase err: %"PRId64"ns crystal est: %dHz\n",
			   sc->unit,
			   (err*1000*1000*1000)>>32,
			   (offset*1000*1000*1000)>>32,
			   duck->cfreq >> cshift);			
			DN("dag%d: clock verbose freq err: %"PRId64"ppb phase err: %"PRId64"ns crystal: %"PRId64"Hz smoothed: %dHz want: %"PRId64"ns dds: 0x%08x\n",
			   sc->unit,
			   (err*1000*1000*1000)>>32,
			   (offset*1000*1000*1000)>>32,
			   temp_freq,
			   duck->cfreq >> cshift,
			   (want*1000*1000*1000)>>32,
			   duck->dds);			
		} else {
			DN("dag%d: clock freq err: %"PRId64"ppb phase err: %"PRId64"ns crystal: %"PRId64"Hz smoothed: %dHz want: %"PRId64"ns dds: 0x%08x\n",
			   sc->unit,
			   (err*1000*1000*1000)>>32,
			   (offset*1000*1000*1000)>>32,
			   temp_freq,
			   duck->cfreq >> cshift,
			   (want*1000*1000*1000)>>32,
			   duck->dds);			
		}
	} else {
		/* Prepare for next call */
		duck->last = now;
	}

	/* Store the value of the TSC. */
	duck->last_tsc = tsc_now;

#if defined(__linux__)

	wake_up_interruptible(&duck->wait); /* Wake DAGIOCPPSWAIT */

#elif defined (__FreeBSD__)

	wakeup(&duck->pulses); /* Wake DAGIOCPPSWAIT */

#endif /* Platform-specific code. */
}

int
duck_ioctl(dag_duck_t *duck, duckinf_t *duckinf)
{
	if(duckinf->Set_Duck_Field&Set_Duck_Clear_Stats) {
		duck_clear_stats(duck);
		duckinf->Set_Duck_Field &= ~Set_Duck_Clear_Stats;
	}

	if(duckinf->Set_Duck_Field&Set_Duck_Health_Thresh) {
		duck->health_threshold = (duckinf->Health_Thresh>>(32-duck->width))<<(32-duck->width);
		duckinf->Set_Duck_Field &= ~Set_Duck_Health_Thresh;
	}

	if(duckinf->Set_Duck_Field&Set_Duck_Phase_Corr) {
		duck->phase_correction = duckinf->Phase_Correction;
		duckinf->Set_Duck_Field &= ~Set_Duck_Phase_Corr;
	}

	duckinf->Crystal_Freq		= duck->cfreq >> cshift;
	duckinf->Synth_Freq		= duck->sfreq;
	duckinf->Last_Ticks		= duck->last;
	duckinf->Resyncs		= duck->resyncs;
	duckinf->Bad_Pulses		= duck->bad_pulses;
	duckinf->Worst_Freq_Err		= duck->worst_freq;
	duckinf->Worst_Phase_Err	= duck->worst_phase;
	duckinf->Health_Thresh		= duck->health_threshold;
	duckinf->Pulses			= duck->pulses;
	duckinf->Single_Pulses_Missing	= duck->single_misses;
	duckinf->Longest_Pulse_Missing	= duck->longest_miss;
	duckinf->Health			= duck->health;
	duckinf->Sickness		= duck->sickness;
	duckinf->Freq_Err		= duck->freq_error;
	duckinf->Phase_Err		= duck->phase_error;
	duckinf->Stat_Start		= duck->stat_start;
	duckinf->Stat_End		= duck->stat_end;
	duckinf->Last_TSC		= duck->last_tsc;
	duckinf->Phase_Correction	= duck->phase_correction;
	return 0;
}

int 
duck_get_irq_time_ioctl(dag_duck_t *duck, duck_irq_time_t *duck_irq_time)
{
	duck_irq_time->sec		= duck->last_ts_sec;
	duck_irq_time->nsec		= duck->last_ts_nsec;
	return 0;
}


dag_duck_t*
duck_new(void)
{
	dag_duck_t *duck;

#if defined(__linux__)

	duck = kmalloc(sizeof(dag_duck_t), GFP_KERNEL);

	if (duck) {
		memset(duck, 0, sizeof(dag_duck_t));

		/* Initialise DUCK wait queue */
		init_waitqueue_head(&duck->wait);
		
		/* allocate memory for duck timer */
		duck->timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
		if (duck->timer)
			init_timer(duck->timer);
	}

#elif defined (__FreeBSD__)

	duck = malloc(sizeof(dag_duck_t), M_DEVBUF, M_NOWAIT | M_ZERO);

	DD("duck malloc'd: duck->base = 0x%08x\n", (uint32_t) duck->base);
	callout_handle_init(&duck->timer);

#elif defined (__SVR4) && defined (__sun)

	duck = kmem_zalloc (sizeof(dag_duck_t), KM_NOSLEEP);

#endif /* Platform-specific code. */

	return duck;
}

void
duck_destroy(struct dag_softc *sc)
{
#if defined(__linux__)

	/* the health watchdog might be running */
	if (sc->duck->timer) {
		del_timer_sync(sc->duck->timer);
		kfree(sc->duck->timer);
	}
	kfree(sc->duck);
	
#elif defined (__FreeBSD__)

	untimeout((void *)duck_health_timeout, sc, sc->duck->timer);
	free(sc->duck, M_DEVBUF);

#elif defined (__SVR4) && defined (__sun)

	untimeout(sc->duck->timer);
	kmem_free(sc->duck, sizeof(dag_duck_t));

#endif /* Platform-specific code. */
	
	sc->duck = NULL;
}
