kern_cctr.c revision 1.1
1/* $NetBSD: kern_cctr.c,v 1.1 2007/07/21 11:47:07 tsutsui Exp $ */
2
3
4/*-
5 * Copyright (c) 2006 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * re-implementation of TSC for MP systems merging cc_microtime and
9 * TSC for timecounters by Frank Kardel
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This product includes software developed by the NetBSD
22 *	Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 *    contributors may be used to endorse or promote products derived
25 *    from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* basic calibration ideas are (kern_microtime.c): */
41/******************************************************************************
42 *                                                                            *
43 * Copyright (c) David L. Mills 1993, 1994                                    *
44 *                                                                            *
45 * Permission to use, copy, modify, and distribute this software and its      *
46 * documentation for any purpose and without fee is hereby granted, provided  *
47 * that the above copyright notice appears in all copies and that both the    *
48 * copyright notice and this permission notice appear in supporting           *
49 * documentation, and that the name University of Delaware not be used in     *
50 * advertising or publicity pertaining to distribution of the software        *
51 * without specific, written prior permission.  The University of Delaware    *
52 * makes no representations about the suitability this software for any       *
53 * purpose.  It is provided "as is" without express or implied warranty.      *
54 *                                                                            *
55 ******************************************************************************/
56
57/* reminiscents from older version of this file are: */
58/*-
59 * Copyright (c) 1998-2003 Poul-Henning Kamp
60 * All rights reserved.
61 *
62 * Redistribution and use in source and binary forms, with or without
63 * modification, are permitted provided that the following conditions
64 * are met:
65 * 1. Redistributions of source code must retain the above copyright
66 *    notice, this list of conditions and the following disclaimer.
67 * 2. Redistributions in binary form must reproduce the above copyright
68 *    notice, this list of conditions and the following disclaimer in the
69 *    documentation and/or other materials provided with the distribution.
70 *
71 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
72 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
73 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
74 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
75 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
76 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
77 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
78 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
79 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
80 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
81 * SUCH DAMAGE.
82 */
83
84#include <sys/cdefs.h>
85/* __FBSDID("$FreeBSD: src/sys/i386/i386/tsc.c,v 1.204 2003/10/21 18:28:34 silby Exp $"); */
86__KERNEL_RCSID(0, "$NetBSD: kern_cctr.c,v 1.1 2007/07/21 11:47:07 tsutsui Exp $");
87
88#include "opt_multiprocessor.h"
89
90#include <sys/param.h>
91#include <sys/systm.h>
92#include <sys/sysctl.h>
93#include <sys/time.h>
94#include <sys/timetc.h>
95#include <sys/kernel.h>
96#include <sys/power.h>
97#include <machine/cpu.h>
98#include <machine/cpu_counter.h>
99
100/* XXX make cc_timecounter.tc_frequency settable by sysctl() */
101
102static timecounter_get_t cc_get_timecount;
103static timecounter_pps_t cc_calibrate;
104
105void cc_calibrate_cpu(struct cpu_info *);
106
107static int64_t cc_cal_val;  /* last calibrate time stamp */
108
109static struct timecounter cc_timecounter = {
110	.tc_get_timecount	= cc_get_timecount,
111	.tc_poll_pps		= cc_calibrate,
112	.tc_counter_mask	= ~0u,
113	.tc_frequency		= 0,
114	.tc_name		= "unkown cycle counter",
115	/*
116	 * don't pick cycle counter automatically
117	 * if frequency changes might affect cycle counter
118	 */
119	.tc_quality		= -100000,
120
121	.tc_priv		= NULL,
122	.tc_next		= NULL
123};
124
125/*
126 * initialize cycle counter based timecounter
127 */
128struct timecounter *
129cc_init(uint64_t freq, const char *name, int quality)
130{
131
132	cc_timecounter.tc_frequency = freq;
133	cc_timecounter.tc_name = name;
134	cc_timecounter.tc_quality = quality;
135	tc_init(&cc_timecounter);
136
137	return &cc_timecounter;
138}
139
140/*
141 * pick up tick count scaled to reference tick count
142 */
143static u_int
144cc_get_timecount(struct timecounter *tc)
145{
146	struct cpu_info *ci = curcpu();
147	int64_t rcc, cc;
148	u_int gen;
149
150	if (ci->ci_cc.cc_denom == 0) {
151		/*
152		 * This is our first time here on this CPU.  Just
153		 * start with reasonable initial values.
154		 */
155	        ci->ci_cc.cc_cc    = cpu_counter32();
156		ci->ci_cc.cc_val   = 0;
157		if (ci->ci_cc.cc_gen == 0)
158			ci->ci_cc.cc_gen++;
159
160		ci->ci_cc.cc_denom = cpu_frequency(ci);
161		if (ci->ci_cc.cc_denom == 0)
162			ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;
163		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
164	}
165
166	/*
167	 * read counter and re-read when the re-calibration
168	 * strikes inbetween
169	 */
170	do {
171		/* pick up current generation number */
172		gen = ci->ci_cc.cc_gen;
173
174		/* determine local delta ticks */
175		cc = cpu_counter32() - ci->ci_cc.cc_cc;
176		if (cc < 0)
177			cc += 0x100000000LL;
178
179		/* scale to primary */
180		rcc = (cc * ci->ci_cc.cc_delta) / ci->ci_cc.cc_denom
181		    + ci->ci_cc.cc_val;
182	} while (gen == 0 || gen != ci->ci_cc.cc_gen);
183
184	return rcc;
185}
186
187/*
188 * called once per clock tick via the pps callback
189 * for the calibration of the TSC counters.
190 * it is called only for the PRIMARY cpu. all
191 * other cpus are called via a broadcast IPI
192 * calibration interval is 1 second - we call
193 * the calobration code only every hz calls
194 */
195static void
196cc_calibrate(struct timecounter *tc)
197{
198	static int calls;
199	struct cpu_info *ci;
200
201	 /*
202	  * XXX: for high interrupt frequency
203	  * support: ++calls < hz / tc_tick
204	  */
205	if (++calls < hz)
206		return;
207
208	calls = 0;
209	ci = curcpu();
210	/* pick up reference ticks */
211	cc_cal_val = cpu_counter32();
212
213#if defined(MULTIPROCESSOR)
214	cc_calibrate_mp(ci);
215#endif
216	cc_calibrate_cpu(ci);
217}
218
219/*
220 * This routine is called about once per second directly by the master
221 * processor and via an interprocessor interrupt for other processors.
222 * It determines the CC frequency of each processor relative to the
223 * master clock and the time this determination is made.  These values
224 * are used by cc_get_timecount() to interpolate the ticks between
225 * timer interrupts.  Note that we assume the kernel variables have
226 * been zeroed early in life.
227 */
228void
229cc_calibrate_cpu(struct cpu_info *ci)
230{
231	u_int   gen;
232	int64_t val;
233	int64_t delta, denom;
234	int s;
235#ifdef TIMECOUNTER_DEBUG
236	int64_t factor, old_factor;
237#endif
238	val = cc_cal_val;
239
240	s = splhigh();
241	/* create next generation number */
242	gen = ci->ci_cc.cc_gen;
243	gen++;
244	if (gen == 0)
245		gen++;
246
247	/* update in progress */
248	ci->ci_cc.cc_gen = 0;
249
250	denom = ci->ci_cc.cc_cc;
251	ci->ci_cc.cc_cc = cpu_counter32();
252
253	if (ci->ci_cc.cc_denom == 0) {
254		/*
255		 * This is our first time here on this CPU.  Just
256		 * start with reasonable initial values.
257		 */
258		ci->ci_cc.cc_val = val;
259		ci->ci_cc.cc_denom = cpu_frequency(ci);
260		if (ci->ci_cc.cc_denom == 0)
261			ci->ci_cc.cc_denom = cc_timecounter.tc_frequency;;
262		ci->ci_cc.cc_delta = ci->ci_cc.cc_denom;
263		ci->ci_cc.cc_gen = gen;
264		splx(s);
265		return;
266	}
267
268#ifdef TIMECOUNTER_DEBUG
269	old_factor = (ci->ci_cc.cc_delta * 1000 ) / ci->ci_cc.cc_denom;
270#endif
271
272	/* local ticks per period */
273	denom = ci->ci_cc.cc_cc - denom;
274	if (denom < 0)
275		denom += 0x100000000LL;
276
277	ci->ci_cc.cc_denom = denom;
278
279	/* reference ticks per period */
280	delta = val - ci->ci_cc.cc_val;
281	if (delta < 0)
282		delta += 0x100000000LL;
283
284	ci->ci_cc.cc_val = val;
285	ci->ci_cc.cc_delta = delta;
286
287	/* publish new generation number */
288	ci->ci_cc.cc_gen = gen;
289	splx(s);
290
291#ifdef TIMECOUNTER_DEBUG
292	factor = (delta * 1000) / denom - old_factor;
293	if (factor < 0)
294		factor = -factor;
295
296	if (factor > old_factor / 10)
297		printf("cc_calibrate_cpu[%lu]: 10%% exceeded - delta %"
298		    PRId64 ", denom %" PRId64 ", factor %" PRId64
299		    ", old factor %" PRId64"\n", ci->ci_cpuid,
300		    delta, denom, (delta * 1000) / denom, old_factor);
301#endif /* TIMECOUNTER_DEBUG */
302}
303