xref: /titanic_52/usr/src/uts/i86pc/io/pcplusmp/apic_timer.c (revision 223b8c65a9498294013b99c37d5b9024433237ec)
141afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * CDDL HEADER START
341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * The contents of this file are subject to the terms of the
541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Common Development and Distribution License (the "License").
641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * You may not use this file except in compliance with the License.
741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * or http://www.opensolaris.org/os/licensing.
1041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * See the License for the specific language governing permissions
1141afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * and limitations under the License.
1241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
1341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * When distributing Covered Code, include this CDDL HEADER in each
1441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * If applicable, add the following below this CDDL HEADER, with the
1641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * fields enclosed by brackets "[]" replaced with your own identifying
1741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * information: Portions Copyright [yyyy] [name of copyright owner]
1841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
1941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * CDDL HEADER END
2041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
2141afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
2241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
2441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
2541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Copyright (c) 2010, Intel Corporation.
2641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * All rights reserved.
2741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
28*223b8c65SGarrett D'Amore /*
29*223b8c65SGarrett D'Amore  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
30*223b8c65SGarrett D'Amore  */
3141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
3241afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/time.h>
3341afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/psm.h>
3441afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/psm_common.h>
3541afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/apic.h>
3641afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/pit.h>
3741afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/x86_archext.h>
3841afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/archsystm.h>
3941afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/machsystm.h>
4041afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/cpuvar.h>
4141afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/clock.h>
4241afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/apic_timer.h>
4341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
4441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
4541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * preferred apic timer mode, allow tuning from the /etc/system file.
4641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
4741afdfa7SKrishnendu Sadhukhan - Sun Microsystems int		apic_timer_preferred_mode = APIC_TIMER_MODE_DEADLINE;
4841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
4941afdfa7SKrishnendu Sadhukhan - Sun Microsystems int		apic_oneshot = 0;
5041afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint_t		apic_hertz_count;
5141afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint_t		apic_nsec_per_intr = 0;
5241afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint64_t	apic_ticks_per_SFnsecs;		/* # of ticks in SF nsecs */
5341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
5441afdfa7SKrishnendu Sadhukhan - Sun Microsystems static int		apic_min_timer_ticks = 1; /* minimum timer tick */
5541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static hrtime_t		apic_nsec_max;
5641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
5741afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_enable(void);
5841afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_disable(void);
5941afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_reprogram(hrtime_t);
6041afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_enable(void);
6141afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_disable(void);
6241afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_reprogram(hrtime_t);
6341afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_enable(void);
6441afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_disable(void);
6541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_reprogram(hrtime_t);
6641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
6741afdfa7SKrishnendu Sadhukhan - Sun Microsystems extern int	apic_clkvect;
6841afdfa7SKrishnendu Sadhukhan - Sun Microsystems extern uint32_t	apic_divide_reg_init;
6941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
7041afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
7141afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * apic timer data structure
7241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
7341afdfa7SKrishnendu Sadhukhan - Sun Microsystems typedef struct apic_timer {
7441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int	mode;
7541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_enable_ops)(void);
7641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_disable_ops)(void);
7741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_reprogram_ops)(hrtime_t);
7841afdfa7SKrishnendu Sadhukhan - Sun Microsystems } apic_timer_t;
7941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
8041afdfa7SKrishnendu Sadhukhan - Sun Microsystems static apic_timer_t	apic_timer;
8141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
8241afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
8341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * apic timer initialization
8441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
8541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * For the one-shot mode request case, the function returns the
8641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * resolution (in nanoseconds) for the hardware timer interrupt.
8741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * If one-shot mode capability is not available, the return value
8841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * will be 0.
8941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
9041afdfa7SKrishnendu Sadhukhan - Sun Microsystems int
9141afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_init(int hertz)
9241afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
9341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t		apic_ticks = 0;
9441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t		pit_ticks;
9541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int		ret, timer_mode;
9641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint16_t	pit_ticks_adj;
9741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	static int	firsttime = 1;
9841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
9941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (firsttime) {
10041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* first time calibrate on CPU0 only */
10141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_DIVIDE_REG, apic_divide_reg_init);
10341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
10441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj);
10541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* total number of PIT ticks corresponding to apic_ticks */
10741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		pit_ticks = APIC_TIME_COUNT + pit_ticks_adj;
10841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
11041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * Determine the number of nanoseconds per APIC clock tick
11141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * and then determine how many APIC ticks to interrupt at the
11241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * desired frequency
11341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s
11441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s
11541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9)
11641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * pic_ticks_per_SFns =
11741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9)
11841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
11941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_ticks_per_SFnsecs = ((SF * apic_ticks * PIT_HZ) /
12041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    ((uint64_t)pit_ticks * NANOSEC));
12141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
12241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* the interval timer initial count is 32 bit max */
12341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL);
12441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		firsttime = 0;
12541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
12641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
12741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (hertz == 0) {
12841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* requested one_shot */
12941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
13041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
13141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * return 0 if TSC is not supported.
13241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
13341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if (!tsc_gethrtime_enable)
13441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			return (0);
13541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
13641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * return 0 if one_shot is not preferred.
13741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * here, APIC_TIMER_DEADLINE is also an one_shot mode.
13841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
13941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if ((apic_timer_preferred_mode != APIC_TIMER_MODE_ONESHOT) &&
14041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    (apic_timer_preferred_mode != APIC_TIMER_MODE_DEADLINE))
14141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			return (0);
14241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
14341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_oneshot = 1;
14441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ret = (int)APIC_TICKS_TO_NSECS(1);
14541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if ((apic_timer_preferred_mode == APIC_TIMER_MODE_DEADLINE) &&
14641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    cpuid_deadline_tsc_supported()) {
14741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			timer_mode = APIC_TIMER_MODE_DEADLINE;
14841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		} else {
14941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			timer_mode = APIC_TIMER_MODE_ONESHOT;
15041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		}
15141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else {
15241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* periodic */
15341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_nsec_per_intr = NANOSEC / hertz;
15441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr);
15541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
15641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* program the local APIC to interrupt at the given frequency */
15741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_INIT_COUNT, apic_hertz_count);
15841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
15941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
16041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_oneshot = 0;
16141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		timer_mode = APIC_TIMER_MODE_PERIODIC;
16241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ret = NANOSEC / hertz;
16341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
16441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
16541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
16641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * initialize apic_timer data structure, install the timer ops
16741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
16841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.mode = timer_mode;
16941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	switch (timer_mode) {
17041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	default:
17141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* FALLTHROUGH */
17241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_ONESHOT:
17341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = oneshot_timer_enable;
17441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = oneshot_timer_disable;
17541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = oneshot_timer_reprogram;
17641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
17741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
17841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_PERIODIC:
17941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = periodic_timer_enable;
18041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = periodic_timer_disable;
18141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = periodic_timer_reprogram;
18241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
18341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
18441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_DEADLINE:
18541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = deadline_timer_enable;
18641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = deadline_timer_disable;
18741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = deadline_timer_reprogram;
18841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
18941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
19041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
19141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	return (ret);
19241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
19341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
19441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
19541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * periodic timer mode ops
19641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
19741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer enable */
19841afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
19941afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_enable(void)
20041afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
20141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
20241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
20341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
20441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
20541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer disable */
20641afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
20741afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_disable(void)
20841afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
20941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
21041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
21141afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
21241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
21341afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer reprogram */
21441afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
21541afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_reprogram(hrtime_t time)
21641afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
21741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t	ticks;
21841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/* time is the interval for periodic mode */
21941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ticks = APIC_NSECS_TO_TICKS(time);
22041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
22141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (ticks < apic_min_timer_ticks)
22241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
22341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
22441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
22541afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
22641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
22741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
22841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * oneshot timer mode ops
22941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
23041afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer enable */
23141afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
23241afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_enable(void)
23341afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
23441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
23541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT));
23641afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
23741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
23841afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer disable */
23941afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
24041afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_disable(void)
24141afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
24241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
24341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
24441afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
24541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
24641afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer reprogram */
24741afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
24841afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_reprogram(hrtime_t time)
24941afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
25041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	hrtime_t	now;
25141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int64_t		delta;
25241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t		ticks;
25341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
25441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	now = gethrtime();
25541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	delta = time - now;
25641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
25741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (delta <= 0) {
25841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
25941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * requested to generate an interrupt in the past
26041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * generate an interrupt as soon as possible
26141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
26241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
26341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else if (delta > apic_nsec_max) {
26441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
26541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * requested to generate an interrupt at a time
26641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * further than what we are capable of. Set to max
26741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * the hardware can handle
26841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
26941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = APIC_MAXVAL;
27041afdfa7SKrishnendu Sadhukhan - Sun Microsystems #ifdef DEBUG
27141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		cmn_err(CE_CONT, "apic_timer_reprogram, request at"
27241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    "  %lld  too far in future, current time"
27341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    "  %lld \n", time, now);
27441afdfa7SKrishnendu Sadhukhan - Sun Microsystems #endif
27541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else {
27641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = APIC_NSECS_TO_TICKS(delta);
27741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
27841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
27941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (ticks < apic_min_timer_ticks)
28041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
28141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
28241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
28341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
28441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
28541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
28641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * deadline timer mode ops
28741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
28841afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer enable */
28941afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
29041afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_enable(void)
29141afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
292*223b8c65SGarrett D'Amore 	uint64_t ticks;
293*223b8c65SGarrett D'Amore 
29441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
29541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_DEADLINE);
296*223b8c65SGarrett D'Amore 	/*
297*223b8c65SGarrett D'Amore 	 * Now we have to serialize this per the SDM.  That is to
298*223b8c65SGarrett D'Amore 	 * say, the above enabling can race in the pipeline with
299*223b8c65SGarrett D'Amore 	 * changes to the MSR.  We need to make sure the above
300*223b8c65SGarrett D'Amore 	 * operation is complete before we proceed to reprogram
301*223b8c65SGarrett D'Amore 	 * the deadline value in reprogram().  The algorithm
302*223b8c65SGarrett D'Amore 	 * recommended by the Intel SDM 3A in 10.5.1.4 is:
303*223b8c65SGarrett D'Amore 	 *
304*223b8c65SGarrett D'Amore 	 * a) write a big value to the deadline register
305*223b8c65SGarrett D'Amore 	 * b) read the register back
306*223b8c65SGarrett D'Amore 	 * c) if it reads zero, go back to a and try again
307*223b8c65SGarrett D'Amore 	 */
308*223b8c65SGarrett D'Amore 
309*223b8c65SGarrett D'Amore 	do {
310*223b8c65SGarrett D'Amore 		/* write a really big value */
311*223b8c65SGarrett D'Amore 		wrmsr(IA32_DEADLINE_TSC_MSR, 1ULL << 63);
312*223b8c65SGarrett D'Amore 		ticks = rdmsr(IA32_DEADLINE_TSC_MSR);
313*223b8c65SGarrett D'Amore 	} while (ticks == 0);
31441afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
31541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
31641afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer disable */
31741afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
31841afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_disable(void)
31941afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
32041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
32141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
32241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
32341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
32441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer reprogram */
32541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
32641afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_reprogram(hrtime_t time)
32741afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
328*223b8c65SGarrett D'Amore 	int64_t		delta;
32941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint64_t	ticks;
33041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
33141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
332*223b8c65SGarrett D'Amore 	 * Note that this entire routine is called with
333*223b8c65SGarrett D'Amore 	 * CBE_HIGH_PIL, so we needn't worry about preemption.
33441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
335*223b8c65SGarrett D'Amore 	delta = time - gethrtime();
33641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
337*223b8c65SGarrett D'Amore 	/* The unscalehrtime wants unsigned values. */
338*223b8c65SGarrett D'Amore 	delta = max(delta, 0);
339*223b8c65SGarrett D'Amore 
340*223b8c65SGarrett D'Amore 	/* Now we shouldn't be interrupted, we can set the deadline */
341*223b8c65SGarrett D'Amore 	ticks = (uint64_t)tsc_read() + unscalehrtime(delta);
34241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	wrmsr(IA32_DEADLINE_TSC_MSR, ticks);
34341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
34441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
34541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
34641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will reprogram the timer.
34741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
34841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * When in oneshot mode the argument is the absolute time in future to
34941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * generate the interrupt at.
35041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
35141afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * When in periodic mode, the argument is the interval at which the
35241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * interrupts should be generated. There is no need to support the periodic
35341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * mode timer change at this time.
35441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
35541afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
35641afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_reprogram(hrtime_t time)
35741afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
35841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
35941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
36041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
36141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
36241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_reprogram_ops(time);
36341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
36441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
36541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
36641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will enable timer interrupts.
36741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
36841afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
36941afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_enable(void)
37041afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
37141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
37241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
37341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
37441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
37541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_enable_ops();
37641afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
37741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
37841afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
37941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will disable timer interrupts.
38041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
38141afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
38241afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_disable(void)
38341afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
38441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
38541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
38641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
38741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
38841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_disable_ops();
38941afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
39041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
39141afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
39241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Set timer far into the future and return timer
39341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * current count in nanoseconds.
39441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
39541afdfa7SKrishnendu Sadhukhan - Sun Microsystems hrtime_t
39641afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_stop_count(void)
39741afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
39841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	hrtime_t	ns_val;
39941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int		enable_val, count_val;
40041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
40141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
40241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * Should be called with interrupts disabled.
40341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
40441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ASSERT(!interrupts_enabled());
40541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
40641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	enable_val = apic_reg_ops->apic_read(APIC_LOCAL_TIMER);
40741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if ((enable_val & AV_MASK) == AV_MASK)
40841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		return ((hrtime_t)-1);	/* timer is disabled */
40941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
41041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	count_val = apic_reg_ops->apic_read(APIC_CURR_COUNT);
41141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ns_val = APIC_TICKS_TO_NSECS(count_val);
41241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
41341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
41441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
41541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	return (ns_val);
41641afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
41741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
41841afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
41941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Reprogram timer after Deep C-State.
42041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
42141afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
42241afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_restart(hrtime_t time)
42341afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
42441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer_reprogram(time);
42541afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
426