xref: /freebsd-src/sys/x86/isa/clock.c (revision df615735960370255d3acc4ac2a6f4fd297b7461)
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