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