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.
23*e8763682SPavel Zakharov  * Copyright (c) 2017 by Delphix. All rights reserved.
2441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
2541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
2641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Copyright (c) 2010, Intel Corporation.
2741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * All rights reserved.
2841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
29223b8c65SGarrett D'Amore /*
30223b8c65SGarrett D'Amore  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
31223b8c65SGarrett D'Amore  */
3241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
3341afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/time.h>
3441afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/psm.h>
3541afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/psm_common.h>
3641afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/apic.h>
3741afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/pit.h>
3841afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/x86_archext.h>
3941afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/archsystm.h>
4041afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/machsystm.h>
4141afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/cpuvar.h>
4241afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/clock.h>
4341afdfa7SKrishnendu Sadhukhan - Sun Microsystems #include <sys/apic_timer.h>
4441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
4541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
4641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * preferred apic timer mode, allow tuning from the /etc/system file.
4741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
4841afdfa7SKrishnendu Sadhukhan - Sun Microsystems int		apic_timer_preferred_mode = APIC_TIMER_MODE_DEADLINE;
4941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
5041afdfa7SKrishnendu Sadhukhan - Sun Microsystems int		apic_oneshot = 0;
5141afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint_t		apic_hertz_count;
5241afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint_t		apic_nsec_per_intr = 0;
5341afdfa7SKrishnendu Sadhukhan - Sun Microsystems uint64_t	apic_ticks_per_SFnsecs;		/* # of ticks in SF nsecs */
5441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
5541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static int		apic_min_timer_ticks = 1; /* minimum timer tick */
5641afdfa7SKrishnendu Sadhukhan - Sun Microsystems static hrtime_t		apic_nsec_max;
5741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
5841afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_enable(void);
5941afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_disable(void);
6041afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	periodic_timer_reprogram(hrtime_t);
6141afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_enable(void);
6241afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_disable(void);
6341afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	oneshot_timer_reprogram(hrtime_t);
6441afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_enable(void);
6541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_disable(void);
6641afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void	deadline_timer_reprogram(hrtime_t);
6741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
6841afdfa7SKrishnendu Sadhukhan - Sun Microsystems extern int	apic_clkvect;
6941afdfa7SKrishnendu Sadhukhan - Sun Microsystems extern uint32_t	apic_divide_reg_init;
7041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
7141afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
7241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * apic timer data structure
7341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
7441afdfa7SKrishnendu Sadhukhan - Sun Microsystems typedef struct apic_timer {
7541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int	mode;
7641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_enable_ops)(void);
7741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_disable_ops)(void);
7841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	void	(*apic_timer_reprogram_ops)(hrtime_t);
7941afdfa7SKrishnendu Sadhukhan - Sun Microsystems } apic_timer_t;
8041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
8141afdfa7SKrishnendu Sadhukhan - Sun Microsystems static apic_timer_t	apic_timer;
8241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
8341afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
8441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * apic timer initialization
8541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
8641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * For the one-shot mode request case, the function returns the
8741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * resolution (in nanoseconds) for the hardware timer interrupt.
8841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * If one-shot mode capability is not available, the return value
8941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * will be 0.
9041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
9141afdfa7SKrishnendu Sadhukhan - Sun Microsystems int
apic_timer_init(int hertz)9241afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_init(int hertz)
9341afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
9441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int		ret, timer_mode;
9541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	static int	firsttime = 1;
9641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
9741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (firsttime) {
9841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* first time calibrate on CPU0 only */
99*e8763682SPavel Zakharov 		apic_ticks_per_SFnsecs = apic_calibrate();
10041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* the interval timer initial count is 32 bit max */
10241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL);
10341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		firsttime = 0;
10441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
10541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (hertz == 0) {
10741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* requested one_shot */
10841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
10941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
11041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * return 0 if TSC is not supported.
11141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
11241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if (!tsc_gethrtime_enable)
11341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			return (0);
11441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
11541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * return 0 if one_shot is not preferred.
11641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * here, APIC_TIMER_DEADLINE is also an one_shot mode.
11741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
11841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if ((apic_timer_preferred_mode != APIC_TIMER_MODE_ONESHOT) &&
11941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    (apic_timer_preferred_mode != APIC_TIMER_MODE_DEADLINE))
12041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			return (0);
12141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
12241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_oneshot = 1;
12341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ret = (int)APIC_TICKS_TO_NSECS(1);
12441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		if ((apic_timer_preferred_mode == APIC_TIMER_MODE_DEADLINE) &&
12541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    cpuid_deadline_tsc_supported()) {
12641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			timer_mode = APIC_TIMER_MODE_DEADLINE;
12741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		} else {
12841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 			timer_mode = APIC_TIMER_MODE_ONESHOT;
12941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		}
13041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else {
13141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* periodic */
13241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_nsec_per_intr = NANOSEC / hertz;
13341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr);
13441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
13541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* program the local APIC to interrupt at the given frequency */
13641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_INIT_COUNT, apic_hertz_count);
13741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
13841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
13941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_oneshot = 0;
14041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		timer_mode = APIC_TIMER_MODE_PERIODIC;
14141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ret = NANOSEC / hertz;
14241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
14341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
14441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
14541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * initialize apic_timer data structure, install the timer ops
14641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
14741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.mode = timer_mode;
14841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	switch (timer_mode) {
14941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	default:
15041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/* FALLTHROUGH */
15141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_ONESHOT:
15241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = oneshot_timer_enable;
15341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = oneshot_timer_disable;
15441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = oneshot_timer_reprogram;
15541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
15641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
15741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_PERIODIC:
15841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = periodic_timer_enable;
15941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = periodic_timer_disable;
16041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = periodic_timer_reprogram;
16141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
16241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
16341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	case APIC_TIMER_MODE_DEADLINE:
16441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_enable_ops = deadline_timer_enable;
16541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_disable_ops = deadline_timer_disable;
16641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		apic_timer.apic_timer_reprogram_ops = deadline_timer_reprogram;
16741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		break;
16841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
16941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
17041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	return (ret);
17141afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
17241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
17341afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
17441afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * periodic timer mode ops
17541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
17641afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer enable */
17741afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
periodic_timer_enable(void)17841afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_enable(void)
17941afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
18041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
18141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_PERIODIC);
18241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
18341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
18441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer disable */
18541afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
periodic_timer_disable(void)18641afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_disable(void)
18741afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
18841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
18941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
19041afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
19141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
19241afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* periodic timer reprogram */
19341afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
periodic_timer_reprogram(hrtime_t time)19441afdfa7SKrishnendu Sadhukhan - Sun Microsystems periodic_timer_reprogram(hrtime_t time)
19541afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
19641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t	ticks;
19741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/* time is the interval for periodic mode */
19841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ticks = APIC_NSECS_TO_TICKS(time);
19941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
20041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (ticks < apic_min_timer_ticks)
20141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
20241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
20341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
20441afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
20541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
20641afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
20741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * oneshot timer mode ops
20841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
20941afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer enable */
21041afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
oneshot_timer_enable(void)21141afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_enable(void)
21241afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
21341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
21441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT));
21541afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
21641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
21741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer disable */
21841afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
oneshot_timer_disable(void)21941afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_disable(void)
22041afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
22141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
22241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
22341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
22441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
22541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* oneshot timer reprogram */
22641afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
oneshot_timer_reprogram(hrtime_t time)22741afdfa7SKrishnendu Sadhukhan - Sun Microsystems oneshot_timer_reprogram(hrtime_t time)
22841afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
22941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	hrtime_t	now;
23041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int64_t		delta;
23141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint_t		ticks;
23241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
23341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	now = gethrtime();
23441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	delta = time - now;
23541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
23641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (delta <= 0) {
23741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
23841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * requested to generate an interrupt in the past
23941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * generate an interrupt as soon as possible
24041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
24141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
24241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else if (delta > apic_nsec_max) {
24341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		/*
24441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * requested to generate an interrupt at a time
24541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * further than what we are capable of. Set to max
24641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 * the hardware can handle
24741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		 */
24841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = APIC_MAXVAL;
24941afdfa7SKrishnendu Sadhukhan - Sun Microsystems #ifdef DEBUG
25041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		cmn_err(CE_CONT, "apic_timer_reprogram, request at"
25141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    "  %lld  too far in future, current time"
25241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		    "  %lld \n", time, now);
25341afdfa7SKrishnendu Sadhukhan - Sun Microsystems #endif
25441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	} else {
25541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = APIC_NSECS_TO_TICKS(delta);
25641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	}
25741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
25841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if (ticks < apic_min_timer_ticks)
25941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		ticks = apic_min_timer_ticks;
26041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
26141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, ticks);
26241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
26341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
26441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
26541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * deadline timer mode ops
26641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
26741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer enable */
26841afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
deadline_timer_enable(void)26941afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_enable(void)
27041afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
271223b8c65SGarrett D'Amore 	uint64_t ticks;
272223b8c65SGarrett D'Amore 
27341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
27441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_DEADLINE);
275223b8c65SGarrett D'Amore 	/*
276223b8c65SGarrett D'Amore 	 * Now we have to serialize this per the SDM.  That is to
277223b8c65SGarrett D'Amore 	 * say, the above enabling can race in the pipeline with
278223b8c65SGarrett D'Amore 	 * changes to the MSR.  We need to make sure the above
279223b8c65SGarrett D'Amore 	 * operation is complete before we proceed to reprogram
280223b8c65SGarrett D'Amore 	 * the deadline value in reprogram().  The algorithm
281223b8c65SGarrett D'Amore 	 * recommended by the Intel SDM 3A in 10.5.1.4 is:
282223b8c65SGarrett D'Amore 	 *
283223b8c65SGarrett D'Amore 	 * a) write a big value to the deadline register
284223b8c65SGarrett D'Amore 	 * b) read the register back
285223b8c65SGarrett D'Amore 	 * c) if it reads zero, go back to a and try again
286223b8c65SGarrett D'Amore 	 */
287223b8c65SGarrett D'Amore 
288223b8c65SGarrett D'Amore 	do {
289223b8c65SGarrett D'Amore 		/* write a really big value */
290223b8c65SGarrett D'Amore 		wrmsr(IA32_DEADLINE_TSC_MSR, 1ULL << 63);
291223b8c65SGarrett D'Amore 		ticks = rdmsr(IA32_DEADLINE_TSC_MSR);
292223b8c65SGarrett D'Amore 	} while (ticks == 0);
29341afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
29441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
29541afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer disable */
29641afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
deadline_timer_disable(void)29741afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_disable(void)
29841afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
29941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_LOCAL_TIMER,
30041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	    (apic_clkvect + APIC_BASE_VECT) | AV_MASK);
30141afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
30241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
30341afdfa7SKrishnendu Sadhukhan - Sun Microsystems /* deadline timer reprogram */
30441afdfa7SKrishnendu Sadhukhan - Sun Microsystems static void
deadline_timer_reprogram(hrtime_t time)30541afdfa7SKrishnendu Sadhukhan - Sun Microsystems deadline_timer_reprogram(hrtime_t time)
30641afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
307223b8c65SGarrett D'Amore 	int64_t		delta;
30841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	uint64_t	ticks;
30941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
310223b8c65SGarrett D'Amore 	/*
311223b8c65SGarrett D'Amore 	 * Note that this entire routine is called with
312223b8c65SGarrett D'Amore 	 * CBE_HIGH_PIL, so we needn't worry about preemption.
313223b8c65SGarrett D'Amore 	 */
314223b8c65SGarrett D'Amore 	delta = time - gethrtime();
315223b8c65SGarrett D'Amore 
316223b8c65SGarrett D'Amore 	/* The unscalehrtime wants unsigned values. */
317223b8c65SGarrett D'Amore 	delta = max(delta, 0);
31841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
319223b8c65SGarrett D'Amore 	/* Now we shouldn't be interrupted, we can set the deadline */
320223b8c65SGarrett D'Amore 	ticks = (uint64_t)tsc_read() + unscalehrtime(delta);
32141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	wrmsr(IA32_DEADLINE_TSC_MSR, ticks);
32241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
32341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
32441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
32541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will reprogram the timer.
32641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
32741afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * When in oneshot mode the argument is the absolute time in future to
32841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * generate the interrupt at.
32941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  *
33041afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * When in periodic mode, the argument is the interval at which the
33141afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * interrupts should be generated. There is no need to support the periodic
33241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * mode timer change at this time.
33341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
33441afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
apic_timer_reprogram(hrtime_t time)33541afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_reprogram(hrtime_t time)
33641afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
33741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
33841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
33941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
34041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
34141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_reprogram_ops(time);
34241afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
34341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
34441afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
34541afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will enable timer interrupts.
34641afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
34741afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
apic_timer_enable(void)34841afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_enable(void)
34941afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
35041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
35141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
35241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
35341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
35441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_enable_ops();
35541afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
35641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
35741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
35841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * This function will disable timer interrupts.
35941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
36041afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
apic_timer_disable(void)36141afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_disable(void)
36241afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
36341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
36441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * we should be Called from high PIL context (CBE_HIGH_PIL),
36541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * so kpreempt is disabled.
36641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
36741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer.apic_timer_disable_ops();
36841afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
36941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
37041afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
37141afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Set timer far into the future and return timer
37241afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * current count in nanoseconds.
37341afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
37441afdfa7SKrishnendu Sadhukhan - Sun Microsystems hrtime_t
apic_timer_stop_count(void)37541afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_stop_count(void)
37641afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
37741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	hrtime_t	ns_val;
37841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	int		enable_val, count_val;
37941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
38041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	/*
38141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 * Should be called with interrupts disabled.
38241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	 */
38341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ASSERT(!interrupts_enabled());
38441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
38541afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	enable_val = apic_reg_ops->apic_read(APIC_LOCAL_TIMER);
38641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	if ((enable_val & AV_MASK) == AV_MASK)
38741afdfa7SKrishnendu Sadhukhan - Sun Microsystems 		return ((hrtime_t)-1);	/* timer is disabled */
38841afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
38941afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	count_val = apic_reg_ops->apic_read(APIC_CURR_COUNT);
39041afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	ns_val = APIC_TICKS_TO_NSECS(count_val);
39141afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
39241afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_reg_ops->apic_write(APIC_INIT_COUNT, APIC_MAXVAL);
39341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
39441afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	return (ns_val);
39541afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
39641afdfa7SKrishnendu Sadhukhan - Sun Microsystems 
39741afdfa7SKrishnendu Sadhukhan - Sun Microsystems /*
39841afdfa7SKrishnendu Sadhukhan - Sun Microsystems  * Reprogram timer after Deep C-State.
39941afdfa7SKrishnendu Sadhukhan - Sun Microsystems  */
40041afdfa7SKrishnendu Sadhukhan - Sun Microsystems void
apic_timer_restart(hrtime_t time)40141afdfa7SKrishnendu Sadhukhan - Sun Microsystems apic_timer_restart(hrtime_t time)
40241afdfa7SKrishnendu Sadhukhan - Sun Microsystems {
40341afdfa7SKrishnendu Sadhukhan - Sun Microsystems 	apic_timer_reprogram(time);
40441afdfa7SKrishnendu Sadhukhan - Sun Microsystems }
405