Subject: Re: Need help with timecounters/todr
To: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
From: Frank Kardel <kardel@netbsd.org>
List: port-alpha
Date: 02/24/2007 13:48:39
This is a multi-part message in MIME format.
--------------000108010203060706030209
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Izumi Tsutsui wrote:
> 
> 
> I'm not sure what is the best way to switch alpha to timecounter,
> but I just take functions from x86/x86/tsc.c and adapt it to
> alpha's PCC counter.
> 
> Could anyone review the attached patch?
> (which also includes generic todr(9) changes)
In general I agree with the strategy but I think we
should try to avoid a copy if tsc.c here. I include an
old patch here that attempts to split tsc.c into a
MD part (tsc.c) and an MI part kern_cctr.c. Maybe you can
use that for the cycle counter part of the part - note on
alpha this patch lacks the cc_init() call. Also note
that the patch assumes that the counter is read by
cpu_counter32() - same headfile magic as was used
for kern_microtime.c.
The patch works for x86 - it should work for alpha when
cc_init is called at the right place - I hope - I have no
hw to check.
> 
> Note "make regress" on regress/sys/kern/sleeping fails
> even without this patch on alpha.

I what way? Usually the check uncovers descrepancies between
timeout and timekeeping code.

> ---
> Izumi Tsutsui
> 

Frank

--------------000108010203060706030209
Content-Type: text/plain;
 name="common-cycle-counter-diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="common-cycle-counter-diff"

Index: sys/arch/alpha/alpha/clock.c
===================================================================
RCS file: /cvsroot/src/sys/arch/alpha/alpha/clock.c,v
retrieving revision 1.36
diff -u -r1.36 clock.c
--- sys/arch/alpha/alpha/clock.c	11 Dec 2005 12:16:10 -0000	1.36
+++ sys/arch/alpha/alpha/clock.c	20 Jan 2007 19:02:55 -0000
@@ -157,14 +157,6 @@
 		panic("cpu_initclocks: no clock attached");
 
 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
-	tickfix = 1000000 - (hz * tick);
-	if (tickfix) {
-		int ftp;
-
-		ftp = min(ffs(tickfix), ffs(hz));
-		tickfix >>= (ftp - 1);
-		tickfixinterval = hz >> (ftp - 1);
-        }
 
 	/*
 	 * Establish the clock interrupt; it's a special case.
@@ -259,9 +251,6 @@
 #ifdef DEBUG
 	printf("=>%ld (%d)\n", time.tv_sec, base);
 #endif
-	cc_microset_time = time;
-	cc_microset(curcpu());
-
 	if (!badbase) {
 		/*
 		 * See if we gained/lost two or more days;
Index: sys/arch/alpha/alpha/ipifuncs.c
===================================================================
RCS file: /cvsroot/src/sys/arch/alpha/alpha/ipifuncs.c,v
retrieving revision 1.33
diff -u -r1.33 ipifuncs.c
--- sys/arch/alpha/alpha/ipifuncs.c	5 Feb 2003 12:16:42 -0000	1.33
+++ sys/arch/alpha/alpha/ipifuncs.c	20 Jan 2007 19:02:55 -0000
@@ -257,7 +257,7 @@
 alpha_ipi_microset(struct cpu_info *ci, struct trapframe *framep)
 {
 
-	cc_microset(ci);
+        cc_calibrate_cpu(ci);
 }
 
 void
Index: sys/arch/alpha/include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/alpha/include/cpu.h,v
retrieving revision 1.65
diff -u -r1.65 cpu.h
--- sys/arch/alpha/include/cpu.h	24 Dec 2005 20:06:46 -0000	1.65
+++ sys/arch/alpha/include/cpu.h	20 Jan 2007 19:02:55 -0000
@@ -305,7 +305,6 @@
 struct trapframe;
 
 int	badaddr(void *, size_t);
-#define microtime(tv)	cc_microtime(tv)
 
 #endif /* _KERNEL */
 #endif /* _ALPHA_CPU_H_ */
Index: sys/arch/alpha/include/types.h
===================================================================
RCS file: /cvsroot/src/sys/arch/alpha/include/types.h,v
retrieving revision 1.35
diff -u -r1.35 types.h
--- sys/arch/alpha/include/types.h	3 Sep 2006 13:51:23 -0000	1.35
+++ sys/arch/alpha/include/types.h	20 Jan 2007 19:02:55 -0000
@@ -63,6 +63,7 @@
 #define	__HAVE_GENERIC_SOFT_INTERRUPTS
 #define	__HAVE_ATOMIC_OPERATIONS
 #define	__HAVE_CPU_COUNTER
+#define __HAVE_TIMECOUNTER
 #define	__HAVE_SYSCALL_INTERN
 #define	__HAVE_MINIMAL_EMUL
 #define	__HAVE_AST_PERPROC
cvs diff: Diffing sys/arch/alpha/isa
cvs diff: Diffing sys/arch/alpha/jensenio
cvs diff: Diffing sys/arch/alpha/mcbus
cvs diff: Diffing sys/arch/alpha/pci
cvs diff: Diffing sys/arch/alpha/sableio
cvs diff: Diffing sys/arch/alpha/stand
cvs diff: Diffing sys/arch/alpha/stand/boot
cvs diff: Diffing sys/arch/alpha/stand/bootxx_cd9660
cvs diff: Diffing sys/arch/alpha/stand/bootxx_ffs
cvs diff: Diffing sys/arch/alpha/stand/bootxx_ffsv2
cvs diff: Diffing sys/arch/alpha/stand/bootxx_lfs
cvs diff: Diffing sys/arch/alpha/stand/common
cvs diff: Diffing sys/arch/alpha/stand/mkbootimage
cvs diff: Diffing sys/arch/alpha/stand/netboot
cvs diff: Diffing sys/arch/alpha/stand/setnetbootinfo
cvs diff: Diffing sys/arch/alpha/stand/standtest
cvs diff: Diffing sys/arch/alpha/stand/ustarboot
cvs diff: Diffing sys/arch/alpha/tc
cvs diff: Diffing sys/arch/alpha/tlsb
cvs diff: Diffing sys/arch/amd64
cvs diff: Diffing sys/arch/amd64/acpi
cvs diff: Diffing sys/arch/amd64/amd64
Index: sys/arch/amd64/conf/files.amd64
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/files.amd64,v
retrieving revision 1.30
diff -u -r1.30 files.amd64
--- sys/arch/amd64/conf/files.amd64	18 Dec 2006 12:08:05 -0000	1.30
+++ sys/arch/amd64/conf/files.amd64	20 Jan 2007 19:02:57 -0000
@@ -33,6 +33,7 @@
 file	arch/amd64/amd64/db_trace.c		ddb
 file	arch/amd64/amd64/kgdb_machdep.c		kgdb
 file	kern/subr_disk_mbr.c			disk
+file	kern/kern_cctr.c
 file	arch/amd64/amd64/gdt.c
 #
 # XXXfvdl write the optimized versions for these.
Index: sys/arch/i386/conf/files.i386
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.299
diff -u -r1.299 files.i386
--- sys/arch/i386/conf/files.i386	6 Jan 2007 18:42:36 -0000	1.299
+++ sys/arch/i386/conf/files.i386	20 Jan 2007 19:03:04 -0000
@@ -75,6 +75,7 @@
 file	arch/i386/i386/db_memrw.c	ddb | kgdb
 file	arch/i386/i386/db_trace.c	ddb
 file	kern/subr_disk_mbr.c		disk
+file	kern/kern_cctr.c
 file	arch/i386/i386/gdt.c
 file	arch/i386/i386/in_cksum.S	inet | inet6
 file	arch/i386/i386/ipkdb_glue.c	ipkdb
Index: sys/arch/i386/i386/ipifuncs.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/ipifuncs.c,v
retrieving revision 1.13
diff -u -r1.13 ipifuncs.c
--- sys/arch/i386/i386/ipifuncs.c	7 Jun 2006 22:37:58 -0000	1.13
+++ sys/arch/i386/i386/ipifuncs.c	20 Jan 2007 19:03:04 -0000
@@ -87,7 +87,7 @@
 {
 	i386_ipi_halt,
 #if defined(I586_CPU) || defined(I686_CPU)
-	tsc_calibrate_cpu,	/* keep cycle counters synchronized */
+	cc_calibrate_cpu,	/* keep cycle counters synchronized */
 #else
 	0,
 #endif
Index: sys/arch/x86/x86/tsc.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/tsc.c,v
retrieving revision 1.10
diff -u -r1.10 tsc.c
--- sys/arch/x86/x86/tsc.c	16 Nov 2006 01:32:39 -0000	1.10
+++ sys/arch/x86/x86/tsc.c	20 Jan 2007 19:03:11 -0000
@@ -81,6 +81,7 @@
  * SUCH DAMAGE.
  */
 
+
 #include <sys/cdefs.h>
 /* __FBSDID("$FreeBSD: src/sys/i386/i386/tsc.c,v 1.204 2003/10/21 18:28:34 silby Exp $"); */
 __KERNEL_RCSID(0, "$NetBSD: tsc.c,v 1.10 2006/11/16 01:32:39 christos Exp $");
@@ -110,29 +111,7 @@
 uint64_t	tsc_freq;
 u_int		tsc_present;
 int		tsc_is_broken = 0;
-
-static int64_t tsc_cal_val;  /* last calibrate time stamp */
-
-static timecounter_get_t tsc_get_timecount;
-static timecounter_pps_t tsc_calibrate;
-
-void tsc_calibrate_cpu(struct cpu_info *);
-
-static struct timecounter tsc_timecounter = {
-	tsc_get_timecount,	/* get_timecount */
-	tsc_calibrate,		/* once per second - used to calibrate cpu TSC */
- 	~0u,			/* counter_mask */
-	0,			/* frequency */
-	 "TSC",			/* name */
-#if (defined(ENHANCED_SPEEDSTEP) || defined(POWERNOW_K7) || defined(POWERNOW_K8))
-	-100,			/* don't pick TSC automatically */
-				/* if frequency changes might affect TSC */
-#else
-	800,			/* quality (adjusted in code) */
-#endif
-	NULL,
-	NULL,
-};
+int		tsc_qual = -100000;
 
 void
 init_TSC(void)
@@ -157,6 +136,12 @@
 	} while (tscval[1] < tscval[0]);
 
 	tsc_freq = 10 * (tscval[1] - tscval[0]);
+#if (defined(ENHANCED_SPEEDSTEP) || defined(POWERNOW_K7) || defined(POWERNOW_K8))
+	tsc_qual = -100;	/* don't pick TSC automatically */
+				/* if frequency changes might affect TSC */
+#else
+	tsc_qual = 800;		/* quality (adjusted in code) */
+#endif
 	if (bootverbose)
 		printf("TSC clock: %" PRId64 " Hz\n", tsc_freq);
 }
@@ -165,163 +150,6 @@
 init_TSC_tc(void)
 {
 	if (tsc_present && tsc_freq != 0 && !tsc_is_broken) {
-		tsc_timecounter.tc_frequency = tsc_freq;
-		tc_init(&tsc_timecounter);
-	}
-}
-
-/* XXX make tsc_timecounter.tc_frequency settable by sysctl() */
-
-/*
- * pick up tick count scaled to reference tick count
- */
-static u_int
-tsc_get_timecount(struct timecounter *tc)
-{
-	struct cpu_info *ci = curcpu();
-	int64_t rcc, cc;
-	u_int gen;
-
-	if (ci->ci_cc.cc_denom == 0) {
-		/*
-		 * This is our first time here on this CPU.  Just
-		 * start with reasonable initial values.
-		 */
-	        ci->ci_cc.cc_cc    = cpu_counter32();
-		ci->ci_cc.cc_val   = 0;
-		if (ci->ci_cc.cc_gen == 0)
-			ci->ci_cc.cc_gen++;
-
-		ci->ci_cc.cc_denom = cpu_frequency(ci);
-		if (ci->ci_cc.cc_denom == 0)
-			ci->ci_cc.cc_denom = tsc_freq;
-		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
-	}
-
-	/* read counter and re-read when the re-calibration
-	   strikes inbetween */
-	do {
-		/* pick up current generation number */
-		gen = ci->ci_cc.cc_gen;
-
-		/* determine local delta ticks */
-		cc = cpu_counter32() - ci->ci_cc.cc_cc;
-		if (cc < 0)
-			cc += 0x100000000LL;
-
-		/* scale to primary */
-		rcc = (cc * ci->ci_cc.cc_delta) / ci->ci_cc.cc_denom
-			+ ci->ci_cc.cc_val;
-	} while (gen == 0 || gen != ci->ci_cc.cc_gen);
-
-	return rcc;
-}
-
-/*
- * called once per second via the pps callback
- * for the calibration of the TSC counters.
- * it is called only for the PRIMARY cpu. all
- * other cpus are called via a broadcast IPI
- */
-static void
-tsc_calibrate(struct timecounter *tc)
-{
-	struct cpu_info *ci = curcpu();
-	
-	/* pick up reference ticks */
-	tsc_cal_val = cpu_counter32();
-
-#if defined(MULTIPROCESSOR)
-	x86_broadcast_ipi(X86_IPI_MICROSET);
-#endif
-
-	tsc_calibrate_cpu(ci);
-}
-
-/*
- * This routine is called about once per second directly by the master
- * processor and via an interprocessor interrupt for other processors.
- * It determines the CC frequency of each processor relative to the
- * master clock and the time this determination is made.  These values
- * are used by tsc_get_timecount() to interpolate the ticks between
- * timer interrupts.  Note that we assume the kernel variables have
- * been zeroed early in life.
- */
-void
-tsc_calibrate_cpu(struct cpu_info *ci)
-{
-	u_int   gen;
-	int64_t val;
-	int64_t delta, denom;
-	int s;
-#ifdef TIMECOUNTER_DEBUG
-	int64_t factor, old_factor;
-#endif
-	val = tsc_cal_val;
-
-	s = splhigh();
-	/* create next generation number */
-	gen = ci->ci_cc.cc_gen;
-	gen++;
-	if (gen == 0)
-		gen++;
-	/* update in progress */
-	ci->ci_cc.cc_gen = 0;
-
-	denom = ci->ci_cc.cc_cc;
-	ci->ci_cc.cc_cc = cpu_counter32();
-
-	if (ci->ci_cc.cc_denom == 0) {
-		/*
-		 * This is our first time here on this CPU.  Just
-		 * start with reasonable initial values.
-		 */
-		ci->ci_cc.cc_val = val;
-		ci->ci_cc.cc_denom = cpu_frequency(ci);
-		if (ci->ci_cc.cc_denom == 0)
-			ci->ci_cc.cc_denom = tsc_freq;
-		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
-		ci->ci_cc.cc_gen = gen;
-		splx(s);
-		return;
+	  (void)cc_init(tsc_freq, "TSC", tsc_qual);
 	}
-
-#ifdef TIMECOUNTER_DEBUG
-	old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
-#endif
-
-	/* local ticks per period */
-	denom = ci->ci_cc.cc_cc - denom;
-	if (denom < 0)
-		denom += 0x100000000LL;
-
-	ci->ci_cc.cc_denom = denom;
-
-	/* reference ticks per period */
-	delta = val - ci->ci_cc.cc_val;
-	if (delta < 0)
-		delta += 0x100000000LL;
-
-	ci->ci_cc.cc_val = val;
-	ci->ci_cc.cc_delta = delta;
-	
-	/* publish new generation number */
-	ci->ci_cc.cc_gen = gen;
-	splx(s);
-
-#ifdef TIMECOUNTER_DEBUG
-	factor = (delta * 1000) / denom - old_factor;
-	if (factor < 0)
-		factor = -factor;
-
-	if (factor > old_factor / 10)
-		printf("tsc_calibrate_cpu[%lu]: 10%% exceeded - delta %"
-		       PRId64 ", denom %" PRId64 ", factor %" PRId64
-		       ", old factor %" PRId64"\n", ci->ci_cpuid,
-		       delta, denom, (delta * 1000) / denom, old_factor);
-#if 0
-	printf("tsc_calibrate_cpu[%lu]: delta %" PRId64
-	       ", denom %" PRId64 ", factor %" PRId64 "\n", ci->ci_cpuid, delta, denom, (delta * 1000) / denom);
-#endif
-#endif /* TIMECOUNTER_DEBUG */
 }
Index: sys/compat/osf1/osf1_misc.c
===================================================================
RCS file: /cvsroot/src/sys/compat/osf1/osf1_misc.c,v
retrieving revision 1.73
diff -u -r1.73 osf1_misc.c
--- sys/compat/osf1/osf1_misc.c	6 Apr 2006 15:45:20 -0000	1.73
+++ sys/compat/osf1/osf1_misc.c	20 Jan 2007 19:03:13 -0000
@@ -409,16 +409,14 @@
 	if (ticks == 0)
 		ticks = 1;
 
-	s = splclock();
-	tv = time;
-	splx(s);
+	getmicrotime(&tv);
 
 	tsleep(l, PUSER|PCATCH, "uslpthrd", ticks);	/* XXX */
 
 	if (SCARG(uap, slept) != NULL) {
-		s = splclock();
+		struct timeval time;
+		getmicrotime(&time);
 		timersub(&time, &tv, &endtv);
-		splx(s);
 		if (endtv.tv_sec < 0 || endtv.tv_usec < 0)
 			endtv.tv_sec = endtv.tv_usec = 0;
 
Index: sys/sys/cc_microtime.h
===================================================================
RCS file: /cvsroot/src/sys/sys/cc_microtime.h,v
retrieving revision 1.3
diff -u -r1.3 cc_microtime.h
--- sys/sys/cc_microtime.h	7 Jun 2006 22:34:18 -0000	1.3
+++ sys/sys/cc_microtime.h	20 Jan 2007 19:03:19 -0000
@@ -51,19 +51,22 @@
 	int64_t cc_denom;
 };
 #else
-/* XXX this file is not named right - rather something like tsc.h */
-void tsc_calibrate_cpu(struct cpu_info *);
+/* XXX this file is not named right - rather something like cctr.h */
+
+void cc_calibrate_cpu(struct cpu_info *);
+struct timecounter *cc_init(uint64_t, const char*, int);
+
 /*
  * Variables used by cc_microtime().
  */
 struct cc_microtime_state {
 	volatile u_int   cc_gen;   /* generation number for this data set */
-	volatile int64_t cc_val;   /* reference TSC value at calibration time */
-	volatile int64_t cc_cc;	   /* local TSC value at calibration time */
-	volatile int64_t cc_delta; /* reference TSC difference for
+	volatile int64_t cc_val;   /* reference CC value at calibration time */
+	volatile int64_t cc_cc;	   /* local CC value at calibration time */
+	volatile int64_t cc_delta; /* reference CC difference for
 				      last calibration period */
-	volatile int64_t cc_denom; /* local TSC difference for
+	volatile int64_t cc_denom; /* local CC difference for
 				      last calibration period */
 };
 #endif /* __HAVE_TIMECOUNTER */
Index: sys/kern/kern_cctr.c
===================================================================
diff -u -/dev/null sys/kern/kern_cctr.c
--- /dev/null	2007-01-20 20:10:18.000000000 +0100
+++ sys/kern/kern_cctr.c	2006-12-23 18:02:15.000000000 +0100
@@ -0,0 +1,294 @@
+/* $NetBSD: tsc.c,v 1.10 2006/11/16 01:32:39 christos Exp $ */
+
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * re-implementation of TSC for MP systems merging cc_microtime and
+ * TSC for timecounters by Frank Kardel
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the NetBSD
+ *	Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* basic calibration ideas are (kern_microtime.c): */
+/******************************************************************************
+ *                                                                            *
+ * Copyright (c) David L. Mills 1993, 1994                                    *
+ *                                                                            *
+ * Permission to use, copy, modify, and distribute this software and its      *
+ * documentation for any purpose and without fee is hereby granted, provided  *
+ * that the above copyright notice appears in all copies and that both the    *
+ * copyright notice and this permission notice appear in supporting           *
+ * documentation, and that the name University of Delaware not be used in     *
+ * advertising or publicity pertaining to distribution of the software        *
+ * without specific, written prior permission.  The University of Delaware    *
+ * makes no representations about the suitability this software for any       *
+ * purpose.  It is provided "as is" without express or implied warranty.      *
+ *                                                                            *
+ ******************************************************************************/
+
+/* reminiscents from older version of this file are: */
+/*-
+ * Copyright (c) 1998-2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+/* __FBSDID("$FreeBSD: src/sys/i386/i386/tsc.c,v 1.204 2003/10/21 18:28:34 silby Exp $"); */
+__KERNEL_RCSID(0, "$NetBSD: tsc.c,v 1.10 2006/11/16 01:32:39 christos Exp $");
+
+#include "opt_multiprocessor.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/timetc.h>
+#include <sys/kernel.h>
+#include <sys/power.h>
+#include <machine/cpu.h>
+#include <machine/cpu_counter.h>
+
+/* XXX make cc_timecounter.tc_frequency settable by sysctl() */
+
+static timecounter_get_t cc_get_timecount;
+static timecounter_pps_t cc_calibrate;
+
+void cc_calibrate_cpu(struct cpu_info *);
+
+static int64_t cc_cal_val;  /* last calibrate time stamp */
+
+static struct timecounter cc_timecounter = {
+	cc_get_timecount,	/* get_timecount */
+	cc_calibrate,		/* once per clock interrupt - used to calibrate cpu cycle counters */
+ 	~0u,			/* counter_mask */
+	0,			/* frequency */
+	 "unkown cycle counter",/* name */
+	-100000,                /* don't pick cycle counter automatically */
+				/* if frequency changes might affect TSC */
+	NULL,
+	NULL,
+};
+
+/*
+ * initialize cycle counter based timecounter
+ */
+struct timecounter *
+cc_init(uint64_t freq, const char *name, int quality)
+{
+  cc_timecounter.tc_frequency = freq;
+  cc_timecounter.tc_name = name;
+  cc_timecounter.tc_quality = quality;
+  tc_init(&cc_timecounter);
+  return &cc_timecounter;
+}
+  
+/*
+ * pick up tick count scaled to reference tick count
+ */
+static u_int
+cc_get_timecount(struct timecounter *tc)
+{
+	struct cpu_info *ci = curcpu();
+	int64_t rcc, cc;
+	u_int gen;
+
+	if (ci->ci_cc.cc_denom == 0) {
+		/*
+		 * This is our first time here on this CPU.  Just
+		 * start with reasonable initial values.
+		 */
+	        ci->ci_cc.cc_cc    = cpu_counter32();
+		ci->ci_cc.cc_val   = 0;
+		if (ci->ci_cc.cc_gen == 0)
+			ci->ci_cc.cc_gen++;
+
+		ci->ci_cc.cc_denom = cpu_frequency(ci);
+		if (ci->ci_cc.cc_denom == 0)
+			ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;
+		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
+	}
+
+	/* read counter and re-read when the re-calibration
+	   strikes inbetween */
+	do {
+		/* pick up current generation number */
+		gen = ci->ci_cc.cc_gen;
+
+		/* determine local delta ticks */
+		cc = cpu_counter32() - ci->ci_cc.cc_cc;
+		if (cc < 0)
+			cc += 0x100000000LL;
+
+		/* scale to primary */
+		rcc = (cc * ci->ci_cc.cc_delta) / ci->ci_cc.cc_denom
+			+ ci->ci_cc.cc_val;
+	} while (gen == 0 || gen != ci->ci_cc.cc_gen);
+
+	return rcc;
+}
+
+/*
+ * called once per clock tick via the pps callback
+ * for the calibration of the TSC counters.
+ * it is called only for the PRIMARY cpu. all
+ * other cpus are called via a broadcast IPI
+ * calibration interval is 1 second - we call
+ * the calobration code only every hz calls
+ */
+static void
+cc_calibrate(struct timecounter *tc)
+{
+	static int calls;
+	struct cpu_info *ci;
+	
+	 /*
+	  * XXX: for high interrupt frequency
+	  * support: ++calls < hz / tc_tick
+	  */
+	if (++calls < hz)
+		return;
+
+	calls = 0;
+	ci = curcpu();
+	/* pick up reference ticks */
+	cc_cal_val = cpu_counter32();
+
+#if defined(MULTIPROCESSOR)
+	x86_broadcast_ipi(X86_IPI_MICROSET);
+#endif
+	cc_calibrate_cpu(ci);
+}
+
+/*
+ * This routine is called about once per second directly by the master
+ * processor and via an interprocessor interrupt for other processors.
+ * It determines the CC frequency of each processor relative to the
+ * master clock and the time this determination is made.  These values
+ * are used by cc_get_timecount() to interpolate the ticks between
+ * timer interrupts.  Note that we assume the kernel variables have
+ * been zeroed early in life.
+ */
+void
+cc_calibrate_cpu(struct cpu_info *ci)
+{
+	u_int   gen;
+	int64_t val;
+	int64_t delta, denom;
+	int s;
+#ifdef TIMECOUNTER_DEBUG
+	int64_t factor, old_factor;
+#endif
+	val = cc_cal_val;
+
+	s = splhigh();
+	/* create next generation number */
+	gen = ci->ci_cc.cc_gen;
+	gen++;
+	if (gen == 0)
+		gen++;
+
+	/* update in progress */
+	ci->ci_cc.cc_gen = 0;
+
+	denom = ci->ci_cc.cc_cc;
+	ci->ci_cc.cc_cc = cpu_counter32();
+
+	if (ci->ci_cc.cc_denom == 0) {
+		/*
+		 * This is our first time here on this CPU.  Just
+		 * start with reasonable initial values.
+		 */
+		ci->ci_cc.cc_val = val;
+		ci->ci_cc.cc_denom = cpu_frequency(ci);
+		if (ci->ci_cc.cc_denom == 0)
+			ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;;
+		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
+		ci->ci_cc.cc_gen = gen;
+		splx(s);
+		return;
+	}
+
+#ifdef TIMECOUNTER_DEBUG
+	old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
+#endif
+
+	/* local ticks per period */
+	denom = ci->ci_cc.cc_cc - denom;
+	if (denom < 0)
+		denom += 0x100000000LL;
+
+	ci->ci_cc.cc_denom = denom;
+
+	/* reference ticks per period */
+	delta = val - ci->ci_cc.cc_val;
+	if (delta < 0)
+		delta += 0x100000000LL;
+
+	ci->ci_cc.cc_val = val;
+	ci->ci_cc.cc_delta = delta;
+	
+	/* publish new generation number */
+	ci->ci_cc.cc_gen = gen;
+	splx(s);
+
+#ifdef TIMECOUNTER_DEBUG
+	factor = (delta * 1000) / denom - old_factor;
+	if (factor < 0)
+		factor = -factor;
+
+	if (factor > old_factor / 10)
+		printf("cc_calibrate_cpu[%lu]: 10%% exceeded - delta %"
+		       PRId64 ", denom %" PRId64 ", factor %" PRId64
+		       ", old factor %" PRId64"\n", ci->ci_cpuid,
+		       delta, denom, (delta * 1000) / denom, old_factor);
+#endif /* TIMECOUNTER_DEBUG */
+}

--------------000108010203060706030209--