xref: /onnv-gate/usr/src/uts/i86pc/io/hpet_acpi.c (revision 9283:2ee48b3d20ef)
18906SEric.Saxe@Sun.COM /*
28906SEric.Saxe@Sun.COM  * CDDL HEADER START
38906SEric.Saxe@Sun.COM  *
48906SEric.Saxe@Sun.COM  * The contents of this file are subject to the terms of the
58906SEric.Saxe@Sun.COM  * Common Development and Distribution License (the "License").
68906SEric.Saxe@Sun.COM  * You may not use this file except in compliance with the License.
78906SEric.Saxe@Sun.COM  *
88906SEric.Saxe@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98906SEric.Saxe@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108906SEric.Saxe@Sun.COM  * See the License for the specific language governing permissions
118906SEric.Saxe@Sun.COM  * and limitations under the License.
128906SEric.Saxe@Sun.COM  *
138906SEric.Saxe@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148906SEric.Saxe@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158906SEric.Saxe@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168906SEric.Saxe@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178906SEric.Saxe@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188906SEric.Saxe@Sun.COM  *
198906SEric.Saxe@Sun.COM  * CDDL HEADER END
208906SEric.Saxe@Sun.COM  */
218906SEric.Saxe@Sun.COM /*
228906SEric.Saxe@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238906SEric.Saxe@Sun.COM  * Use is subject to license terms.
248906SEric.Saxe@Sun.COM  */
258906SEric.Saxe@Sun.COM 
268906SEric.Saxe@Sun.COM #include <sys/hpet_acpi.h>
278906SEric.Saxe@Sun.COM #include <sys/hpet.h>
288906SEric.Saxe@Sun.COM #include <sys/bitmap.h>
298906SEric.Saxe@Sun.COM #include <sys/inttypes.h>
308906SEric.Saxe@Sun.COM #include <sys/time.h>
318906SEric.Saxe@Sun.COM #include <sys/sunddi.h>
328906SEric.Saxe@Sun.COM #include <sys/ksynch.h>
338906SEric.Saxe@Sun.COM #include <sys/apic.h>
348906SEric.Saxe@Sun.COM #include <sys/callb.h>
358906SEric.Saxe@Sun.COM #include <sys/clock.h>
368906SEric.Saxe@Sun.COM #include <sys/archsystm.h>
378906SEric.Saxe@Sun.COM #include <sys/cpupart.h>
388906SEric.Saxe@Sun.COM 
398906SEric.Saxe@Sun.COM /*
408906SEric.Saxe@Sun.COM  * hpet_state_lock is used to synchronize disabling/enabling deep c-states
418906SEric.Saxe@Sun.COM  * and to synchronize suspend/resume.
428906SEric.Saxe@Sun.COM  */
438906SEric.Saxe@Sun.COM static kmutex_t		hpet_state_lock;
448906SEric.Saxe@Sun.COM static struct hpet_state {
458906SEric.Saxe@Sun.COM 	boolean_t	proxy_installed;	/* CBE proxy interrupt setup */
468906SEric.Saxe@Sun.COM 	boolean_t	cpr;			/* currently in CPR */
478906SEric.Saxe@Sun.COM 	boolean_t	cpu_deep_idle;		/* user enable/disable */
488906SEric.Saxe@Sun.COM 	boolean_t	uni_cstate;		/* disable if only one cstate */
498906SEric.Saxe@Sun.COM } hpet_state = { B_FALSE, B_FALSE, B_TRUE, B_TRUE};
508906SEric.Saxe@Sun.COM 
518906SEric.Saxe@Sun.COM uint64_t hpet_spin_check = HPET_SPIN_CHECK;
528906SEric.Saxe@Sun.COM uint64_t hpet_spin_timeout = HPET_SPIN_TIMEOUT;
538906SEric.Saxe@Sun.COM uint64_t hpet_idle_spin_timeout = HPET_SPIN_TIMEOUT;
548906SEric.Saxe@Sun.COM uint64_t hpet_isr_spin_timeout = HPET_SPIN_TIMEOUT;
558906SEric.Saxe@Sun.COM 
568906SEric.Saxe@Sun.COM static kmutex_t		hpet_proxy_lock;	/* lock for lAPIC proxy data */
578906SEric.Saxe@Sun.COM /*
588906SEric.Saxe@Sun.COM  * hpet_proxy_users is a per-cpu array.
598906SEric.Saxe@Sun.COM  */
608906SEric.Saxe@Sun.COM static hpet_proxy_t	*hpet_proxy_users;	/* one per CPU */
618906SEric.Saxe@Sun.COM 
628906SEric.Saxe@Sun.COM 
638906SEric.Saxe@Sun.COM ACPI_TABLE_HPET		*hpet_table;		/* ACPI HPET table */
648906SEric.Saxe@Sun.COM hpet_info_t		hpet_info;		/* Human readable Information */
658906SEric.Saxe@Sun.COM 
668906SEric.Saxe@Sun.COM /*
678906SEric.Saxe@Sun.COM  * Provide HPET access from unix.so.
688906SEric.Saxe@Sun.COM  * Set up pointers to access symbols in pcplusmp.
698906SEric.Saxe@Sun.COM  */
708906SEric.Saxe@Sun.COM static void
718906SEric.Saxe@Sun.COM hpet_establish_hooks(void)
728906SEric.Saxe@Sun.COM {
738906SEric.Saxe@Sun.COM 	hpet.install_proxy = &hpet_install_proxy;
748906SEric.Saxe@Sun.COM 	hpet.callback = &hpet_callback;
758906SEric.Saxe@Sun.COM 	hpet.use_hpet_timer = &hpet_use_hpet_timer;
768906SEric.Saxe@Sun.COM 	hpet.use_lapic_timer = &hpet_use_lapic_timer;
778906SEric.Saxe@Sun.COM }
788906SEric.Saxe@Sun.COM 
798906SEric.Saxe@Sun.COM /*
808906SEric.Saxe@Sun.COM  * Get the ACPI "HPET" table.
818906SEric.Saxe@Sun.COM  * acpi_probe() calls this function from mp_startup before drivers are loaded.
828906SEric.Saxe@Sun.COM  * acpi_probe() verified the system is using ACPI before calling this.
838906SEric.Saxe@Sun.COM  *
848906SEric.Saxe@Sun.COM  * There may be more than one ACPI HPET table (Itanium only?).
858906SEric.Saxe@Sun.COM  * Intel's HPET spec defines each timer block to have up to 32 counters and
868906SEric.Saxe@Sun.COM  * be 1024 bytes long.  There can be more than one timer block of 32 counters.
878906SEric.Saxe@Sun.COM  * Each timer block would have an additional ACPI HPET table.
888906SEric.Saxe@Sun.COM  * Typical x86 systems today only have 1 HPET with 3 counters.
898906SEric.Saxe@Sun.COM  * On x86 we only consume HPET table "1" for now.
908906SEric.Saxe@Sun.COM  */
918906SEric.Saxe@Sun.COM int
928906SEric.Saxe@Sun.COM hpet_acpi_init(int *hpet_vect, iflag_t *hpet_flags)
938906SEric.Saxe@Sun.COM {
948906SEric.Saxe@Sun.COM 	extern hrtime_t tsc_read(void);
958906SEric.Saxe@Sun.COM 	extern int	idle_cpu_no_deep_c;
968906SEric.Saxe@Sun.COM 	extern int	cpuid_deep_cstates_supported(void);
978906SEric.Saxe@Sun.COM 	void		*la;
988906SEric.Saxe@Sun.COM 	uint64_t	ret;
998906SEric.Saxe@Sun.COM 	uint_t		num_timers;
1008906SEric.Saxe@Sun.COM 	uint_t		ti;
1018906SEric.Saxe@Sun.COM 
1028906SEric.Saxe@Sun.COM 	(void) memset(&hpet_info, 0, sizeof (hpet_info));
1038906SEric.Saxe@Sun.COM 	hpet.supported = HPET_NO_SUPPORT;
1048906SEric.Saxe@Sun.COM 
1058906SEric.Saxe@Sun.COM 	if (idle_cpu_no_deep_c)
1068906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1078906SEric.Saxe@Sun.COM 
1088906SEric.Saxe@Sun.COM 	if (!cpuid_deep_cstates_supported())
1098906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1108906SEric.Saxe@Sun.COM 
1118906SEric.Saxe@Sun.COM 	hpet_establish_hooks();
1128906SEric.Saxe@Sun.COM 
1138906SEric.Saxe@Sun.COM 	/*
1148906SEric.Saxe@Sun.COM 	 * Get HPET ACPI table 1.
1158906SEric.Saxe@Sun.COM 	 */
1168906SEric.Saxe@Sun.COM 	if (ACPI_FAILURE(AcpiGetTable(ACPI_SIG_HPET, HPET_TABLE_1,
1178906SEric.Saxe@Sun.COM 	    (ACPI_TABLE_HEADER **)&hpet_table))) {
1188906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: unable to get ACPI HPET table");
1198906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1208906SEric.Saxe@Sun.COM 	}
1218906SEric.Saxe@Sun.COM 
1228906SEric.Saxe@Sun.COM 	if (hpet_validate_table(hpet_table) != AE_OK) {
1238906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: invalid HPET table");
1248906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1258906SEric.Saxe@Sun.COM 	}
1268906SEric.Saxe@Sun.COM 
1278906SEric.Saxe@Sun.COM 	la = hpet_memory_map(hpet_table);
1288906SEric.Saxe@Sun.COM 	if (la == NULL) {
1298906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: memory map HPET failed");
1308906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1318906SEric.Saxe@Sun.COM 	}
1328906SEric.Saxe@Sun.COM 	hpet_info.logical_address = la;
1338906SEric.Saxe@Sun.COM 
1348906SEric.Saxe@Sun.COM 	ret = hpet_read_gen_cap(&hpet_info);
1358906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.counter_clk_period = HPET_GCAP_CNTR_CLK_PERIOD(ret);
1368906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.vendor_id = HPET_GCAP_VENDOR_ID(ret);
1378906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.leg_route_cap = HPET_GCAP_LEG_ROUTE_CAP(ret);
1388906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.count_size_cap = HPET_GCAP_CNT_SIZE_CAP(ret);
1398906SEric.Saxe@Sun.COM 	/*
1408906SEric.Saxe@Sun.COM 	 * Hardware contains the last timer's number.
1418906SEric.Saxe@Sun.COM 	 * Add 1 to get the number of timers.
1428906SEric.Saxe@Sun.COM 	 */
1438906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.num_tim_cap = HPET_GCAP_NUM_TIM_CAP(ret) + 1;
1448906SEric.Saxe@Sun.COM 	hpet_info.gen_cap.rev_id = HPET_GCAP_REV_ID(ret);
1458906SEric.Saxe@Sun.COM 
1468906SEric.Saxe@Sun.COM 	if (hpet_info.gen_cap.counter_clk_period > HPET_MAX_CLK_PERIOD) {
1478906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: COUNTER_CLK_PERIOD 0x%lx > 0x%lx",
1488906SEric.Saxe@Sun.COM 		    (long)hpet_info.gen_cap.counter_clk_period,
1498906SEric.Saxe@Sun.COM 		    (long)HPET_MAX_CLK_PERIOD);
1508906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1518906SEric.Saxe@Sun.COM 	}
1528906SEric.Saxe@Sun.COM 
1538906SEric.Saxe@Sun.COM 	num_timers = (uint_t)hpet_info.gen_cap.num_tim_cap;
1548906SEric.Saxe@Sun.COM 	if ((num_timers < 3) || (num_timers > 32)) {
1558906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: invalid number of HPET timers "
1568906SEric.Saxe@Sun.COM 		    "%lx", (long)num_timers);
1578906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
1588906SEric.Saxe@Sun.COM 	}
1598906SEric.Saxe@Sun.COM 	hpet_info.timer_n_config = (hpet_TN_conf_cap_t *)kmem_zalloc(
1608906SEric.Saxe@Sun.COM 	    num_timers * sizeof (uint64_t), KM_SLEEP);
1618906SEric.Saxe@Sun.COM 
1628906SEric.Saxe@Sun.COM 	ret = hpet_read_gen_config(&hpet_info);
1638906SEric.Saxe@Sun.COM 	hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
1648906SEric.Saxe@Sun.COM 	hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
1658906SEric.Saxe@Sun.COM 
1668906SEric.Saxe@Sun.COM 	/*
1678906SEric.Saxe@Sun.COM 	 * Solaris does not use the HPET Legacy Replacement Route capabilities.
1688906SEric.Saxe@Sun.COM 	 * This feature has been off by default on test systems.
1698906SEric.Saxe@Sun.COM 	 * The HPET spec does not specify if Legacy Replacement Route is
1708906SEric.Saxe@Sun.COM 	 * on or off by default, so we explicitely set it off here.
1718906SEric.Saxe@Sun.COM 	 * It should not matter which mode the HPET is in since we use
1728906SEric.Saxe@Sun.COM 	 * the first available non-legacy replacement timer: timer 2.
1738906SEric.Saxe@Sun.COM 	 */
1748906SEric.Saxe@Sun.COM 	(void) hpet_set_leg_rt_cnf(&hpet_info, 0);
1758906SEric.Saxe@Sun.COM 
1768906SEric.Saxe@Sun.COM 	ret = hpet_read_gen_config(&hpet_info);
1778906SEric.Saxe@Sun.COM 	hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
1788906SEric.Saxe@Sun.COM 	hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
1798906SEric.Saxe@Sun.COM 
1808906SEric.Saxe@Sun.COM 	hpet_info.gen_intrpt_stat = hpet_read_gen_intrpt_stat(&hpet_info);
1818906SEric.Saxe@Sun.COM 	hpet_info.main_counter_value = hpet_read_main_counter_value(&hpet_info);
1828906SEric.Saxe@Sun.COM 
1838906SEric.Saxe@Sun.COM 	for (ti = 0; ti < num_timers; ++ti) {
1848906SEric.Saxe@Sun.COM 		ret = hpet_read_timer_N_config(&hpet_info, ti);
1858906SEric.Saxe@Sun.COM 		/*
1868906SEric.Saxe@Sun.COM 		 * Make sure no timers are enabled (think fast reboot or
1878906SEric.Saxe@Sun.COM 		 * virtual hardware).
1888906SEric.Saxe@Sun.COM 		 */
1898906SEric.Saxe@Sun.COM 		if (ret & HPET_TIMER_N_INT_ENB_CNF_BIT) {
1908906SEric.Saxe@Sun.COM 			hpet_disable_timer(&hpet_info, ti);
1918906SEric.Saxe@Sun.COM 			ret &= ~HPET_TIMER_N_INT_ENB_CNF_BIT;
1928906SEric.Saxe@Sun.COM 		}
1938906SEric.Saxe@Sun.COM 
1948906SEric.Saxe@Sun.COM 		hpet_info.timer_n_config[ti] = hpet_convert_timer_N_config(ret);
1958906SEric.Saxe@Sun.COM 	}
1968906SEric.Saxe@Sun.COM 
1978906SEric.Saxe@Sun.COM 	/*
1988906SEric.Saxe@Sun.COM 	 * Be aware the Main Counter may need to be initialized in the future
1998906SEric.Saxe@Sun.COM 	 * if it is used for more than just Deep C-State support.
2008906SEric.Saxe@Sun.COM 	 * The HPET's Main Counter does not need to be initialize to a specific
2018906SEric.Saxe@Sun.COM 	 * value before starting it for use to wake up CPUs from Deep C-States.
2028906SEric.Saxe@Sun.COM 	 */
2038906SEric.Saxe@Sun.COM 	if (hpet_start_main_counter(&hpet_info) != AE_OK) {
2048906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_acpi: hpet_start_main_counter failed");
2058906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
2068906SEric.Saxe@Sun.COM 	}
2078906SEric.Saxe@Sun.COM 
2088906SEric.Saxe@Sun.COM 	hpet_info.period = hpet_info.gen_cap.counter_clk_period;
2098906SEric.Saxe@Sun.COM 	/*
2108906SEric.Saxe@Sun.COM 	 * Read main counter twice to record HPET latency for debugging.
2118906SEric.Saxe@Sun.COM 	 */
2128906SEric.Saxe@Sun.COM 	hpet_info.tsc[0] = tsc_read();
2138906SEric.Saxe@Sun.COM 	hpet_info.hpet_main_counter_reads[0] =
2148906SEric.Saxe@Sun.COM 	    hpet_read_main_counter_value(&hpet_info);
2158906SEric.Saxe@Sun.COM 	hpet_info.tsc[1] = tsc_read();
2168906SEric.Saxe@Sun.COM 	hpet_info.hpet_main_counter_reads[1] =
2178906SEric.Saxe@Sun.COM 	    hpet_read_main_counter_value(&hpet_info);
2188906SEric.Saxe@Sun.COM 	hpet_info.tsc[2] = tsc_read();
2198906SEric.Saxe@Sun.COM 
2208906SEric.Saxe@Sun.COM 	ret = hpet_read_gen_config(&hpet_info);
2218906SEric.Saxe@Sun.COM 	hpet_info.gen_config.leg_rt_cnf = HPET_GCFR_LEG_RT_CNF_BITX(ret);
2228906SEric.Saxe@Sun.COM 	hpet_info.gen_config.enable_cnf = HPET_GCFR_ENABLE_CNF_BITX(ret);
2238906SEric.Saxe@Sun.COM 
2248906SEric.Saxe@Sun.COM 	/*
2258906SEric.Saxe@Sun.COM 	 * HPET main counter reads are supported now.
2268906SEric.Saxe@Sun.COM 	 */
2278906SEric.Saxe@Sun.COM 	hpet.supported = HPET_TIMER_SUPPORT;
2288906SEric.Saxe@Sun.COM 
2298906SEric.Saxe@Sun.COM 	return (hpet_init_proxy(hpet_vect, hpet_flags));
2308906SEric.Saxe@Sun.COM }
2318906SEric.Saxe@Sun.COM 
2328906SEric.Saxe@Sun.COM void
2338906SEric.Saxe@Sun.COM hpet_acpi_fini(void)
2348906SEric.Saxe@Sun.COM {
2358906SEric.Saxe@Sun.COM 	if (hpet.supported == HPET_NO_SUPPORT)
2368906SEric.Saxe@Sun.COM 		return;
2378906SEric.Saxe@Sun.COM 	if (hpet.supported >= HPET_TIMER_SUPPORT)
2388983SBill.Holler@Sun.COM 		(void) hpet_stop_main_counter(&hpet_info);
2398906SEric.Saxe@Sun.COM 	if (hpet.supported > HPET_TIMER_SUPPORT)
2408906SEric.Saxe@Sun.COM 		hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
2418906SEric.Saxe@Sun.COM }
2428906SEric.Saxe@Sun.COM 
2438906SEric.Saxe@Sun.COM /*
2448906SEric.Saxe@Sun.COM  * Do initial setup to use a HPET timer as a proxy for Deep C-state stalled
2458906SEric.Saxe@Sun.COM  * LAPIC Timers.  Get a free HPET timer that supports I/O APIC routed interrupt.
2468906SEric.Saxe@Sun.COM  * Setup data to handle the timer's ISR, and add the timer's interrupt.
2478906SEric.Saxe@Sun.COM  *
2488906SEric.Saxe@Sun.COM  * The ddi cannot be use to allocate the HPET timer's interrupt.
2498906SEric.Saxe@Sun.COM  * ioapic_init_intr() in mp_platform_common() later sets up the I/O APIC
2508906SEric.Saxe@Sun.COM  * to handle the HPET timer's interrupt.
2518906SEric.Saxe@Sun.COM  *
2528906SEric.Saxe@Sun.COM  * Note: FSB (MSI) interrupts are not currently supported by Intel HPETs as of
2538906SEric.Saxe@Sun.COM  * ICH9.  The HPET spec allows for MSI.  In the future MSI may be prefered.
2548906SEric.Saxe@Sun.COM  */
2558906SEric.Saxe@Sun.COM static int
2568906SEric.Saxe@Sun.COM hpet_init_proxy(int *hpet_vect, iflag_t *hpet_flags)
2578906SEric.Saxe@Sun.COM {
2588906SEric.Saxe@Sun.COM 	if (hpet_get_IOAPIC_intr_capable_timer(&hpet_info) == -1) {
2598906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_acpi: get ioapic intr failed.");
2608906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
2618906SEric.Saxe@Sun.COM 	}
2628906SEric.Saxe@Sun.COM 
2638906SEric.Saxe@Sun.COM 	hpet_init_proxy_data();
2648906SEric.Saxe@Sun.COM 
2658906SEric.Saxe@Sun.COM 	if (hpet_install_interrupt_handler(&hpet_isr,
2668906SEric.Saxe@Sun.COM 	    hpet_info.cstate_timer.intr) != AE_OK) {
2678906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_acpi: install interrupt failed.");
2688906SEric.Saxe@Sun.COM 		return (DDI_FAILURE);
2698906SEric.Saxe@Sun.COM 	}
2708906SEric.Saxe@Sun.COM 	*hpet_vect = hpet_info.cstate_timer.intr;
2718906SEric.Saxe@Sun.COM 	hpet_flags->intr_el = INTR_EL_LEVEL;
2728906SEric.Saxe@Sun.COM 	hpet_flags->intr_po = INTR_PO_ACTIVE_HIGH;
2738906SEric.Saxe@Sun.COM 	hpet_flags->bustype = BUS_PCI;		/*  we *do* conform to PCI */
2748906SEric.Saxe@Sun.COM 
2758906SEric.Saxe@Sun.COM 	/*
2768906SEric.Saxe@Sun.COM 	 * Avoid a possibly stuck interrupt by programing the HPET's timer here
2778906SEric.Saxe@Sun.COM 	 * before the I/O APIC is programmed to handle this interrupt.
2788906SEric.Saxe@Sun.COM 	 */
2798906SEric.Saxe@Sun.COM 	hpet_timer_set_up(&hpet_info, hpet_info.cstate_timer.timer,
2808906SEric.Saxe@Sun.COM 	    hpet_info.cstate_timer.intr);
2818906SEric.Saxe@Sun.COM 
2828906SEric.Saxe@Sun.COM 	/*
2838906SEric.Saxe@Sun.COM 	 * All HPET functionality is supported.
2848906SEric.Saxe@Sun.COM 	 */
2858906SEric.Saxe@Sun.COM 	hpet.supported = HPET_FULL_SUPPORT;
2868906SEric.Saxe@Sun.COM 	return (DDI_SUCCESS);
2878906SEric.Saxe@Sun.COM }
2888906SEric.Saxe@Sun.COM 
2898906SEric.Saxe@Sun.COM /*
2908906SEric.Saxe@Sun.COM  * Called by kernel if it can support Deep C-States.
2918906SEric.Saxe@Sun.COM  */
2928906SEric.Saxe@Sun.COM static boolean_t
2938906SEric.Saxe@Sun.COM hpet_install_proxy(void)
2948906SEric.Saxe@Sun.COM {
2958906SEric.Saxe@Sun.COM 	if (hpet_state.proxy_installed == B_TRUE)
2968906SEric.Saxe@Sun.COM 		return (B_TRUE);
2978906SEric.Saxe@Sun.COM 
2988906SEric.Saxe@Sun.COM 	if (hpet.supported != HPET_FULL_SUPPORT)
2998906SEric.Saxe@Sun.COM 		return (B_FALSE);
3008906SEric.Saxe@Sun.COM 
3018906SEric.Saxe@Sun.COM 	hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
3028906SEric.Saxe@Sun.COM 	hpet_state.proxy_installed = B_TRUE;
3038906SEric.Saxe@Sun.COM 
3048906SEric.Saxe@Sun.COM 	return (B_TRUE);
3058906SEric.Saxe@Sun.COM }
3068906SEric.Saxe@Sun.COM 
3078906SEric.Saxe@Sun.COM /*
3088906SEric.Saxe@Sun.COM  * Remove the interrupt that was added with add_avintr() in
3098906SEric.Saxe@Sun.COM  * hpet_install_interrupt_handler().
3108906SEric.Saxe@Sun.COM  */
3118906SEric.Saxe@Sun.COM static void
3128906SEric.Saxe@Sun.COM hpet_uninstall_interrupt_handler(void)
3138906SEric.Saxe@Sun.COM {
3148906SEric.Saxe@Sun.COM 	rem_avintr(NULL, CBE_HIGH_PIL, (avfunc)&hpet_isr,
3158906SEric.Saxe@Sun.COM 	    hpet_info.cstate_timer.intr);
3168906SEric.Saxe@Sun.COM }
3178906SEric.Saxe@Sun.COM 
3188906SEric.Saxe@Sun.COM static int
3198906SEric.Saxe@Sun.COM hpet_validate_table(ACPI_TABLE_HPET *hpet_table)
3208906SEric.Saxe@Sun.COM {
3218906SEric.Saxe@Sun.COM 	ACPI_TABLE_HEADER	*table_header = (ACPI_TABLE_HEADER *)hpet_table;
3228906SEric.Saxe@Sun.COM 
3238906SEric.Saxe@Sun.COM 	if (table_header->Length != sizeof (ACPI_TABLE_HPET)) {
3248906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_validate_table: Length %lx != sizeof ("
3258906SEric.Saxe@Sun.COM 		    "ACPI_TABLE_HPET) %lx.",
3268906SEric.Saxe@Sun.COM 		    (unsigned long)((ACPI_TABLE_HEADER *)hpet_table)->Length,
3278906SEric.Saxe@Sun.COM 		    (unsigned long)sizeof (ACPI_TABLE_HPET));
3288906SEric.Saxe@Sun.COM 		return (AE_ERROR);
3298906SEric.Saxe@Sun.COM 	}
3308906SEric.Saxe@Sun.COM 
3318906SEric.Saxe@Sun.COM 	if (!ACPI_COMPARE_NAME(table_header->Signature, ACPI_SIG_HPET)) {
3328906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_validate_table: Invalid HPET table "
3338906SEric.Saxe@Sun.COM 		    "signature");
3348906SEric.Saxe@Sun.COM 		return (AE_ERROR);
3358906SEric.Saxe@Sun.COM 	}
3368906SEric.Saxe@Sun.COM 
3378906SEric.Saxe@Sun.COM 	if (!hpet_checksum_table((unsigned char *)hpet_table,
3388906SEric.Saxe@Sun.COM 	    (unsigned int)table_header->Length)) {
3398906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_validate_table: Invalid HPET checksum");
3408906SEric.Saxe@Sun.COM 		return (AE_ERROR);
3418906SEric.Saxe@Sun.COM 	}
3428906SEric.Saxe@Sun.COM 
3438906SEric.Saxe@Sun.COM 	/*
3448906SEric.Saxe@Sun.COM 	 * Sequence should be table number - 1.  We are using table 1.
3458906SEric.Saxe@Sun.COM 	 */
3468906SEric.Saxe@Sun.COM 	if (hpet_table->Sequence != HPET_TABLE_1 - 1) {
3478906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_validate_table: Invalid Sequence %lx",
3488906SEric.Saxe@Sun.COM 		    (long)hpet_table->Sequence);
3498906SEric.Saxe@Sun.COM 		return (AE_ERROR);
3508906SEric.Saxe@Sun.COM 	}
3518906SEric.Saxe@Sun.COM 
3528906SEric.Saxe@Sun.COM 	return (AE_OK);
3538906SEric.Saxe@Sun.COM }
3548906SEric.Saxe@Sun.COM 
3558906SEric.Saxe@Sun.COM static boolean_t
3568906SEric.Saxe@Sun.COM hpet_checksum_table(unsigned char *table, unsigned int length)
3578906SEric.Saxe@Sun.COM {
3588906SEric.Saxe@Sun.COM 	unsigned char	checksum = 0;
3598906SEric.Saxe@Sun.COM 	int		i;
3608906SEric.Saxe@Sun.COM 
3618906SEric.Saxe@Sun.COM 	for (i = 0; i < length; ++i, ++table)
3628906SEric.Saxe@Sun.COM 		checksum += *table;
3638906SEric.Saxe@Sun.COM 
3648906SEric.Saxe@Sun.COM 	return (checksum == 0);
3658906SEric.Saxe@Sun.COM }
3668906SEric.Saxe@Sun.COM 
3678906SEric.Saxe@Sun.COM static void *
3688906SEric.Saxe@Sun.COM hpet_memory_map(ACPI_TABLE_HPET *hpet_table)
3698906SEric.Saxe@Sun.COM {
3708906SEric.Saxe@Sun.COM 	return (AcpiOsMapMemory(hpet_table->Address.Address, HPET_SIZE));
3718906SEric.Saxe@Sun.COM }
3728906SEric.Saxe@Sun.COM 
3738906SEric.Saxe@Sun.COM static int
3748906SEric.Saxe@Sun.COM hpet_start_main_counter(hpet_info_t *hip)
3758906SEric.Saxe@Sun.COM {
3768906SEric.Saxe@Sun.COM 	uint64_t	*gcr_ptr;
3778906SEric.Saxe@Sun.COM 	uint64_t	gcr;
3788906SEric.Saxe@Sun.COM 
3798906SEric.Saxe@Sun.COM 	gcr_ptr = (uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address);
3808906SEric.Saxe@Sun.COM 	gcr = *gcr_ptr;
3818906SEric.Saxe@Sun.COM 
3828906SEric.Saxe@Sun.COM 	gcr |= HPET_GCFR_ENABLE_CNF;
3838906SEric.Saxe@Sun.COM 	*gcr_ptr = gcr;
3848906SEric.Saxe@Sun.COM 	gcr = *gcr_ptr;
3858906SEric.Saxe@Sun.COM 
3868906SEric.Saxe@Sun.COM 	return (gcr & HPET_GCFR_ENABLE_CNF ? AE_OK : ~AE_OK);
3878906SEric.Saxe@Sun.COM }
3888906SEric.Saxe@Sun.COM 
3898906SEric.Saxe@Sun.COM static int
3908906SEric.Saxe@Sun.COM hpet_stop_main_counter(hpet_info_t *hip)
3918906SEric.Saxe@Sun.COM {
3928906SEric.Saxe@Sun.COM 	uint64_t	*gcr_ptr;
3938906SEric.Saxe@Sun.COM 	uint64_t	gcr;
3948906SEric.Saxe@Sun.COM 
3958906SEric.Saxe@Sun.COM 	gcr_ptr = (uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address);
3968906SEric.Saxe@Sun.COM 	gcr = *gcr_ptr;
3978906SEric.Saxe@Sun.COM 
3988906SEric.Saxe@Sun.COM 	gcr &= ~HPET_GCFR_ENABLE_CNF;
3998906SEric.Saxe@Sun.COM 	*gcr_ptr = gcr;
4008906SEric.Saxe@Sun.COM 	gcr = *gcr_ptr;
4018906SEric.Saxe@Sun.COM 
4028906SEric.Saxe@Sun.COM 	return (gcr & HPET_GCFR_ENABLE_CNF ? ~AE_OK : AE_OK);
4038906SEric.Saxe@Sun.COM }
4048906SEric.Saxe@Sun.COM 
4058906SEric.Saxe@Sun.COM /*
4068906SEric.Saxe@Sun.COM  * Set the Legacy Replacement Route bit.
4078906SEric.Saxe@Sun.COM  * This should be called before setting up timers.
4088906SEric.Saxe@Sun.COM  * The HPET specification is silent regarding setting this after timers are
4098906SEric.Saxe@Sun.COM  * programmed.
4108906SEric.Saxe@Sun.COM  */
4118906SEric.Saxe@Sun.COM static uint64_t
4128906SEric.Saxe@Sun.COM hpet_set_leg_rt_cnf(hpet_info_t *hip, uint32_t new_value)
4138906SEric.Saxe@Sun.COM {
4148906SEric.Saxe@Sun.COM 	uint64_t gen_conf = hpet_read_gen_config(hip);
4158906SEric.Saxe@Sun.COM 
4168906SEric.Saxe@Sun.COM 	switch (new_value) {
4178906SEric.Saxe@Sun.COM 	case 0:
4188906SEric.Saxe@Sun.COM 		gen_conf &= ~HPET_GCFR_LEG_RT_CNF;
4198906SEric.Saxe@Sun.COM 		break;
4208906SEric.Saxe@Sun.COM 
4218906SEric.Saxe@Sun.COM 	case HPET_GCFR_LEG_RT_CNF:
4228906SEric.Saxe@Sun.COM 		gen_conf |= HPET_GCFR_LEG_RT_CNF;
4238906SEric.Saxe@Sun.COM 		break;
4248906SEric.Saxe@Sun.COM 
4258906SEric.Saxe@Sun.COM 	default:
4268906SEric.Saxe@Sun.COM 		ASSERT(new_value == 0 || new_value == HPET_GCFR_LEG_RT_CNF);
4278906SEric.Saxe@Sun.COM 		break;
4288906SEric.Saxe@Sun.COM 	}
4298906SEric.Saxe@Sun.COM 	hpet_write_gen_config(hip, gen_conf);
4308906SEric.Saxe@Sun.COM 	return (gen_conf);
4318906SEric.Saxe@Sun.COM }
4328906SEric.Saxe@Sun.COM 
4338906SEric.Saxe@Sun.COM static uint64_t
4348906SEric.Saxe@Sun.COM hpet_read_gen_cap(hpet_info_t *hip)
4358906SEric.Saxe@Sun.COM {
4368906SEric.Saxe@Sun.COM 	return (*(uint64_t *)HPET_GEN_CAP_ADDRESS(hip->logical_address));
4378906SEric.Saxe@Sun.COM }
4388906SEric.Saxe@Sun.COM 
4398906SEric.Saxe@Sun.COM static uint64_t
4408906SEric.Saxe@Sun.COM hpet_read_gen_config(hpet_info_t *hip)
4418906SEric.Saxe@Sun.COM {
4428906SEric.Saxe@Sun.COM 	return (*(uint64_t *)
4438906SEric.Saxe@Sun.COM 	    HPET_GEN_CONFIG_ADDRESS(hip->logical_address));
4448906SEric.Saxe@Sun.COM }
4458906SEric.Saxe@Sun.COM 
4468906SEric.Saxe@Sun.COM static uint64_t
4478906SEric.Saxe@Sun.COM hpet_read_gen_intrpt_stat(hpet_info_t *hip)
4488906SEric.Saxe@Sun.COM {
4498906SEric.Saxe@Sun.COM 	hip->gen_intrpt_stat = *(uint64_t *)HPET_GEN_INTR_STAT_ADDRESS(
4508906SEric.Saxe@Sun.COM 	    hip->logical_address);
4518906SEric.Saxe@Sun.COM 	return (hip->gen_intrpt_stat);
4528906SEric.Saxe@Sun.COM }
4538906SEric.Saxe@Sun.COM 
4548906SEric.Saxe@Sun.COM static uint64_t
4558906SEric.Saxe@Sun.COM hpet_read_timer_N_config(hpet_info_t *hip, uint_t n)
4568906SEric.Saxe@Sun.COM {
4578906SEric.Saxe@Sun.COM 	uint64_t conf = *(uint64_t *)HPET_TIMER_N_CONF_ADDRESS(
4588906SEric.Saxe@Sun.COM 	    hip->logical_address, n);
4598906SEric.Saxe@Sun.COM 	hip->timer_n_config[n] = hpet_convert_timer_N_config(conf);
4608906SEric.Saxe@Sun.COM 	return (conf);
4618906SEric.Saxe@Sun.COM }
4628906SEric.Saxe@Sun.COM 
4638906SEric.Saxe@Sun.COM static hpet_TN_conf_cap_t
4648906SEric.Saxe@Sun.COM hpet_convert_timer_N_config(uint64_t conf)
4658906SEric.Saxe@Sun.COM {
4668906SEric.Saxe@Sun.COM 	hpet_TN_conf_cap_t cc = { 0 };
4678906SEric.Saxe@Sun.COM 
4688906SEric.Saxe@Sun.COM 	cc.int_route_cap = HPET_TIMER_N_INT_ROUTE_CAP(conf);
4698906SEric.Saxe@Sun.COM 	cc.fsb_int_del_cap = HPET_TIMER_N_FSB_INT_DEL_CAP(conf);
4708906SEric.Saxe@Sun.COM 	cc.fsb_int_en_cnf = HPET_TIMER_N_FSB_EN_CNF(conf);
4718906SEric.Saxe@Sun.COM 	cc.int_route_cnf = HPET_TIMER_N_INT_ROUTE_CNF(conf);
4728906SEric.Saxe@Sun.COM 	cc.mode32_cnf = HPET_TIMER_N_MODE32_CNF(conf);
4738906SEric.Saxe@Sun.COM 	cc.val_set_cnf = HPET_TIMER_N_VAL_SET_CNF(conf);
4748906SEric.Saxe@Sun.COM 	cc.size_cap = HPET_TIMER_N_SIZE_CAP(conf);
4758906SEric.Saxe@Sun.COM 	cc.per_int_cap = HPET_TIMER_N_PER_INT_CAP(conf);
4768906SEric.Saxe@Sun.COM 	cc.type_cnf = HPET_TIMER_N_TYPE_CNF(conf);
4778906SEric.Saxe@Sun.COM 	cc.int_enb_cnf = HPET_TIMER_N_INT_ENB_CNF(conf);
4788906SEric.Saxe@Sun.COM 	cc.int_type_cnf = HPET_TIMER_N_INT_TYPE_CNF(conf);
4798906SEric.Saxe@Sun.COM 
4808906SEric.Saxe@Sun.COM 	return (cc);
4818906SEric.Saxe@Sun.COM }
4828906SEric.Saxe@Sun.COM 
4838906SEric.Saxe@Sun.COM static uint64_t
4848906SEric.Saxe@Sun.COM hpet_read_timer_N_comp(hpet_info_t *hip, uint_t n)
4858906SEric.Saxe@Sun.COM {
4868906SEric.Saxe@Sun.COM 	if (hip->timer_n_config[n].size_cap == 1)
4878906SEric.Saxe@Sun.COM 		return (*(uint64_t *)
4888906SEric.Saxe@Sun.COM 		    HPET_TIMER_N_COMP_ADDRESS(hip->logical_address, n));
4898906SEric.Saxe@Sun.COM 	else
4908906SEric.Saxe@Sun.COM 		return (*(uint32_t *)
4918906SEric.Saxe@Sun.COM 		    HPET_TIMER_N_COMP_ADDRESS(hip->logical_address, n));
4928906SEric.Saxe@Sun.COM }
4938906SEric.Saxe@Sun.COM 
4948906SEric.Saxe@Sun.COM static uint64_t
4958906SEric.Saxe@Sun.COM hpet_read_main_counter_value(hpet_info_t *hip)
4968906SEric.Saxe@Sun.COM {
4978906SEric.Saxe@Sun.COM 	uint64_t	value;
4988906SEric.Saxe@Sun.COM 	uint32_t	*counter;
4998906SEric.Saxe@Sun.COM 	uint32_t	high1, high2, low;
5008906SEric.Saxe@Sun.COM 
5018906SEric.Saxe@Sun.COM 	counter = (uint32_t *)HPET_MAIN_COUNTER_ADDRESS(hip->logical_address);
5028906SEric.Saxe@Sun.COM 
5038906SEric.Saxe@Sun.COM 	/*
5048906SEric.Saxe@Sun.COM 	 * 32-bit main counters
5058906SEric.Saxe@Sun.COM 	 */
5068906SEric.Saxe@Sun.COM 	if (hip->gen_cap.count_size_cap == 0) {
5078906SEric.Saxe@Sun.COM 		value = (uint64_t)*counter;
5088906SEric.Saxe@Sun.COM 		hip->main_counter_value = value;
5098906SEric.Saxe@Sun.COM 		return (value);
5108906SEric.Saxe@Sun.COM 	}
5118906SEric.Saxe@Sun.COM 
5128906SEric.Saxe@Sun.COM 	/*
5138906SEric.Saxe@Sun.COM 	 * HPET spec claims a 64-bit read can be split into two 32-bit reads
5148906SEric.Saxe@Sun.COM 	 * by the hardware connection to the HPET.
5158906SEric.Saxe@Sun.COM 	 */
5168906SEric.Saxe@Sun.COM 	high2 = counter[1];
5178906SEric.Saxe@Sun.COM 	do {
5188906SEric.Saxe@Sun.COM 		high1 = high2;
5198906SEric.Saxe@Sun.COM 		low = counter[0];
5208906SEric.Saxe@Sun.COM 		high2 = counter[1];
5218906SEric.Saxe@Sun.COM 	} while (high2 != high1);
5228906SEric.Saxe@Sun.COM 
5238906SEric.Saxe@Sun.COM 	value = ((uint64_t)high1 << 32) | low;
5248906SEric.Saxe@Sun.COM 	hip->main_counter_value = value;
5258906SEric.Saxe@Sun.COM 	return (value);
5268906SEric.Saxe@Sun.COM }
5278906SEric.Saxe@Sun.COM 
5288906SEric.Saxe@Sun.COM static void
5298906SEric.Saxe@Sun.COM hpet_write_gen_cap(hpet_info_t *hip, uint64_t l)
5308906SEric.Saxe@Sun.COM {
5318906SEric.Saxe@Sun.COM 	*(uint64_t *)HPET_GEN_CAP_ADDRESS(hip->logical_address) = l;
5328906SEric.Saxe@Sun.COM }
5338906SEric.Saxe@Sun.COM 
5348906SEric.Saxe@Sun.COM static void
5358906SEric.Saxe@Sun.COM hpet_write_gen_config(hpet_info_t *hip, uint64_t l)
5368906SEric.Saxe@Sun.COM {
5378906SEric.Saxe@Sun.COM 	*(uint64_t *)HPET_GEN_CONFIG_ADDRESS(hip->logical_address) = l;
5388906SEric.Saxe@Sun.COM }
5398906SEric.Saxe@Sun.COM 
5408906SEric.Saxe@Sun.COM static void
5418906SEric.Saxe@Sun.COM hpet_write_gen_intrpt_stat(hpet_info_t *hip, uint64_t l)
5428906SEric.Saxe@Sun.COM {
5438906SEric.Saxe@Sun.COM 	*(uint64_t *)HPET_GEN_INTR_STAT_ADDRESS(hip->logical_address) = l;
5448906SEric.Saxe@Sun.COM }
5458906SEric.Saxe@Sun.COM 
5468906SEric.Saxe@Sun.COM static void
5478906SEric.Saxe@Sun.COM hpet_write_timer_N_config(hpet_info_t *hip, uint_t n, uint64_t l)
5488906SEric.Saxe@Sun.COM {
5498906SEric.Saxe@Sun.COM 	if (hip->timer_n_config[n].size_cap == 1)
5508906SEric.Saxe@Sun.COM 		*(uint64_t *)HPET_TIMER_N_CONF_ADDRESS(
5518906SEric.Saxe@Sun.COM 		    hip->logical_address, n) = l;
5528906SEric.Saxe@Sun.COM 	else
5538906SEric.Saxe@Sun.COM 		*(uint32_t *)HPET_TIMER_N_CONF_ADDRESS(
5548906SEric.Saxe@Sun.COM 		    hip->logical_address, n) = (uint32_t)(0xFFFFFFFF & l);
5558906SEric.Saxe@Sun.COM }
5568906SEric.Saxe@Sun.COM 
5578906SEric.Saxe@Sun.COM static void
5588906SEric.Saxe@Sun.COM hpet_write_timer_N_comp(hpet_info_t *hip, uint_t n, uint64_t l)
5598906SEric.Saxe@Sun.COM {
5608906SEric.Saxe@Sun.COM 	*(uint64_t *)HPET_TIMER_N_COMP_ADDRESS(hip->logical_address, n) = l;
5618906SEric.Saxe@Sun.COM }
5628906SEric.Saxe@Sun.COM 
5638906SEric.Saxe@Sun.COM static void
5648906SEric.Saxe@Sun.COM hpet_disable_timer(hpet_info_t *hip, uint32_t timer_n)
5658906SEric.Saxe@Sun.COM {
5668906SEric.Saxe@Sun.COM 	uint64_t l;
5678906SEric.Saxe@Sun.COM 
5688906SEric.Saxe@Sun.COM 	l = hpet_read_timer_N_config(hip, timer_n);
5698906SEric.Saxe@Sun.COM 	l &= ~HPET_TIMER_N_INT_ENB_CNF_BIT;
5708906SEric.Saxe@Sun.COM 	hpet_write_timer_N_config(hip, timer_n, l);
5718906SEric.Saxe@Sun.COM }
5728906SEric.Saxe@Sun.COM 
5738906SEric.Saxe@Sun.COM static void
5748906SEric.Saxe@Sun.COM hpet_enable_timer(hpet_info_t *hip, uint32_t timer_n)
5758906SEric.Saxe@Sun.COM {
5768906SEric.Saxe@Sun.COM 	uint64_t l;
5778906SEric.Saxe@Sun.COM 
5788906SEric.Saxe@Sun.COM 	l = hpet_read_timer_N_config(hip, timer_n);
5798906SEric.Saxe@Sun.COM 	l |= HPET_TIMER_N_INT_ENB_CNF_BIT;
5808906SEric.Saxe@Sun.COM 	hpet_write_timer_N_config(hip, timer_n, l);
5818906SEric.Saxe@Sun.COM }
5828906SEric.Saxe@Sun.COM 
5838906SEric.Saxe@Sun.COM static void
5848906SEric.Saxe@Sun.COM hpet_write_main_counter_value(hpet_info_t *hip, uint64_t l)
5858906SEric.Saxe@Sun.COM {
5868906SEric.Saxe@Sun.COM 	uint32_t	*address;
5878906SEric.Saxe@Sun.COM 
5888906SEric.Saxe@Sun.COM 	/*
5898906SEric.Saxe@Sun.COM 	 * HPET spec 1.0a states main counter register should be halted before
5908906SEric.Saxe@Sun.COM 	 * it is written to.
5918906SEric.Saxe@Sun.COM 	 */
5928906SEric.Saxe@Sun.COM 	ASSERT(!(hpet_read_gen_config(hip) & HPET_GCFR_ENABLE_CNF));
5938906SEric.Saxe@Sun.COM 
5948906SEric.Saxe@Sun.COM 	if (hip->gen_cap.count_size_cap == 1) {
5958906SEric.Saxe@Sun.COM 		*(uint64_t *)HPET_MAIN_COUNTER_ADDRESS(hip->logical_address)
5968906SEric.Saxe@Sun.COM 		    = l;
5978906SEric.Saxe@Sun.COM 	} else {
5988906SEric.Saxe@Sun.COM 		address = (uint32_t *)HPET_MAIN_COUNTER_ADDRESS(
5998906SEric.Saxe@Sun.COM 		    hip->logical_address);
6008906SEric.Saxe@Sun.COM 
6018906SEric.Saxe@Sun.COM 		address[0] = (uint32_t)(l & 0xFFFFFFFF);
6028906SEric.Saxe@Sun.COM 	}
6038906SEric.Saxe@Sun.COM }
6048906SEric.Saxe@Sun.COM 
6058906SEric.Saxe@Sun.COM /*
6068906SEric.Saxe@Sun.COM  * Add the interrupt handler for I/O APIC interrupt number (interrupt line).
6078906SEric.Saxe@Sun.COM  *
6088906SEric.Saxe@Sun.COM  * The I/O APIC line (vector) is programmed in ioapic_init_intr() called
6098906SEric.Saxe@Sun.COM  * from apic_picinit() psm_ops apic_ops entry point after we return from
6108906SEric.Saxe@Sun.COM  * apic_init() psm_ops entry point.
6118906SEric.Saxe@Sun.COM  */
6128906SEric.Saxe@Sun.COM static uint32_t
6138906SEric.Saxe@Sun.COM hpet_install_interrupt_handler(uint_t (*func)(char *), int vector)
6148906SEric.Saxe@Sun.COM {
6158906SEric.Saxe@Sun.COM 	uint32_t retval;
6168906SEric.Saxe@Sun.COM 
6178906SEric.Saxe@Sun.COM 	retval = add_avintr(NULL, CBE_HIGH_PIL, (avfunc)func, "HPET Timer",
6188906SEric.Saxe@Sun.COM 	    vector, NULL, NULL, NULL, NULL);
6198906SEric.Saxe@Sun.COM 	if (retval == 0) {
6208906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!hpet_acpi: add_avintr() failed");
6218906SEric.Saxe@Sun.COM 		return (AE_BAD_PARAMETER);
6228906SEric.Saxe@Sun.COM 	}
6238906SEric.Saxe@Sun.COM 	return (AE_OK);
6248906SEric.Saxe@Sun.COM }
6258906SEric.Saxe@Sun.COM 
6268906SEric.Saxe@Sun.COM /*
6278906SEric.Saxe@Sun.COM  * The HPET timers specify which I/O APIC interrupts they can be routed to.
6288906SEric.Saxe@Sun.COM  * Find the first available non-legacy-replacement timer and its I/O APIC irq.
6298906SEric.Saxe@Sun.COM  * Supported I/O APIC IRQs are specified in the int_route_cap bitmap in each
6308906SEric.Saxe@Sun.COM  * timer's timer_n_config register.
6318906SEric.Saxe@Sun.COM  */
6328906SEric.Saxe@Sun.COM static int
6338906SEric.Saxe@Sun.COM hpet_get_IOAPIC_intr_capable_timer(hpet_info_t *hip)
6348906SEric.Saxe@Sun.COM {
6358906SEric.Saxe@Sun.COM 	int	timer;
6368906SEric.Saxe@Sun.COM 	int	intr;
6378906SEric.Saxe@Sun.COM 
6388906SEric.Saxe@Sun.COM 	for (timer = HPET_FIRST_NON_LEGACY_TIMER;
6398906SEric.Saxe@Sun.COM 	    timer < hip->gen_cap.num_tim_cap; ++timer) {
6408906SEric.Saxe@Sun.COM 
6418906SEric.Saxe@Sun.COM 		if (!hpet_timer_available(hip->allocated_timers, timer))
6428906SEric.Saxe@Sun.COM 			continue;
6438906SEric.Saxe@Sun.COM 
6448906SEric.Saxe@Sun.COM 		intr = lowbit(hip->timer_n_config[timer].int_route_cap) - 1;
6458906SEric.Saxe@Sun.COM 		if (intr >= 0) {
6468906SEric.Saxe@Sun.COM 			hpet_timer_alloc(&hip->allocated_timers, timer);
6478906SEric.Saxe@Sun.COM 			hip->cstate_timer.timer = timer;
6488906SEric.Saxe@Sun.COM 			hip->cstate_timer.intr = intr;
6498906SEric.Saxe@Sun.COM 			return (timer);
6508906SEric.Saxe@Sun.COM 		}
6518906SEric.Saxe@Sun.COM 	}
6528906SEric.Saxe@Sun.COM 
6538906SEric.Saxe@Sun.COM 	return (-1);
6548906SEric.Saxe@Sun.COM }
6558906SEric.Saxe@Sun.COM 
6568906SEric.Saxe@Sun.COM /*
6578906SEric.Saxe@Sun.COM  * Mark this timer as used.
6588906SEric.Saxe@Sun.COM  */
6598906SEric.Saxe@Sun.COM static void
6608906SEric.Saxe@Sun.COM hpet_timer_alloc(uint32_t *allocated_timers, uint32_t n)
6618906SEric.Saxe@Sun.COM {
6628906SEric.Saxe@Sun.COM 	*allocated_timers |= 1 << n;
6638906SEric.Saxe@Sun.COM }
6648906SEric.Saxe@Sun.COM 
6658906SEric.Saxe@Sun.COM /*
6668906SEric.Saxe@Sun.COM  * Check if this timer is available.
6678906SEric.Saxe@Sun.COM  * No mutual exclusion because only one thread uses this.
6688906SEric.Saxe@Sun.COM  */
6698906SEric.Saxe@Sun.COM static int
6708906SEric.Saxe@Sun.COM hpet_timer_available(uint32_t allocated_timers, uint32_t n)
6718906SEric.Saxe@Sun.COM {
6728906SEric.Saxe@Sun.COM 	return ((allocated_timers & (1 << n)) == 0);
6738906SEric.Saxe@Sun.COM }
6748906SEric.Saxe@Sun.COM 
6758906SEric.Saxe@Sun.COM /*
6768906SEric.Saxe@Sun.COM  * Setup timer N to route its interrupt to I/O APIC.
6778906SEric.Saxe@Sun.COM  */
6788906SEric.Saxe@Sun.COM static void
6798906SEric.Saxe@Sun.COM hpet_timer_set_up(hpet_info_t *hip, uint32_t timer_n, uint32_t interrupt)
6808906SEric.Saxe@Sun.COM {
6818906SEric.Saxe@Sun.COM 	uint64_t conf;
6828906SEric.Saxe@Sun.COM 
6838906SEric.Saxe@Sun.COM 	conf = hpet_read_timer_N_config(hip, timer_n);
6848906SEric.Saxe@Sun.COM 
6858906SEric.Saxe@Sun.COM 	/*
6868906SEric.Saxe@Sun.COM 	 * Caller is required to verify this interrupt route is supported.
6878906SEric.Saxe@Sun.COM 	 */
6888906SEric.Saxe@Sun.COM 	ASSERT(HPET_TIMER_N_INT_ROUTE_CAP(conf) & (1 << interrupt));
6898906SEric.Saxe@Sun.COM 
6908906SEric.Saxe@Sun.COM 	conf &= ~HPET_TIMER_N_FSB_EN_CNF_BIT;	/* use IOAPIC */
6918906SEric.Saxe@Sun.COM 	conf |= HPET_TIMER_N_INT_ROUTE_SHIFT(interrupt);
6928906SEric.Saxe@Sun.COM 	conf &= ~HPET_TIMER_N_TYPE_CNF_BIT;	/* non periodic */
6938906SEric.Saxe@Sun.COM 	conf &= ~HPET_TIMER_N_INT_ENB_CNF_BIT;	/* disabled */
6948906SEric.Saxe@Sun.COM 	conf |= HPET_TIMER_N_INT_TYPE_CNF_BIT;	/* Level Triggered */
6958906SEric.Saxe@Sun.COM 
6968906SEric.Saxe@Sun.COM 	hpet_write_timer_N_config(hip, timer_n, conf);
6978906SEric.Saxe@Sun.COM }
6988906SEric.Saxe@Sun.COM 
6998906SEric.Saxe@Sun.COM /*
7008906SEric.Saxe@Sun.COM  * The HPET's Main Counter is not stopped before programming an HPET timer.
7018906SEric.Saxe@Sun.COM  * This will allow the HPET to be used as a time source.
7028906SEric.Saxe@Sun.COM  * The programmed timer interrupt may occur before this function returns.
7038906SEric.Saxe@Sun.COM  * Callers must block interrupts before calling this function if they must
7048906SEric.Saxe@Sun.COM  * guarantee the interrupt is handled after this function returns.
7058906SEric.Saxe@Sun.COM  *
7068906SEric.Saxe@Sun.COM  * Return 0 if main counter is less than timer after enabling timer.
7078906SEric.Saxe@Sun.COM  * The interrupt was programmed, but it may fire before this returns.
7088906SEric.Saxe@Sun.COM  * Return !0 if main counter is greater than timer after enabling timer.
7098906SEric.Saxe@Sun.COM  * In other words: the timer will not fire, and we do not know if it did fire.
7108906SEric.Saxe@Sun.COM  *
7118906SEric.Saxe@Sun.COM  * delta is in HPET ticks.
7128906SEric.Saxe@Sun.COM  *
7138906SEric.Saxe@Sun.COM  * Writing a 64-bit value to a 32-bit register will "wrap around".
7148906SEric.Saxe@Sun.COM  * A 32-bit HPET timer will wrap around in a little over 5 minutes.
7158906SEric.Saxe@Sun.COM  */
7168906SEric.Saxe@Sun.COM int
7178906SEric.Saxe@Sun.COM hpet_timer_program(hpet_info_t *hip, uint32_t timer, uint64_t delta)
7188906SEric.Saxe@Sun.COM {
7198906SEric.Saxe@Sun.COM 	uint64_t time, program;
7208906SEric.Saxe@Sun.COM 
7218906SEric.Saxe@Sun.COM 	program = hpet_read_main_counter_value(hip);
7228906SEric.Saxe@Sun.COM 	program += delta;
7238906SEric.Saxe@Sun.COM 	hpet_write_timer_N_comp(hip, timer, program);
7248906SEric.Saxe@Sun.COM 
7258906SEric.Saxe@Sun.COM 	time = hpet_read_main_counter_value(hip);
7268906SEric.Saxe@Sun.COM 	if (time < program)
7278906SEric.Saxe@Sun.COM 		return (AE_OK);
7288906SEric.Saxe@Sun.COM 
7298906SEric.Saxe@Sun.COM 	return (AE_TIME);
7308906SEric.Saxe@Sun.COM }
7318906SEric.Saxe@Sun.COM 
7328906SEric.Saxe@Sun.COM /*
7338906SEric.Saxe@Sun.COM  * CPR and power policy-change callback entry point.
7348906SEric.Saxe@Sun.COM  */
7358906SEric.Saxe@Sun.COM boolean_t
7368906SEric.Saxe@Sun.COM hpet_callback(int code)
7378906SEric.Saxe@Sun.COM {
7388906SEric.Saxe@Sun.COM 	switch (code) {
7398906SEric.Saxe@Sun.COM 	case PM_DEFAULT_CPU_DEEP_IDLE:
7408906SEric.Saxe@Sun.COM 		/*FALLTHROUGH*/
7418906SEric.Saxe@Sun.COM 	case PM_ENABLE_CPU_DEEP_IDLE:
7428906SEric.Saxe@Sun.COM 		/*FALLTHROUGH*/
7438906SEric.Saxe@Sun.COM 	case PM_DISABLE_CPU_DEEP_IDLE:
7448906SEric.Saxe@Sun.COM 		return (hpet_deep_idle_config(code));
7458906SEric.Saxe@Sun.COM 
7468906SEric.Saxe@Sun.COM 	case CB_CODE_CPR_RESUME:
7478906SEric.Saxe@Sun.COM 		/*FALLTHROUGH*/
7488906SEric.Saxe@Sun.COM 	case CB_CODE_CPR_CHKPT:
7498906SEric.Saxe@Sun.COM 		return (hpet_cpr(code));
7508906SEric.Saxe@Sun.COM 
7518906SEric.Saxe@Sun.COM 	case CST_EVENT_MULTIPLE_CSTATES:
7528906SEric.Saxe@Sun.COM 		hpet_cst_callback(CST_EVENT_MULTIPLE_CSTATES);
7538906SEric.Saxe@Sun.COM 		return (B_TRUE);
7548906SEric.Saxe@Sun.COM 
7558906SEric.Saxe@Sun.COM 	case CST_EVENT_ONE_CSTATE:
7568906SEric.Saxe@Sun.COM 		hpet_cst_callback(CST_EVENT_ONE_CSTATE);
7578906SEric.Saxe@Sun.COM 		return (B_TRUE);
7588906SEric.Saxe@Sun.COM 
7598906SEric.Saxe@Sun.COM 	default:
7608906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_callback: invalid code %d\n", code);
7618906SEric.Saxe@Sun.COM 		return (B_FALSE);
7628906SEric.Saxe@Sun.COM 	}
7638906SEric.Saxe@Sun.COM }
7648906SEric.Saxe@Sun.COM 
7658906SEric.Saxe@Sun.COM /*
7668906SEric.Saxe@Sun.COM  * According to the HPET spec 1.0a: the Operating System must save and restore
7678906SEric.Saxe@Sun.COM  * HPET event timer hardware context through ACPI sleep state transitions.
7688906SEric.Saxe@Sun.COM  * Timer registers (including the main counter) may not be preserved through
7698906SEric.Saxe@Sun.COM  * ACPI S3, S4, or S5 sleep states.  This code does not not support S1 nor S2.
7708906SEric.Saxe@Sun.COM  *
7718906SEric.Saxe@Sun.COM  * Current HPET state is already in hpet.supported and
7728906SEric.Saxe@Sun.COM  * hpet_state.proxy_installed.  hpet_info contains the proxy interrupt HPET
7738906SEric.Saxe@Sun.COM  * Timer state.
7748906SEric.Saxe@Sun.COM  *
7758906SEric.Saxe@Sun.COM  * Future projects beware: the HPET Main Counter is undefined after ACPI S3 or
7768906SEric.Saxe@Sun.COM  * S4, and it is not saved/restored here.  Future projects cannot expect the
7778906SEric.Saxe@Sun.COM  * Main Counter to be monotomically (or accurately) increasing across CPR.
7788906SEric.Saxe@Sun.COM  *
7798906SEric.Saxe@Sun.COM  * Note: the CPR Checkpoint path later calls pause_cpus() which ensures all
7808906SEric.Saxe@Sun.COM  * CPUs are awake and in a spin loop before the system suspends.  The HPET is
7818906SEric.Saxe@Sun.COM  * not needed for Deep C-state wakeup when CPUs are in cpu_pause().
7828906SEric.Saxe@Sun.COM  * It is safe to leave the HPET running as the system suspends; we just
7838906SEric.Saxe@Sun.COM  * disable the timer from generating interrupts here.
7848906SEric.Saxe@Sun.COM  */
7858906SEric.Saxe@Sun.COM static boolean_t
7868906SEric.Saxe@Sun.COM hpet_cpr(int code)
7878906SEric.Saxe@Sun.COM {
7888906SEric.Saxe@Sun.COM 	ulong_t		intr, dead_count = 0;
7898906SEric.Saxe@Sun.COM 	hrtime_t	dead = gethrtime() + hpet_spin_timeout;
7908906SEric.Saxe@Sun.COM 	boolean_t	ret = B_TRUE;
7918906SEric.Saxe@Sun.COM 
7928906SEric.Saxe@Sun.COM 	mutex_enter(&hpet_state_lock);
7938906SEric.Saxe@Sun.COM 	switch (code) {
7948906SEric.Saxe@Sun.COM 	case CB_CODE_CPR_CHKPT:
7958906SEric.Saxe@Sun.COM 		if (hpet_state.proxy_installed == B_FALSE)
7968906SEric.Saxe@Sun.COM 			break;
7978906SEric.Saxe@Sun.COM 
7988906SEric.Saxe@Sun.COM 		hpet_state.cpr = B_TRUE;
7998906SEric.Saxe@Sun.COM 
8008906SEric.Saxe@Sun.COM 		intr = intr_clear();
8018906SEric.Saxe@Sun.COM 		while (!mutex_tryenter(&hpet_proxy_lock)) {
8028906SEric.Saxe@Sun.COM 			/*
8038906SEric.Saxe@Sun.COM 			 * spin
8048906SEric.Saxe@Sun.COM 			 */
8058906SEric.Saxe@Sun.COM 			intr_restore(intr);
8068906SEric.Saxe@Sun.COM 			if (dead_count++ > hpet_spin_check) {
8078906SEric.Saxe@Sun.COM 				dead_count = 0;
8088906SEric.Saxe@Sun.COM 				if (gethrtime() > dead) {
8098906SEric.Saxe@Sun.COM 					hpet_state.cpr = B_FALSE;
8108906SEric.Saxe@Sun.COM 					mutex_exit(&hpet_state_lock);
8118906SEric.Saxe@Sun.COM 					cmn_err(CE_NOTE, "!hpet_cpr: deadman");
8128906SEric.Saxe@Sun.COM 					return (B_FALSE);
8138906SEric.Saxe@Sun.COM 				}
8148906SEric.Saxe@Sun.COM 			}
8158906SEric.Saxe@Sun.COM 			intr = intr_clear();
8168906SEric.Saxe@Sun.COM 		}
8178906SEric.Saxe@Sun.COM 		hpet_expire_all();
8188906SEric.Saxe@Sun.COM 		mutex_exit(&hpet_proxy_lock);
8198906SEric.Saxe@Sun.COM 		intr_restore(intr);
8208906SEric.Saxe@Sun.COM 
8218906SEric.Saxe@Sun.COM 		hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
8228906SEric.Saxe@Sun.COM 		break;
8238906SEric.Saxe@Sun.COM 
8248906SEric.Saxe@Sun.COM 	case CB_CODE_CPR_RESUME:
8258906SEric.Saxe@Sun.COM 		if (hpet_resume() == B_TRUE)
8268906SEric.Saxe@Sun.COM 			hpet_state.cpr = B_FALSE;
8278906SEric.Saxe@Sun.COM 		else
8288906SEric.Saxe@Sun.COM 			cmn_err(CE_NOTE, "!hpet_resume failed.");
8298906SEric.Saxe@Sun.COM 		break;
8308906SEric.Saxe@Sun.COM 
8318906SEric.Saxe@Sun.COM 	default:
8328906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_cpr: invalid code %d\n", code);
8338906SEric.Saxe@Sun.COM 		ret = B_FALSE;
8348906SEric.Saxe@Sun.COM 		break;
8358906SEric.Saxe@Sun.COM 	}
8368906SEric.Saxe@Sun.COM 	mutex_exit(&hpet_state_lock);
8378906SEric.Saxe@Sun.COM 	return (ret);
8388906SEric.Saxe@Sun.COM }
8398906SEric.Saxe@Sun.COM 
8408906SEric.Saxe@Sun.COM /*
8418906SEric.Saxe@Sun.COM  * Assume the HPET stopped in Suspend state and timer state was lost.
8428906SEric.Saxe@Sun.COM  */
8438906SEric.Saxe@Sun.COM static boolean_t
8448906SEric.Saxe@Sun.COM hpet_resume(void)
8458906SEric.Saxe@Sun.COM {
8468906SEric.Saxe@Sun.COM 	if (hpet.supported != HPET_TIMER_SUPPORT)
8478906SEric.Saxe@Sun.COM 		return (B_TRUE);
8488906SEric.Saxe@Sun.COM 
8498906SEric.Saxe@Sun.COM 	/*
8508906SEric.Saxe@Sun.COM 	 * The HPET spec does not specify if Legacy Replacement Route is
8518906SEric.Saxe@Sun.COM 	 * on or off by default, so we set it off here.
8528906SEric.Saxe@Sun.COM 	 */
8538906SEric.Saxe@Sun.COM 	(void) hpet_set_leg_rt_cnf(&hpet_info, 0);
8548906SEric.Saxe@Sun.COM 
8558906SEric.Saxe@Sun.COM 	if (hpet_start_main_counter(&hpet_info) != AE_OK) {
8568906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_resume: start main counter failed");
8578906SEric.Saxe@Sun.COM 		hpet.supported = HPET_NO_SUPPORT;
8588906SEric.Saxe@Sun.COM 		if (hpet_state.proxy_installed == B_TRUE) {
8598906SEric.Saxe@Sun.COM 			hpet_state.proxy_installed = B_FALSE;
8608906SEric.Saxe@Sun.COM 			hpet_uninstall_interrupt_handler();
8618906SEric.Saxe@Sun.COM 		}
8628906SEric.Saxe@Sun.COM 		return (B_FALSE);
8638906SEric.Saxe@Sun.COM 	}
8648906SEric.Saxe@Sun.COM 
8658906SEric.Saxe@Sun.COM 	if (hpet_state.proxy_installed == B_FALSE)
8668906SEric.Saxe@Sun.COM 		return (B_TRUE);
8678906SEric.Saxe@Sun.COM 
8688906SEric.Saxe@Sun.COM 	hpet_timer_set_up(&hpet_info, hpet_info.cstate_timer.timer,
8698906SEric.Saxe@Sun.COM 	    hpet_info.cstate_timer.intr);
8708906SEric.Saxe@Sun.COM 	if (hpet_state.cpu_deep_idle == B_TRUE)
8718906SEric.Saxe@Sun.COM 		hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
8728906SEric.Saxe@Sun.COM 
8738906SEric.Saxe@Sun.COM 	return (B_TRUE);
8748906SEric.Saxe@Sun.COM }
8758906SEric.Saxe@Sun.COM 
8768906SEric.Saxe@Sun.COM /*
8778906SEric.Saxe@Sun.COM  * Callback to enable/disable Deep C-States based on power.conf setting.
8788906SEric.Saxe@Sun.COM  */
8798906SEric.Saxe@Sun.COM static boolean_t
8808906SEric.Saxe@Sun.COM hpet_deep_idle_config(int code)
8818906SEric.Saxe@Sun.COM {
8828906SEric.Saxe@Sun.COM 	ulong_t		intr, dead_count = 0;
8838906SEric.Saxe@Sun.COM 	hrtime_t	dead = gethrtime() + hpet_spin_timeout;
8848906SEric.Saxe@Sun.COM 	boolean_t	ret = B_TRUE;
8858906SEric.Saxe@Sun.COM 
8868906SEric.Saxe@Sun.COM 	mutex_enter(&hpet_state_lock);
8878906SEric.Saxe@Sun.COM 	switch (code) {
8888906SEric.Saxe@Sun.COM 	case PM_DEFAULT_CPU_DEEP_IDLE:
8898906SEric.Saxe@Sun.COM 		/*FALLTHROUGH*/
8908906SEric.Saxe@Sun.COM 	case PM_ENABLE_CPU_DEEP_IDLE:
8918906SEric.Saxe@Sun.COM 
8928906SEric.Saxe@Sun.COM 		if (hpet_state.cpu_deep_idle == B_TRUE)
8938906SEric.Saxe@Sun.COM 			break;
8948906SEric.Saxe@Sun.COM 
8958906SEric.Saxe@Sun.COM 		if (hpet_state.proxy_installed == B_FALSE) {
8968906SEric.Saxe@Sun.COM 			ret = B_FALSE;  /* Deep C-States not supported */
8978906SEric.Saxe@Sun.COM 			break;
8988906SEric.Saxe@Sun.COM 		}
8998906SEric.Saxe@Sun.COM 
9008906SEric.Saxe@Sun.COM 		hpet_enable_timer(&hpet_info, hpet_info.cstate_timer.timer);
9018906SEric.Saxe@Sun.COM 		hpet_state.cpu_deep_idle = B_TRUE;
9028906SEric.Saxe@Sun.COM 		break;
9038906SEric.Saxe@Sun.COM 
9048906SEric.Saxe@Sun.COM 	case PM_DISABLE_CPU_DEEP_IDLE:
9058906SEric.Saxe@Sun.COM 
9068906SEric.Saxe@Sun.COM 		if ((hpet_state.cpu_deep_idle == B_FALSE) ||
9078906SEric.Saxe@Sun.COM 		    (hpet_state.proxy_installed == B_FALSE))
9088906SEric.Saxe@Sun.COM 			break;
9098906SEric.Saxe@Sun.COM 
9108906SEric.Saxe@Sun.COM 		/*
9118906SEric.Saxe@Sun.COM 		 * The order of these operations is important to avoid
9128906SEric.Saxe@Sun.COM 		 * lost wakeups: Set a flag to refuse all future LAPIC Timer
9138906SEric.Saxe@Sun.COM 		 * proxy requests, then wake up all CPUs from deep C-state,
9148906SEric.Saxe@Sun.COM 		 * and finally disable the HPET interrupt-generating timer.
9158906SEric.Saxe@Sun.COM 		 */
9168906SEric.Saxe@Sun.COM 		hpet_state.cpu_deep_idle = B_FALSE;
9178906SEric.Saxe@Sun.COM 
9188906SEric.Saxe@Sun.COM 		intr = intr_clear();
9198906SEric.Saxe@Sun.COM 		while (!mutex_tryenter(&hpet_proxy_lock)) {
9208906SEric.Saxe@Sun.COM 			/*
9218906SEric.Saxe@Sun.COM 			 * spin
9228906SEric.Saxe@Sun.COM 			 */
9238906SEric.Saxe@Sun.COM 			intr_restore(intr);
9248906SEric.Saxe@Sun.COM 			if (dead_count++ > hpet_spin_check) {
9258906SEric.Saxe@Sun.COM 				dead_count = 0;
9268906SEric.Saxe@Sun.COM 				if (gethrtime() > dead) {
9278906SEric.Saxe@Sun.COM 					hpet_state.cpu_deep_idle = B_TRUE;
9288906SEric.Saxe@Sun.COM 					mutex_exit(&hpet_state_lock);
9298906SEric.Saxe@Sun.COM 					cmn_err(CE_NOTE,
9308906SEric.Saxe@Sun.COM 					    "!hpet_deep_idle_config: deadman");
9318906SEric.Saxe@Sun.COM 					return (B_FALSE);
9328906SEric.Saxe@Sun.COM 				}
9338906SEric.Saxe@Sun.COM 			}
9348906SEric.Saxe@Sun.COM 			intr = intr_clear();
9358906SEric.Saxe@Sun.COM 		}
9368906SEric.Saxe@Sun.COM 		hpet_expire_all();
9378906SEric.Saxe@Sun.COM 		mutex_exit(&hpet_proxy_lock);
9388906SEric.Saxe@Sun.COM 		intr_restore(intr);
9398906SEric.Saxe@Sun.COM 
9408906SEric.Saxe@Sun.COM 		hpet_disable_timer(&hpet_info, hpet_info.cstate_timer.timer);
9418906SEric.Saxe@Sun.COM 		break;
9428906SEric.Saxe@Sun.COM 
9438906SEric.Saxe@Sun.COM 	default:
9448906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_deep_idle_config: invalid code %d\n",
9458906SEric.Saxe@Sun.COM 		    code);
9468906SEric.Saxe@Sun.COM 		ret = B_FALSE;
9478906SEric.Saxe@Sun.COM 		break;
9488906SEric.Saxe@Sun.COM 	}
9498906SEric.Saxe@Sun.COM 	mutex_exit(&hpet_state_lock);
9508906SEric.Saxe@Sun.COM 
9518906SEric.Saxe@Sun.COM 	return (ret);
9528906SEric.Saxe@Sun.COM }
9538906SEric.Saxe@Sun.COM 
9548906SEric.Saxe@Sun.COM /*
9558906SEric.Saxe@Sun.COM  * Callback for _CST c-state change notifications.
9568906SEric.Saxe@Sun.COM  */
9578906SEric.Saxe@Sun.COM static void
9588906SEric.Saxe@Sun.COM hpet_cst_callback(uint32_t code)
9598906SEric.Saxe@Sun.COM {
9608906SEric.Saxe@Sun.COM 	ulong_t		intr, dead_count = 0;
9618906SEric.Saxe@Sun.COM 	hrtime_t	dead = gethrtime() + hpet_spin_timeout;
9628906SEric.Saxe@Sun.COM 
9638906SEric.Saxe@Sun.COM 	switch (code) {
9648906SEric.Saxe@Sun.COM 	case CST_EVENT_ONE_CSTATE:
9658906SEric.Saxe@Sun.COM 		hpet_state.uni_cstate = B_TRUE;
9668906SEric.Saxe@Sun.COM 		intr = intr_clear();
9678906SEric.Saxe@Sun.COM 		while (!mutex_tryenter(&hpet_proxy_lock)) {
9688906SEric.Saxe@Sun.COM 			/*
9698906SEric.Saxe@Sun.COM 			 * spin
9708906SEric.Saxe@Sun.COM 			 */
9718906SEric.Saxe@Sun.COM 			intr_restore(intr);
9728906SEric.Saxe@Sun.COM 			if (dead_count++ > hpet_spin_check) {
9738906SEric.Saxe@Sun.COM 				dead_count = 0;
9748906SEric.Saxe@Sun.COM 				if (gethrtime() > dead) {
9758906SEric.Saxe@Sun.COM 					hpet_expire_all();
9768906SEric.Saxe@Sun.COM 					cmn_err(CE_NOTE,
9778906SEric.Saxe@Sun.COM 					    "!hpet_cst_callback: deadman");
9788906SEric.Saxe@Sun.COM 					return;
9798906SEric.Saxe@Sun.COM 				}
9808906SEric.Saxe@Sun.COM 			}
9818906SEric.Saxe@Sun.COM 			intr = intr_clear();
9828906SEric.Saxe@Sun.COM 		}
9838906SEric.Saxe@Sun.COM 		hpet_expire_all();
9848906SEric.Saxe@Sun.COM 		mutex_exit(&hpet_proxy_lock);
9858906SEric.Saxe@Sun.COM 		intr_restore(intr);
9868906SEric.Saxe@Sun.COM 		break;
9878906SEric.Saxe@Sun.COM 
9888906SEric.Saxe@Sun.COM 	case CST_EVENT_MULTIPLE_CSTATES:
9898906SEric.Saxe@Sun.COM 		hpet_state.uni_cstate = B_FALSE;
9908906SEric.Saxe@Sun.COM 		break;
9918906SEric.Saxe@Sun.COM 
9928906SEric.Saxe@Sun.COM 	default:
9938906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!hpet_cst_callback: invalid code %d\n", code);
9948906SEric.Saxe@Sun.COM 		break;
9958906SEric.Saxe@Sun.COM 	}
9968906SEric.Saxe@Sun.COM }
9978906SEric.Saxe@Sun.COM 
9988906SEric.Saxe@Sun.COM /*
9998906SEric.Saxe@Sun.COM  * Interrupt Service Routine for HPET I/O-APIC-generated interrupts.
10008906SEric.Saxe@Sun.COM  * Used to wakeup CPUs from Deep C-state when their Local APIC Timer stops.
10018906SEric.Saxe@Sun.COM  * This ISR runs on one CPU which pokes other CPUs out of Deep C-state as
10028906SEric.Saxe@Sun.COM  * needed.
10038906SEric.Saxe@Sun.COM  */
10048906SEric.Saxe@Sun.COM /* ARGSUSED */
10058906SEric.Saxe@Sun.COM static uint_t
10068906SEric.Saxe@Sun.COM hpet_isr(char *arg)
10078906SEric.Saxe@Sun.COM {
10088906SEric.Saxe@Sun.COM 	uint64_t	timer_status;
10098906SEric.Saxe@Sun.COM 	uint64_t	timer_mask;
10108906SEric.Saxe@Sun.COM 	ulong_t		intr, dead_count = 0;
10118906SEric.Saxe@Sun.COM 	hrtime_t	dead = gethrtime() + hpet_isr_spin_timeout;
10128906SEric.Saxe@Sun.COM 
10138906SEric.Saxe@Sun.COM 	timer_mask = HPET_INTR_STATUS_MASK(hpet_info.cstate_timer.timer);
10148906SEric.Saxe@Sun.COM 
10158906SEric.Saxe@Sun.COM 	/*
10168906SEric.Saxe@Sun.COM 	 * We are using a level-triggered interrupt.
10178906SEric.Saxe@Sun.COM 	 * HPET sets timer's General Interrupt Status Register bit N.
10188906SEric.Saxe@Sun.COM 	 * ISR checks this bit to see if it needs servicing.
10198906SEric.Saxe@Sun.COM 	 * ISR then clears this bit by writing 1 to that bit.
10208906SEric.Saxe@Sun.COM 	 */
10218906SEric.Saxe@Sun.COM 	timer_status = hpet_read_gen_intrpt_stat(&hpet_info);
10228906SEric.Saxe@Sun.COM 	if (!(timer_status & timer_mask))
10238906SEric.Saxe@Sun.COM 		return (DDI_INTR_UNCLAIMED);
10248906SEric.Saxe@Sun.COM 	hpet_write_gen_intrpt_stat(&hpet_info, timer_mask);
10258906SEric.Saxe@Sun.COM 
10268906SEric.Saxe@Sun.COM 	/*
10278906SEric.Saxe@Sun.COM 	 * Do not touch ISR data structures before checking the HPET's General
10288906SEric.Saxe@Sun.COM 	 * Interrupt Status register.  The General Interrupt Status register
10298906SEric.Saxe@Sun.COM 	 * will not be set by hardware until after timer interrupt generation
10308906SEric.Saxe@Sun.COM 	 * is enabled by software.  Software allocates necessary data
10318906SEric.Saxe@Sun.COM 	 * structures before enabling timer interrupts.  ASSERT the software
10328906SEric.Saxe@Sun.COM 	 * data structures required to handle this interrupt are initialized.
10338906SEric.Saxe@Sun.COM 	 */
10348906SEric.Saxe@Sun.COM 	ASSERT(hpet_proxy_users != NULL);
10358906SEric.Saxe@Sun.COM 
10368906SEric.Saxe@Sun.COM 	/*
10378906SEric.Saxe@Sun.COM 	 * CPUs in deep c-states do not enable interrupts until after
10388906SEric.Saxe@Sun.COM 	 * performing idle cleanup which includes descheduling themselves from
10398906SEric.Saxe@Sun.COM 	 * the HPET.  The CPU running this ISR will NEVER find itself in the
10408906SEric.Saxe@Sun.COM 	 * proxy list.  A lost wakeup may occur if this is false.
10418906SEric.Saxe@Sun.COM 	 */
10428906SEric.Saxe@Sun.COM 	ASSERT(hpet_proxy_users[CPU->cpu_id] == HPET_INFINITY);
10438906SEric.Saxe@Sun.COM 
10448906SEric.Saxe@Sun.COM 	/*
10458906SEric.Saxe@Sun.COM 	 * Higher level interrupts may deadlock with CPUs going idle if this
10468906SEric.Saxe@Sun.COM 	 * ISR is prempted while holding hpet_proxy_lock.
10478906SEric.Saxe@Sun.COM 	 */
10488906SEric.Saxe@Sun.COM 	intr = intr_clear();
10498906SEric.Saxe@Sun.COM 	while (!mutex_tryenter(&hpet_proxy_lock)) {
10508906SEric.Saxe@Sun.COM 		/*
10518906SEric.Saxe@Sun.COM 		 * spin
10528906SEric.Saxe@Sun.COM 		 */
10538906SEric.Saxe@Sun.COM 		intr_restore(intr);
10548906SEric.Saxe@Sun.COM 		if (dead_count++ > hpet_spin_check) {
10558906SEric.Saxe@Sun.COM 			dead_count = 0;
10568906SEric.Saxe@Sun.COM 			if (gethrtime() > dead) {
10578906SEric.Saxe@Sun.COM 				hpet_expire_all();
10588906SEric.Saxe@Sun.COM 				return (DDI_INTR_CLAIMED);
10598906SEric.Saxe@Sun.COM 			}
10608906SEric.Saxe@Sun.COM 		}
10618906SEric.Saxe@Sun.COM 		intr = intr_clear();
10628906SEric.Saxe@Sun.COM 	}
10638906SEric.Saxe@Sun.COM 	(void) hpet_guaranteed_schedule(HPET_INFINITY);
10648906SEric.Saxe@Sun.COM 	mutex_exit(&hpet_proxy_lock);
10658906SEric.Saxe@Sun.COM 	intr_restore(intr);
10668906SEric.Saxe@Sun.COM 
10678906SEric.Saxe@Sun.COM 	return (DDI_INTR_CLAIMED);
10688906SEric.Saxe@Sun.COM }
10698906SEric.Saxe@Sun.COM 
10708906SEric.Saxe@Sun.COM /*
10718906SEric.Saxe@Sun.COM  * Used when disabling the HPET Timer interrupt.  CPUs in Deep C-state must be
10728906SEric.Saxe@Sun.COM  * woken up because they can no longer rely on the HPET's Timer to wake them.
10738906SEric.Saxe@Sun.COM  * We do not need to wait for CPUs to wakeup.
10748906SEric.Saxe@Sun.COM  */
10758906SEric.Saxe@Sun.COM static void
10768906SEric.Saxe@Sun.COM hpet_expire_all(void)
10778906SEric.Saxe@Sun.COM {
10788906SEric.Saxe@Sun.COM 	processorid_t	id;
10798906SEric.Saxe@Sun.COM 
10808906SEric.Saxe@Sun.COM 	for (id = 0; id < ncpus; ++id) {
10818906SEric.Saxe@Sun.COM 		if (hpet_proxy_users[id] != HPET_INFINITY) {
10828906SEric.Saxe@Sun.COM 			hpet_proxy_users[id] = HPET_INFINITY;
10838906SEric.Saxe@Sun.COM 			if (id != CPU->cpu_id)
10848906SEric.Saxe@Sun.COM 				poke_cpu(id);
10858906SEric.Saxe@Sun.COM 		}
10868906SEric.Saxe@Sun.COM 	}
10878906SEric.Saxe@Sun.COM }
10888906SEric.Saxe@Sun.COM 
10898906SEric.Saxe@Sun.COM /*
10908906SEric.Saxe@Sun.COM  * To avoid missed wakeups this function must guarantee either the HPET timer
10918906SEric.Saxe@Sun.COM  * was successfully programmed to the next expire time or there are no waiting
10928906SEric.Saxe@Sun.COM  * CPUs.
10938906SEric.Saxe@Sun.COM  *
10948906SEric.Saxe@Sun.COM  * Callers cannot enter C2 or deeper if the HPET could not be programmed to
10958906SEric.Saxe@Sun.COM  * generate its next interrupt to happen at required_wakeup_time or sooner.
10968906SEric.Saxe@Sun.COM  * Returns B_TRUE if the HPET was programmed to interrupt by
10978906SEric.Saxe@Sun.COM  * required_wakeup_time, B_FALSE if not.
10988906SEric.Saxe@Sun.COM  */
10998906SEric.Saxe@Sun.COM static boolean_t
11008906SEric.Saxe@Sun.COM hpet_guaranteed_schedule(hrtime_t required_wakeup_time)
11018906SEric.Saxe@Sun.COM {
11028906SEric.Saxe@Sun.COM 	hrtime_t	now, next_proxy_time;
11038906SEric.Saxe@Sun.COM 	processorid_t	id, next_proxy_id;
11048906SEric.Saxe@Sun.COM 	int		proxy_timer = hpet_info.cstate_timer.timer;
11058906SEric.Saxe@Sun.COM 	boolean_t	done = B_FALSE;
11068906SEric.Saxe@Sun.COM 
11078906SEric.Saxe@Sun.COM 	ASSERT(mutex_owned(&hpet_proxy_lock));
11088906SEric.Saxe@Sun.COM 
11098906SEric.Saxe@Sun.COM 	/*
11108906SEric.Saxe@Sun.COM 	 * Loop until we successfully program the HPET,
11118906SEric.Saxe@Sun.COM 	 * or no CPUs are scheduled to use the HPET as a proxy.
11128906SEric.Saxe@Sun.COM 	 */
11138906SEric.Saxe@Sun.COM 	do {
11148906SEric.Saxe@Sun.COM 		/*
11158906SEric.Saxe@Sun.COM 		 * Wake all CPUs that expired before now.
11168906SEric.Saxe@Sun.COM 		 * Find the next CPU to wake up and next HPET program time.
11178906SEric.Saxe@Sun.COM 		 */
11188906SEric.Saxe@Sun.COM 		now = gethrtime();
11198906SEric.Saxe@Sun.COM 		next_proxy_time = HPET_INFINITY;
11208906SEric.Saxe@Sun.COM 		next_proxy_id = CPU->cpu_id;
11218906SEric.Saxe@Sun.COM 		for (id = 0; id < ncpus; ++id) {
11228906SEric.Saxe@Sun.COM 			if (hpet_proxy_users[id] < now) {
11238906SEric.Saxe@Sun.COM 				hpet_proxy_users[id] = HPET_INFINITY;
11248906SEric.Saxe@Sun.COM 				if (id != CPU->cpu_id)
11258906SEric.Saxe@Sun.COM 					poke_cpu(id);
11268906SEric.Saxe@Sun.COM 			} else if (hpet_proxy_users[id] < next_proxy_time) {
11278906SEric.Saxe@Sun.COM 				next_proxy_time = hpet_proxy_users[id];
11288906SEric.Saxe@Sun.COM 				next_proxy_id = id;
11298906SEric.Saxe@Sun.COM 			}
11308906SEric.Saxe@Sun.COM 		}
11318906SEric.Saxe@Sun.COM 
11328906SEric.Saxe@Sun.COM 		if (next_proxy_time == HPET_INFINITY) {
11338906SEric.Saxe@Sun.COM 			done = B_TRUE;
11348906SEric.Saxe@Sun.COM 			/*
11358906SEric.Saxe@Sun.COM 			 * There are currently no CPUs using the HPET's Timer
11368906SEric.Saxe@Sun.COM 			 * as a proxy for their LAPIC Timer.  The HPET's Timer
11378906SEric.Saxe@Sun.COM 			 * does not need to be programmed.
11388906SEric.Saxe@Sun.COM 			 *
11398906SEric.Saxe@Sun.COM 			 * Letting the HPET timer wrap around to the current
11408906SEric.Saxe@Sun.COM 			 * time is the longest possible timeout.
11418906SEric.Saxe@Sun.COM 			 * A 64-bit timer will wrap around in ~ 2^44 seconds.
11428906SEric.Saxe@Sun.COM 			 * A 32-bit timer will wrap around in ~ 2^12 seconds.
11438906SEric.Saxe@Sun.COM 			 *
11448906SEric.Saxe@Sun.COM 			 * Disabling the HPET's timer interrupt requires a
11458906SEric.Saxe@Sun.COM 			 * (relatively expensive) write to the HPET.
11468906SEric.Saxe@Sun.COM 			 * Instead we do nothing.
11478906SEric.Saxe@Sun.COM 			 *
11488906SEric.Saxe@Sun.COM 			 * We are gambling some CPU will attempt to enter a
11498906SEric.Saxe@Sun.COM 			 * deep c-state before the timer wraps around.
11508906SEric.Saxe@Sun.COM 			 * We assume one spurious interrupt in a little over an
11518906SEric.Saxe@Sun.COM 			 * hour has less performance impact than writing to the
11528906SEric.Saxe@Sun.COM 			 * HPET's timer disable bit every time all CPUs wakeup
11538906SEric.Saxe@Sun.COM 			 * from deep c-state.
11548906SEric.Saxe@Sun.COM 			 */
11558906SEric.Saxe@Sun.COM 
11568906SEric.Saxe@Sun.COM 		} else {
11578906SEric.Saxe@Sun.COM 			/*
11588906SEric.Saxe@Sun.COM 			 * Idle CPUs disable interrupts before programming the
11598906SEric.Saxe@Sun.COM 			 * HPET to prevent a lost wakeup if the HPET
11608906SEric.Saxe@Sun.COM 			 * interrupts the idle cpu before it can enter a
11618906SEric.Saxe@Sun.COM 			 * Deep C-State.
11628906SEric.Saxe@Sun.COM 			 */
11638906SEric.Saxe@Sun.COM 			if (hpet_timer_program(&hpet_info, proxy_timer,
11648906SEric.Saxe@Sun.COM 			    HRTIME_TO_HPET_TICKS(next_proxy_time - gethrtime()))
11658906SEric.Saxe@Sun.COM 			    != AE_OK) {
11668906SEric.Saxe@Sun.COM 				/*
11678906SEric.Saxe@Sun.COM 				 * We could not program the HPET to wakeup the
11688906SEric.Saxe@Sun.COM 				 * next CPU.  We must wake the CPU ourself to
11698906SEric.Saxe@Sun.COM 				 * avoid a lost wakeup.
11708906SEric.Saxe@Sun.COM 				 */
11718906SEric.Saxe@Sun.COM 				hpet_proxy_users[next_proxy_id] = HPET_INFINITY;
11728906SEric.Saxe@Sun.COM 				if (next_proxy_id != CPU->cpu_id)
11738906SEric.Saxe@Sun.COM 					poke_cpu(next_proxy_id);
11748906SEric.Saxe@Sun.COM 			} else {
11758906SEric.Saxe@Sun.COM 				done = B_TRUE;
11768906SEric.Saxe@Sun.COM 			}
11778906SEric.Saxe@Sun.COM 		}
11788906SEric.Saxe@Sun.COM 
11798906SEric.Saxe@Sun.COM 	} while (!done);
11808906SEric.Saxe@Sun.COM 
11818906SEric.Saxe@Sun.COM 	return (next_proxy_time <= required_wakeup_time);
11828906SEric.Saxe@Sun.COM }
11838906SEric.Saxe@Sun.COM 
11848906SEric.Saxe@Sun.COM /*
11858906SEric.Saxe@Sun.COM  * Use an HPET timer to act as this CPU's proxy local APIC timer.
11868906SEric.Saxe@Sun.COM  * Used in deep c-states C2 and above while the CPU's local APIC timer stalls.
11878906SEric.Saxe@Sun.COM  * Called by the idle thread with interrupts enabled.
11888906SEric.Saxe@Sun.COM  * Always returns with interrupts disabled.
11898906SEric.Saxe@Sun.COM  *
11908906SEric.Saxe@Sun.COM  * There are 3 possible outcomes from this function:
11918906SEric.Saxe@Sun.COM  * 1. The Local APIC Timer was already disabled before this function was called.
11928906SEric.Saxe@Sun.COM  *	LAPIC TIMER	: disabled
11938906SEric.Saxe@Sun.COM  *	HPET		: not scheduled to wake this CPU
11948906SEric.Saxe@Sun.COM  *	*lapic_expire	: (hrtime_t)HPET_INFINITY
11958906SEric.Saxe@Sun.COM  *	Returns		: B_TRUE
11968906SEric.Saxe@Sun.COM  * 2. Successfully programmed the HPET to act as a LAPIC Timer proxy.
11978906SEric.Saxe@Sun.COM  *	LAPIC TIMER	: disabled
11988906SEric.Saxe@Sun.COM  *	HPET		: scheduled to wake this CPU
11998906SEric.Saxe@Sun.COM  *	*lapic_expire	: hrtime_t when LAPIC timer would have expired
12008906SEric.Saxe@Sun.COM  *	Returns		: B_TRUE
12018906SEric.Saxe@Sun.COM  * 3. Failed to programmed the HPET to act as a LAPIC Timer proxy.
12028906SEric.Saxe@Sun.COM  *	LAPIC TIMER	: enabled
12038906SEric.Saxe@Sun.COM  *	HPET		: not scheduled to wake this CPU
12048906SEric.Saxe@Sun.COM  *	*lapic_expire	: (hrtime_t)HPET_INFINITY
12058906SEric.Saxe@Sun.COM  *	Returns		: B_FALSE
12068906SEric.Saxe@Sun.COM  *
12078906SEric.Saxe@Sun.COM  * The idle thread cannot enter Deep C-State in case 3.
12088906SEric.Saxe@Sun.COM  * The idle thread must re-enable & re-program the LAPIC_TIMER in case 2.
12098906SEric.Saxe@Sun.COM  */
12108906SEric.Saxe@Sun.COM static boolean_t
12118906SEric.Saxe@Sun.COM hpet_use_hpet_timer(hrtime_t *lapic_expire)
12128906SEric.Saxe@Sun.COM {
12138906SEric.Saxe@Sun.COM 	extern hrtime_t	apic_timer_stop_count(void);
12148906SEric.Saxe@Sun.COM 	extern void	apic_timer_restart(hrtime_t);
12158906SEric.Saxe@Sun.COM 	hrtime_t	now, expire, dead;
12168906SEric.Saxe@Sun.COM 	uint64_t	lapic_count, dead_count;
12178906SEric.Saxe@Sun.COM 	cpupart_t	*cpu_part;
12188906SEric.Saxe@Sun.COM 	processorid_t	cpu_sid;
12198906SEric.Saxe@Sun.COM 	processorid_t	cpu_id = CPU->cpu_id;
12208906SEric.Saxe@Sun.COM 	processorid_t	id;
12218906SEric.Saxe@Sun.COM 	boolean_t	rslt;
12228906SEric.Saxe@Sun.COM 	boolean_t	hset_update;
12238906SEric.Saxe@Sun.COM 
12248906SEric.Saxe@Sun.COM 	cpu_part = CPU->cpu_part;
12258906SEric.Saxe@Sun.COM 	cpu_sid = CPU->cpu_seqid;
12268906SEric.Saxe@Sun.COM 
12278906SEric.Saxe@Sun.COM 	ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread);
12288906SEric.Saxe@Sun.COM 
12298906SEric.Saxe@Sun.COM 	/*
12308906SEric.Saxe@Sun.COM 	 * A critical section exists between when the HPET is programmed
12318906SEric.Saxe@Sun.COM 	 * to interrupt the CPU and when this CPU enters an idle state.
12328906SEric.Saxe@Sun.COM 	 * Interrupts must be blocked during that time to prevent lost
12338906SEric.Saxe@Sun.COM 	 * CBE wakeup interrupts from either LAPIC or HPET.
12348906SEric.Saxe@Sun.COM 	 *
12358906SEric.Saxe@Sun.COM 	 * Must block interrupts before acquiring hpet_proxy_lock to prevent
12368906SEric.Saxe@Sun.COM 	 * a deadlock with the ISR if the ISR runs on this CPU after the
12378906SEric.Saxe@Sun.COM 	 * idle thread acquires the mutex but before it clears interrupts.
12388906SEric.Saxe@Sun.COM 	 */
1239*9283SBill.Holler@Sun.COM 	ASSERT(!interrupts_enabled());
12408906SEric.Saxe@Sun.COM 	lapic_count = apic_timer_stop_count();
12418906SEric.Saxe@Sun.COM 	now = gethrtime();
12428906SEric.Saxe@Sun.COM 	dead = now + hpet_idle_spin_timeout;
12438906SEric.Saxe@Sun.COM 	*lapic_expire = expire = now + lapic_count;
12448906SEric.Saxe@Sun.COM 	if (lapic_count == (hrtime_t)-1) {
12458906SEric.Saxe@Sun.COM 		/*
12468906SEric.Saxe@Sun.COM 		 * LAPIC timer is currently disabled.
12478906SEric.Saxe@Sun.COM 		 * Will not use the HPET as a LAPIC Timer proxy.
12488906SEric.Saxe@Sun.COM 		 */
12498906SEric.Saxe@Sun.COM 		*lapic_expire = (hrtime_t)HPET_INFINITY;
12508906SEric.Saxe@Sun.COM 		return (B_TRUE);
12518906SEric.Saxe@Sun.COM 	}
12528906SEric.Saxe@Sun.COM 
12538906SEric.Saxe@Sun.COM 	/*
12548906SEric.Saxe@Sun.COM 	 * Serialize hpet_proxy data structure manipulation.
12558906SEric.Saxe@Sun.COM 	 */
12568906SEric.Saxe@Sun.COM 	dead_count = 0;
12578906SEric.Saxe@Sun.COM 	while (!mutex_tryenter(&hpet_proxy_lock)) {
12588906SEric.Saxe@Sun.COM 		/*
12598906SEric.Saxe@Sun.COM 		 * spin
12608906SEric.Saxe@Sun.COM 		 */
12618906SEric.Saxe@Sun.COM 		apic_timer_restart(expire);
12628906SEric.Saxe@Sun.COM 		sti();
12638906SEric.Saxe@Sun.COM 		cli();
12648906SEric.Saxe@Sun.COM 
12658906SEric.Saxe@Sun.COM 		if (dead_count++ > hpet_spin_check) {
12668906SEric.Saxe@Sun.COM 			dead_count = 0;
12678906SEric.Saxe@Sun.COM 			hset_update = (((CPU->cpu_flags & CPU_OFFLINE) == 0) &&
12688906SEric.Saxe@Sun.COM 			    (ncpus > 1));
12698906SEric.Saxe@Sun.COM 			if (hset_update &&
12708906SEric.Saxe@Sun.COM 			    !bitset_in_set(&cpu_part->cp_haltset, cpu_sid)) {
12718906SEric.Saxe@Sun.COM 				*lapic_expire = (hrtime_t)HPET_INFINITY;
12728906SEric.Saxe@Sun.COM 				return (B_FALSE);
12738906SEric.Saxe@Sun.COM 			}
12748906SEric.Saxe@Sun.COM 		}
12758906SEric.Saxe@Sun.COM 
12768906SEric.Saxe@Sun.COM 		lapic_count = apic_timer_stop_count();
12778906SEric.Saxe@Sun.COM 		now = gethrtime();
12788906SEric.Saxe@Sun.COM 		*lapic_expire = expire = now + lapic_count;
12798906SEric.Saxe@Sun.COM 		if (lapic_count == (hrtime_t)-1) {
12808906SEric.Saxe@Sun.COM 			/*
12818906SEric.Saxe@Sun.COM 			 * LAPIC timer is currently disabled.
12828906SEric.Saxe@Sun.COM 			 * Will not use the HPET as a LAPIC Timer proxy.
12838906SEric.Saxe@Sun.COM 			 */
12848906SEric.Saxe@Sun.COM 			*lapic_expire = (hrtime_t)HPET_INFINITY;
12858906SEric.Saxe@Sun.COM 			return (B_TRUE);
12868906SEric.Saxe@Sun.COM 		}
12878906SEric.Saxe@Sun.COM 		if (now > dead) {
12888906SEric.Saxe@Sun.COM 			apic_timer_restart(expire);
12898906SEric.Saxe@Sun.COM 			*lapic_expire = (hrtime_t)HPET_INFINITY;
12908906SEric.Saxe@Sun.COM 			return (B_FALSE);
12918906SEric.Saxe@Sun.COM 		}
12928906SEric.Saxe@Sun.COM 	}
12938906SEric.Saxe@Sun.COM 
12948906SEric.Saxe@Sun.COM 	if ((hpet_state.cpr == B_TRUE) ||
12958906SEric.Saxe@Sun.COM 	    (hpet_state.cpu_deep_idle == B_FALSE) ||
12968906SEric.Saxe@Sun.COM 	    (hpet_state.proxy_installed == B_FALSE) ||
12978906SEric.Saxe@Sun.COM 	    (hpet_state.uni_cstate == B_TRUE)) {
12988906SEric.Saxe@Sun.COM 		mutex_exit(&hpet_proxy_lock);
12998906SEric.Saxe@Sun.COM 		apic_timer_restart(expire);
13008906SEric.Saxe@Sun.COM 		*lapic_expire = (hrtime_t)HPET_INFINITY;
13018906SEric.Saxe@Sun.COM 		return (B_FALSE);
13028906SEric.Saxe@Sun.COM 	}
13038906SEric.Saxe@Sun.COM 
13048906SEric.Saxe@Sun.COM 	hpet_proxy_users[cpu_id] = expire;
13058906SEric.Saxe@Sun.COM 
13068906SEric.Saxe@Sun.COM 	/*
13078906SEric.Saxe@Sun.COM 	 * We are done if another cpu is scheduled on the HPET with an
13088906SEric.Saxe@Sun.COM 	 * expire time before us.  The next HPET interrupt has been programmed
13098906SEric.Saxe@Sun.COM 	 * to fire before our expire time.
13108906SEric.Saxe@Sun.COM 	 */
13118906SEric.Saxe@Sun.COM 	for (id = 0; id < ncpus; ++id) {
13128906SEric.Saxe@Sun.COM 		if ((hpet_proxy_users[id] <= expire) && (id != cpu_id)) {
13138906SEric.Saxe@Sun.COM 			mutex_exit(&hpet_proxy_lock);
13148906SEric.Saxe@Sun.COM 			return (B_TRUE);
13158906SEric.Saxe@Sun.COM 		}
13168906SEric.Saxe@Sun.COM 	}
13178906SEric.Saxe@Sun.COM 
13188906SEric.Saxe@Sun.COM 	/*
13198906SEric.Saxe@Sun.COM 	 * We are the next lAPIC to expire.
13208906SEric.Saxe@Sun.COM 	 * Program the HPET with our expire time.
13218906SEric.Saxe@Sun.COM 	 */
13228906SEric.Saxe@Sun.COM 	rslt = hpet_guaranteed_schedule(expire);
13238906SEric.Saxe@Sun.COM 	mutex_exit(&hpet_proxy_lock);
13248906SEric.Saxe@Sun.COM 
13258906SEric.Saxe@Sun.COM 	if (rslt == B_FALSE) {
13268906SEric.Saxe@Sun.COM 		apic_timer_restart(expire);
13278906SEric.Saxe@Sun.COM 		*lapic_expire = (hrtime_t)HPET_INFINITY;
13288906SEric.Saxe@Sun.COM 	}
13298906SEric.Saxe@Sun.COM 
13308906SEric.Saxe@Sun.COM 	return (rslt);
13318906SEric.Saxe@Sun.COM }
13328906SEric.Saxe@Sun.COM 
13338906SEric.Saxe@Sun.COM /*
13348906SEric.Saxe@Sun.COM  * Called by the idle thread when waking up from Deep C-state before enabling
13358906SEric.Saxe@Sun.COM  * interrupts.  With an array data structure it is faster to always remove
13368906SEric.Saxe@Sun.COM  * ourself from the array without checking if the HPET ISR already removed.
13378906SEric.Saxe@Sun.COM  *
13388906SEric.Saxe@Sun.COM  * We use a lazy algorithm for removing CPUs from the HPET's schedule.
13398906SEric.Saxe@Sun.COM  * We do not reprogram the HPET here because this CPU has real work to do.
13408906SEric.Saxe@Sun.COM  * On a idle system the CPU was probably woken up by the HPET's ISR.
13418906SEric.Saxe@Sun.COM  * On a heavily loaded system CPUs are not going into Deep C-state.
13428906SEric.Saxe@Sun.COM  * On a moderately loaded system another CPU will usually enter Deep C-state
13438906SEric.Saxe@Sun.COM  * and reprogram the HPET before the HPET fires with our wakeup.
13448906SEric.Saxe@Sun.COM  */
13458906SEric.Saxe@Sun.COM static void
13468906SEric.Saxe@Sun.COM hpet_use_lapic_timer(hrtime_t expire)
13478906SEric.Saxe@Sun.COM {
13488906SEric.Saxe@Sun.COM 	extern void	apic_timer_restart(hrtime_t);
13498906SEric.Saxe@Sun.COM 	processorid_t	cpu_id = CPU->cpu_id;
13508906SEric.Saxe@Sun.COM 
13518906SEric.Saxe@Sun.COM 	ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread);
13528906SEric.Saxe@Sun.COM 	ASSERT(!interrupts_enabled());
13538906SEric.Saxe@Sun.COM 
13548906SEric.Saxe@Sun.COM 	hpet_proxy_users[cpu_id] = HPET_INFINITY;
13558906SEric.Saxe@Sun.COM 
13568906SEric.Saxe@Sun.COM 	/*
13578906SEric.Saxe@Sun.COM 	 * Do not enable a LAPIC Timer that was initially disabled.
13588906SEric.Saxe@Sun.COM 	 */
13598906SEric.Saxe@Sun.COM 	if (expire != HPET_INFINITY)
13608906SEric.Saxe@Sun.COM 		apic_timer_restart(expire);
13618906SEric.Saxe@Sun.COM }
13628906SEric.Saxe@Sun.COM 
13638906SEric.Saxe@Sun.COM /*
13648906SEric.Saxe@Sun.COM  * Initialize data structure to keep track of CPUs using HPET as a proxy for
13658906SEric.Saxe@Sun.COM  * their stalled local APIC timer.  For now this is just an array.
13668906SEric.Saxe@Sun.COM  */
13678906SEric.Saxe@Sun.COM static void
13688906SEric.Saxe@Sun.COM hpet_init_proxy_data(void)
13698906SEric.Saxe@Sun.COM {
13708906SEric.Saxe@Sun.COM 	processorid_t	id;
13718906SEric.Saxe@Sun.COM 
13728906SEric.Saxe@Sun.COM 	/*
13738906SEric.Saxe@Sun.COM 	 * Use apic_nproc because we are in boot before max_ncpus has been
13748906SEric.Saxe@Sun.COM 	 * initialized.
13758906SEric.Saxe@Sun.COM 	 */
13768906SEric.Saxe@Sun.COM 	hpet_proxy_users = kmem_zalloc(apic_nproc * sizeof (*hpet_proxy_users),
13778906SEric.Saxe@Sun.COM 	    KM_SLEEP);
13788906SEric.Saxe@Sun.COM 
13798906SEric.Saxe@Sun.COM 	/*
13808906SEric.Saxe@Sun.COM 	 * Unused entries always contain HPET_INFINITY.
13818906SEric.Saxe@Sun.COM 	 */
13828906SEric.Saxe@Sun.COM 	for (id = 0; id < apic_nproc; ++id)
13838906SEric.Saxe@Sun.COM 		hpet_proxy_users[id] = HPET_INFINITY;
13848906SEric.Saxe@Sun.COM }
1385