1c8fe38aeSMatthew Dillon /*- 2c8fe38aeSMatthew Dillon * Copyright (c) 1990 The Regents of the University of California. 3c8fe38aeSMatthew Dillon * Copyright (c) 2008 The DragonFly Project. 4c8fe38aeSMatthew Dillon * All rights reserved. 5c8fe38aeSMatthew Dillon * 6c8fe38aeSMatthew Dillon * This code is derived from software contributed to Berkeley by 7c8fe38aeSMatthew Dillon * William Jolitz and Don Ahn. 8c8fe38aeSMatthew Dillon * 9c8fe38aeSMatthew Dillon * Redistribution and use in source and binary forms, with or without 10c8fe38aeSMatthew Dillon * modification, are permitted provided that the following conditions 11c8fe38aeSMatthew Dillon * are met: 12c8fe38aeSMatthew Dillon * 1. Redistributions of source code must retain the above copyright 13c8fe38aeSMatthew Dillon * notice, this list of conditions and the following disclaimer. 14c8fe38aeSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 15c8fe38aeSMatthew Dillon * notice, this list of conditions and the following disclaimer in the 16c8fe38aeSMatthew Dillon * documentation and/or other materials provided with the distribution. 172c64e990Szrj * 3. Neither the name of the University nor the names of its contributors 18c8fe38aeSMatthew Dillon * may be used to endorse or promote products derived from this software 19c8fe38aeSMatthew Dillon * without specific prior written permission. 20c8fe38aeSMatthew Dillon * 21c8fe38aeSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22c8fe38aeSMatthew Dillon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23c8fe38aeSMatthew Dillon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24c8fe38aeSMatthew Dillon * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25c8fe38aeSMatthew Dillon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26c8fe38aeSMatthew Dillon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27c8fe38aeSMatthew Dillon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28c8fe38aeSMatthew Dillon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29c8fe38aeSMatthew Dillon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30c8fe38aeSMatthew Dillon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31c8fe38aeSMatthew Dillon * SUCH DAMAGE. 32c8fe38aeSMatthew Dillon * 33c8fe38aeSMatthew Dillon * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 34c8fe38aeSMatthew Dillon * $FreeBSD: src/sys/i386/isa/clock.c,v 1.149.2.6 2002/11/02 04:41:50 iwasaki Exp $ 35c8fe38aeSMatthew Dillon */ 36c8fe38aeSMatthew Dillon 37c8fe38aeSMatthew Dillon /* 38c8fe38aeSMatthew Dillon * Routines to handle clock hardware. 39c8fe38aeSMatthew Dillon */ 40c8fe38aeSMatthew Dillon 41c8fe38aeSMatthew Dillon /* 42c8fe38aeSMatthew Dillon * inittodr, settodr and support routines written 43c8fe38aeSMatthew Dillon * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at> 44c8fe38aeSMatthew Dillon * 45c8fe38aeSMatthew Dillon * reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94 46c8fe38aeSMatthew Dillon */ 47c8fe38aeSMatthew Dillon 4840672791SSepherosa Ziehau #if 0 4940672791SSepherosa Ziehau #include "opt_clock.h" 5040672791SSepherosa Ziehau #endif 51c8fe38aeSMatthew Dillon 52c8fe38aeSMatthew Dillon #include <sys/param.h> 53c8fe38aeSMatthew Dillon #include <sys/systm.h> 54c8fe38aeSMatthew Dillon #include <sys/eventhandler.h> 55c8fe38aeSMatthew Dillon #include <sys/time.h> 56c8fe38aeSMatthew Dillon #include <sys/kernel.h> 57c8fe38aeSMatthew Dillon #include <sys/bus.h> 58c8fe38aeSMatthew Dillon #include <sys/sysctl.h> 59c8fe38aeSMatthew Dillon #include <sys/cons.h> 60ce7866b8SMatthew Dillon #include <sys/kbio.h> 61c8fe38aeSMatthew Dillon #include <sys/systimer.h> 62c8fe38aeSMatthew Dillon #include <sys/globaldata.h> 63c8fe38aeSMatthew Dillon #include <sys/machintr.h> 641b505979SSepherosa Ziehau #include <sys/interrupt.h> 65c8fe38aeSMatthew Dillon 66ce7866b8SMatthew Dillon #include <sys/thread2.h> 67ce7866b8SMatthew Dillon 68c8fe38aeSMatthew Dillon #include <machine/clock.h> 69c8fe38aeSMatthew Dillon #include <machine/cputypes.h> 70c8fe38aeSMatthew Dillon #include <machine/frame.h> 71c8fe38aeSMatthew Dillon #include <machine/ipl.h> 72c8fe38aeSMatthew Dillon #include <machine/limits.h> 73c8fe38aeSMatthew Dillon #include <machine/md_var.h> 74c8fe38aeSMatthew Dillon #include <machine/psl.h> 75c8fe38aeSMatthew Dillon #include <machine/segments.h> 76c8fe38aeSMatthew Dillon #include <machine/smp.h> 77c8fe38aeSMatthew Dillon #include <machine/specialreg.h> 7857a9c56bSSepherosa Ziehau #include <machine/intr_machdep.h> 79c8fe38aeSMatthew Dillon 80ed4d621dSSepherosa Ziehau #include <machine_base/apic/ioapic.h> 816b809ec7SSepherosa Ziehau #include <machine_base/apic/ioapic_abi.h> 82c8fe38aeSMatthew Dillon #include <machine_base/icu/icu.h> 830855a2afSJordan Gordeev #include <bus/isa/isa.h> 84c8fe38aeSMatthew Dillon #include <bus/isa/rtc.h> 85c8fe38aeSMatthew Dillon #include <machine_base/isa/timerreg.h> 86c8fe38aeSMatthew Dillon 87c8fe38aeSMatthew Dillon static void i8254_restore(void); 88c8fe38aeSMatthew Dillon static void resettodr_on_shutdown(void *arg __unused); 89c8fe38aeSMatthew Dillon 90c8fe38aeSMatthew Dillon /* 91c8fe38aeSMatthew Dillon * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we 92c8fe38aeSMatthew Dillon * can use a simple formula for leap years. 93c8fe38aeSMatthew Dillon */ 94c8fe38aeSMatthew Dillon #define LEAPYEAR(y) ((u_int)(y) % 4 == 0) 95c8fe38aeSMatthew Dillon #define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31) 96c8fe38aeSMatthew Dillon 97c8fe38aeSMatthew Dillon #ifndef TIMER_FREQ 98c8fe38aeSMatthew Dillon #define TIMER_FREQ 1193182 99c8fe38aeSMatthew Dillon #endif 100c8fe38aeSMatthew Dillon 101c8fe38aeSMatthew Dillon static uint8_t i8254_walltimer_sel; 102c8fe38aeSMatthew Dillon static uint16_t i8254_walltimer_cntr; 103c8fe38aeSMatthew Dillon 104c8fe38aeSMatthew Dillon int adjkerntz; /* local offset from GMT in seconds */ 105c8fe38aeSMatthew Dillon int disable_rtc_set; /* disable resettodr() if != 0 */ 106c8fe38aeSMatthew Dillon int tsc_present; 1075a81b19fSSepherosa Ziehau int tsc_invariant; 108dda44f1eSSepherosa Ziehau int tsc_mpsync; 109c8fe38aeSMatthew Dillon int tsc_is_broken; 110c8fe38aeSMatthew Dillon int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ 111c8fe38aeSMatthew Dillon int timer0_running; 1125b49787bSMatthew Dillon tsc_uclock_t tsc_frequency; 1135b49787bSMatthew Dillon tsc_uclock_t tsc_oneus_approx; /* always at least 1, approx only */ 1145b49787bSMatthew Dillon 115c8fe38aeSMatthew Dillon enum tstate { RELEASED, ACQUIRED }; 116c8fe38aeSMatthew Dillon enum tstate timer0_state; 117c8fe38aeSMatthew Dillon enum tstate timer1_state; 118c8fe38aeSMatthew Dillon enum tstate timer2_state; 119c8fe38aeSMatthew Dillon 120c8fe38aeSMatthew Dillon static int beeping = 0; 121c8fe38aeSMatthew Dillon static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 122c8fe38aeSMatthew Dillon static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; 123c8fe38aeSMatthew Dillon static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR; 124c8fe38aeSMatthew Dillon static int rtc_loaded; 125c8fe38aeSMatthew Dillon 126c8fe38aeSMatthew Dillon static int i8254_cputimer_div; 127c8fe38aeSMatthew Dillon 12840672791SSepherosa Ziehau static int i8254_nointr; 1294d517764SSepherosa Ziehau static int i8254_intr_disable = 1; 13040672791SSepherosa Ziehau TUNABLE_INT("hw.i8254.intr_disable", &i8254_intr_disable); 13140672791SSepherosa Ziehau 132242fd95fSImre Vadász static int calibrate_timers_with_rtc = 0; 133242fd95fSImre Vadász TUNABLE_INT("hw.calibrate_timers_with_rtc", &calibrate_timers_with_rtc); 134242fd95fSImre Vadász 135c8fe38aeSMatthew Dillon static struct callout sysbeepstop_ch; 136c8fe38aeSMatthew Dillon 137c8fe38aeSMatthew Dillon static sysclock_t i8254_cputimer_count(void); 138c8fe38aeSMatthew Dillon static void i8254_cputimer_construct(struct cputimer *cputimer, sysclock_t last); 139c8fe38aeSMatthew Dillon static void i8254_cputimer_destruct(struct cputimer *cputimer); 140c8fe38aeSMatthew Dillon 141c8fe38aeSMatthew Dillon static struct cputimer i8254_cputimer = { 1420087561dSSepherosa Ziehau .next = SLIST_ENTRY_INITIALIZER, 1430087561dSSepherosa Ziehau .name = "i8254", 1440087561dSSepherosa Ziehau .pri = CPUTIMER_PRI_8254, 1450087561dSSepherosa Ziehau .type = 0, /* determined later */ 1460087561dSSepherosa Ziehau .count = i8254_cputimer_count, 1470087561dSSepherosa Ziehau .fromhz = cputimer_default_fromhz, 1480087561dSSepherosa Ziehau .fromus = cputimer_default_fromus, 1490087561dSSepherosa Ziehau .construct = i8254_cputimer_construct, 1500087561dSSepherosa Ziehau .destruct = i8254_cputimer_destruct, 1510087561dSSepherosa Ziehau .freq = TIMER_FREQ 152c8fe38aeSMatthew Dillon }; 153c8fe38aeSMatthew Dillon 15459878316SSepherosa Ziehau static sysclock_t tsc_cputimer_count_mfence(void); 15559878316SSepherosa Ziehau static sysclock_t tsc_cputimer_count_lfence(void); 1568d23b56cSSepherosa Ziehau static void tsc_cputimer_construct(struct cputimer *, sysclock_t); 1578d23b56cSSepherosa Ziehau 1588d23b56cSSepherosa Ziehau static struct cputimer tsc_cputimer = { 1590087561dSSepherosa Ziehau .next = SLIST_ENTRY_INITIALIZER, 1600087561dSSepherosa Ziehau .name = "TSC", 1610087561dSSepherosa Ziehau .pri = CPUTIMER_PRI_TSC, 1620087561dSSepherosa Ziehau .type = CPUTIMER_TSC, 1630087561dSSepherosa Ziehau .count = NULL, /* determined later */ 1640087561dSSepherosa Ziehau .fromhz = cputimer_default_fromhz, 1650087561dSSepherosa Ziehau .fromus = cputimer_default_fromus, 1660087561dSSepherosa Ziehau .construct = tsc_cputimer_construct, 1670087561dSSepherosa Ziehau .destruct = cputimer_default_destruct, 1680087561dSSepherosa Ziehau .freq = 0 /* determined later */ 1698d23b56cSSepherosa Ziehau }; 1708d23b56cSSepherosa Ziehau 1716b91ee43SSepherosa Ziehau static struct cpucounter tsc_cpucounter = { 1726b91ee43SSepherosa Ziehau .freq = 0, /* determined later */ 1736b91ee43SSepherosa Ziehau .count = NULL, /* determined later */ 1746b91ee43SSepherosa Ziehau .flags = 0, /* adjusted later */ 1756b91ee43SSepherosa Ziehau .prio = CPUCOUNTER_PRIO_TSC, 1766b91ee43SSepherosa Ziehau .type = CPUCOUNTER_TSC 1776b91ee43SSepherosa Ziehau }; 1786b91ee43SSepherosa Ziehau 17940672791SSepherosa Ziehau static void i8254_intr_reload(struct cputimer_intr *, sysclock_t); 18040672791SSepherosa Ziehau static void i8254_intr_config(struct cputimer_intr *, const struct cputimer *); 18140672791SSepherosa Ziehau static void i8254_intr_initclock(struct cputimer_intr *, boolean_t); 18240672791SSepherosa Ziehau 18340672791SSepherosa Ziehau static struct cputimer_intr i8254_cputimer_intr = { 18440672791SSepherosa Ziehau .freq = TIMER_FREQ, 18540672791SSepherosa Ziehau .reload = i8254_intr_reload, 18640672791SSepherosa Ziehau .enable = cputimer_intr_default_enable, 18740672791SSepherosa Ziehau .config = i8254_intr_config, 18840672791SSepherosa Ziehau .restart = cputimer_intr_default_restart, 18940672791SSepherosa Ziehau .pmfixup = cputimer_intr_default_pmfixup, 19040672791SSepherosa Ziehau .initclock = i8254_intr_initclock, 19142098fc3SSepherosa Ziehau .pcpuhand = NULL, 19240672791SSepherosa Ziehau .next = SLIST_ENTRY_INITIALIZER, 19340672791SSepherosa Ziehau .name = "i8254", 19440672791SSepherosa Ziehau .type = CPUTIMER_INTR_8254, 19540672791SSepherosa Ziehau .prio = CPUTIMER_INTR_PRIO_8254, 19642098fc3SSepherosa Ziehau .caps = CPUTIMER_INTR_CAP_PS, 19742098fc3SSepherosa Ziehau .priv = NULL 19840672791SSepherosa Ziehau }; 19940672791SSepherosa Ziehau 200c8fe38aeSMatthew Dillon /* 201c8fe38aeSMatthew Dillon * timer0 clock interrupt. Timer0 is in one-shot mode and has stopped 202c8fe38aeSMatthew Dillon * counting as of this interrupt. We use timer1 in free-running mode (not 203c8fe38aeSMatthew Dillon * generating any interrupts) as our main counter. Each cpu has timeouts 204c8fe38aeSMatthew Dillon * pending. 205c8fe38aeSMatthew Dillon * 206c8fe38aeSMatthew Dillon * This code is INTR_MPSAFE and may be called without the BGL held. 207c8fe38aeSMatthew Dillon */ 208c8fe38aeSMatthew Dillon static void 209c8fe38aeSMatthew Dillon clkintr(void *dummy, void *frame_arg) 210c8fe38aeSMatthew Dillon { 211c8fe38aeSMatthew Dillon static sysclock_t sysclock_count; /* NOTE! Must be static */ 212c8fe38aeSMatthew Dillon struct globaldata *gd = mycpu; 213c8fe38aeSMatthew Dillon struct globaldata *gscan; 214c8fe38aeSMatthew Dillon int n; 215c8fe38aeSMatthew Dillon 216c8fe38aeSMatthew Dillon /* 217c8fe38aeSMatthew Dillon * SWSTROBE mode is a one-shot, the timer is no longer running 218c8fe38aeSMatthew Dillon */ 219c8fe38aeSMatthew Dillon timer0_running = 0; 220c8fe38aeSMatthew Dillon 221c8fe38aeSMatthew Dillon /* 222c8fe38aeSMatthew Dillon * XXX the dispatcher needs work. right now we call systimer_intr() 223c8fe38aeSMatthew Dillon * directly or via IPI for any cpu with systimers queued, which is 224c8fe38aeSMatthew Dillon * usually *ALL* of them. We need to use the LAPIC timer for this. 225c8fe38aeSMatthew Dillon */ 226c8fe38aeSMatthew Dillon sysclock_count = sys_cputimer->count(); 227c8fe38aeSMatthew Dillon for (n = 0; n < ncpus; ++n) { 228c8fe38aeSMatthew Dillon gscan = globaldata_find(n); 229c8fe38aeSMatthew Dillon if (TAILQ_FIRST(&gscan->gd_systimerq) == NULL) 230c8fe38aeSMatthew Dillon continue; 231c8fe38aeSMatthew Dillon if (gscan != gd) { 232c8fe38aeSMatthew Dillon lwkt_send_ipiq3(gscan, (ipifunc3_t)systimer_intr, 23396d52ac8SSepherosa Ziehau &sysclock_count, 1); 234c8fe38aeSMatthew Dillon } else { 235c8fe38aeSMatthew Dillon systimer_intr(&sysclock_count, 0, frame_arg); 236c8fe38aeSMatthew Dillon } 237c8fe38aeSMatthew Dillon } 238c8fe38aeSMatthew Dillon } 239c8fe38aeSMatthew Dillon 240c8fe38aeSMatthew Dillon 241c8fe38aeSMatthew Dillon /* 242c8fe38aeSMatthew Dillon * NOTE! not MP safe. 243c8fe38aeSMatthew Dillon */ 244c8fe38aeSMatthew Dillon int 245c8fe38aeSMatthew Dillon acquire_timer2(int mode) 246c8fe38aeSMatthew Dillon { 247c8fe38aeSMatthew Dillon if (timer2_state != RELEASED) 248c8fe38aeSMatthew Dillon return (-1); 249c8fe38aeSMatthew Dillon timer2_state = ACQUIRED; 250c8fe38aeSMatthew Dillon 251c8fe38aeSMatthew Dillon /* 252c8fe38aeSMatthew Dillon * This access to the timer registers is as atomic as possible 253c8fe38aeSMatthew Dillon * because it is a single instruction. We could do better if we 254c8fe38aeSMatthew Dillon * knew the rate. 255c8fe38aeSMatthew Dillon */ 256c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL2 | (mode & 0x3f)); 257c8fe38aeSMatthew Dillon return (0); 258c8fe38aeSMatthew Dillon } 259c8fe38aeSMatthew Dillon 260c8fe38aeSMatthew Dillon int 261c8fe38aeSMatthew Dillon release_timer2(void) 262c8fe38aeSMatthew Dillon { 263c8fe38aeSMatthew Dillon if (timer2_state != ACQUIRED) 264c8fe38aeSMatthew Dillon return (-1); 265c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT); 266c8fe38aeSMatthew Dillon timer2_state = RELEASED; 267c8fe38aeSMatthew Dillon return (0); 268c8fe38aeSMatthew Dillon } 269c8fe38aeSMatthew Dillon 270c8fe38aeSMatthew Dillon #include "opt_ddb.h" 271c8fe38aeSMatthew Dillon #ifdef DDB 272c8fe38aeSMatthew Dillon #include <ddb/ddb.h> 273c8fe38aeSMatthew Dillon 274c8fe38aeSMatthew Dillon DB_SHOW_COMMAND(rtc, rtc) 275c8fe38aeSMatthew Dillon { 276c8fe38aeSMatthew Dillon kprintf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n", 277c8fe38aeSMatthew Dillon rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY), 278c8fe38aeSMatthew Dillon rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC), 279c8fe38aeSMatthew Dillon rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR)); 280c8fe38aeSMatthew Dillon } 281c8fe38aeSMatthew Dillon #endif /* DDB */ 282c8fe38aeSMatthew Dillon 283c8fe38aeSMatthew Dillon /* 284c8fe38aeSMatthew Dillon * Return the current cpu timer count as a 32 bit integer. 285c8fe38aeSMatthew Dillon */ 286c8fe38aeSMatthew Dillon static 287c8fe38aeSMatthew Dillon sysclock_t 288c8fe38aeSMatthew Dillon i8254_cputimer_count(void) 289c8fe38aeSMatthew Dillon { 290e28c8ef4SSascha Wildner static uint16_t cputimer_last; 291e28c8ef4SSascha Wildner uint16_t count; 292c8fe38aeSMatthew Dillon sysclock_t ret; 293c8fe38aeSMatthew Dillon 294c8fe38aeSMatthew Dillon clock_lock(); 295c8fe38aeSMatthew Dillon outb(TIMER_MODE, i8254_walltimer_sel | TIMER_LATCH); 296e28c8ef4SSascha Wildner count = (uint8_t)inb(i8254_walltimer_cntr); /* get countdown */ 297e28c8ef4SSascha Wildner count |= ((uint8_t)inb(i8254_walltimer_cntr) << 8); 298c8fe38aeSMatthew Dillon count = -count; /* -> countup */ 299c8fe38aeSMatthew Dillon if (count < cputimer_last) /* rollover */ 300c8fe38aeSMatthew Dillon i8254_cputimer.base += 0x00010000; 301c8fe38aeSMatthew Dillon ret = i8254_cputimer.base | count; 302c8fe38aeSMatthew Dillon cputimer_last = count; 303c8fe38aeSMatthew Dillon clock_unlock(); 304c8fe38aeSMatthew Dillon return(ret); 305c8fe38aeSMatthew Dillon } 306c8fe38aeSMatthew Dillon 307c8fe38aeSMatthew Dillon /* 308c8fe38aeSMatthew Dillon * This function is called whenever the system timebase changes, allowing 309c8fe38aeSMatthew Dillon * us to calculate what is needed to convert a system timebase tick 310c8fe38aeSMatthew Dillon * into an 8254 tick for the interrupt timer. If we can convert to a 311c8fe38aeSMatthew Dillon * simple shift, multiplication, or division, we do so. Otherwise 64 312c8fe38aeSMatthew Dillon * bit arithmatic is required every time the interrupt timer is reloaded. 313c8fe38aeSMatthew Dillon */ 31440672791SSepherosa Ziehau static void 31540672791SSepherosa Ziehau i8254_intr_config(struct cputimer_intr *cti, const struct cputimer *timer) 316c8fe38aeSMatthew Dillon { 317c8fe38aeSMatthew Dillon int freq; 318c8fe38aeSMatthew Dillon int div; 319c8fe38aeSMatthew Dillon 320c8fe38aeSMatthew Dillon /* 321c8fe38aeSMatthew Dillon * Will a simple divide do the trick? 322c8fe38aeSMatthew Dillon */ 32340672791SSepherosa Ziehau div = (timer->freq + (cti->freq / 2)) / cti->freq; 32440672791SSepherosa Ziehau freq = cti->freq * div; 325c8fe38aeSMatthew Dillon 326c8fe38aeSMatthew Dillon if (freq >= timer->freq - 1 && freq <= timer->freq + 1) 327c8fe38aeSMatthew Dillon i8254_cputimer_div = div; 328c8fe38aeSMatthew Dillon else 329c8fe38aeSMatthew Dillon i8254_cputimer_div = 0; 330c8fe38aeSMatthew Dillon } 331c8fe38aeSMatthew Dillon 332c8fe38aeSMatthew Dillon /* 333c8fe38aeSMatthew Dillon * Reload for the next timeout. It is possible for the reload value 334c8fe38aeSMatthew Dillon * to be 0 or negative, indicating that an immediate timer interrupt 335c8fe38aeSMatthew Dillon * is desired. For now make the minimum 2 ticks. 336c8fe38aeSMatthew Dillon * 337c8fe38aeSMatthew Dillon * We may have to convert from the system timebase to the 8254 timebase. 338c8fe38aeSMatthew Dillon */ 339c5b8324cSSepherosa Ziehau static void 34040672791SSepherosa Ziehau i8254_intr_reload(struct cputimer_intr *cti, sysclock_t reload) 341c8fe38aeSMatthew Dillon { 342e28c8ef4SSascha Wildner uint16_t count; 343c8fe38aeSMatthew Dillon 344c8fe38aeSMatthew Dillon if (i8254_cputimer_div) 345c8fe38aeSMatthew Dillon reload /= i8254_cputimer_div; 346c8fe38aeSMatthew Dillon else 34740672791SSepherosa Ziehau reload = (int64_t)reload * cti->freq / sys_cputimer->freq; 348c8fe38aeSMatthew Dillon 349c8fe38aeSMatthew Dillon if ((int)reload < 2) 350c8fe38aeSMatthew Dillon reload = 2; 351c8fe38aeSMatthew Dillon 352c8fe38aeSMatthew Dillon clock_lock(); 353c8fe38aeSMatthew Dillon if (timer0_running) { 354c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); /* count-down timer */ 355e28c8ef4SSascha Wildner count = (uint8_t)inb(TIMER_CNTR0); /* lsb */ 356e28c8ef4SSascha Wildner count |= ((uint8_t)inb(TIMER_CNTR0) << 8); /* msb */ 357c8fe38aeSMatthew Dillon if (reload < count) { 358c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); 359e28c8ef4SSascha Wildner outb(TIMER_CNTR0, (uint8_t)reload); /* lsb */ 360e28c8ef4SSascha Wildner outb(TIMER_CNTR0, (uint8_t)(reload >> 8)); /* msb */ 361c8fe38aeSMatthew Dillon } 362c8fe38aeSMatthew Dillon } else { 363c8fe38aeSMatthew Dillon timer0_running = 1; 364c8fe38aeSMatthew Dillon if (reload > 0xFFFF) 365c8fe38aeSMatthew Dillon reload = 0; /* full count */ 366c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); 367e28c8ef4SSascha Wildner outb(TIMER_CNTR0, (uint8_t)reload); /* lsb */ 368e28c8ef4SSascha Wildner outb(TIMER_CNTR0, (uint8_t)(reload >> 8)); /* msb */ 369c8fe38aeSMatthew Dillon } 370c8fe38aeSMatthew Dillon clock_unlock(); 371c8fe38aeSMatthew Dillon } 372c8fe38aeSMatthew Dillon 373c8fe38aeSMatthew Dillon /* 374c8fe38aeSMatthew Dillon * DELAY(usec) - Spin for the specified number of microseconds. 375c8fe38aeSMatthew Dillon * DRIVERSLEEP(usec) - Spin for the specified number of microseconds, 376c8fe38aeSMatthew Dillon * but do a thread switch in the loop 377c8fe38aeSMatthew Dillon * 378c8fe38aeSMatthew Dillon * Relies on timer 1 counting down from (cputimer_freq / hz) 379c8fe38aeSMatthew Dillon * Note: timer had better have been programmed before this is first used! 380c8fe38aeSMatthew Dillon */ 381c8fe38aeSMatthew Dillon static void 382c8fe38aeSMatthew Dillon DODELAY(int n, int doswitch) 383c8fe38aeSMatthew Dillon { 3848a224941SSepherosa Ziehau ssysclock_t delta, ticks_left; 3858a224941SSepherosa Ziehau sysclock_t prev_tick, tick; 386c8fe38aeSMatthew Dillon 387c8fe38aeSMatthew Dillon #ifdef DELAYDEBUG 388c8fe38aeSMatthew Dillon int getit_calls = 1; 389c8fe38aeSMatthew Dillon int n1; 390c8fe38aeSMatthew Dillon static int state = 0; 391c8fe38aeSMatthew Dillon 392c8fe38aeSMatthew Dillon if (state == 0) { 393c8fe38aeSMatthew Dillon state = 1; 394c8fe38aeSMatthew Dillon for (n1 = 1; n1 <= 10000000; n1 *= 10) 395c8fe38aeSMatthew Dillon DELAY(n1); 396c8fe38aeSMatthew Dillon state = 2; 397c8fe38aeSMatthew Dillon } 398c8fe38aeSMatthew Dillon if (state == 1) 399c8fe38aeSMatthew Dillon kprintf("DELAY(%d)...", n); 400c8fe38aeSMatthew Dillon #endif 401c8fe38aeSMatthew Dillon /* 402c8fe38aeSMatthew Dillon * Guard against the timer being uninitialized if we are called 403c8fe38aeSMatthew Dillon * early for console i/o. 404c8fe38aeSMatthew Dillon */ 405c8fe38aeSMatthew Dillon if (timer0_state == RELEASED) 406c8fe38aeSMatthew Dillon i8254_restore(); 407c8fe38aeSMatthew Dillon 408c8fe38aeSMatthew Dillon /* 409c8fe38aeSMatthew Dillon * Read the counter first, so that the rest of the setup overhead is 410c8fe38aeSMatthew Dillon * counted. Then calculate the number of hardware timer ticks 411c8fe38aeSMatthew Dillon * required, rounding up to be sure we delay at least the requested 412c8fe38aeSMatthew Dillon * number of microseconds. 413c8fe38aeSMatthew Dillon */ 414c8fe38aeSMatthew Dillon prev_tick = sys_cputimer->count(); 415c8fe38aeSMatthew Dillon ticks_left = ((u_int)n * (int64_t)sys_cputimer->freq + 999999) / 416c8fe38aeSMatthew Dillon 1000000; 417c8fe38aeSMatthew Dillon 418c8fe38aeSMatthew Dillon /* 419c8fe38aeSMatthew Dillon * Loop until done. 420c8fe38aeSMatthew Dillon */ 421c8fe38aeSMatthew Dillon while (ticks_left > 0) { 422c8fe38aeSMatthew Dillon tick = sys_cputimer->count(); 423c8fe38aeSMatthew Dillon #ifdef DELAYDEBUG 424c8fe38aeSMatthew Dillon ++getit_calls; 425c8fe38aeSMatthew Dillon #endif 426c8fe38aeSMatthew Dillon delta = tick - prev_tick; 427c8fe38aeSMatthew Dillon prev_tick = tick; 428c8fe38aeSMatthew Dillon if (delta < 0) 429c8fe38aeSMatthew Dillon delta = 0; 430c8fe38aeSMatthew Dillon ticks_left -= delta; 431c8fe38aeSMatthew Dillon if (doswitch && ticks_left > 0) 432c8fe38aeSMatthew Dillon lwkt_switch(); 433c5724852SMatthew Dillon cpu_pause(); 434c8fe38aeSMatthew Dillon } 435c8fe38aeSMatthew Dillon #ifdef DELAYDEBUG 436c8fe38aeSMatthew Dillon if (state == 1) 437c8fe38aeSMatthew Dillon kprintf(" %d calls to getit() at %d usec each\n", 438c8fe38aeSMatthew Dillon getit_calls, (n + 5) / getit_calls); 439c8fe38aeSMatthew Dillon #endif 440c8fe38aeSMatthew Dillon } 441c8fe38aeSMatthew Dillon 44277912481SMatthew Dillon /* 44377912481SMatthew Dillon * DELAY() never switches. 44477912481SMatthew Dillon */ 445c8fe38aeSMatthew Dillon void 446c8fe38aeSMatthew Dillon DELAY(int n) 447c8fe38aeSMatthew Dillon { 448c8fe38aeSMatthew Dillon DODELAY(n, 0); 449c8fe38aeSMatthew Dillon } 450c8fe38aeSMatthew Dillon 45177912481SMatthew Dillon /* 452d8129ed3SMatthew Dillon * Returns non-zero if the specified time period has elapsed. Call 453d8129ed3SMatthew Dillon * first with last_clock set to 0. 454d8129ed3SMatthew Dillon */ 455d8129ed3SMatthew Dillon int 456d8129ed3SMatthew Dillon CHECKTIMEOUT(TOTALDELAY *tdd) 457d8129ed3SMatthew Dillon { 458d8129ed3SMatthew Dillon sysclock_t delta; 459d8129ed3SMatthew Dillon int us; 460d8129ed3SMatthew Dillon 461d8129ed3SMatthew Dillon if (tdd->started == 0) { 462d8129ed3SMatthew Dillon if (timer0_state == RELEASED) 463d8129ed3SMatthew Dillon i8254_restore(); 464d8129ed3SMatthew Dillon tdd->last_clock = sys_cputimer->count(); 465d8129ed3SMatthew Dillon tdd->started = 1; 466d8129ed3SMatthew Dillon return(0); 467d8129ed3SMatthew Dillon } 468d8129ed3SMatthew Dillon delta = sys_cputimer->count() - tdd->last_clock; 469d8129ed3SMatthew Dillon us = (u_int64_t)delta * (u_int64_t)1000000 / 470d8129ed3SMatthew Dillon (u_int64_t)sys_cputimer->freq; 471d8129ed3SMatthew Dillon tdd->last_clock += (u_int64_t)us * (u_int64_t)sys_cputimer->freq / 472d8129ed3SMatthew Dillon 1000000; 473d8129ed3SMatthew Dillon tdd->us -= us; 474d8129ed3SMatthew Dillon return (tdd->us < 0); 475d8129ed3SMatthew Dillon } 476d8129ed3SMatthew Dillon 477d8129ed3SMatthew Dillon 478d8129ed3SMatthew Dillon /* 47977912481SMatthew Dillon * DRIVERSLEEP() does not switch if called with a spinlock held or 48077912481SMatthew Dillon * from a hard interrupt. 48177912481SMatthew Dillon */ 482c8fe38aeSMatthew Dillon void 483c8fe38aeSMatthew Dillon DRIVERSLEEP(int usec) 484c8fe38aeSMatthew Dillon { 485c8fe38aeSMatthew Dillon globaldata_t gd = mycpu; 486c8fe38aeSMatthew Dillon 4870846e4ceSMatthew Dillon if (gd->gd_intr_nesting_level || gd->gd_spinlocks) { 488c8fe38aeSMatthew Dillon DODELAY(usec, 0); 489c8fe38aeSMatthew Dillon } else { 490c8fe38aeSMatthew Dillon DODELAY(usec, 1); 491c8fe38aeSMatthew Dillon } 492c8fe38aeSMatthew Dillon } 493c8fe38aeSMatthew Dillon 494c8fe38aeSMatthew Dillon static void 495c8fe38aeSMatthew Dillon sysbeepstop(void *chan) 496c8fe38aeSMatthew Dillon { 497c8fe38aeSMatthew Dillon outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */ 498c8fe38aeSMatthew Dillon beeping = 0; 499c8fe38aeSMatthew Dillon release_timer2(); 500c8fe38aeSMatthew Dillon } 501c8fe38aeSMatthew Dillon 502c8fe38aeSMatthew Dillon int 503c8fe38aeSMatthew Dillon sysbeep(int pitch, int period) 504c8fe38aeSMatthew Dillon { 505c8fe38aeSMatthew Dillon if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) 506c8fe38aeSMatthew Dillon return(-1); 5077caeaffeSMatthew Dillon if (sysbeep_enable == 0) 5087caeaffeSMatthew Dillon return(-1); 509c8fe38aeSMatthew Dillon /* 510c8fe38aeSMatthew Dillon * Nobody else is using timer2, we do not need the clock lock 511c8fe38aeSMatthew Dillon */ 512c8fe38aeSMatthew Dillon outb(TIMER_CNTR2, pitch); 513c8fe38aeSMatthew Dillon outb(TIMER_CNTR2, (pitch>>8)); 514c8fe38aeSMatthew Dillon if (!beeping) { 515c8fe38aeSMatthew Dillon /* enable counter2 output to speaker */ 516c8fe38aeSMatthew Dillon outb(IO_PPI, inb(IO_PPI) | 3); 517c8fe38aeSMatthew Dillon beeping = period; 518c8fe38aeSMatthew Dillon callout_reset(&sysbeepstop_ch, period, sysbeepstop, NULL); 519c8fe38aeSMatthew Dillon } 520c8fe38aeSMatthew Dillon return (0); 521c8fe38aeSMatthew Dillon } 522c8fe38aeSMatthew Dillon 523c8fe38aeSMatthew Dillon /* 524c8fe38aeSMatthew Dillon * RTC support routines 525c8fe38aeSMatthew Dillon */ 526c8fe38aeSMatthew Dillon 527c8fe38aeSMatthew Dillon int 528c8fe38aeSMatthew Dillon rtcin(int reg) 529c8fe38aeSMatthew Dillon { 530c8fe38aeSMatthew Dillon u_char val; 531c8fe38aeSMatthew Dillon 532c8fe38aeSMatthew Dillon crit_enter(); 533c8fe38aeSMatthew Dillon outb(IO_RTC, reg); 534c8fe38aeSMatthew Dillon inb(0x84); 535c8fe38aeSMatthew Dillon val = inb(IO_RTC + 1); 536c8fe38aeSMatthew Dillon inb(0x84); 537c8fe38aeSMatthew Dillon crit_exit(); 538c8fe38aeSMatthew Dillon return (val); 539c8fe38aeSMatthew Dillon } 540c8fe38aeSMatthew Dillon 541c8fe38aeSMatthew Dillon static __inline void 542c8fe38aeSMatthew Dillon writertc(u_char reg, u_char val) 543c8fe38aeSMatthew Dillon { 544c8fe38aeSMatthew Dillon crit_enter(); 545c8fe38aeSMatthew Dillon inb(0x84); 546c8fe38aeSMatthew Dillon outb(IO_RTC, reg); 547c8fe38aeSMatthew Dillon inb(0x84); 548c8fe38aeSMatthew Dillon outb(IO_RTC + 1, val); 549c8fe38aeSMatthew Dillon inb(0x84); /* XXX work around wrong order in rtcin() */ 550c8fe38aeSMatthew Dillon crit_exit(); 551c8fe38aeSMatthew Dillon } 552c8fe38aeSMatthew Dillon 553c8fe38aeSMatthew Dillon static __inline int 554c8fe38aeSMatthew Dillon readrtc(int port) 555c8fe38aeSMatthew Dillon { 556c8fe38aeSMatthew Dillon return(bcd2bin(rtcin(port))); 557c8fe38aeSMatthew Dillon } 558c8fe38aeSMatthew Dillon 559c8fe38aeSMatthew Dillon static u_int 560c8fe38aeSMatthew Dillon calibrate_clocks(void) 561c8fe38aeSMatthew Dillon { 5625b49787bSMatthew Dillon tsc_uclock_t old_tsc; 5638a224941SSepherosa Ziehau u_int tot_count; 5648a224941SSepherosa Ziehau sysclock_t count, prev_count; 565c8fe38aeSMatthew Dillon int sec, start_sec, timeout; 566c8fe38aeSMatthew Dillon 567c8fe38aeSMatthew Dillon if (bootverbose) 5685a81b19fSSepherosa Ziehau kprintf("Calibrating clock(s) ...\n"); 569c8fe38aeSMatthew Dillon if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) 570c8fe38aeSMatthew Dillon goto fail; 571c8fe38aeSMatthew Dillon timeout = 100000000; 572c8fe38aeSMatthew Dillon 573c8fe38aeSMatthew Dillon /* Read the mc146818A seconds counter. */ 574c8fe38aeSMatthew Dillon for (;;) { 575c8fe38aeSMatthew Dillon if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) { 576c8fe38aeSMatthew Dillon sec = rtcin(RTC_SEC); 577c8fe38aeSMatthew Dillon break; 578c8fe38aeSMatthew Dillon } 579c8fe38aeSMatthew Dillon if (--timeout == 0) 580c8fe38aeSMatthew Dillon goto fail; 581c8fe38aeSMatthew Dillon } 582c8fe38aeSMatthew Dillon 583c8fe38aeSMatthew Dillon /* Wait for the mC146818A seconds counter to change. */ 584c8fe38aeSMatthew Dillon start_sec = sec; 585c8fe38aeSMatthew Dillon for (;;) { 586c8fe38aeSMatthew Dillon if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) { 587c8fe38aeSMatthew Dillon sec = rtcin(RTC_SEC); 588c8fe38aeSMatthew Dillon if (sec != start_sec) 589c8fe38aeSMatthew Dillon break; 590c8fe38aeSMatthew Dillon } 591c8fe38aeSMatthew Dillon if (--timeout == 0) 592c8fe38aeSMatthew Dillon goto fail; 593c8fe38aeSMatthew Dillon } 594c8fe38aeSMatthew Dillon 595c8fe38aeSMatthew Dillon /* Start keeping track of the i8254 counter. */ 596c8fe38aeSMatthew Dillon prev_count = sys_cputimer->count(); 597c8fe38aeSMatthew Dillon tot_count = 0; 598c8fe38aeSMatthew Dillon 599c8fe38aeSMatthew Dillon if (tsc_present) 600c8fe38aeSMatthew Dillon old_tsc = rdtsc(); 601c8fe38aeSMatthew Dillon else 602c8fe38aeSMatthew Dillon old_tsc = 0; /* shut up gcc */ 603c8fe38aeSMatthew Dillon 604c8fe38aeSMatthew Dillon /* 605c8fe38aeSMatthew Dillon * Wait for the mc146818A seconds counter to change. Read the i8254 606c8fe38aeSMatthew Dillon * counter for each iteration since this is convenient and only 607c8fe38aeSMatthew Dillon * costs a few usec of inaccuracy. The timing of the final reads 608c8fe38aeSMatthew Dillon * of the counters almost matches the timing of the initial reads, 609c8fe38aeSMatthew Dillon * so the main cause of inaccuracy is the varying latency from 610c8fe38aeSMatthew Dillon * inside getit() or rtcin(RTC_STATUSA) to the beginning of the 611c8fe38aeSMatthew Dillon * rtcin(RTC_SEC) that returns a changed seconds count. The 612c8fe38aeSMatthew Dillon * maximum inaccuracy from this cause is < 10 usec on 486's. 613c8fe38aeSMatthew Dillon */ 614c8fe38aeSMatthew Dillon start_sec = sec; 615c8fe38aeSMatthew Dillon for (;;) { 616c8fe38aeSMatthew Dillon if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) 617c8fe38aeSMatthew Dillon sec = rtcin(RTC_SEC); 618c8fe38aeSMatthew Dillon count = sys_cputimer->count(); 619c8fe38aeSMatthew Dillon tot_count += (int)(count - prev_count); 620c8fe38aeSMatthew Dillon prev_count = count; 621c8fe38aeSMatthew Dillon if (sec != start_sec) 622c8fe38aeSMatthew Dillon break; 623c8fe38aeSMatthew Dillon if (--timeout == 0) 624c8fe38aeSMatthew Dillon goto fail; 625c8fe38aeSMatthew Dillon } 626c8fe38aeSMatthew Dillon 627c8fe38aeSMatthew Dillon /* 628c8fe38aeSMatthew Dillon * Read the cpu cycle counter. The timing considerations are 629c8fe38aeSMatthew Dillon * similar to those for the i8254 clock. 630c8fe38aeSMatthew Dillon */ 631c8fe38aeSMatthew Dillon if (tsc_present) { 632c8fe38aeSMatthew Dillon tsc_frequency = rdtsc() - old_tsc; 633632f4575SSepherosa Ziehau if (bootverbose) { 634632f4575SSepherosa Ziehau kprintf("TSC clock: %jd Hz (Method A)\n", 635632f4575SSepherosa Ziehau (intmax_t)tsc_frequency); 636632f4575SSepherosa Ziehau } 637c8fe38aeSMatthew Dillon } 6385b49787bSMatthew Dillon tsc_oneus_approx = ((tsc_frequency|1) + 999999) / 1000000; 639c8fe38aeSMatthew Dillon 640c8fe38aeSMatthew Dillon kprintf("i8254 clock: %u Hz\n", tot_count); 641c8fe38aeSMatthew Dillon return (tot_count); 642c8fe38aeSMatthew Dillon 643c8fe38aeSMatthew Dillon fail: 644c8fe38aeSMatthew Dillon kprintf("failed, using default i8254 clock of %u Hz\n", 645c8fe38aeSMatthew Dillon i8254_cputimer.freq); 646c8fe38aeSMatthew Dillon return (i8254_cputimer.freq); 647c8fe38aeSMatthew Dillon } 648c8fe38aeSMatthew Dillon 649c8fe38aeSMatthew Dillon static void 650c8fe38aeSMatthew Dillon i8254_restore(void) 651c8fe38aeSMatthew Dillon { 652c8fe38aeSMatthew Dillon timer0_state = ACQUIRED; 653c8fe38aeSMatthew Dillon 654c8fe38aeSMatthew Dillon clock_lock(); 655c8fe38aeSMatthew Dillon 656c8fe38aeSMatthew Dillon /* 657c8fe38aeSMatthew Dillon * Timer0 is our fine-grained variable clock interrupt 658c8fe38aeSMatthew Dillon */ 659c8fe38aeSMatthew Dillon outb(TIMER_MODE, TIMER_SEL0 | TIMER_SWSTROBE | TIMER_16BIT); 660c8fe38aeSMatthew Dillon outb(TIMER_CNTR0, 2); /* lsb */ 661c8fe38aeSMatthew Dillon outb(TIMER_CNTR0, 0); /* msb */ 662c8fe38aeSMatthew Dillon clock_unlock(); 663c8fe38aeSMatthew Dillon 66440672791SSepherosa Ziehau if (!i8254_nointr) { 66540672791SSepherosa Ziehau cputimer_intr_register(&i8254_cputimer_intr); 66640672791SSepherosa Ziehau cputimer_intr_select(&i8254_cputimer_intr, 0); 66740672791SSepherosa Ziehau } 66840672791SSepherosa Ziehau 669c8fe38aeSMatthew Dillon /* 670c8fe38aeSMatthew Dillon * Timer1 or timer2 is our free-running clock, but only if another 671c8fe38aeSMatthew Dillon * has not been selected. 672c8fe38aeSMatthew Dillon */ 673c8fe38aeSMatthew Dillon cputimer_register(&i8254_cputimer); 674c8fe38aeSMatthew Dillon cputimer_select(&i8254_cputimer, 0); 675c8fe38aeSMatthew Dillon } 676c8fe38aeSMatthew Dillon 677c8fe38aeSMatthew Dillon static void 678c8fe38aeSMatthew Dillon i8254_cputimer_construct(struct cputimer *timer, sysclock_t oldclock) 679c8fe38aeSMatthew Dillon { 680c8fe38aeSMatthew Dillon int which; 681c8fe38aeSMatthew Dillon 682c8fe38aeSMatthew Dillon /* 683c8fe38aeSMatthew Dillon * Should we use timer 1 or timer 2 ? 684c8fe38aeSMatthew Dillon */ 685c8fe38aeSMatthew Dillon which = 0; 686c8fe38aeSMatthew Dillon TUNABLE_INT_FETCH("hw.i8254.walltimer", &which); 687c8fe38aeSMatthew Dillon if (which != 1 && which != 2) 688c8fe38aeSMatthew Dillon which = 2; 689c8fe38aeSMatthew Dillon 690c8fe38aeSMatthew Dillon switch(which) { 691c8fe38aeSMatthew Dillon case 1: 692c8fe38aeSMatthew Dillon timer->name = "i8254_timer1"; 693c8fe38aeSMatthew Dillon timer->type = CPUTIMER_8254_SEL1; 694c8fe38aeSMatthew Dillon i8254_walltimer_sel = TIMER_SEL1; 695c8fe38aeSMatthew Dillon i8254_walltimer_cntr = TIMER_CNTR1; 696c8fe38aeSMatthew Dillon timer1_state = ACQUIRED; 697c8fe38aeSMatthew Dillon break; 698c8fe38aeSMatthew Dillon case 2: 699c8fe38aeSMatthew Dillon timer->name = "i8254_timer2"; 700c8fe38aeSMatthew Dillon timer->type = CPUTIMER_8254_SEL2; 701c8fe38aeSMatthew Dillon i8254_walltimer_sel = TIMER_SEL2; 702c8fe38aeSMatthew Dillon i8254_walltimer_cntr = TIMER_CNTR2; 703c8fe38aeSMatthew Dillon timer2_state = ACQUIRED; 704c8fe38aeSMatthew Dillon break; 705c8fe38aeSMatthew Dillon } 706c8fe38aeSMatthew Dillon 707c8fe38aeSMatthew Dillon timer->base = (oldclock + 0xFFFF) & ~0xFFFF; 708c8fe38aeSMatthew Dillon 709c8fe38aeSMatthew Dillon clock_lock(); 710c8fe38aeSMatthew Dillon outb(TIMER_MODE, i8254_walltimer_sel | TIMER_RATEGEN | TIMER_16BIT); 711c8fe38aeSMatthew Dillon outb(i8254_walltimer_cntr, 0); /* lsb */ 712c8fe38aeSMatthew Dillon outb(i8254_walltimer_cntr, 0); /* msb */ 713c8fe38aeSMatthew Dillon outb(IO_PPI, inb(IO_PPI) | 1); /* bit 0: enable gate, bit 1: spkr */ 714c8fe38aeSMatthew Dillon clock_unlock(); 715c8fe38aeSMatthew Dillon } 716c8fe38aeSMatthew Dillon 717c8fe38aeSMatthew Dillon static void 718c8fe38aeSMatthew Dillon i8254_cputimer_destruct(struct cputimer *timer) 719c8fe38aeSMatthew Dillon { 720c8fe38aeSMatthew Dillon switch(timer->type) { 721c8fe38aeSMatthew Dillon case CPUTIMER_8254_SEL1: 722c8fe38aeSMatthew Dillon timer1_state = RELEASED; 723c8fe38aeSMatthew Dillon break; 724c8fe38aeSMatthew Dillon case CPUTIMER_8254_SEL2: 725c8fe38aeSMatthew Dillon timer2_state = RELEASED; 726c8fe38aeSMatthew Dillon break; 727c8fe38aeSMatthew Dillon default: 728c8fe38aeSMatthew Dillon break; 729c8fe38aeSMatthew Dillon } 730c8fe38aeSMatthew Dillon timer->type = 0; 731c8fe38aeSMatthew Dillon } 732c8fe38aeSMatthew Dillon 733c8fe38aeSMatthew Dillon static void 734c8fe38aeSMatthew Dillon rtc_restore(void) 735c8fe38aeSMatthew Dillon { 736c8fe38aeSMatthew Dillon /* Restore all of the RTC's "status" (actually, control) registers. */ 737c8fe38aeSMatthew Dillon writertc(RTC_STATUSB, RTCSB_24HR); 738c8fe38aeSMatthew Dillon writertc(RTC_STATUSA, rtc_statusa); 739c8fe38aeSMatthew Dillon writertc(RTC_STATUSB, rtc_statusb); 740c8fe38aeSMatthew Dillon } 741c8fe38aeSMatthew Dillon 742c8fe38aeSMatthew Dillon /* 743c8fe38aeSMatthew Dillon * Restore all the timers. 744c8fe38aeSMatthew Dillon * 745c8fe38aeSMatthew Dillon * This function is called to resynchronize our core timekeeping after a 746c8fe38aeSMatthew Dillon * long halt, e.g. from apm_default_resume() and friends. It is also 747c8fe38aeSMatthew Dillon * called if after a BIOS call we have detected munging of the 8254. 748c8fe38aeSMatthew Dillon * It is necessary because cputimer_count() counter's delta may have grown 749c8fe38aeSMatthew Dillon * too large for nanouptime() and friends to handle, or (in the case of 8254 750c8fe38aeSMatthew Dillon * munging) might cause the SYSTIMER code to prematurely trigger. 751c8fe38aeSMatthew Dillon */ 752c8fe38aeSMatthew Dillon void 753c8fe38aeSMatthew Dillon timer_restore(void) 754c8fe38aeSMatthew Dillon { 755c8fe38aeSMatthew Dillon crit_enter(); 756c8fe38aeSMatthew Dillon i8254_restore(); /* restore timer_freq and hz */ 757c8fe38aeSMatthew Dillon rtc_restore(); /* reenable RTC interrupts */ 758c8fe38aeSMatthew Dillon crit_exit(); 759c8fe38aeSMatthew Dillon } 760c8fe38aeSMatthew Dillon 761c8fe38aeSMatthew Dillon /* 762c8fe38aeSMatthew Dillon * Initialize 8254 timer 0 early so that it can be used in DELAY(). 763c8fe38aeSMatthew Dillon */ 764c8fe38aeSMatthew Dillon void 765c8fe38aeSMatthew Dillon startrtclock(void) 766c8fe38aeSMatthew Dillon { 767c8fe38aeSMatthew Dillon u_int delta, freq; 768c8fe38aeSMatthew Dillon 769c8fe38aeSMatthew Dillon /* 770c8fe38aeSMatthew Dillon * Can we use the TSC? 7711997b4c2SMatthew Dillon * 7721997b4c2SMatthew Dillon * NOTE: If running under qemu, probably a good idea to force the 7731997b4c2SMatthew Dillon * TSC because we are not likely to detect it as being 7741997b4c2SMatthew Dillon * invariant or mpsyncd if you don't. This will greatly 7751997b4c2SMatthew Dillon * reduce SMP contention. 776c8fe38aeSMatthew Dillon */ 7775a81b19fSSepherosa Ziehau if (cpu_feature & CPUID_TSC) { 778c8fe38aeSMatthew Dillon tsc_present = 1; 7791997b4c2SMatthew Dillon TUNABLE_INT_FETCH("hw.tsc_cputimer_force", &tsc_invariant); 7801997b4c2SMatthew Dillon 7815a81b19fSSepherosa Ziehau if ((cpu_vendor_id == CPU_VENDOR_INTEL || 7825a81b19fSSepherosa Ziehau cpu_vendor_id == CPU_VENDOR_AMD) && 7835a81b19fSSepherosa Ziehau cpu_exthigh >= 0x80000007) { 7845a81b19fSSepherosa Ziehau u_int regs[4]; 7855a81b19fSSepherosa Ziehau 7865a81b19fSSepherosa Ziehau do_cpuid(0x80000007, regs); 7875a81b19fSSepherosa Ziehau if (regs[3] & 0x100) 7885a81b19fSSepherosa Ziehau tsc_invariant = 1; 7895a81b19fSSepherosa Ziehau } 7905a81b19fSSepherosa Ziehau } else { 791c8fe38aeSMatthew Dillon tsc_present = 0; 7925a81b19fSSepherosa Ziehau } 793c8fe38aeSMatthew Dillon 794c8fe38aeSMatthew Dillon /* 795c8fe38aeSMatthew Dillon * Initial RTC state, don't do anything unexpected 796c8fe38aeSMatthew Dillon */ 797c8fe38aeSMatthew Dillon writertc(RTC_STATUSA, rtc_statusa); 798c8fe38aeSMatthew Dillon writertc(RTC_STATUSB, RTCSB_24HR); 799c8fe38aeSMatthew Dillon 800c8fe38aeSMatthew Dillon /* 801c8fe38aeSMatthew Dillon * Set the 8254 timer0 in TIMER_SWSTROBE mode and cause it to 802c8fe38aeSMatthew Dillon * generate an interrupt, which we will ignore for now. 803c8fe38aeSMatthew Dillon * 804c8fe38aeSMatthew Dillon * Set the 8254 timer1 in TIMER_RATEGEN mode and load 0x0000 805c8fe38aeSMatthew Dillon * (so it counts a full 2^16 and repeats). We will use this timer 806c8fe38aeSMatthew Dillon * for our counting. 807c8fe38aeSMatthew Dillon */ 808c8fe38aeSMatthew Dillon i8254_restore(); 809242fd95fSImre Vadász 810242fd95fSImre Vadász /* 811242fd95fSImre Vadász * When booting without verbose messages, it's pointless to run the 812242fd95fSImre Vadász * calibrate_clocks() calibration code, when we don't use the 813242fd95fSImre Vadász * results in any way. With bootverbose, we are at least printing 814242fd95fSImre Vadász * this information to the kernel log. 815242fd95fSImre Vadász */ 816242fd95fSImre Vadász if (calibrate_timers_with_rtc == 0 && !bootverbose) 817242fd95fSImre Vadász goto skip_rtc_based; 818242fd95fSImre Vadász 819c8fe38aeSMatthew Dillon freq = calibrate_clocks(); 820c8fe38aeSMatthew Dillon #ifdef CLK_CALIBRATION_LOOP 821c8fe38aeSMatthew Dillon if (bootverbose) { 822ce7866b8SMatthew Dillon int c; 823ce7866b8SMatthew Dillon 824ce7866b8SMatthew Dillon cnpoll(TRUE); 825ce7866b8SMatthew Dillon kprintf("Press a key on the console to " 826ce7866b8SMatthew Dillon "abort clock calibration\n"); 827ce7866b8SMatthew Dillon while ((c = cncheckc()) == -1 || c == NOKEY) 828c8fe38aeSMatthew Dillon calibrate_clocks(); 829ce7866b8SMatthew Dillon cnpoll(FALSE); 830c8fe38aeSMatthew Dillon } 831c8fe38aeSMatthew Dillon #endif 832c8fe38aeSMatthew Dillon 833c8fe38aeSMatthew Dillon /* 834c8fe38aeSMatthew Dillon * Use the calibrated i8254 frequency if it seems reasonable. 835c8fe38aeSMatthew Dillon * Otherwise use the default, and don't use the calibrated i586 836c8fe38aeSMatthew Dillon * frequency. 837c8fe38aeSMatthew Dillon */ 838c8fe38aeSMatthew Dillon delta = freq > i8254_cputimer.freq ? 839c8fe38aeSMatthew Dillon freq - i8254_cputimer.freq : i8254_cputimer.freq - freq; 840c8fe38aeSMatthew Dillon if (delta < i8254_cputimer.freq / 100) { 841242fd95fSImre Vadász if (calibrate_timers_with_rtc == 0) { 842c8fe38aeSMatthew Dillon kprintf( 843242fd95fSImre Vadász "hw.calibrate_timers_with_rtc not set - using default i8254 frequency\n"); 844c8fe38aeSMatthew Dillon freq = i8254_cputimer.freq; 845242fd95fSImre Vadász } 84640672791SSepherosa Ziehau /* 84740672791SSepherosa Ziehau * NOTE: 84840672791SSepherosa Ziehau * Interrupt timer's freq must be adjusted 84940672791SSepherosa Ziehau * before we change the cuptimer's frequency. 85040672791SSepherosa Ziehau */ 85140672791SSepherosa Ziehau i8254_cputimer_intr.freq = freq; 852c8fe38aeSMatthew Dillon cputimer_set_frequency(&i8254_cputimer, freq); 853c8fe38aeSMatthew Dillon } else { 854c8fe38aeSMatthew Dillon if (bootverbose) 8555b49787bSMatthew Dillon kprintf("%d Hz differs from default of %d Hz " 8565b49787bSMatthew Dillon "by more than 1%%\n", 857c8fe38aeSMatthew Dillon freq, i8254_cputimer.freq); 858c8fe38aeSMatthew Dillon tsc_frequency = 0; 859c8fe38aeSMatthew Dillon } 860c8fe38aeSMatthew Dillon 861242fd95fSImre Vadász if (tsc_frequency != 0 && calibrate_timers_with_rtc == 0) { 8625b49787bSMatthew Dillon kprintf("hw.calibrate_timers_with_rtc not " 8635b49787bSMatthew Dillon "set - using old calibration method\n"); 864c8fe38aeSMatthew Dillon tsc_frequency = 0; 865c8fe38aeSMatthew Dillon } 866242fd95fSImre Vadász 867242fd95fSImre Vadász skip_rtc_based: 868c8fe38aeSMatthew Dillon if (tsc_present && tsc_frequency == 0) { 869c8fe38aeSMatthew Dillon /* 870c8fe38aeSMatthew Dillon * Calibration of the i586 clock relative to the mc146818A 871c8fe38aeSMatthew Dillon * clock failed. Do a less accurate calibration relative 872c8fe38aeSMatthew Dillon * to the i8254 clock. 873c8fe38aeSMatthew Dillon */ 874c8fe38aeSMatthew Dillon u_int64_t old_tsc = rdtsc(); 875c8fe38aeSMatthew Dillon 876c8fe38aeSMatthew Dillon DELAY(1000000); 877c8fe38aeSMatthew Dillon tsc_frequency = rdtsc() - old_tsc; 878242fd95fSImre Vadász if (bootverbose && calibrate_timers_with_rtc) { 879632f4575SSepherosa Ziehau kprintf("TSC clock: %jd Hz (Method B)\n", 880632f4575SSepherosa Ziehau (intmax_t)tsc_frequency); 881c8fe38aeSMatthew Dillon } 882c8fe38aeSMatthew Dillon } 883c8fe38aeSMatthew Dillon 884632f4575SSepherosa Ziehau if (tsc_present) { 885632f4575SSepherosa Ziehau kprintf("TSC%s clock: %jd Hz\n", 886632f4575SSepherosa Ziehau tsc_invariant ? " invariant" : "", 887632f4575SSepherosa Ziehau (intmax_t)tsc_frequency); 888632f4575SSepherosa Ziehau } 8895b49787bSMatthew Dillon tsc_oneus_approx = ((tsc_frequency|1) + 999999) / 1000000; 890632f4575SSepherosa Ziehau 8915b49787bSMatthew Dillon EVENTHANDLER_REGISTER(shutdown_post_sync, resettodr_on_shutdown, 8925b49787bSMatthew Dillon NULL, SHUTDOWN_PRI_LAST); 893c8fe38aeSMatthew Dillon } 894c8fe38aeSMatthew Dillon 895c8fe38aeSMatthew Dillon /* 896c8fe38aeSMatthew Dillon * Sync the time of day back to the RTC on shutdown, but only if 897c8fe38aeSMatthew Dillon * we have already loaded it and have not crashed. 898c8fe38aeSMatthew Dillon */ 899c8fe38aeSMatthew Dillon static void 900c8fe38aeSMatthew Dillon resettodr_on_shutdown(void *arg __unused) 901c8fe38aeSMatthew Dillon { 902c8fe38aeSMatthew Dillon if (rtc_loaded && panicstr == NULL) { 903c8fe38aeSMatthew Dillon resettodr(); 904c8fe38aeSMatthew Dillon } 905c8fe38aeSMatthew Dillon } 906c8fe38aeSMatthew Dillon 907c8fe38aeSMatthew Dillon /* 908c8fe38aeSMatthew Dillon * Initialize the time of day register, based on the time base which is, e.g. 909c8fe38aeSMatthew Dillon * from a filesystem. 910c8fe38aeSMatthew Dillon */ 911c8fe38aeSMatthew Dillon void 912c8fe38aeSMatthew Dillon inittodr(time_t base) 913c8fe38aeSMatthew Dillon { 914c8fe38aeSMatthew Dillon unsigned long sec, days; 915c8fe38aeSMatthew Dillon int year, month; 916c8fe38aeSMatthew Dillon int y, m; 917c8fe38aeSMatthew Dillon struct timespec ts; 918c8fe38aeSMatthew Dillon 919c8fe38aeSMatthew Dillon if (base) { 920c8fe38aeSMatthew Dillon ts.tv_sec = base; 921c8fe38aeSMatthew Dillon ts.tv_nsec = 0; 922c8fe38aeSMatthew Dillon set_timeofday(&ts); 923c8fe38aeSMatthew Dillon } 924c8fe38aeSMatthew Dillon 925c8fe38aeSMatthew Dillon /* Look if we have a RTC present and the time is valid */ 926c8fe38aeSMatthew Dillon if (!(rtcin(RTC_STATUSD) & RTCSD_PWR)) 927c8fe38aeSMatthew Dillon goto wrong_time; 928c8fe38aeSMatthew Dillon 929c8fe38aeSMatthew Dillon /* wait for time update to complete */ 930c8fe38aeSMatthew Dillon /* If RTCSA_TUP is zero, we have at least 244us before next update */ 931c8fe38aeSMatthew Dillon crit_enter(); 932c8fe38aeSMatthew Dillon while (rtcin(RTC_STATUSA) & RTCSA_TUP) { 933c8fe38aeSMatthew Dillon crit_exit(); 934c8fe38aeSMatthew Dillon crit_enter(); 935c8fe38aeSMatthew Dillon } 936c8fe38aeSMatthew Dillon 937c8fe38aeSMatthew Dillon days = 0; 938c8fe38aeSMatthew Dillon #ifdef USE_RTC_CENTURY 939c8fe38aeSMatthew Dillon year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100; 940c8fe38aeSMatthew Dillon #else 941c8fe38aeSMatthew Dillon year = readrtc(RTC_YEAR) + 1900; 942c8fe38aeSMatthew Dillon if (year < 1970) 943c8fe38aeSMatthew Dillon year += 100; 944c8fe38aeSMatthew Dillon #endif 945c8fe38aeSMatthew Dillon if (year < 1970) { 946c8fe38aeSMatthew Dillon crit_exit(); 947c8fe38aeSMatthew Dillon goto wrong_time; 948c8fe38aeSMatthew Dillon } 949c8fe38aeSMatthew Dillon month = readrtc(RTC_MONTH); 950c8fe38aeSMatthew Dillon for (m = 1; m < month; m++) 951c8fe38aeSMatthew Dillon days += daysinmonth[m-1]; 952c8fe38aeSMatthew Dillon if ((month > 2) && LEAPYEAR(year)) 953c8fe38aeSMatthew Dillon days ++; 954c8fe38aeSMatthew Dillon days += readrtc(RTC_DAY) - 1; 955c8fe38aeSMatthew Dillon for (y = 1970; y < year; y++) 956c8fe38aeSMatthew Dillon days += DAYSPERYEAR + LEAPYEAR(y); 957c8fe38aeSMatthew Dillon sec = ((( days * 24 + 958c8fe38aeSMatthew Dillon readrtc(RTC_HRS)) * 60 + 959c8fe38aeSMatthew Dillon readrtc(RTC_MIN)) * 60 + 960c8fe38aeSMatthew Dillon readrtc(RTC_SEC)); 961c8fe38aeSMatthew Dillon /* sec now contains the number of seconds, since Jan 1 1970, 962c8fe38aeSMatthew Dillon in the local time zone */ 963c8fe38aeSMatthew Dillon 964c8fe38aeSMatthew Dillon sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); 965c8fe38aeSMatthew Dillon 966cec73927SMatthew Dillon y = (int)(time_second - sec); 967c8fe38aeSMatthew Dillon if (y <= -2 || y >= 2) { 968c8fe38aeSMatthew Dillon /* badly off, adjust it */ 969c8fe38aeSMatthew Dillon ts.tv_sec = sec; 970c8fe38aeSMatthew Dillon ts.tv_nsec = 0; 971c8fe38aeSMatthew Dillon set_timeofday(&ts); 972c8fe38aeSMatthew Dillon } 973c8fe38aeSMatthew Dillon rtc_loaded = 1; 974c8fe38aeSMatthew Dillon crit_exit(); 975c8fe38aeSMatthew Dillon return; 976c8fe38aeSMatthew Dillon 977c8fe38aeSMatthew Dillon wrong_time: 978c8fe38aeSMatthew Dillon kprintf("Invalid time in real time clock.\n"); 979c8fe38aeSMatthew Dillon kprintf("Check and reset the date immediately!\n"); 980c8fe38aeSMatthew Dillon } 981c8fe38aeSMatthew Dillon 982c8fe38aeSMatthew Dillon /* 983c8fe38aeSMatthew Dillon * Write system time back to RTC 984c8fe38aeSMatthew Dillon */ 985c8fe38aeSMatthew Dillon void 986c8fe38aeSMatthew Dillon resettodr(void) 987c8fe38aeSMatthew Dillon { 988c8fe38aeSMatthew Dillon struct timeval tv; 989c8fe38aeSMatthew Dillon unsigned long tm; 990c8fe38aeSMatthew Dillon int m; 991c8fe38aeSMatthew Dillon int y; 992c8fe38aeSMatthew Dillon 993c8fe38aeSMatthew Dillon if (disable_rtc_set) 994c8fe38aeSMatthew Dillon return; 995c8fe38aeSMatthew Dillon 996c8fe38aeSMatthew Dillon microtime(&tv); 997c8fe38aeSMatthew Dillon tm = tv.tv_sec; 998c8fe38aeSMatthew Dillon 999c8fe38aeSMatthew Dillon crit_enter(); 1000c8fe38aeSMatthew Dillon /* Disable RTC updates and interrupts. */ 1001c8fe38aeSMatthew Dillon writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); 1002c8fe38aeSMatthew Dillon 1003c8fe38aeSMatthew Dillon /* Calculate local time to put in RTC */ 1004c8fe38aeSMatthew Dillon 1005c8fe38aeSMatthew Dillon tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); 1006c8fe38aeSMatthew Dillon 1007c8fe38aeSMatthew Dillon writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */ 1008c8fe38aeSMatthew Dillon writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */ 1009c8fe38aeSMatthew Dillon writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */ 1010c8fe38aeSMatthew Dillon 1011c8fe38aeSMatthew Dillon /* We have now the days since 01-01-1970 in tm */ 1012c8fe38aeSMatthew Dillon writertc(RTC_WDAY, (tm+4)%7); /* Write back Weekday */ 1013c8fe38aeSMatthew Dillon for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y); 1014c8fe38aeSMatthew Dillon tm >= m; 1015c8fe38aeSMatthew Dillon y++, m = DAYSPERYEAR + LEAPYEAR(y)) 1016c8fe38aeSMatthew Dillon tm -= m; 1017c8fe38aeSMatthew Dillon 1018c8fe38aeSMatthew Dillon /* Now we have the years in y and the day-of-the-year in tm */ 1019c8fe38aeSMatthew Dillon writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */ 1020c8fe38aeSMatthew Dillon #ifdef USE_RTC_CENTURY 1021c8fe38aeSMatthew Dillon writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */ 1022c8fe38aeSMatthew Dillon #endif 1023c8fe38aeSMatthew Dillon for (m = 0; ; m++) { 1024c8fe38aeSMatthew Dillon int ml; 1025c8fe38aeSMatthew Dillon 1026c8fe38aeSMatthew Dillon ml = daysinmonth[m]; 1027c8fe38aeSMatthew Dillon if (m == 1 && LEAPYEAR(y)) 1028c8fe38aeSMatthew Dillon ml++; 1029c8fe38aeSMatthew Dillon if (tm < ml) 1030c8fe38aeSMatthew Dillon break; 1031c8fe38aeSMatthew Dillon tm -= ml; 1032c8fe38aeSMatthew Dillon } 1033c8fe38aeSMatthew Dillon 1034c8fe38aeSMatthew Dillon writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */ 1035c8fe38aeSMatthew Dillon writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */ 1036c8fe38aeSMatthew Dillon 1037c8fe38aeSMatthew Dillon /* Reenable RTC updates and interrupts. */ 1038c8fe38aeSMatthew Dillon writertc(RTC_STATUSB, rtc_statusb); 1039c8fe38aeSMatthew Dillon crit_exit(); 1040c8fe38aeSMatthew Dillon } 1041c8fe38aeSMatthew Dillon 10426b809ec7SSepherosa Ziehau static int 10436b809ec7SSepherosa Ziehau i8254_ioapic_trial(int irq, struct cputimer_intr *cti) 10446b809ec7SSepherosa Ziehau { 10456b809ec7SSepherosa Ziehau sysclock_t base; 10466b809ec7SSepherosa Ziehau long lastcnt; 10476b809ec7SSepherosa Ziehau 10486b809ec7SSepherosa Ziehau /* 10496b809ec7SSepherosa Ziehau * Following code assumes the 8254 is the cpu timer, 10506b809ec7SSepherosa Ziehau * so make sure it is. 10516b809ec7SSepherosa Ziehau */ 10526b809ec7SSepherosa Ziehau KKASSERT(sys_cputimer == &i8254_cputimer); 10536b809ec7SSepherosa Ziehau KKASSERT(cti == &i8254_cputimer_intr); 10546b809ec7SSepherosa Ziehau 1055c83c147eSSepherosa Ziehau lastcnt = get_interrupt_counter(irq, mycpuid); 10566b809ec7SSepherosa Ziehau 10576b809ec7SSepherosa Ziehau /* 10586b809ec7SSepherosa Ziehau * Force an 8254 Timer0 interrupt and wait 1/100s for 10596b809ec7SSepherosa Ziehau * it to happen, then see if we got it. 10606b809ec7SSepherosa Ziehau */ 10616b809ec7SSepherosa Ziehau kprintf("IOAPIC: testing 8254 interrupt delivery\n"); 10626b809ec7SSepherosa Ziehau 10636b809ec7SSepherosa Ziehau i8254_intr_reload(cti, 2); 10646b809ec7SSepherosa Ziehau base = sys_cputimer->count(); 10656b809ec7SSepherosa Ziehau while (sys_cputimer->count() - base < sys_cputimer->freq / 100) 10666b809ec7SSepherosa Ziehau ; /* nothing */ 10676b809ec7SSepherosa Ziehau 1068c83c147eSSepherosa Ziehau if (get_interrupt_counter(irq, mycpuid) - lastcnt == 0) 10696b809ec7SSepherosa Ziehau return ENOENT; 10706b809ec7SSepherosa Ziehau return 0; 10716b809ec7SSepherosa Ziehau } 10726b809ec7SSepherosa Ziehau 1073adc34348SSepherosa Ziehau /* 1074adc34348SSepherosa Ziehau * Start both clocks running. DragonFly note: the stat clock is no longer 1075adc34348SSepherosa Ziehau * used. Instead, 8254 based systimers are used for all major clock 1076d426f67aSSepherosa Ziehau * interrupts. 1077adc34348SSepherosa Ziehau */ 107840672791SSepherosa Ziehau static void 1079adc34348SSepherosa Ziehau i8254_intr_initclock(struct cputimer_intr *cti, boolean_t selected) 1080c8fe38aeSMatthew Dillon { 1081c79ae131SSascha Wildner void *clkdesc = NULL; 10826b809ec7SSepherosa Ziehau int irq = 0, mixed_mode = 0, error; 1083adc34348SSepherosa Ziehau 10846355d931SSepherosa Ziehau KKASSERT(mycpuid == 0); 1085bf0ecf68SMatthew Dillon callout_init_mp(&sysbeepstop_ch); 1086c8fe38aeSMatthew Dillon 10876b809ec7SSepherosa Ziehau if (!selected && i8254_intr_disable) 10886b809ec7SSepherosa Ziehau goto nointr; 1089adc34348SSepherosa Ziehau 1090c8fe38aeSMatthew Dillon /* 1091c8fe38aeSMatthew Dillon * The stat interrupt mask is different without the 1092c8fe38aeSMatthew Dillon * statistics clock. Also, don't set the interrupt 1093c8fe38aeSMatthew Dillon * flag which would normally cause the RTC to generate 1094c8fe38aeSMatthew Dillon * interrupts. 1095c8fe38aeSMatthew Dillon */ 1096c8fe38aeSMatthew Dillon rtc_statusb = RTCSB_24HR; 1097c8fe38aeSMatthew Dillon 1098da23a592SMatthew Dillon /* Finish initializing 8254 timer 0. */ 1099f45bfca0SSepherosa Ziehau if (ioapic_enable) { 110086d692feSSepherosa Ziehau irq = machintr_legacy_intr_find(0, INTR_TRIGGER_EDGE, 11016b809ec7SSepherosa Ziehau INTR_POLARITY_HIGH); 11026b809ec7SSepherosa Ziehau if (irq < 0) { 11036b809ec7SSepherosa Ziehau mixed_mode_setup: 1104027bbbfeSSepherosa Ziehau error = ioapic_conf_legacy_extint(0); 11056b809ec7SSepherosa Ziehau if (!error) { 110686d692feSSepherosa Ziehau irq = machintr_legacy_intr_find(0, 1107027bbbfeSSepherosa Ziehau INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH); 11086b809ec7SSepherosa Ziehau if (irq < 0) 11096b809ec7SSepherosa Ziehau error = ENOENT; 11106b809ec7SSepherosa Ziehau } 11116b809ec7SSepherosa Ziehau 11126b809ec7SSepherosa Ziehau if (error) { 11136b809ec7SSepherosa Ziehau if (!selected) { 11146b809ec7SSepherosa Ziehau kprintf("IOAPIC: setup mixed mode for " 11156b809ec7SSepherosa Ziehau "irq 0 failed: %d\n", error); 11166b809ec7SSepherosa Ziehau goto nointr; 11176b809ec7SSepherosa Ziehau } else { 11186b809ec7SSepherosa Ziehau panic("IOAPIC: setup mixed mode for " 11196b809ec7SSepherosa Ziehau "irq 0 failed: %d\n", error); 11206b809ec7SSepherosa Ziehau } 11216b809ec7SSepherosa Ziehau } 11226b809ec7SSepherosa Ziehau mixed_mode = 1; 11236b809ec7SSepherosa Ziehau } 11246b809ec7SSepherosa Ziehau clkdesc = register_int(irq, clkintr, NULL, "clk", 11256b809ec7SSepherosa Ziehau NULL, 11266b809ec7SSepherosa Ziehau INTR_EXCL | INTR_CLOCK | 11276b809ec7SSepherosa Ziehau INTR_NOPOLL | INTR_MPSAFE | 11286355d931SSepherosa Ziehau INTR_NOENTROPY, 0); 1129faaf4131SMichael Neumann } else { 1130adc34348SSepherosa Ziehau register_int(0, clkintr, NULL, "clk", NULL, 1131f8a09be1SMatthew Dillon INTR_EXCL | INTR_CLOCK | 1132adc34348SSepherosa Ziehau INTR_NOPOLL | INTR_MPSAFE | 11336355d931SSepherosa Ziehau INTR_NOENTROPY, 0); 1134faaf4131SMichael Neumann } 1135adc34348SSepherosa Ziehau 1136adc34348SSepherosa Ziehau /* Initialize RTC. */ 1137adc34348SSepherosa Ziehau writertc(RTC_STATUSA, rtc_statusa); 1138adc34348SSepherosa Ziehau writertc(RTC_STATUSB, RTCSB_24HR); 1139adc34348SSepherosa Ziehau 1140f45bfca0SSepherosa Ziehau if (ioapic_enable) { 11416b809ec7SSepherosa Ziehau error = i8254_ioapic_trial(irq, cti); 11426b809ec7SSepherosa Ziehau if (error) { 11436b809ec7SSepherosa Ziehau if (mixed_mode) { 11446b809ec7SSepherosa Ziehau if (!selected) { 11456b809ec7SSepherosa Ziehau kprintf("IOAPIC: mixed mode for irq %d " 11467a603b36SSepherosa Ziehau "trial failed: %d\n", 11477a603b36SSepherosa Ziehau irq, error); 11486b809ec7SSepherosa Ziehau goto nointr; 11496b809ec7SSepherosa Ziehau } else { 11506b809ec7SSepherosa Ziehau panic("IOAPIC: mixed mode for irq %d " 11516b809ec7SSepherosa Ziehau "trial failed: %d\n", irq, error); 11526b809ec7SSepherosa Ziehau } 11536b809ec7SSepherosa Ziehau } else { 11546b809ec7SSepherosa Ziehau kprintf("IOAPIC: warning 8254 is not connected " 11556b809ec7SSepherosa Ziehau "to the correct pin, try mixed mode\n"); 11566355d931SSepherosa Ziehau unregister_int(clkdesc, 0); 11576b809ec7SSepherosa Ziehau goto mixed_mode_setup; 11586b809ec7SSepherosa Ziehau } 11596b809ec7SSepherosa Ziehau } 1160faaf4131SMichael Neumann } 11616b809ec7SSepherosa Ziehau return; 11626b809ec7SSepherosa Ziehau 11636b809ec7SSepherosa Ziehau nointr: 11646b809ec7SSepherosa Ziehau i8254_nointr = 1; /* don't try to register again */ 11656b809ec7SSepherosa Ziehau cputimer_intr_deregister(cti); 1166c8fe38aeSMatthew Dillon } 1167c8fe38aeSMatthew Dillon 1168c8fe38aeSMatthew Dillon void 1169c8fe38aeSMatthew Dillon setstatclockrate(int newhz) 1170c8fe38aeSMatthew Dillon { 1171c8fe38aeSMatthew Dillon if (newhz == RTC_PROFRATE) 1172c8fe38aeSMatthew Dillon rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF; 1173c8fe38aeSMatthew Dillon else 1174c8fe38aeSMatthew Dillon rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; 1175c8fe38aeSMatthew Dillon writertc(RTC_STATUSA, rtc_statusa); 1176c8fe38aeSMatthew Dillon } 1177c8fe38aeSMatthew Dillon 1178c8fe38aeSMatthew Dillon #if 0 1179c8fe38aeSMatthew Dillon static unsigned 1180c8fe38aeSMatthew Dillon tsc_get_timecount(struct timecounter *tc) 1181c8fe38aeSMatthew Dillon { 1182c8fe38aeSMatthew Dillon return (rdtsc()); 1183c8fe38aeSMatthew Dillon } 1184c8fe38aeSMatthew Dillon #endif 1185c8fe38aeSMatthew Dillon 1186c8fe38aeSMatthew Dillon #ifdef KERN_TIMESTAMP 1187c8fe38aeSMatthew Dillon #define KERN_TIMESTAMP_SIZE 16384 1188c8fe38aeSMatthew Dillon static u_long tsc[KERN_TIMESTAMP_SIZE] ; 1189c8fe38aeSMatthew Dillon SYSCTL_OPAQUE(_debug, OID_AUTO, timestamp, CTLFLAG_RD, tsc, 1190c8fe38aeSMatthew Dillon sizeof(tsc), "LU", "Kernel timestamps"); 1191c8fe38aeSMatthew Dillon void 1192c8fe38aeSMatthew Dillon _TSTMP(u_int32_t x) 1193c8fe38aeSMatthew Dillon { 1194c8fe38aeSMatthew Dillon static int i; 1195c8fe38aeSMatthew Dillon 1196c8fe38aeSMatthew Dillon tsc[i] = (u_int32_t)rdtsc(); 1197c8fe38aeSMatthew Dillon tsc[i+1] = x; 1198c8fe38aeSMatthew Dillon i = i + 2; 1199c8fe38aeSMatthew Dillon if (i >= KERN_TIMESTAMP_SIZE) 1200c8fe38aeSMatthew Dillon i = 0; 1201c8fe38aeSMatthew Dillon tsc[i] = 0; /* mark last entry */ 1202c8fe38aeSMatthew Dillon } 1203c8fe38aeSMatthew Dillon #endif /* KERN_TIMESTAMP */ 1204c8fe38aeSMatthew Dillon 1205c8fe38aeSMatthew Dillon /* 1206c8fe38aeSMatthew Dillon * 1207c8fe38aeSMatthew Dillon */ 1208c8fe38aeSMatthew Dillon 1209c8fe38aeSMatthew Dillon static int 1210c8fe38aeSMatthew Dillon hw_i8254_timestamp(SYSCTL_HANDLER_ARGS) 1211c8fe38aeSMatthew Dillon { 1212c8fe38aeSMatthew Dillon sysclock_t count; 1213e28c8ef4SSascha Wildner uint64_t tscval; 1214c8fe38aeSMatthew Dillon char buf[32]; 1215c8fe38aeSMatthew Dillon 1216c8fe38aeSMatthew Dillon crit_enter(); 1217c8fe38aeSMatthew Dillon if (sys_cputimer == &i8254_cputimer) 1218c8fe38aeSMatthew Dillon count = sys_cputimer->count(); 1219c8fe38aeSMatthew Dillon else 1220c8fe38aeSMatthew Dillon count = 0; 1221c8fe38aeSMatthew Dillon if (tsc_present) 1222c8fe38aeSMatthew Dillon tscval = rdtsc(); 1223c8fe38aeSMatthew Dillon else 1224c8fe38aeSMatthew Dillon tscval = 0; 1225c8fe38aeSMatthew Dillon crit_exit(); 1226c8fe38aeSMatthew Dillon ksnprintf(buf, sizeof(buf), "%08x %016llx", count, (long long)tscval); 1227c8fe38aeSMatthew Dillon return(SYSCTL_OUT(req, buf, strlen(buf) + 1)); 1228c8fe38aeSMatthew Dillon } 1229c8fe38aeSMatthew Dillon 123000ec69c7SSepherosa Ziehau struct tsc_mpsync_arg { 123100ec69c7SSepherosa Ziehau volatile uint64_t tsc_target; 123200ec69c7SSepherosa Ziehau volatile int tsc_mpsync; 123300ec69c7SSepherosa Ziehau }; 123400ec69c7SSepherosa Ziehau 123500ec69c7SSepherosa Ziehau struct tsc_mpsync_thr { 123600ec69c7SSepherosa Ziehau volatile int tsc_done_cnt; 123700ec69c7SSepherosa Ziehau volatile int tsc_mpsync_cnt; 123800ec69c7SSepherosa Ziehau }; 1239dda44f1eSSepherosa Ziehau 1240dda44f1eSSepherosa Ziehau static void 124100ec69c7SSepherosa Ziehau tsc_mpsync_test_remote(void *xarg) 1242dda44f1eSSepherosa Ziehau { 124300ec69c7SSepherosa Ziehau struct tsc_mpsync_arg *arg = xarg; 1244dda44f1eSSepherosa Ziehau uint64_t tsc; 1245dda44f1eSSepherosa Ziehau 1246ea9728caSSepherosa Ziehau tsc = rdtsc_ordered(); 124700ec69c7SSepherosa Ziehau if (tsc < arg->tsc_target) 124800ec69c7SSepherosa Ziehau arg->tsc_mpsync = 0; 124900ec69c7SSepherosa Ziehau } 125000ec69c7SSepherosa Ziehau 125100ec69c7SSepherosa Ziehau static void 125200ec69c7SSepherosa Ziehau tsc_mpsync_test_loop(struct tsc_mpsync_arg *arg) 125300ec69c7SSepherosa Ziehau { 125400ec69c7SSepherosa Ziehau struct globaldata *gd = mycpu; 12555b49787bSMatthew Dillon tsc_uclock_t test_end, test_begin; 125600ec69c7SSepherosa Ziehau u_int i; 125700ec69c7SSepherosa Ziehau 125800ec69c7SSepherosa Ziehau if (bootverbose) { 125900ec69c7SSepherosa Ziehau kprintf("cpu%d: TSC testing MP synchronization ...\n", 126000ec69c7SSepherosa Ziehau gd->gd_cpuid); 126100ec69c7SSepherosa Ziehau } 126200ec69c7SSepherosa Ziehau 1263ea9728caSSepherosa Ziehau test_begin = rdtsc_ordered(); 126400ec69c7SSepherosa Ziehau /* Run test for 100ms */ 126500ec69c7SSepherosa Ziehau test_end = test_begin + (tsc_frequency / 10); 126600ec69c7SSepherosa Ziehau 126700ec69c7SSepherosa Ziehau arg->tsc_mpsync = 1; 126800ec69c7SSepherosa Ziehau arg->tsc_target = test_begin; 126900ec69c7SSepherosa Ziehau 127000ec69c7SSepherosa Ziehau #define TSC_TEST_TRYMAX 1000000 /* Make sure we could stop */ 127100ec69c7SSepherosa Ziehau #define TSC_TEST_TRYMIN 50000 127200ec69c7SSepherosa Ziehau 127300ec69c7SSepherosa Ziehau for (i = 0; i < TSC_TEST_TRYMAX; ++i) { 127400ec69c7SSepherosa Ziehau struct lwkt_cpusync cs; 127500ec69c7SSepherosa Ziehau 127600ec69c7SSepherosa Ziehau crit_enter(); 127700ec69c7SSepherosa Ziehau lwkt_cpusync_init(&cs, gd->gd_other_cpus, 127800ec69c7SSepherosa Ziehau tsc_mpsync_test_remote, arg); 127900ec69c7SSepherosa Ziehau lwkt_cpusync_interlock(&cs); 1280ea9728caSSepherosa Ziehau arg->tsc_target = rdtsc_ordered(); 128100ec69c7SSepherosa Ziehau cpu_mfence(); 128200ec69c7SSepherosa Ziehau lwkt_cpusync_deinterlock(&cs); 128300ec69c7SSepherosa Ziehau crit_exit(); 128400ec69c7SSepherosa Ziehau 128500ec69c7SSepherosa Ziehau if (!arg->tsc_mpsync) { 128600ec69c7SSepherosa Ziehau kprintf("cpu%d: TSC is not MP synchronized @%u\n", 128700ec69c7SSepherosa Ziehau gd->gd_cpuid, i); 128800ec69c7SSepherosa Ziehau break; 128900ec69c7SSepherosa Ziehau } 129000ec69c7SSepherosa Ziehau if (arg->tsc_target > test_end && i >= TSC_TEST_TRYMIN) 129100ec69c7SSepherosa Ziehau break; 129200ec69c7SSepherosa Ziehau } 129300ec69c7SSepherosa Ziehau 129400ec69c7SSepherosa Ziehau #undef TSC_TEST_TRYMIN 129500ec69c7SSepherosa Ziehau #undef TSC_TEST_TRYMAX 129600ec69c7SSepherosa Ziehau 129700ec69c7SSepherosa Ziehau if (arg->tsc_target == test_begin) { 129800ec69c7SSepherosa Ziehau kprintf("cpu%d: TSC does not tick?!\n", gd->gd_cpuid); 129900ec69c7SSepherosa Ziehau /* XXX disable TSC? */ 130000ec69c7SSepherosa Ziehau tsc_invariant = 0; 130100ec69c7SSepherosa Ziehau arg->tsc_mpsync = 0; 130200ec69c7SSepherosa Ziehau return; 130300ec69c7SSepherosa Ziehau } 130400ec69c7SSepherosa Ziehau 130500ec69c7SSepherosa Ziehau if (arg->tsc_mpsync && bootverbose) { 130600ec69c7SSepherosa Ziehau kprintf("cpu%d: TSC is MP synchronized after %u tries\n", 130700ec69c7SSepherosa Ziehau gd->gd_cpuid, i); 130800ec69c7SSepherosa Ziehau } 130900ec69c7SSepherosa Ziehau } 131000ec69c7SSepherosa Ziehau 131100ec69c7SSepherosa Ziehau static void 131200ec69c7SSepherosa Ziehau tsc_mpsync_ap_thread(void *xthr) 131300ec69c7SSepherosa Ziehau { 131400ec69c7SSepherosa Ziehau struct tsc_mpsync_thr *thr = xthr; 131500ec69c7SSepherosa Ziehau struct tsc_mpsync_arg arg; 131600ec69c7SSepherosa Ziehau 131700ec69c7SSepherosa Ziehau tsc_mpsync_test_loop(&arg); 131800ec69c7SSepherosa Ziehau if (arg.tsc_mpsync) { 131900ec69c7SSepherosa Ziehau atomic_add_int(&thr->tsc_mpsync_cnt, 1); 132000ec69c7SSepherosa Ziehau cpu_sfence(); 132100ec69c7SSepherosa Ziehau } 132200ec69c7SSepherosa Ziehau atomic_add_int(&thr->tsc_done_cnt, 1); 132300ec69c7SSepherosa Ziehau 132400ec69c7SSepherosa Ziehau lwkt_exit(); 1325dda44f1eSSepherosa Ziehau } 1326dda44f1eSSepherosa Ziehau 1327dda44f1eSSepherosa Ziehau static void 1328dda44f1eSSepherosa Ziehau tsc_mpsync_test(void) 1329dda44f1eSSepherosa Ziehau { 133000ec69c7SSepherosa Ziehau struct tsc_mpsync_arg arg; 1331dda44f1eSSepherosa Ziehau 1332dda44f1eSSepherosa Ziehau if (!tsc_invariant) { 1333dda44f1eSSepherosa Ziehau /* Not even invariant TSC */ 1334dda44f1eSSepherosa Ziehau return; 1335dda44f1eSSepherosa Ziehau } 1336dda44f1eSSepherosa Ziehau 1337dda44f1eSSepherosa Ziehau if (ncpus == 1) { 1338dda44f1eSSepherosa Ziehau /* Only one CPU */ 1339dda44f1eSSepherosa Ziehau tsc_mpsync = 1; 1340dda44f1eSSepherosa Ziehau return; 1341dda44f1eSSepherosa Ziehau } 1342dda44f1eSSepherosa Ziehau 13431997b4c2SMatthew Dillon /* 13441997b4c2SMatthew Dillon * Forcing can be used w/qemu to reduce contention 13451997b4c2SMatthew Dillon */ 13461997b4c2SMatthew Dillon TUNABLE_INT_FETCH("hw.tsc_cputimer_force", &tsc_mpsync); 134779c04d9cSMatthew Dillon 134879c04d9cSMatthew Dillon if (tsc_mpsync == 0) { 134979c04d9cSMatthew Dillon switch (cpu_vendor_id) { 135079c04d9cSMatthew Dillon case CPU_VENDOR_INTEL: 135179c04d9cSMatthew Dillon /* 135279c04d9cSMatthew Dillon * Intel probably works 135379c04d9cSMatthew Dillon */ 135479c04d9cSMatthew Dillon break; 1355*33bb59d9SSepherosa Ziehau 135679c04d9cSMatthew Dillon case CPU_VENDOR_AMD: 135779c04d9cSMatthew Dillon /* 135879c04d9cSMatthew Dillon * AMD < Ryzen probably doesn't work 135979c04d9cSMatthew Dillon */ 136079c04d9cSMatthew Dillon if (CPUID_TO_FAMILY(cpu_id) < 0x17) 136179c04d9cSMatthew Dillon return; 136279c04d9cSMatthew Dillon break; 1363*33bb59d9SSepherosa Ziehau 136479c04d9cSMatthew Dillon default: 136579c04d9cSMatthew Dillon /* probably won't work */ 13661997b4c2SMatthew Dillon return; 13671997b4c2SMatthew Dillon } 1368*33bb59d9SSepherosa Ziehau } else if (tsc_mpsync < 0) { 1369*33bb59d9SSepherosa Ziehau kprintf("TSC MP synchronization test is disabled\n"); 1370*33bb59d9SSepherosa Ziehau tsc_mpsync = 0; 1371*33bb59d9SSepherosa Ziehau return; 1372dda44f1eSSepherosa Ziehau } 1373dda44f1eSSepherosa Ziehau 137479c04d9cSMatthew Dillon /* 137579c04d9cSMatthew Dillon * Test even if forced above. If forced, we will use the TSC 137679c04d9cSMatthew Dillon * even if the test fails. 137779c04d9cSMatthew Dillon */ 1378dda44f1eSSepherosa Ziehau kprintf("TSC testing MP synchronization ...\n"); 137900ec69c7SSepherosa Ziehau 138000ec69c7SSepherosa Ziehau tsc_mpsync_test_loop(&arg); 138100ec69c7SSepherosa Ziehau if (arg.tsc_mpsync) { 138200ec69c7SSepherosa Ziehau struct tsc_mpsync_thr thr; 138300ec69c7SSepherosa Ziehau int cpu; 138400ec69c7SSepherosa Ziehau 138500ec69c7SSepherosa Ziehau /* 138600ec69c7SSepherosa Ziehau * Test TSC MP synchronization on APs. 138700ec69c7SSepherosa Ziehau */ 138800ec69c7SSepherosa Ziehau 138900ec69c7SSepherosa Ziehau thr.tsc_done_cnt = 1; 139000ec69c7SSepherosa Ziehau thr.tsc_mpsync_cnt = 1; 139100ec69c7SSepherosa Ziehau 139200ec69c7SSepherosa Ziehau for (cpu = 0; cpu < ncpus; ++cpu) { 139300ec69c7SSepherosa Ziehau if (cpu == mycpuid) 139400ec69c7SSepherosa Ziehau continue; 139500ec69c7SSepherosa Ziehau 139600ec69c7SSepherosa Ziehau lwkt_create(tsc_mpsync_ap_thread, &thr, NULL, 139700ec69c7SSepherosa Ziehau NULL, 0, cpu, "tsc mpsync %d", cpu); 139800ec69c7SSepherosa Ziehau } 139900ec69c7SSepherosa Ziehau 140000ec69c7SSepherosa Ziehau while (thr.tsc_done_cnt != ncpus) { 140100ec69c7SSepherosa Ziehau cpu_pause(); 140200ec69c7SSepherosa Ziehau cpu_lfence(); 140300ec69c7SSepherosa Ziehau } 140400ec69c7SSepherosa Ziehau if (thr.tsc_mpsync_cnt == ncpus) 1405dda44f1eSSepherosa Ziehau tsc_mpsync = 1; 1406dda44f1eSSepherosa Ziehau } 1407dda44f1eSSepherosa Ziehau 140800ec69c7SSepherosa Ziehau if (tsc_mpsync) 140900ec69c7SSepherosa Ziehau kprintf("TSC is MP synchronized\n"); 141000ec69c7SSepherosa Ziehau else 141100ec69c7SSepherosa Ziehau kprintf("TSC is not MP synchronized\n"); 1412dda44f1eSSepherosa Ziehau } 1413dda44f1eSSepherosa Ziehau SYSINIT(tsc_mpsync, SI_BOOT2_FINISH_SMP, SI_ORDER_ANY, tsc_mpsync_test, NULL); 1414dda44f1eSSepherosa Ziehau 14158d23b56cSSepherosa Ziehau #define TSC_CPUTIMER_FREQMAX 128000000 /* 128Mhz */ 14168d23b56cSSepherosa Ziehau 14178d23b56cSSepherosa Ziehau static int tsc_cputimer_shift; 14188d23b56cSSepherosa Ziehau 14198d23b56cSSepherosa Ziehau static void 14208d23b56cSSepherosa Ziehau tsc_cputimer_construct(struct cputimer *timer, sysclock_t oldclock) 14218d23b56cSSepherosa Ziehau { 14228d23b56cSSepherosa Ziehau timer->base = 0; 142359878316SSepherosa Ziehau timer->base = oldclock - timer->count(); 14248d23b56cSSepherosa Ziehau } 14258d23b56cSSepherosa Ziehau 142659878316SSepherosa Ziehau static __inline sysclock_t 14278d23b56cSSepherosa Ziehau tsc_cputimer_count(void) 14288d23b56cSSepherosa Ziehau { 14298d23b56cSSepherosa Ziehau uint64_t tsc; 14308d23b56cSSepherosa Ziehau 14318d23b56cSSepherosa Ziehau tsc = rdtsc(); 14328d23b56cSSepherosa Ziehau tsc >>= tsc_cputimer_shift; 14338d23b56cSSepherosa Ziehau 14348d23b56cSSepherosa Ziehau return (tsc + tsc_cputimer.base); 14358d23b56cSSepherosa Ziehau } 14368d23b56cSSepherosa Ziehau 143759878316SSepherosa Ziehau static sysclock_t 143859878316SSepherosa Ziehau tsc_cputimer_count_lfence(void) 143959878316SSepherosa Ziehau { 144059878316SSepherosa Ziehau cpu_lfence(); 144159878316SSepherosa Ziehau return tsc_cputimer_count(); 144259878316SSepherosa Ziehau } 144359878316SSepherosa Ziehau 144459878316SSepherosa Ziehau static sysclock_t 144559878316SSepherosa Ziehau tsc_cputimer_count_mfence(void) 144659878316SSepherosa Ziehau { 144759878316SSepherosa Ziehau cpu_mfence(); 144859878316SSepherosa Ziehau return tsc_cputimer_count(); 144959878316SSepherosa Ziehau } 145059878316SSepherosa Ziehau 14516b91ee43SSepherosa Ziehau static uint64_t 14526b91ee43SSepherosa Ziehau tsc_cpucounter_count_lfence(void) 14536b91ee43SSepherosa Ziehau { 14546b91ee43SSepherosa Ziehau 14556b91ee43SSepherosa Ziehau cpu_lfence(); 14566b91ee43SSepherosa Ziehau return (rdtsc()); 14576b91ee43SSepherosa Ziehau } 14586b91ee43SSepherosa Ziehau 14596b91ee43SSepherosa Ziehau static uint64_t 14606b91ee43SSepherosa Ziehau tsc_cpucounter_count_mfence(void) 14616b91ee43SSepherosa Ziehau { 14626b91ee43SSepherosa Ziehau 14636b91ee43SSepherosa Ziehau cpu_mfence(); 14646b91ee43SSepherosa Ziehau return (rdtsc()); 14656b91ee43SSepherosa Ziehau } 14666b91ee43SSepherosa Ziehau 14678d23b56cSSepherosa Ziehau static void 14688d23b56cSSepherosa Ziehau tsc_cputimer_register(void) 14698d23b56cSSepherosa Ziehau { 14708d23b56cSSepherosa Ziehau uint64_t freq; 14718d23b56cSSepherosa Ziehau int enable = 1; 14728d23b56cSSepherosa Ziehau 14736b91ee43SSepherosa Ziehau if (!tsc_mpsync) { 14746b91ee43SSepherosa Ziehau if (tsc_invariant) { 14756b91ee43SSepherosa Ziehau /* Per-cpu cpucounter still works. */ 14766b91ee43SSepherosa Ziehau goto regcnt; 14776b91ee43SSepherosa Ziehau } 14788d23b56cSSepherosa Ziehau return; 14796b91ee43SSepherosa Ziehau } 14808d23b56cSSepherosa Ziehau 14818d23b56cSSepherosa Ziehau TUNABLE_INT_FETCH("hw.tsc_cputimer_enable", &enable); 14828d23b56cSSepherosa Ziehau if (!enable) 14838d23b56cSSepherosa Ziehau return; 14848d23b56cSSepherosa Ziehau 14858d23b56cSSepherosa Ziehau freq = tsc_frequency; 14868d23b56cSSepherosa Ziehau while (freq > TSC_CPUTIMER_FREQMAX) { 14878d23b56cSSepherosa Ziehau freq >>= 1; 14888d23b56cSSepherosa Ziehau ++tsc_cputimer_shift; 14898d23b56cSSepherosa Ziehau } 14908d23b56cSSepherosa Ziehau kprintf("TSC: cputimer freq %ju, shift %d\n", 14918d23b56cSSepherosa Ziehau (uintmax_t)freq, tsc_cputimer_shift); 14928d23b56cSSepherosa Ziehau 14938d23b56cSSepherosa Ziehau tsc_cputimer.freq = freq; 14948d23b56cSSepherosa Ziehau 149559878316SSepherosa Ziehau if (cpu_vendor_id == CPU_VENDOR_INTEL) 149659878316SSepherosa Ziehau tsc_cputimer.count = tsc_cputimer_count_lfence; 149759878316SSepherosa Ziehau else 149859878316SSepherosa Ziehau tsc_cputimer.count = tsc_cputimer_count_mfence; /* safe bet */ 149959878316SSepherosa Ziehau 15008d23b56cSSepherosa Ziehau cputimer_register(&tsc_cputimer); 15018d23b56cSSepherosa Ziehau cputimer_select(&tsc_cputimer, 0); 15026b91ee43SSepherosa Ziehau 15036b91ee43SSepherosa Ziehau tsc_cpucounter.flags |= CPUCOUNTER_FLAG_MPSYNC; 15046b91ee43SSepherosa Ziehau regcnt: 15056b91ee43SSepherosa Ziehau tsc_cpucounter.freq = tsc_frequency; 15066b91ee43SSepherosa Ziehau if (cpu_vendor_id == CPU_VENDOR_INTEL) { 15076b91ee43SSepherosa Ziehau tsc_cpucounter.count = 15086b91ee43SSepherosa Ziehau tsc_cpucounter_count_lfence; 15096b91ee43SSepherosa Ziehau } else { 15106b91ee43SSepherosa Ziehau tsc_cpucounter.count = 15116b91ee43SSepherosa Ziehau tsc_cpucounter_count_mfence; /* safe bet */ 15126b91ee43SSepherosa Ziehau } 15136b91ee43SSepherosa Ziehau cpucounter_register(&tsc_cpucounter); 15148d23b56cSSepherosa Ziehau } 15151997b4c2SMatthew Dillon SYSINIT(tsc_cputimer_reg, SI_BOOT2_POST_SMP, SI_ORDER_FIRST, 15168d23b56cSSepherosa Ziehau tsc_cputimer_register, NULL); 15178d23b56cSSepherosa Ziehau 1518c8fe38aeSMatthew Dillon SYSCTL_NODE(_hw, OID_AUTO, i8254, CTLFLAG_RW, 0, "I8254"); 1519c8fe38aeSMatthew Dillon SYSCTL_UINT(_hw_i8254, OID_AUTO, freq, CTLFLAG_RD, &i8254_cputimer.freq, 0, 1520c8fe38aeSMatthew Dillon "frequency"); 1521c8fe38aeSMatthew Dillon SYSCTL_PROC(_hw_i8254, OID_AUTO, timestamp, CTLTYPE_STRING|CTLFLAG_RD, 1522c8fe38aeSMatthew Dillon 0, 0, hw_i8254_timestamp, "A", ""); 1523c8fe38aeSMatthew Dillon 1524c8fe38aeSMatthew Dillon SYSCTL_INT(_hw, OID_AUTO, tsc_present, CTLFLAG_RD, 1525c8fe38aeSMatthew Dillon &tsc_present, 0, "TSC Available"); 15265a81b19fSSepherosa Ziehau SYSCTL_INT(_hw, OID_AUTO, tsc_invariant, CTLFLAG_RD, 15275a81b19fSSepherosa Ziehau &tsc_invariant, 0, "Invariant TSC"); 1528dda44f1eSSepherosa Ziehau SYSCTL_INT(_hw, OID_AUTO, tsc_mpsync, CTLFLAG_RD, 1529dda44f1eSSepherosa Ziehau &tsc_mpsync, 0, "TSC is synchronized across CPUs"); 1530c8fe38aeSMatthew Dillon SYSCTL_QUAD(_hw, OID_AUTO, tsc_frequency, CTLFLAG_RD, 1531c8fe38aeSMatthew Dillon &tsc_frequency, 0, "TSC Frequency"); 1532