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