132580301SAttilio Rao /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 432580301SAttilio Rao * Copyright (c) 1990 The Regents of the University of California. 5875b8844SAlexander Motin * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org> 632580301SAttilio Rao * All rights reserved. 732580301SAttilio Rao * 832580301SAttilio Rao * This code is derived from software contributed to Berkeley by 932580301SAttilio Rao * William Jolitz and Don Ahn. 1032580301SAttilio Rao * 1132580301SAttilio Rao * Redistribution and use in source and binary forms, with or without 1232580301SAttilio Rao * modification, are permitted provided that the following conditions 1332580301SAttilio Rao * are met: 1432580301SAttilio Rao * 1. Redistributions of source code must retain the above copyright 1532580301SAttilio Rao * notice, this list of conditions and the following disclaimer. 1632580301SAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright 1732580301SAttilio Rao * notice, this list of conditions and the following disclaimer in the 1832580301SAttilio Rao * documentation and/or other materials provided with the distribution. 19fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 2032580301SAttilio Rao * may be used to endorse or promote products derived from this software 2132580301SAttilio Rao * without specific prior written permission. 2232580301SAttilio Rao * 2332580301SAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2432580301SAttilio Rao * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2532580301SAttilio Rao * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2632580301SAttilio Rao * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2732580301SAttilio Rao * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2832580301SAttilio Rao * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2932580301SAttilio Rao * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3032580301SAttilio Rao * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3132580301SAttilio Rao * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3232580301SAttilio Rao * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3332580301SAttilio Rao * SUCH DAMAGE. 3432580301SAttilio Rao */ 3532580301SAttilio Rao 3632580301SAttilio Rao #include <sys/cdefs.h> 3732580301SAttilio Rao /* 3832580301SAttilio Rao * Routines to handle clock hardware. 3932580301SAttilio Rao */ 4032580301SAttilio Rao 41aa597d40SMark Johnston #ifdef __amd64__ 42aa597d40SMark Johnston #define DEV_APIC 43aa597d40SMark Johnston #else 44aa597d40SMark Johnston #include "opt_apic.h" 45aa597d40SMark Johnston #endif 4632580301SAttilio Rao #include "opt_clock.h" 4732580301SAttilio Rao #include "opt_isa.h" 4832580301SAttilio Rao 4932580301SAttilio Rao #include <sys/param.h> 5032580301SAttilio Rao #include <sys/systm.h> 5132580301SAttilio Rao #include <sys/bus.h> 5232580301SAttilio Rao #include <sys/lock.h> 5332580301SAttilio Rao #include <sys/kdb.h> 5432580301SAttilio Rao #include <sys/mutex.h> 5532580301SAttilio Rao #include <sys/proc.h> 5632580301SAttilio Rao #include <sys/kernel.h> 5732580301SAttilio Rao #include <sys/module.h> 58875b8844SAlexander Motin #include <sys/rman.h> 5932580301SAttilio Rao #include <sys/sched.h> 6032580301SAttilio Rao #include <sys/smp.h> 6132580301SAttilio Rao #include <sys/sysctl.h> 62875b8844SAlexander Motin #include <sys/timeet.h> 63875b8844SAlexander Motin #include <sys/timetc.h> 6432580301SAttilio Rao 6532580301SAttilio Rao #include <machine/clock.h> 6632580301SAttilio Rao #include <machine/cpu.h> 6732580301SAttilio Rao #include <machine/intr_machdep.h> 6862d09b46SMark Johnston #include <x86/apicvar.h> 695f05c794SRoger Pau Monné #include <x86/init.h> 70d1f4c44aSDmitry Chagin #include <x86/ppireg.h> 71*de4da6cdSDmitry Chagin #include <x86/timerreg.h> 7232580301SAttilio Rao 7332580301SAttilio Rao #include <isa/rtc.h> 7432580301SAttilio Rao #ifdef DEV_ISA 7532580301SAttilio Rao #include <isa/isareg.h> 7632580301SAttilio Rao #include <isa/isavar.h> 7732580301SAttilio Rao #endif 7832580301SAttilio Rao 7932580301SAttilio Rao int clkintr_pending; 8032580301SAttilio Rao #ifndef TIMER_FREQ 8132580301SAttilio Rao #define TIMER_FREQ 1193182 8232580301SAttilio Rao #endif 8332580301SAttilio Rao u_int i8254_freq = TIMER_FREQ; 8432580301SAttilio Rao TUNABLE_INT("hw.i8254.freq", &i8254_freq); 8532580301SAttilio Rao int i8254_max_count; 869500655eSAlexander Motin static int i8254_timecounter = 1; 8732580301SAttilio Rao 888355852fSIan Lepore static struct mtx clock_lock; 8932580301SAttilio Rao static struct intsrc *i8254_intsrc; 90875b8844SAlexander Motin static uint16_t i8254_lastcount; 91875b8844SAlexander Motin static uint16_t i8254_offset; 9232580301SAttilio Rao static int (*i8254_pending)(struct intsrc *); 9332580301SAttilio Rao static int i8254_ticked; 94875b8844SAlexander Motin 95875b8844SAlexander Motin struct attimer_softc { 96875b8844SAlexander Motin int intr_en; 9791751b1aSAlexander Motin int port_rid, intr_rid; 9891751b1aSAlexander Motin struct resource *port_res; 99875b8844SAlexander Motin struct resource *intr_res; 100875b8844SAlexander Motin void *intr_handler; 101875b8844SAlexander Motin struct timecounter tc; 102875b8844SAlexander Motin struct eventtimer et; 1039500655eSAlexander Motin int mode; 1049500655eSAlexander Motin #define MODE_STOP 0 1059500655eSAlexander Motin #define MODE_PERIODIC 1 1069500655eSAlexander Motin #define MODE_ONESHOT 2 1079500655eSAlexander Motin uint32_t period; 108875b8844SAlexander Motin }; 109875b8844SAlexander Motin static struct attimer_softc *attimer_sc = NULL; 11032580301SAttilio Rao 111d3979248SAlexander Motin static int timer0_period = -2; 112a937c507SAlexander Motin static int timer0_mode = 0xffff; 113a937c507SAlexander Motin static int timer0_last = 0xffff; 114d3979248SAlexander Motin 11532580301SAttilio Rao /* Values for timerX_state: */ 11632580301SAttilio Rao #define RELEASED 0 11732580301SAttilio Rao #define RELEASE_PENDING 1 11832580301SAttilio Rao #define ACQUIRED 2 11932580301SAttilio Rao #define ACQUIRE_PENDING 3 12032580301SAttilio Rao 12132580301SAttilio Rao static u_char timer2_state; 12232580301SAttilio Rao 12332580301SAttilio Rao static unsigned i8254_get_timecount(struct timecounter *tc); 1249500655eSAlexander Motin static void set_i8254_freq(int mode, uint32_t period); 12532580301SAttilio Rao 1265f05c794SRoger Pau Monné void 1275f05c794SRoger Pau Monné clock_init(void) 1285f05c794SRoger Pau Monné { 1295f05c794SRoger Pau Monné /* Init the clock lock */ 1305f05c794SRoger Pau Monné mtx_init(&clock_lock, "clk", NULL, MTX_SPIN | MTX_NOPROFILE); 1315f05c794SRoger Pau Monné /* Init the clock in order to use DELAY */ 1325f05c794SRoger Pau Monné init_ops.early_clock_source_init(); 1331ca34862SRoger Pau Monné tsc_init(); 1345f05c794SRoger Pau Monné } 1355f05c794SRoger Pau Monné 13632580301SAttilio Rao static int 137875b8844SAlexander Motin clkintr(void *arg) 13832580301SAttilio Rao { 139875b8844SAlexander Motin struct attimer_softc *sc = (struct attimer_softc *)arg; 14032580301SAttilio Rao 1419500655eSAlexander Motin if (i8254_timecounter && sc->period != 0) { 14232580301SAttilio Rao mtx_lock_spin(&clock_lock); 14332580301SAttilio Rao if (i8254_ticked) 14432580301SAttilio Rao i8254_ticked = 0; 14532580301SAttilio Rao else { 14632580301SAttilio Rao i8254_offset += i8254_max_count; 14732580301SAttilio Rao i8254_lastcount = 0; 14832580301SAttilio Rao } 14932580301SAttilio Rao clkintr_pending = 0; 15032580301SAttilio Rao mtx_unlock_spin(&clock_lock); 15132580301SAttilio Rao } 15232580301SAttilio Rao 1530eda5b3fSJung-uk Kim if (sc->et.et_active && sc->mode != MODE_STOP) 1548a687080SAlexander Motin sc->et.et_event_cb(&sc->et, sc->et.et_arg); 15532580301SAttilio Rao 15632580301SAttilio Rao return (FILTER_HANDLED); 15732580301SAttilio Rao } 15832580301SAttilio Rao 15932580301SAttilio Rao int 16032580301SAttilio Rao timer_spkr_acquire(void) 16132580301SAttilio Rao { 16232580301SAttilio Rao int mode; 16332580301SAttilio Rao 16432580301SAttilio Rao mode = TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT; 16532580301SAttilio Rao 16632580301SAttilio Rao if (timer2_state != RELEASED) 16732580301SAttilio Rao return (-1); 16832580301SAttilio Rao timer2_state = ACQUIRED; 16932580301SAttilio Rao 17032580301SAttilio Rao /* 17132580301SAttilio Rao * This access to the timer registers is as atomic as possible 17232580301SAttilio Rao * because it is a single instruction. We could do better if we 17332580301SAttilio Rao * knew the rate. Use of splclock() limits glitches to 10-100us, 17432580301SAttilio Rao * and this is probably good enough for timer2, so we aren't as 17532580301SAttilio Rao * careful with it as with timer0. 17632580301SAttilio Rao */ 17732580301SAttilio Rao outb(TIMER_MODE, TIMER_SEL2 | (mode & 0x3f)); 1782b375b4eSYoshihiro Takahashi 17932580301SAttilio Rao ppi_spkr_on(); /* enable counter2 output to speaker */ 18032580301SAttilio Rao return (0); 18132580301SAttilio Rao } 18232580301SAttilio Rao 18332580301SAttilio Rao int 18432580301SAttilio Rao timer_spkr_release(void) 18532580301SAttilio Rao { 18632580301SAttilio Rao 18732580301SAttilio Rao if (timer2_state != ACQUIRED) 18832580301SAttilio Rao return (-1); 18932580301SAttilio Rao timer2_state = RELEASED; 19032580301SAttilio Rao outb(TIMER_MODE, TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT); 1912b375b4eSYoshihiro Takahashi 19232580301SAttilio Rao ppi_spkr_off(); /* disable counter2 output to speaker */ 19332580301SAttilio Rao return (0); 19432580301SAttilio Rao } 19532580301SAttilio Rao 19632580301SAttilio Rao void 19732580301SAttilio Rao timer_spkr_setfreq(int freq) 19832580301SAttilio Rao { 19932580301SAttilio Rao 20032580301SAttilio Rao freq = i8254_freq / freq; 20132580301SAttilio Rao mtx_lock_spin(&clock_lock); 20232580301SAttilio Rao outb(TIMER_CNTR2, freq & 0xff); 20332580301SAttilio Rao outb(TIMER_CNTR2, freq >> 8); 20432580301SAttilio Rao mtx_unlock_spin(&clock_lock); 20532580301SAttilio Rao } 20632580301SAttilio Rao 20732580301SAttilio Rao static int 20832580301SAttilio Rao getit(void) 20932580301SAttilio Rao { 21032580301SAttilio Rao int high, low; 21132580301SAttilio Rao 21232580301SAttilio Rao mtx_lock_spin(&clock_lock); 21332580301SAttilio Rao 21432580301SAttilio Rao /* Select timer0 and latch counter value. */ 21532580301SAttilio Rao outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 21632580301SAttilio Rao 21732580301SAttilio Rao low = inb(TIMER_CNTR0); 21832580301SAttilio Rao high = inb(TIMER_CNTR0); 21932580301SAttilio Rao 22032580301SAttilio Rao mtx_unlock_spin(&clock_lock); 22132580301SAttilio Rao return ((high << 8) | low); 22232580301SAttilio Rao } 22332580301SAttilio Rao 224856e88c1SJung-uk Kim /* 225856e88c1SJung-uk Kim * Wait "n" microseconds. 226856e88c1SJung-uk Kim * Relies on timer 1 counting down from (i8254_freq / hz) 227856e88c1SJung-uk Kim * Note: timer had better have been programmed before this is first used! 228856e88c1SJung-uk Kim */ 229856e88c1SJung-uk Kim void 2305f05c794SRoger Pau Monné i8254_delay(int n) 231856e88c1SJung-uk Kim { 232856e88c1SJung-uk Kim int delta, prev_tick, tick, ticks_left; 233856e88c1SJung-uk Kim #ifdef DELAYDEBUG 234856e88c1SJung-uk Kim int getit_calls = 1; 235856e88c1SJung-uk Kim int n1; 236856e88c1SJung-uk Kim static int state = 0; 23780c2cdcfSJung-uk Kim 23880c2cdcfSJung-uk Kim if (state == 0) { 23980c2cdcfSJung-uk Kim state = 1; 24080c2cdcfSJung-uk Kim for (n1 = 1; n1 <= 10000000; n1 *= 10) 24180c2cdcfSJung-uk Kim DELAY(n1); 24280c2cdcfSJung-uk Kim state = 2; 24380c2cdcfSJung-uk Kim } 24480c2cdcfSJung-uk Kim if (state == 1) 24580c2cdcfSJung-uk Kim printf("DELAY(%d)...", n); 24632580301SAttilio Rao #endif 24732580301SAttilio Rao /* 24832580301SAttilio Rao * Read the counter first, so that the rest of the setup overhead is 24932580301SAttilio Rao * counted. Guess the initial overhead is 20 usec (on most systems it 25032580301SAttilio Rao * takes about 1.5 usec for each of the i/o's in getit(). The loop 25132580301SAttilio Rao * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The 25232580301SAttilio Rao * multiplications and divisions to scale the count take a while). 25332580301SAttilio Rao * 25432580301SAttilio Rao * However, if ddb is active then use a fake counter since reading 25532580301SAttilio Rao * the i8254 counter involves acquiring a lock. ddb must not do 25632580301SAttilio Rao * locking for many reasons, but it calls here for at least atkbd 25732580301SAttilio Rao * input. 25832580301SAttilio Rao */ 25932580301SAttilio Rao #ifdef KDB 26032580301SAttilio Rao if (kdb_active) 26132580301SAttilio Rao prev_tick = 1; 26232580301SAttilio Rao else 26332580301SAttilio Rao #endif 26432580301SAttilio Rao prev_tick = getit(); 26532580301SAttilio Rao n -= 0; /* XXX actually guess no initial overhead */ 26632580301SAttilio Rao /* 26732580301SAttilio Rao * Calculate (n * (i8254_freq / 1e6)) without using floating point 26832580301SAttilio Rao * and without any avoidable overflows. 26932580301SAttilio Rao */ 27032580301SAttilio Rao if (n <= 0) 27132580301SAttilio Rao ticks_left = 0; 27232580301SAttilio Rao else if (n < 256) 27332580301SAttilio Rao /* 27432580301SAttilio Rao * Use fixed point to avoid a slow division by 1000000. 27532580301SAttilio Rao * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest. 27632580301SAttilio Rao * 2^15 is the first power of 2 that gives exact results 27732580301SAttilio Rao * for n between 0 and 256. 27832580301SAttilio Rao */ 27932580301SAttilio Rao ticks_left = ((u_int)n * 39099 + (1 << 15) - 1) >> 15; 28032580301SAttilio Rao else 28132580301SAttilio Rao /* 28232580301SAttilio Rao * Don't bother using fixed point, although gcc-2.7.2 28332580301SAttilio Rao * generates particularly poor code for the long long 28432580301SAttilio Rao * division, since even the slow way will complete long 28532580301SAttilio Rao * before the delay is up (unless we're interrupted). 28632580301SAttilio Rao */ 28732580301SAttilio Rao ticks_left = ((u_int)n * (long long)i8254_freq + 999999) 28832580301SAttilio Rao / 1000000; 28932580301SAttilio Rao 29032580301SAttilio Rao while (ticks_left > 0) { 29132580301SAttilio Rao #ifdef KDB 29232580301SAttilio Rao if (kdb_active) { 29332580301SAttilio Rao inb(0x84); 29432580301SAttilio Rao tick = prev_tick - 1; 29532580301SAttilio Rao if (tick <= 0) 29632580301SAttilio Rao tick = i8254_max_count; 29732580301SAttilio Rao } else 29832580301SAttilio Rao #endif 29932580301SAttilio Rao tick = getit(); 30032580301SAttilio Rao #ifdef DELAYDEBUG 30132580301SAttilio Rao ++getit_calls; 30232580301SAttilio Rao #endif 30332580301SAttilio Rao delta = prev_tick - tick; 30432580301SAttilio Rao prev_tick = tick; 30532580301SAttilio Rao if (delta < 0) { 30632580301SAttilio Rao delta += i8254_max_count; 30732580301SAttilio Rao /* 30832580301SAttilio Rao * Guard against i8254_max_count being wrong. 30932580301SAttilio Rao * This shouldn't happen in normal operation, 31032580301SAttilio Rao * but it may happen if set_i8254_freq() is 31132580301SAttilio Rao * traced. 31232580301SAttilio Rao */ 31332580301SAttilio Rao if (delta < 0) 31432580301SAttilio Rao delta = 0; 31532580301SAttilio Rao } 31632580301SAttilio Rao ticks_left -= delta; 31732580301SAttilio Rao } 31832580301SAttilio Rao #ifdef DELAYDEBUG 31932580301SAttilio Rao if (state == 1) 32032580301SAttilio Rao printf(" %d calls to getit() at %d usec each\n", 32132580301SAttilio Rao getit_calls, (n + 5) / getit_calls); 32232580301SAttilio Rao #endif 32332580301SAttilio Rao } 32432580301SAttilio Rao 32532580301SAttilio Rao static void 3269500655eSAlexander Motin set_i8254_freq(int mode, uint32_t period) 32732580301SAttilio Rao { 328a937c507SAlexander Motin int new_count, new_mode; 32932580301SAttilio Rao 33032580301SAttilio Rao mtx_lock_spin(&clock_lock); 331d3979248SAlexander Motin if (mode == MODE_STOP) { 332d3979248SAlexander Motin if (i8254_timecounter) { 3339500655eSAlexander Motin mode = MODE_PERIODIC; 334d3979248SAlexander Motin new_count = 0x10000; 335d3979248SAlexander Motin } else 336d3979248SAlexander Motin new_count = -1; 337d3979248SAlexander Motin } else { 338d3979248SAlexander Motin new_count = min(((uint64_t)i8254_freq * period + 339d3979248SAlexander Motin 0x80000000LLU) >> 32, 0x10000); 340d3979248SAlexander Motin } 341d3979248SAlexander Motin if (new_count == timer0_period) 342d3979248SAlexander Motin goto out; 343d3979248SAlexander Motin i8254_max_count = ((new_count & ~0xffff) != 0) ? 0xffff : new_count; 344d3979248SAlexander Motin timer0_period = (mode == MODE_PERIODIC) ? new_count : -1; 3459500655eSAlexander Motin switch (mode) { 3469500655eSAlexander Motin case MODE_STOP: 347a937c507SAlexander Motin new_mode = TIMER_SEL0 | TIMER_INTTC | TIMER_16BIT; 348a937c507SAlexander Motin outb(TIMER_MODE, new_mode); 349d3979248SAlexander Motin outb(TIMER_CNTR0, 0); 350d3979248SAlexander Motin outb(TIMER_CNTR0, 0); 3519500655eSAlexander Motin break; 3529500655eSAlexander Motin case MODE_PERIODIC: 353a937c507SAlexander Motin new_mode = TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT; 354a937c507SAlexander Motin outb(TIMER_MODE, new_mode); 355d3979248SAlexander Motin outb(TIMER_CNTR0, new_count & 0xff); 356d3979248SAlexander Motin outb(TIMER_CNTR0, new_count >> 8); 3579500655eSAlexander Motin break; 3589500655eSAlexander Motin case MODE_ONESHOT: 359a937c507SAlexander Motin if (new_count < 256 && timer0_last < 256) { 360a937c507SAlexander Motin new_mode = TIMER_SEL0 | TIMER_INTTC | TIMER_LSB; 361a937c507SAlexander Motin if (new_mode != timer0_mode) 362a937c507SAlexander Motin outb(TIMER_MODE, new_mode); 363a937c507SAlexander Motin outb(TIMER_CNTR0, new_count & 0xff); 364a937c507SAlexander Motin break; 365a937c507SAlexander Motin } 366a937c507SAlexander Motin new_mode = TIMER_SEL0 | TIMER_INTTC | TIMER_16BIT; 367a937c507SAlexander Motin if (new_mode != timer0_mode) 368a937c507SAlexander Motin outb(TIMER_MODE, new_mode); 369d3979248SAlexander Motin outb(TIMER_CNTR0, new_count & 0xff); 370d3979248SAlexander Motin outb(TIMER_CNTR0, new_count >> 8); 3719500655eSAlexander Motin break; 372ce4642ecSDavide Italiano default: 373ce4642ecSDavide Italiano panic("set_i8254_freq: unknown operational mode"); 37432580301SAttilio Rao } 375a937c507SAlexander Motin timer0_mode = new_mode; 376a937c507SAlexander Motin timer0_last = new_count; 377d3979248SAlexander Motin out: 37832580301SAttilio Rao mtx_unlock_spin(&clock_lock); 37932580301SAttilio Rao } 38032580301SAttilio Rao 38132580301SAttilio Rao static void 38232580301SAttilio Rao i8254_restore(void) 38332580301SAttilio Rao { 38432580301SAttilio Rao 385d3979248SAlexander Motin timer0_period = -2; 386a937c507SAlexander Motin timer0_mode = 0xffff; 387a937c507SAlexander Motin timer0_last = 0xffff; 388d3979248SAlexander Motin if (attimer_sc != NULL) 3899500655eSAlexander Motin set_i8254_freq(attimer_sc->mode, attimer_sc->period); 3909500655eSAlexander Motin else 391cb261f43SBrooks Davis set_i8254_freq(MODE_STOP, 0); 39232580301SAttilio Rao } 39332580301SAttilio Rao 39432580301SAttilio Rao /* This is separate from startrtclock() so that it can be called early. */ 39532580301SAttilio Rao void 39632580301SAttilio Rao i8254_init(void) 39732580301SAttilio Rao { 39832580301SAttilio Rao 399cb261f43SBrooks Davis set_i8254_freq(MODE_STOP, 0); 40032580301SAttilio Rao } 40132580301SAttilio Rao 40232580301SAttilio Rao void 40384369dd5SMark Johnston startrtclock(void) 40432580301SAttilio Rao { 40532580301SAttilio Rao 40684369dd5SMark Johnston start_TSC(); 40732580301SAttilio Rao } 40832580301SAttilio Rao 40932580301SAttilio Rao void 410875b8844SAlexander Motin cpu_initclocks(void) 41132580301SAttilio Rao { 412fdce57a0SJohn Baldwin struct thread *td; 413fdce57a0SJohn Baldwin int i; 41432580301SAttilio Rao 415fdce57a0SJohn Baldwin td = curthread; 41662d09b46SMark Johnston 417553af8f1SMark Johnston tsc_calibrate(); 418aa597d40SMark Johnston #ifdef DEV_APIC 41962d09b46SMark Johnston lapic_calibrate_timer(); 420aa597d40SMark Johnston #endif 421875b8844SAlexander Motin cpu_initclocks_bsp(); 422fdce57a0SJohn Baldwin CPU_FOREACH(i) { 423fdce57a0SJohn Baldwin if (i == 0) 424fdce57a0SJohn Baldwin continue; 425fdce57a0SJohn Baldwin thread_lock(td); 426fdce57a0SJohn Baldwin sched_bind(td, i); 427fdce57a0SJohn Baldwin thread_unlock(td); 428fdce57a0SJohn Baldwin cpu_initclocks_ap(); 429fdce57a0SJohn Baldwin } 430fdce57a0SJohn Baldwin thread_lock(td); 431fdce57a0SJohn Baldwin if (sched_is_bound(td)) 432fdce57a0SJohn Baldwin sched_unbind(td); 433fdce57a0SJohn Baldwin thread_unlock(td); 43432580301SAttilio Rao } 43532580301SAttilio Rao 43632580301SAttilio Rao static int 43732580301SAttilio Rao sysctl_machdep_i8254_freq(SYSCTL_HANDLER_ARGS) 43832580301SAttilio Rao { 43932580301SAttilio Rao int error; 44032580301SAttilio Rao u_int freq; 44132580301SAttilio Rao 44232580301SAttilio Rao /* 44332580301SAttilio Rao * Use `i8254' instead of `timer' in external names because `timer' 444974206cfSRebecca Cran * is too generic. Should use it everywhere. 44532580301SAttilio Rao */ 44632580301SAttilio Rao freq = i8254_freq; 44732580301SAttilio Rao error = sysctl_handle_int(oidp, &freq, 0, req); 448875b8844SAlexander Motin if (error == 0 && req->newptr != NULL) { 4499500655eSAlexander Motin i8254_freq = freq; 450d3979248SAlexander Motin if (attimer_sc != NULL) { 4519500655eSAlexander Motin set_i8254_freq(attimer_sc->mode, attimer_sc->period); 452875b8844SAlexander Motin attimer_sc->tc.tc_frequency = freq; 453875b8844SAlexander Motin } else { 454cb261f43SBrooks Davis set_i8254_freq(MODE_STOP, 0); 455875b8844SAlexander Motin } 456875b8844SAlexander Motin } 45732580301SAttilio Rao return (error); 45832580301SAttilio Rao } 45932580301SAttilio Rao 4607029da5cSPawel Biernacki SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, 4611d6fb900SAlexander Motin CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 4625331d61dSJung-uk Kim 0, sizeof(u_int), sysctl_machdep_i8254_freq, "IU", 4635331d61dSJung-uk Kim "i8254 timer frequency"); 46432580301SAttilio Rao 46532580301SAttilio Rao static unsigned 46632580301SAttilio Rao i8254_get_timecount(struct timecounter *tc) 46732580301SAttilio Rao { 468875b8844SAlexander Motin device_t dev = (device_t)tc->tc_priv; 469875b8844SAlexander Motin struct attimer_softc *sc = device_get_softc(dev); 47032580301SAttilio Rao register_t flags; 471875b8844SAlexander Motin uint16_t count; 47232580301SAttilio Rao u_int high, low; 47332580301SAttilio Rao 4749500655eSAlexander Motin if (sc->period == 0) 475875b8844SAlexander Motin return (i8254_max_count - getit()); 476875b8844SAlexander Motin 47732580301SAttilio Rao #ifdef __amd64__ 47832580301SAttilio Rao flags = read_rflags(); 47932580301SAttilio Rao #else 48032580301SAttilio Rao flags = read_eflags(); 48132580301SAttilio Rao #endif 48232580301SAttilio Rao mtx_lock_spin(&clock_lock); 48332580301SAttilio Rao 48432580301SAttilio Rao /* Select timer0 and latch counter value. */ 48532580301SAttilio Rao outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 48632580301SAttilio Rao 48732580301SAttilio Rao low = inb(TIMER_CNTR0); 48832580301SAttilio Rao high = inb(TIMER_CNTR0); 48932580301SAttilio Rao count = i8254_max_count - ((high << 8) | low); 49032580301SAttilio Rao if (count < i8254_lastcount || 49132580301SAttilio Rao (!i8254_ticked && (clkintr_pending || 49232580301SAttilio Rao ((count < 20 || (!(flags & PSL_I) && 49332580301SAttilio Rao count < i8254_max_count / 2u)) && 49432580301SAttilio Rao i8254_pending != NULL && i8254_pending(i8254_intsrc))))) { 49532580301SAttilio Rao i8254_ticked = 1; 49632580301SAttilio Rao i8254_offset += i8254_max_count; 49732580301SAttilio Rao } 49832580301SAttilio Rao i8254_lastcount = count; 49932580301SAttilio Rao count += i8254_offset; 50032580301SAttilio Rao mtx_unlock_spin(&clock_lock); 50132580301SAttilio Rao return (count); 50232580301SAttilio Rao } 50332580301SAttilio Rao 504875b8844SAlexander Motin static int 505fdc5dd2dSAlexander Motin attimer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 506875b8844SAlexander Motin { 507875b8844SAlexander Motin device_t dev = (device_t)et->et_priv; 508875b8844SAlexander Motin struct attimer_softc *sc = device_get_softc(dev); 509875b8844SAlexander Motin 510fdc5dd2dSAlexander Motin if (period != 0) { 5119500655eSAlexander Motin sc->mode = MODE_PERIODIC; 512fdc5dd2dSAlexander Motin sc->period = period; 5139500655eSAlexander Motin } else { 5149500655eSAlexander Motin sc->mode = MODE_ONESHOT; 515fdc5dd2dSAlexander Motin sc->period = first; 5169500655eSAlexander Motin } 517875b8844SAlexander Motin if (!sc->intr_en) { 518875b8844SAlexander Motin i8254_intsrc->is_pic->pic_enable_source(i8254_intsrc); 519875b8844SAlexander Motin sc->intr_en = 1; 520875b8844SAlexander Motin } 5219500655eSAlexander Motin set_i8254_freq(sc->mode, sc->period); 522875b8844SAlexander Motin return (0); 523875b8844SAlexander Motin } 524875b8844SAlexander Motin 525875b8844SAlexander Motin static int 526875b8844SAlexander Motin attimer_stop(struct eventtimer *et) 527875b8844SAlexander Motin { 528875b8844SAlexander Motin device_t dev = (device_t)et->et_priv; 529875b8844SAlexander Motin struct attimer_softc *sc = device_get_softc(dev); 530875b8844SAlexander Motin 5319500655eSAlexander Motin sc->mode = MODE_STOP; 5329500655eSAlexander Motin sc->period = 0; 5339500655eSAlexander Motin set_i8254_freq(sc->mode, sc->period); 534875b8844SAlexander Motin return (0); 535875b8844SAlexander Motin } 536875b8844SAlexander Motin 53732580301SAttilio Rao #ifdef DEV_ISA 53832580301SAttilio Rao /* 53932580301SAttilio Rao * Attach to the ISA PnP descriptors for the timer 54032580301SAttilio Rao */ 54132580301SAttilio Rao static struct isa_pnp_id attimer_ids[] = { 54232580301SAttilio Rao { 0x0001d041 /* PNP0100 */, "AT timer" }, 54332580301SAttilio Rao { 0 } 54432580301SAttilio Rao }; 54532580301SAttilio Rao 54632580301SAttilio Rao static int 54732580301SAttilio Rao attimer_probe(device_t dev) 54832580301SAttilio Rao { 54932580301SAttilio Rao int result; 55032580301SAttilio Rao 55132580301SAttilio Rao result = ISA_PNP_PROBE(device_get_parent(dev), dev, attimer_ids); 552a7d6757cSAlexander Motin /* ENOENT means no PnP-ID, device is hinted. */ 553a7d6757cSAlexander Motin if (result == ENOENT) { 554a7d6757cSAlexander Motin device_set_desc(dev, "AT timer"); 555a7d6757cSAlexander Motin return (BUS_PROBE_LOW_PRIORITY); 556a7d6757cSAlexander Motin } 55732580301SAttilio Rao return (result); 55832580301SAttilio Rao } 55932580301SAttilio Rao 56032580301SAttilio Rao static int 56132580301SAttilio Rao attimer_attach(device_t dev) 56232580301SAttilio Rao { 563875b8844SAlexander Motin struct attimer_softc *sc; 5642dd1bdf1SJustin Hibbits rman_res_t s; 565875b8844SAlexander Motin int i; 566875b8844SAlexander Motin 567875b8844SAlexander Motin attimer_sc = sc = device_get_softc(dev); 568875b8844SAlexander Motin bzero(sc, sizeof(struct attimer_softc)); 56991751b1aSAlexander Motin if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, 57091751b1aSAlexander Motin &sc->port_rid, IO_TIMER1, IO_TIMER1 + 3, 4, RF_ACTIVE))) 57191751b1aSAlexander Motin device_printf(dev,"Warning: Couldn't map I/O.\n"); 572875b8844SAlexander Motin i8254_intsrc = intr_lookup_source(0); 573875b8844SAlexander Motin if (i8254_intsrc != NULL) 574875b8844SAlexander Motin i8254_pending = i8254_intsrc->is_pic->pic_source_pending; 5759500655eSAlexander Motin resource_int_value(device_get_name(dev), device_get_unit(dev), 5769500655eSAlexander Motin "timecounter", &i8254_timecounter); 577cb261f43SBrooks Davis set_i8254_freq(MODE_STOP, 0); 5789500655eSAlexander Motin if (i8254_timecounter) { 579875b8844SAlexander Motin sc->tc.tc_get_timecount = i8254_get_timecount; 580875b8844SAlexander Motin sc->tc.tc_counter_mask = 0xffff; 581875b8844SAlexander Motin sc->tc.tc_frequency = i8254_freq; 582875b8844SAlexander Motin sc->tc.tc_name = "i8254"; 583875b8844SAlexander Motin sc->tc.tc_quality = 0; 584875b8844SAlexander Motin sc->tc.tc_priv = dev; 585875b8844SAlexander Motin tc_init(&sc->tc); 5869500655eSAlexander Motin } 587875b8844SAlexander Motin if (resource_int_value(device_get_name(dev), device_get_unit(dev), 588875b8844SAlexander Motin "clock", &i) != 0 || i != 0) { 5896019ba4eSAlexander Motin sc->intr_rid = 0; 59091751b1aSAlexander Motin while (bus_get_resource(dev, SYS_RES_IRQ, sc->intr_rid, 59191751b1aSAlexander Motin &s, NULL) == 0 && s != 0) 59291751b1aSAlexander Motin sc->intr_rid++; 59349ed68bbSAlexander Motin if (!(sc->intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, 59449ed68bbSAlexander Motin &sc->intr_rid, 0, 0, 1, RF_ACTIVE))) { 59549ed68bbSAlexander Motin device_printf(dev,"Can't map interrupt.\n"); 59649ed68bbSAlexander Motin return (0); 59749ed68bbSAlexander Motin } 598875b8844SAlexander Motin /* Dirty hack, to make bus_setup_intr to not enable source. */ 599875b8844SAlexander Motin i8254_intsrc->is_handlers++; 600875b8844SAlexander Motin if ((bus_setup_intr(dev, sc->intr_res, 601875b8844SAlexander Motin INTR_MPSAFE | INTR_TYPE_CLK, 602875b8844SAlexander Motin (driver_filter_t *)clkintr, NULL, 603875b8844SAlexander Motin sc, &sc->intr_handler))) { 604875b8844SAlexander Motin device_printf(dev, "Can't setup interrupt.\n"); 60549ed68bbSAlexander Motin i8254_intsrc->is_handlers--; 60649ed68bbSAlexander Motin return (0); 60749ed68bbSAlexander Motin } 60849ed68bbSAlexander Motin i8254_intsrc->is_handlers--; 609875b8844SAlexander Motin i8254_intsrc->is_pic->pic_enable_intr(i8254_intsrc); 610875b8844SAlexander Motin sc->et.et_name = "i8254"; 611875b8844SAlexander Motin sc->et.et_flags = ET_FLAGS_PERIODIC; 6129500655eSAlexander Motin if (!i8254_timecounter) 6139500655eSAlexander Motin sc->et.et_flags |= ET_FLAGS_ONESHOT; 614875b8844SAlexander Motin sc->et.et_quality = 100; 615875b8844SAlexander Motin sc->et.et_frequency = i8254_freq; 616fdc5dd2dSAlexander Motin sc->et.et_min_period = (0x0002LLU << 32) / i8254_freq; 617fdc5dd2dSAlexander Motin sc->et.et_max_period = (0xfffeLLU << 32) / i8254_freq; 618875b8844SAlexander Motin sc->et.et_start = attimer_start; 619875b8844SAlexander Motin sc->et.et_stop = attimer_stop; 620875b8844SAlexander Motin sc->et.et_priv = dev; 621875b8844SAlexander Motin et_register(&sc->et); 622875b8844SAlexander Motin } 62332580301SAttilio Rao return(0); 62432580301SAttilio Rao } 62532580301SAttilio Rao 62632580301SAttilio Rao static int 62732580301SAttilio Rao attimer_resume(device_t dev) 62832580301SAttilio Rao { 62932580301SAttilio Rao 63032580301SAttilio Rao i8254_restore(); 63132580301SAttilio Rao return (0); 63232580301SAttilio Rao } 63332580301SAttilio Rao 63432580301SAttilio Rao static device_method_t attimer_methods[] = { 63532580301SAttilio Rao /* Device interface */ 63632580301SAttilio Rao DEVMETHOD(device_probe, attimer_probe), 63732580301SAttilio Rao DEVMETHOD(device_attach, attimer_attach), 63832580301SAttilio Rao DEVMETHOD(device_resume, attimer_resume), 63932580301SAttilio Rao { 0, 0 } 64032580301SAttilio Rao }; 64132580301SAttilio Rao 64232580301SAttilio Rao static driver_t attimer_driver = { 64332580301SAttilio Rao "attimer", 64432580301SAttilio Rao attimer_methods, 645875b8844SAlexander Motin sizeof(struct attimer_softc), 64632580301SAttilio Rao }; 64732580301SAttilio Rao 64880d2b3deSJohn Baldwin DRIVER_MODULE(attimer, isa, attimer_driver, 0, 0); 64980d2b3deSJohn Baldwin DRIVER_MODULE(attimer, acpi, attimer_driver, 0, 0); 650d6b66397SWarner Losh ISA_PNP_INFO(attimer_ids); 65132580301SAttilio Rao 65232580301SAttilio Rao #endif /* DEV_ISA */ 653