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--