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