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 */ 25*9283SBill.Holler@Sun.COM /* 26*9283SBill.Holler@Sun.COM * Copyright (c) 2009, Intel Corporation. 27*9283SBill.Holler@Sun.COM * All rights reserved. 28*9283SBill.Holler@Sun.COM */ 298906SEric.Saxe@Sun.COM 308906SEric.Saxe@Sun.COM #include <sys/x86_archext.h> 318906SEric.Saxe@Sun.COM #include <sys/machsystm.h> 328906SEric.Saxe@Sun.COM #include <sys/x_call.h> 338906SEric.Saxe@Sun.COM #include <sys/stat.h> 348906SEric.Saxe@Sun.COM #include <sys/acpi/acpi.h> 358906SEric.Saxe@Sun.COM #include <sys/acpica.h> 368906SEric.Saxe@Sun.COM #include <sys/cpu_acpi.h> 378906SEric.Saxe@Sun.COM #include <sys/cpu_idle.h> 388906SEric.Saxe@Sun.COM #include <sys/cpupm.h> 398906SEric.Saxe@Sun.COM #include <sys/hpet.h> 408906SEric.Saxe@Sun.COM #include <sys/archsystm.h> 418906SEric.Saxe@Sun.COM #include <vm/hat_i86.h> 428906SEric.Saxe@Sun.COM #include <sys/dtrace.h> 438906SEric.Saxe@Sun.COM #include <sys/sdt.h> 448906SEric.Saxe@Sun.COM #include <sys/callb.h> 458906SEric.Saxe@Sun.COM 46*9283SBill.Holler@Sun.COM #define CSTATE_USING_HPET 1 47*9283SBill.Holler@Sun.COM #define CSTATE_USING_LAT 2 48*9283SBill.Holler@Sun.COM 498906SEric.Saxe@Sun.COM extern void cpu_idle_adaptive(void); 508983SBill.Holler@Sun.COM extern uint32_t cpupm_next_cstate(cma_c_state_t *cs_data, 518983SBill.Holler@Sun.COM cpu_acpi_cstate_t *cstates, uint32_t cs_count, hrtime_t start); 528906SEric.Saxe@Sun.COM 538906SEric.Saxe@Sun.COM static int cpu_idle_init(cpu_t *); 548906SEric.Saxe@Sun.COM static void cpu_idle_fini(cpu_t *); 558906SEric.Saxe@Sun.COM static boolean_t cpu_deep_idle_callb(void *arg, int code); 568906SEric.Saxe@Sun.COM static boolean_t cpu_idle_cpr_callb(void *arg, int code); 578906SEric.Saxe@Sun.COM static void acpi_cpu_cstate(cpu_acpi_cstate_t *cstate); 588906SEric.Saxe@Sun.COM 59*9283SBill.Holler@Sun.COM static boolean_t cstate_use_timer(hrtime_t *lapic_expire, int timer); 60*9283SBill.Holler@Sun.COM 61*9283SBill.Holler@Sun.COM /* 62*9283SBill.Holler@Sun.COM * the flag of always-running local APIC timer. 63*9283SBill.Holler@Sun.COM * the flag of HPET Timer use in deep cstate. 64*9283SBill.Holler@Sun.COM */ 65*9283SBill.Holler@Sun.COM static boolean_t cpu_cstate_arat = B_FALSE; 66*9283SBill.Holler@Sun.COM static boolean_t cpu_cstate_hpet = B_FALSE; 67*9283SBill.Holler@Sun.COM 688906SEric.Saxe@Sun.COM /* 698906SEric.Saxe@Sun.COM * Interfaces for modules implementing Intel's deep c-state. 708906SEric.Saxe@Sun.COM */ 718906SEric.Saxe@Sun.COM cpupm_state_ops_t cpu_idle_ops = { 728906SEric.Saxe@Sun.COM "Generic ACPI C-state Support", 738906SEric.Saxe@Sun.COM cpu_idle_init, 748906SEric.Saxe@Sun.COM cpu_idle_fini, 758906SEric.Saxe@Sun.COM NULL 768906SEric.Saxe@Sun.COM }; 778906SEric.Saxe@Sun.COM 788906SEric.Saxe@Sun.COM static kmutex_t cpu_idle_callb_mutex; 798906SEric.Saxe@Sun.COM static callb_id_t cpu_deep_idle_callb_id; 808906SEric.Saxe@Sun.COM static callb_id_t cpu_idle_cpr_callb_id; 818906SEric.Saxe@Sun.COM static uint_t cpu_idle_cfg_state; 828906SEric.Saxe@Sun.COM 838906SEric.Saxe@Sun.COM static kmutex_t cpu_idle_mutex; 848906SEric.Saxe@Sun.COM 858906SEric.Saxe@Sun.COM cpu_idle_kstat_t cpu_idle_kstat = { 868906SEric.Saxe@Sun.COM { "address_space_id", KSTAT_DATA_STRING }, 878906SEric.Saxe@Sun.COM { "latency", KSTAT_DATA_UINT32 }, 888906SEric.Saxe@Sun.COM { "power", KSTAT_DATA_UINT32 }, 898906SEric.Saxe@Sun.COM }; 908906SEric.Saxe@Sun.COM 918906SEric.Saxe@Sun.COM /* 928906SEric.Saxe@Sun.COM * kstat update function of the c-state info 938906SEric.Saxe@Sun.COM */ 948906SEric.Saxe@Sun.COM static int 958906SEric.Saxe@Sun.COM cpu_idle_kstat_update(kstat_t *ksp, int flag) 968906SEric.Saxe@Sun.COM { 978906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate = ksp->ks_private; 988906SEric.Saxe@Sun.COM 998906SEric.Saxe@Sun.COM if (flag == KSTAT_WRITE) { 1008906SEric.Saxe@Sun.COM return (EACCES); 1018906SEric.Saxe@Sun.COM } 1028906SEric.Saxe@Sun.COM 1038906SEric.Saxe@Sun.COM if (cstate->cs_addrspace_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { 1048906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1058906SEric.Saxe@Sun.COM "FFixedHW"); 1068906SEric.Saxe@Sun.COM } else if (cstate->cs_addrspace_id == ACPI_ADR_SPACE_SYSTEM_IO) { 1078906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1088906SEric.Saxe@Sun.COM "SystemIO"); 1098906SEric.Saxe@Sun.COM } else { 1108906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1118906SEric.Saxe@Sun.COM "Unsupported"); 1128906SEric.Saxe@Sun.COM } 1138906SEric.Saxe@Sun.COM 1148906SEric.Saxe@Sun.COM cpu_idle_kstat.cs_latency.value.ui32 = cstate->cs_latency; 1158906SEric.Saxe@Sun.COM cpu_idle_kstat.cs_power.value.ui32 = cstate->cs_power; 1168906SEric.Saxe@Sun.COM 1178906SEric.Saxe@Sun.COM return (0); 1188906SEric.Saxe@Sun.COM } 1198906SEric.Saxe@Sun.COM 1208906SEric.Saxe@Sun.COM /* 121*9283SBill.Holler@Sun.COM * Used during configuration callbacks to manage implementation specific 122*9283SBill.Holler@Sun.COM * details of the hardware timer used during Deep C-state. 123*9283SBill.Holler@Sun.COM */ 124*9283SBill.Holler@Sun.COM boolean_t 125*9283SBill.Holler@Sun.COM cstate_timer_callback(int code) 126*9283SBill.Holler@Sun.COM { 127*9283SBill.Holler@Sun.COM if (cpu_cstate_arat) { 128*9283SBill.Holler@Sun.COM return (B_TRUE); 129*9283SBill.Holler@Sun.COM } else if (cpu_cstate_hpet) { 130*9283SBill.Holler@Sun.COM return (hpet.callback(code)); 131*9283SBill.Holler@Sun.COM } 132*9283SBill.Holler@Sun.COM return (B_FALSE); 133*9283SBill.Holler@Sun.COM } 134*9283SBill.Holler@Sun.COM 135*9283SBill.Holler@Sun.COM /* 136*9283SBill.Holler@Sun.COM * Some Local APIC Timers do not work during Deep C-states. 137*9283SBill.Holler@Sun.COM * The Deep C-state idle function uses this function to ensure it is using a 138*9283SBill.Holler@Sun.COM * hardware timer that works during Deep C-states. This function also 139*9283SBill.Holler@Sun.COM * switches the timer back to the LACPI Timer after Deep C-state. 140*9283SBill.Holler@Sun.COM */ 141*9283SBill.Holler@Sun.COM static boolean_t 142*9283SBill.Holler@Sun.COM cstate_use_timer(hrtime_t *lapic_expire, int timer) 143*9283SBill.Holler@Sun.COM { 144*9283SBill.Holler@Sun.COM if (cpu_cstate_arat) 145*9283SBill.Holler@Sun.COM return (B_TRUE); 146*9283SBill.Holler@Sun.COM 147*9283SBill.Holler@Sun.COM /* 148*9283SBill.Holler@Sun.COM * We have to return B_FALSE if no arat or hpet support 149*9283SBill.Holler@Sun.COM */ 150*9283SBill.Holler@Sun.COM if (!cpu_cstate_hpet) 151*9283SBill.Holler@Sun.COM return (B_FALSE); 152*9283SBill.Holler@Sun.COM 153*9283SBill.Holler@Sun.COM switch (timer) { 154*9283SBill.Holler@Sun.COM case CSTATE_USING_HPET: 155*9283SBill.Holler@Sun.COM return (hpet.use_hpet_timer(lapic_expire)); 156*9283SBill.Holler@Sun.COM case CSTATE_USING_LAT: 157*9283SBill.Holler@Sun.COM hpet.use_lapic_timer(*lapic_expire); 158*9283SBill.Holler@Sun.COM return (B_TRUE); 159*9283SBill.Holler@Sun.COM default: 160*9283SBill.Holler@Sun.COM return (B_FALSE); 161*9283SBill.Holler@Sun.COM } 162*9283SBill.Holler@Sun.COM } 163*9283SBill.Holler@Sun.COM 164*9283SBill.Holler@Sun.COM /* 1658906SEric.Saxe@Sun.COM * c-state wakeup function. 1668906SEric.Saxe@Sun.COM * Similar to cpu_wakeup and cpu_wakeup_mwait except this function deals 1678906SEric.Saxe@Sun.COM * with CPUs asleep in MWAIT, HLT, or ACPI Deep C-State. 1688906SEric.Saxe@Sun.COM */ 1698906SEric.Saxe@Sun.COM void 1708906SEric.Saxe@Sun.COM cstate_wakeup(cpu_t *cp, int bound) 1718906SEric.Saxe@Sun.COM { 1728906SEric.Saxe@Sun.COM struct machcpu *mcpu = &(cp->cpu_m); 1738906SEric.Saxe@Sun.COM volatile uint32_t *mcpu_mwait = mcpu->mcpu_mwait; 1748906SEric.Saxe@Sun.COM cpupart_t *cpu_part; 1758906SEric.Saxe@Sun.COM uint_t cpu_found; 1768906SEric.Saxe@Sun.COM processorid_t cpu_sid; 1778906SEric.Saxe@Sun.COM 1788906SEric.Saxe@Sun.COM cpu_part = cp->cpu_part; 1798906SEric.Saxe@Sun.COM cpu_sid = cp->cpu_seqid; 1808906SEric.Saxe@Sun.COM /* 1818906SEric.Saxe@Sun.COM * Clear the halted bit for that CPU since it will be woken up 1828906SEric.Saxe@Sun.COM * in a moment. 1838906SEric.Saxe@Sun.COM */ 1848906SEric.Saxe@Sun.COM if (bitset_in_set(&cpu_part->cp_haltset, cpu_sid)) { 1858906SEric.Saxe@Sun.COM /* 1868906SEric.Saxe@Sun.COM * Clear the halted bit for that CPU since it will be 1878906SEric.Saxe@Sun.COM * poked in a moment. 1888906SEric.Saxe@Sun.COM */ 1898906SEric.Saxe@Sun.COM bitset_atomic_del(&cpu_part->cp_haltset, cpu_sid); 1908906SEric.Saxe@Sun.COM 1918906SEric.Saxe@Sun.COM /* 1928906SEric.Saxe@Sun.COM * We may find the current CPU present in the halted cpuset 1938906SEric.Saxe@Sun.COM * if we're in the context of an interrupt that occurred 1948906SEric.Saxe@Sun.COM * before we had a chance to clear our bit in cpu_idle(). 1958906SEric.Saxe@Sun.COM * Waking ourself is obviously unnecessary, since if 1968906SEric.Saxe@Sun.COM * we're here, we're not halted. 1978906SEric.Saxe@Sun.COM */ 1988906SEric.Saxe@Sun.COM if (cp != CPU) { 1998906SEric.Saxe@Sun.COM /* 2008906SEric.Saxe@Sun.COM * Use correct wakeup mechanism 2018906SEric.Saxe@Sun.COM */ 2028906SEric.Saxe@Sun.COM if ((mcpu_mwait != NULL) && 2038906SEric.Saxe@Sun.COM (*mcpu_mwait == MWAIT_HALTED)) 2048906SEric.Saxe@Sun.COM MWAIT_WAKEUP(cp); 2058906SEric.Saxe@Sun.COM else 2068906SEric.Saxe@Sun.COM poke_cpu(cp->cpu_id); 2078906SEric.Saxe@Sun.COM } 2088906SEric.Saxe@Sun.COM return; 2098906SEric.Saxe@Sun.COM } else { 2108906SEric.Saxe@Sun.COM /* 2118906SEric.Saxe@Sun.COM * This cpu isn't halted, but it's idle or undergoing a 2128906SEric.Saxe@Sun.COM * context switch. No need to awaken anyone else. 2138906SEric.Saxe@Sun.COM */ 2148906SEric.Saxe@Sun.COM if (cp->cpu_thread == cp->cpu_idle_thread || 2158906SEric.Saxe@Sun.COM cp->cpu_disp_flags & CPU_DISP_DONTSTEAL) 2168906SEric.Saxe@Sun.COM return; 2178906SEric.Saxe@Sun.COM } 2188906SEric.Saxe@Sun.COM 2198906SEric.Saxe@Sun.COM /* 2208906SEric.Saxe@Sun.COM * No need to wake up other CPUs if the thread we just enqueued 2218906SEric.Saxe@Sun.COM * is bound. 2228906SEric.Saxe@Sun.COM */ 2238906SEric.Saxe@Sun.COM if (bound) 2248906SEric.Saxe@Sun.COM return; 2258906SEric.Saxe@Sun.COM 2268906SEric.Saxe@Sun.COM 2278906SEric.Saxe@Sun.COM /* 2288906SEric.Saxe@Sun.COM * See if there's any other halted CPUs. If there are, then 2298906SEric.Saxe@Sun.COM * select one, and awaken it. 2308906SEric.Saxe@Sun.COM * It's possible that after we find a CPU, somebody else 2318906SEric.Saxe@Sun.COM * will awaken it before we get the chance. 2328906SEric.Saxe@Sun.COM * In that case, look again. 2338906SEric.Saxe@Sun.COM */ 2348906SEric.Saxe@Sun.COM do { 2358906SEric.Saxe@Sun.COM cpu_found = bitset_find(&cpu_part->cp_haltset); 2368906SEric.Saxe@Sun.COM if (cpu_found == (uint_t)-1) 2378906SEric.Saxe@Sun.COM return; 2388906SEric.Saxe@Sun.COM 2398906SEric.Saxe@Sun.COM } while (bitset_atomic_test_and_del(&cpu_part->cp_haltset, 2408906SEric.Saxe@Sun.COM cpu_found) < 0); 2418906SEric.Saxe@Sun.COM 2428906SEric.Saxe@Sun.COM /* 2438906SEric.Saxe@Sun.COM * Must use correct wakeup mechanism to avoid lost wakeup of 2448906SEric.Saxe@Sun.COM * alternate cpu. 2458906SEric.Saxe@Sun.COM */ 2468906SEric.Saxe@Sun.COM if (cpu_found != CPU->cpu_seqid) { 2478906SEric.Saxe@Sun.COM mcpu_mwait = cpu[cpu_found]->cpu_m.mcpu_mwait; 2488906SEric.Saxe@Sun.COM if ((mcpu_mwait != NULL) && (*mcpu_mwait == MWAIT_HALTED)) 2498906SEric.Saxe@Sun.COM MWAIT_WAKEUP(cpu_seq[cpu_found]); 2508906SEric.Saxe@Sun.COM else 2518906SEric.Saxe@Sun.COM poke_cpu(cpu_seq[cpu_found]->cpu_id); 2528906SEric.Saxe@Sun.COM } 2538906SEric.Saxe@Sun.COM } 2548906SEric.Saxe@Sun.COM 2558906SEric.Saxe@Sun.COM /* 2568906SEric.Saxe@Sun.COM * enter deep c-state handler 2578906SEric.Saxe@Sun.COM */ 2588906SEric.Saxe@Sun.COM static void 2598906SEric.Saxe@Sun.COM acpi_cpu_cstate(cpu_acpi_cstate_t *cstate) 2608906SEric.Saxe@Sun.COM { 2618906SEric.Saxe@Sun.COM volatile uint32_t *mcpu_mwait = CPU->cpu_m.mcpu_mwait; 2628906SEric.Saxe@Sun.COM cpu_t *cpup = CPU; 2638906SEric.Saxe@Sun.COM processorid_t cpu_sid = cpup->cpu_seqid; 2648906SEric.Saxe@Sun.COM cpupart_t *cp = cpup->cpu_part; 2658906SEric.Saxe@Sun.COM hrtime_t lapic_expire; 2668906SEric.Saxe@Sun.COM uint8_t type = cstate->cs_addrspace_id; 2678906SEric.Saxe@Sun.COM uint32_t cs_type = cstate->cs_type; 2688906SEric.Saxe@Sun.COM int hset_update = 1; 269*9283SBill.Holler@Sun.COM boolean_t using_timer; 2708906SEric.Saxe@Sun.COM 2718906SEric.Saxe@Sun.COM /* 2728906SEric.Saxe@Sun.COM * Set our mcpu_mwait here, so we can tell if anyone tries to 2738906SEric.Saxe@Sun.COM * wake us between now and when we call mwait. No other cpu will 2748906SEric.Saxe@Sun.COM * attempt to set our mcpu_mwait until we add ourself to the haltset. 2758906SEric.Saxe@Sun.COM */ 2768906SEric.Saxe@Sun.COM if (mcpu_mwait) { 2778906SEric.Saxe@Sun.COM if (type == ACPI_ADR_SPACE_SYSTEM_IO) 2788906SEric.Saxe@Sun.COM *mcpu_mwait = MWAIT_WAKEUP_IPI; 2798906SEric.Saxe@Sun.COM else 2808906SEric.Saxe@Sun.COM *mcpu_mwait = MWAIT_HALTED; 2818906SEric.Saxe@Sun.COM } 2828906SEric.Saxe@Sun.COM 2838906SEric.Saxe@Sun.COM /* 2848906SEric.Saxe@Sun.COM * If this CPU is online, and there are multiple CPUs 2858906SEric.Saxe@Sun.COM * in the system, then we should note our halting 2868906SEric.Saxe@Sun.COM * by adding ourselves to the partition's halted CPU 2878906SEric.Saxe@Sun.COM * bitmap. This allows other CPUs to find/awaken us when 2888906SEric.Saxe@Sun.COM * work becomes available. 2898906SEric.Saxe@Sun.COM */ 2908906SEric.Saxe@Sun.COM if (cpup->cpu_flags & CPU_OFFLINE || ncpus == 1) 2918906SEric.Saxe@Sun.COM hset_update = 0; 2928906SEric.Saxe@Sun.COM 2938906SEric.Saxe@Sun.COM /* 2948906SEric.Saxe@Sun.COM * Add ourselves to the partition's halted CPUs bitmask 2958906SEric.Saxe@Sun.COM * and set our HALTED flag, if necessary. 2968906SEric.Saxe@Sun.COM * 2978906SEric.Saxe@Sun.COM * When a thread becomes runnable, it is placed on the queue 2988906SEric.Saxe@Sun.COM * and then the halted cpuset is checked to determine who 2998906SEric.Saxe@Sun.COM * (if anyone) should be awakened. We therefore need to first 3008906SEric.Saxe@Sun.COM * add ourselves to the halted cpuset, and and then check if there 3018906SEric.Saxe@Sun.COM * is any work available. 3028906SEric.Saxe@Sun.COM * 3038906SEric.Saxe@Sun.COM * Note that memory barriers after updating the HALTED flag 3048906SEric.Saxe@Sun.COM * are not necessary since an atomic operation (updating the bitmap) 3058906SEric.Saxe@Sun.COM * immediately follows. On x86 the atomic operation acts as a 3068906SEric.Saxe@Sun.COM * memory barrier for the update of cpu_disp_flags. 3078906SEric.Saxe@Sun.COM */ 3088906SEric.Saxe@Sun.COM if (hset_update) { 3098906SEric.Saxe@Sun.COM cpup->cpu_disp_flags |= CPU_DISP_HALTED; 3108906SEric.Saxe@Sun.COM bitset_atomic_add(&cp->cp_haltset, cpu_sid); 3118906SEric.Saxe@Sun.COM } 3128906SEric.Saxe@Sun.COM 3138906SEric.Saxe@Sun.COM /* 3148906SEric.Saxe@Sun.COM * Check to make sure there's really nothing to do. 3158906SEric.Saxe@Sun.COM * Work destined for this CPU may become available after 3168906SEric.Saxe@Sun.COM * this check. We'll be notified through the clearing of our 3178906SEric.Saxe@Sun.COM * bit in the halted CPU bitmask, and a write to our mcpu_mwait. 3188906SEric.Saxe@Sun.COM * 3198906SEric.Saxe@Sun.COM * disp_anywork() checks disp_nrunnable, so we do not have to later. 3208906SEric.Saxe@Sun.COM */ 3218906SEric.Saxe@Sun.COM if (disp_anywork()) { 3228906SEric.Saxe@Sun.COM if (hset_update) { 3238906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 3248906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 3258906SEric.Saxe@Sun.COM } 3268906SEric.Saxe@Sun.COM return; 3278906SEric.Saxe@Sun.COM } 3288906SEric.Saxe@Sun.COM 3298906SEric.Saxe@Sun.COM /* 3308906SEric.Saxe@Sun.COM * We're on our way to being halted. 3318906SEric.Saxe@Sun.COM * 3328906SEric.Saxe@Sun.COM * The local APIC timer can stop in ACPI C2 and deeper c-states. 333*9283SBill.Holler@Sun.COM * Try to program the HPET hardware to substitute for this CPU's 334*9283SBill.Holler@Sun.COM * LAPIC timer. 335*9283SBill.Holler@Sun.COM * cstate_use_timer() could disable the LAPIC Timer. Make sure 336*9283SBill.Holler@Sun.COM * to start the LAPIC Timer again before leaving this function. 3378906SEric.Saxe@Sun.COM * 338*9283SBill.Holler@Sun.COM * Disable interrupts here so we will awaken immediately after halting 339*9283SBill.Holler@Sun.COM * if someone tries to poke us between now and the time we actually 340*9283SBill.Holler@Sun.COM * halt. 3418906SEric.Saxe@Sun.COM */ 342*9283SBill.Holler@Sun.COM cli(); 343*9283SBill.Holler@Sun.COM using_timer = cstate_use_timer(&lapic_expire, CSTATE_USING_HPET); 3448906SEric.Saxe@Sun.COM 3458906SEric.Saxe@Sun.COM /* 3468906SEric.Saxe@Sun.COM * We check for the presence of our bit after disabling interrupts. 3478906SEric.Saxe@Sun.COM * If it's cleared, we'll return. If the bit is cleared after 3488906SEric.Saxe@Sun.COM * we check then the cstate_wakeup() will pop us out of the halted 3498906SEric.Saxe@Sun.COM * state. 3508906SEric.Saxe@Sun.COM * 3518906SEric.Saxe@Sun.COM * This means that the ordering of the cstate_wakeup() and the clearing 3528906SEric.Saxe@Sun.COM * of the bit by cpu_wakeup is important. 3538906SEric.Saxe@Sun.COM * cpu_wakeup() must clear our mc_haltset bit, and then call 3548906SEric.Saxe@Sun.COM * cstate_wakeup(). 3558906SEric.Saxe@Sun.COM * acpi_cpu_cstate() must disable interrupts, then check for the bit. 3568906SEric.Saxe@Sun.COM */ 3578906SEric.Saxe@Sun.COM if (hset_update && bitset_in_set(&cp->cp_haltset, cpu_sid) == 0) { 358*9283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 359*9283SBill.Holler@Sun.COM CSTATE_USING_LAT); 360*9283SBill.Holler@Sun.COM sti(); 3618906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 3628906SEric.Saxe@Sun.COM return; 3638906SEric.Saxe@Sun.COM } 3648906SEric.Saxe@Sun.COM 3658906SEric.Saxe@Sun.COM /* 3668906SEric.Saxe@Sun.COM * The check for anything locally runnable is here for performance 3678906SEric.Saxe@Sun.COM * and isn't needed for correctness. disp_nrunnable ought to be 3688906SEric.Saxe@Sun.COM * in our cache still, so it's inexpensive to check, and if there 3698906SEric.Saxe@Sun.COM * is anything runnable we won't have to wait for the poke. 3708906SEric.Saxe@Sun.COM */ 3718906SEric.Saxe@Sun.COM if (cpup->cpu_disp->disp_nrunnable != 0) { 372*9283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 373*9283SBill.Holler@Sun.COM CSTATE_USING_LAT); 374*9283SBill.Holler@Sun.COM sti(); 3758906SEric.Saxe@Sun.COM if (hset_update) { 3768906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 3778906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 3788906SEric.Saxe@Sun.COM } 3798906SEric.Saxe@Sun.COM return; 3808906SEric.Saxe@Sun.COM } 3818906SEric.Saxe@Sun.COM 382*9283SBill.Holler@Sun.COM if (using_timer == B_FALSE) { 3838906SEric.Saxe@Sun.COM 384*9283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 385*9283SBill.Holler@Sun.COM CSTATE_USING_LAT); 386*9283SBill.Holler@Sun.COM sti(); 3878906SEric.Saxe@Sun.COM 3888906SEric.Saxe@Sun.COM /* 3898906SEric.Saxe@Sun.COM * We are currently unable to program the HPET to act as this 390*9283SBill.Holler@Sun.COM * CPU's proxy LAPIC timer. This CPU cannot enter C2 or deeper 391*9283SBill.Holler@Sun.COM * because no timer is set to wake it up while its LAPIC timer 3928906SEric.Saxe@Sun.COM * stalls in deep C-States. 3938906SEric.Saxe@Sun.COM * Enter C1 instead. 3948906SEric.Saxe@Sun.COM * 3958906SEric.Saxe@Sun.COM * cstate_wake_cpu() will wake this CPU with an IPI which 3968906SEric.Saxe@Sun.COM * works with MWAIT. 3978906SEric.Saxe@Sun.COM */ 3988906SEric.Saxe@Sun.COM i86_monitor(mcpu_mwait, 0, 0); 3998906SEric.Saxe@Sun.COM if ((*mcpu_mwait & ~MWAIT_WAKEUP_IPI) == MWAIT_HALTED) { 4008906SEric.Saxe@Sun.COM cpu_dtrace_idle_probe(CPU_ACPI_C1); 4018906SEric.Saxe@Sun.COM 4028906SEric.Saxe@Sun.COM tlb_going_idle(); 4038906SEric.Saxe@Sun.COM i86_mwait(0, 0); 4048906SEric.Saxe@Sun.COM tlb_service(); 4058906SEric.Saxe@Sun.COM 4068906SEric.Saxe@Sun.COM cpu_dtrace_idle_probe(CPU_ACPI_C0); 4078906SEric.Saxe@Sun.COM } 4088906SEric.Saxe@Sun.COM 4098906SEric.Saxe@Sun.COM /* 4108906SEric.Saxe@Sun.COM * We're no longer halted 4118906SEric.Saxe@Sun.COM */ 4128906SEric.Saxe@Sun.COM if (hset_update) { 4138906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 4148906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 4158906SEric.Saxe@Sun.COM } 4168906SEric.Saxe@Sun.COM return; 4178906SEric.Saxe@Sun.COM } 4188906SEric.Saxe@Sun.COM 4198906SEric.Saxe@Sun.COM cpu_dtrace_idle_probe((uint_t)cs_type); 4208906SEric.Saxe@Sun.COM 4218906SEric.Saxe@Sun.COM if (type == ACPI_ADR_SPACE_FIXED_HARDWARE) { 4228906SEric.Saxe@Sun.COM /* 4238906SEric.Saxe@Sun.COM * We're on our way to being halted. 4248906SEric.Saxe@Sun.COM * To avoid a lost wakeup, arm the monitor before checking 4258906SEric.Saxe@Sun.COM * if another cpu wrote to mcpu_mwait to wake us up. 4268906SEric.Saxe@Sun.COM */ 4278906SEric.Saxe@Sun.COM i86_monitor(mcpu_mwait, 0, 0); 4288906SEric.Saxe@Sun.COM if (*mcpu_mwait == MWAIT_HALTED) { 4298906SEric.Saxe@Sun.COM uint32_t eax = cstate->cs_address; 4308906SEric.Saxe@Sun.COM uint32_t ecx = 1; 4318906SEric.Saxe@Sun.COM 4328906SEric.Saxe@Sun.COM tlb_going_idle(); 4338906SEric.Saxe@Sun.COM i86_mwait(eax, ecx); 4348906SEric.Saxe@Sun.COM tlb_service(); 4358906SEric.Saxe@Sun.COM } 4368906SEric.Saxe@Sun.COM } else if (type == ACPI_ADR_SPACE_SYSTEM_IO) { 4378906SEric.Saxe@Sun.COM uint32_t value; 4388906SEric.Saxe@Sun.COM ACPI_TABLE_FADT *gbl_FADT; 4398906SEric.Saxe@Sun.COM 4408906SEric.Saxe@Sun.COM if (*mcpu_mwait == MWAIT_WAKEUP_IPI) { 4418906SEric.Saxe@Sun.COM tlb_going_idle(); 4428906SEric.Saxe@Sun.COM (void) cpu_acpi_read_port(cstate->cs_address, 4438906SEric.Saxe@Sun.COM &value, 8); 4448906SEric.Saxe@Sun.COM acpica_get_global_FADT(&gbl_FADT); 4458906SEric.Saxe@Sun.COM (void) cpu_acpi_read_port( 4468906SEric.Saxe@Sun.COM gbl_FADT->XPmTimerBlock.Address, &value, 32); 4478906SEric.Saxe@Sun.COM tlb_service(); 4488906SEric.Saxe@Sun.COM } 4498906SEric.Saxe@Sun.COM } 4508906SEric.Saxe@Sun.COM 4518906SEric.Saxe@Sun.COM /* 452*9283SBill.Holler@Sun.COM * The LAPIC timer may have stopped in deep c-state. 453*9283SBill.Holler@Sun.COM * Reprogram this CPU's LAPIC here before enabling interrupts. 4548906SEric.Saxe@Sun.COM */ 455*9283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, CSTATE_USING_LAT); 456*9283SBill.Holler@Sun.COM sti(); 4578906SEric.Saxe@Sun.COM 4588906SEric.Saxe@Sun.COM cpu_dtrace_idle_probe(CPU_ACPI_C0); 4598906SEric.Saxe@Sun.COM 4608906SEric.Saxe@Sun.COM /* 4618906SEric.Saxe@Sun.COM * We're no longer halted 4628906SEric.Saxe@Sun.COM */ 4638906SEric.Saxe@Sun.COM if (hset_update) { 4648906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 4658906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 4668906SEric.Saxe@Sun.COM } 4678906SEric.Saxe@Sun.COM } 4688906SEric.Saxe@Sun.COM 4698906SEric.Saxe@Sun.COM /* 4708906SEric.Saxe@Sun.COM * indicate when bus masters are active 4718906SEric.Saxe@Sun.COM */ 4728906SEric.Saxe@Sun.COM static uint32_t 4738906SEric.Saxe@Sun.COM cpu_acpi_bm_sts(void) 4748906SEric.Saxe@Sun.COM { 4758906SEric.Saxe@Sun.COM uint32_t bm_sts = 0; 4768906SEric.Saxe@Sun.COM 4778906SEric.Saxe@Sun.COM cpu_acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_sts); 4788906SEric.Saxe@Sun.COM 4798906SEric.Saxe@Sun.COM if (bm_sts) 4808906SEric.Saxe@Sun.COM cpu_acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); 4818906SEric.Saxe@Sun.COM 4828906SEric.Saxe@Sun.COM return (bm_sts); 4838906SEric.Saxe@Sun.COM } 4848906SEric.Saxe@Sun.COM 4858906SEric.Saxe@Sun.COM /* 4868906SEric.Saxe@Sun.COM * Idle the present CPU, deep c-state is supported 4878906SEric.Saxe@Sun.COM */ 4888906SEric.Saxe@Sun.COM void 4898906SEric.Saxe@Sun.COM cpu_acpi_idle(void) 4908906SEric.Saxe@Sun.COM { 4918906SEric.Saxe@Sun.COM cpu_t *cp = CPU; 4928906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 4938906SEric.Saxe@Sun.COM cma_c_state_t *cs_data; 4948983SBill.Holler@Sun.COM cpu_acpi_cstate_t *cstates; 4958906SEric.Saxe@Sun.COM hrtime_t start, end; 4968906SEric.Saxe@Sun.COM int cpu_max_cstates; 4978983SBill.Holler@Sun.COM uint32_t cs_indx; 4988983SBill.Holler@Sun.COM uint16_t cs_type; 4998906SEric.Saxe@Sun.COM 5008906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 5018906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 5028906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 5038906SEric.Saxe@Sun.COM ASSERT(CPU_ACPI_CSTATES(handle) != NULL); 5048906SEric.Saxe@Sun.COM 5058906SEric.Saxe@Sun.COM cs_data = mach_state->ms_cstate.cma_state.cstate; 5068983SBill.Holler@Sun.COM cstates = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 5078983SBill.Holler@Sun.COM ASSERT(cstates != NULL); 5088906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 5098906SEric.Saxe@Sun.COM if (cpu_max_cstates > CPU_MAX_CSTATES) 5108906SEric.Saxe@Sun.COM cpu_max_cstates = CPU_MAX_CSTATES; 5118983SBill.Holler@Sun.COM if (cpu_max_cstates == 1) { /* no ACPI c-state data */ 5128983SBill.Holler@Sun.COM (*non_deep_idle_cpu)(); 5138983SBill.Holler@Sun.COM return; 5148983SBill.Holler@Sun.COM } 5158906SEric.Saxe@Sun.COM 5168906SEric.Saxe@Sun.COM start = gethrtime_unscaled(); 5178906SEric.Saxe@Sun.COM 5188983SBill.Holler@Sun.COM cs_indx = cpupm_next_cstate(cs_data, cstates, cpu_max_cstates, start); 5198906SEric.Saxe@Sun.COM 5208906SEric.Saxe@Sun.COM /* 5218906SEric.Saxe@Sun.COM * OSPM uses the BM_STS bit to determine the power state to enter 5228906SEric.Saxe@Sun.COM * when considering a transition to or from the C2/C3 power state. 5238906SEric.Saxe@Sun.COM * if C3 is determined, bus master activity demotes the power state 5248906SEric.Saxe@Sun.COM * to C2. 5258906SEric.Saxe@Sun.COM */ 5268983SBill.Holler@Sun.COM if ((cstates[cs_indx].cs_type >= CPU_ACPI_C3) && cpu_acpi_bm_sts()) 5278983SBill.Holler@Sun.COM --cs_indx; 5288983SBill.Holler@Sun.COM cs_type = cstates[cs_indx].cs_type; 5298906SEric.Saxe@Sun.COM 5308906SEric.Saxe@Sun.COM /* 5318906SEric.Saxe@Sun.COM * BM_RLD determines if the Cx power state was exited as a result of 5328906SEric.Saxe@Sun.COM * bus master requests. Set this bit when using a C3 power state, and 5338906SEric.Saxe@Sun.COM * clear it when using a C1 or C2 power state. 5348906SEric.Saxe@Sun.COM */ 5358906SEric.Saxe@Sun.COM if ((CPU_ACPI_BM_INFO(handle) & BM_RLD) && (cs_type < CPU_ACPI_C3)) { 5368906SEric.Saxe@Sun.COM cpu_acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); 5378906SEric.Saxe@Sun.COM CPU_ACPI_BM_INFO(handle) &= ~BM_RLD; 5388906SEric.Saxe@Sun.COM } 5398906SEric.Saxe@Sun.COM 5408906SEric.Saxe@Sun.COM if ((!(CPU_ACPI_BM_INFO(handle) & BM_RLD)) && 5418906SEric.Saxe@Sun.COM (cs_type >= CPU_ACPI_C3)) { 5428906SEric.Saxe@Sun.COM cpu_acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1); 5438906SEric.Saxe@Sun.COM CPU_ACPI_BM_INFO(handle) |= BM_RLD; 5448906SEric.Saxe@Sun.COM } 5458906SEric.Saxe@Sun.COM 5468906SEric.Saxe@Sun.COM switch (cs_type) { 5478906SEric.Saxe@Sun.COM default: 5488906SEric.Saxe@Sun.COM /* FALLTHROUGH */ 5498906SEric.Saxe@Sun.COM case CPU_ACPI_C1: 5508906SEric.Saxe@Sun.COM (*non_deep_idle_cpu)(); 5518906SEric.Saxe@Sun.COM break; 5528906SEric.Saxe@Sun.COM 5538906SEric.Saxe@Sun.COM case CPU_ACPI_C2: 5548983SBill.Holler@Sun.COM acpi_cpu_cstate(&cstates[cs_indx]); 5558906SEric.Saxe@Sun.COM break; 5568906SEric.Saxe@Sun.COM 5578906SEric.Saxe@Sun.COM case CPU_ACPI_C3: 5588906SEric.Saxe@Sun.COM /* 5598906SEric.Saxe@Sun.COM * recommended in ACPI spec, providing hardware mechanisms 5608906SEric.Saxe@Sun.COM * to prevent master from writing to memory (UP-only) 5618906SEric.Saxe@Sun.COM */ 5628906SEric.Saxe@Sun.COM if ((ncpus_online == 1) && 5638906SEric.Saxe@Sun.COM (CPU_ACPI_BM_INFO(handle) & BM_CTL)) { 5648906SEric.Saxe@Sun.COM cpu_acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1); 5658906SEric.Saxe@Sun.COM CPU_ACPI_BM_INFO(handle) |= BM_ARB_DIS; 5668906SEric.Saxe@Sun.COM /* 5678906SEric.Saxe@Sun.COM * Today all Intel's processor support C3 share cache. 5688906SEric.Saxe@Sun.COM */ 5698906SEric.Saxe@Sun.COM } else if (x86_vendor != X86_VENDOR_Intel) { 5708906SEric.Saxe@Sun.COM __acpi_wbinvd(); 5718906SEric.Saxe@Sun.COM } 5728983SBill.Holler@Sun.COM acpi_cpu_cstate(&cstates[cs_indx]); 5738906SEric.Saxe@Sun.COM if (CPU_ACPI_BM_INFO(handle) & BM_ARB_DIS) { 5748906SEric.Saxe@Sun.COM cpu_acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0); 5758906SEric.Saxe@Sun.COM CPU_ACPI_BM_INFO(handle) &= ~BM_ARB_DIS; 5768906SEric.Saxe@Sun.COM } 5778906SEric.Saxe@Sun.COM break; 5788906SEric.Saxe@Sun.COM } 5798906SEric.Saxe@Sun.COM 5808906SEric.Saxe@Sun.COM end = gethrtime_unscaled(); 5818906SEric.Saxe@Sun.COM 5828906SEric.Saxe@Sun.COM /* 5838906SEric.Saxe@Sun.COM * Update statistics 5848906SEric.Saxe@Sun.COM */ 5858906SEric.Saxe@Sun.COM cpupm_wakeup_cstate_data(cs_data, end); 5868906SEric.Saxe@Sun.COM } 5878906SEric.Saxe@Sun.COM 5888906SEric.Saxe@Sun.COM boolean_t 5898906SEric.Saxe@Sun.COM cpu_deep_cstates_supported(void) 5908906SEric.Saxe@Sun.COM { 5918906SEric.Saxe@Sun.COM extern int idle_cpu_no_deep_c; 5928906SEric.Saxe@Sun.COM 5938906SEric.Saxe@Sun.COM if (idle_cpu_no_deep_c) 5948906SEric.Saxe@Sun.COM return (B_FALSE); 5958906SEric.Saxe@Sun.COM 5968906SEric.Saxe@Sun.COM if (!cpuid_deep_cstates_supported()) 5978906SEric.Saxe@Sun.COM return (B_FALSE); 5988906SEric.Saxe@Sun.COM 599*9283SBill.Holler@Sun.COM if (cpuid_arat_supported()) { 600*9283SBill.Holler@Sun.COM cpu_cstate_arat = B_TRUE; 601*9283SBill.Holler@Sun.COM return (B_TRUE); 602*9283SBill.Holler@Sun.COM } 6038906SEric.Saxe@Sun.COM 604*9283SBill.Holler@Sun.COM if ((hpet.supported == HPET_FULL_SUPPORT) && 605*9283SBill.Holler@Sun.COM hpet.install_proxy()) { 606*9283SBill.Holler@Sun.COM cpu_cstate_hpet = B_TRUE; 607*9283SBill.Holler@Sun.COM return (B_TRUE); 608*9283SBill.Holler@Sun.COM } 609*9283SBill.Holler@Sun.COM 610*9283SBill.Holler@Sun.COM return (B_FALSE); 6118906SEric.Saxe@Sun.COM } 6128906SEric.Saxe@Sun.COM 6138906SEric.Saxe@Sun.COM /* 6148906SEric.Saxe@Sun.COM * Validate that this processor supports deep cstate and if so, 6158906SEric.Saxe@Sun.COM * get the c-state data from ACPI and cache it. 6168906SEric.Saxe@Sun.COM */ 6178906SEric.Saxe@Sun.COM static int 6188906SEric.Saxe@Sun.COM cpu_idle_init(cpu_t *cp) 6198906SEric.Saxe@Sun.COM { 6208906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6218906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 6228906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 6238906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate; 6248906SEric.Saxe@Sun.COM char name[KSTAT_STRLEN]; 6258906SEric.Saxe@Sun.COM int cpu_max_cstates, i; 6268906SEric.Saxe@Sun.COM ACPI_TABLE_FADT *gbl_FADT; 6278906SEric.Saxe@Sun.COM 6288906SEric.Saxe@Sun.COM /* 6298906SEric.Saxe@Sun.COM * Cache the C-state specific ACPI data. 6308906SEric.Saxe@Sun.COM */ 6318906SEric.Saxe@Sun.COM if (cpu_acpi_cache_cstate_data(handle) != 0) { 6328906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, 6338906SEric.Saxe@Sun.COM "!cpu_idle_init: Failed to cache ACPI C-state data\n"); 6348906SEric.Saxe@Sun.COM cpu_idle_fini(cp); 6358906SEric.Saxe@Sun.COM return (-1); 6368906SEric.Saxe@Sun.COM } 6378906SEric.Saxe@Sun.COM 6388906SEric.Saxe@Sun.COM /* 6398906SEric.Saxe@Sun.COM * Check the bus master arbitration control ability. 6408906SEric.Saxe@Sun.COM */ 6418906SEric.Saxe@Sun.COM acpica_get_global_FADT(&gbl_FADT); 6428906SEric.Saxe@Sun.COM if (gbl_FADT->Pm2ControlBlock && gbl_FADT->Pm2ControlLength) 6438906SEric.Saxe@Sun.COM CPU_ACPI_BM_INFO(handle) |= BM_CTL; 6448906SEric.Saxe@Sun.COM 6458906SEric.Saxe@Sun.COM cstate = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 6468906SEric.Saxe@Sun.COM 6478906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 6488906SEric.Saxe@Sun.COM 6498906SEric.Saxe@Sun.COM for (i = CPU_ACPI_C1; i <= cpu_max_cstates; i++) { 6508906SEric.Saxe@Sun.COM (void) snprintf(name, KSTAT_STRLEN - 1, "c%d", cstate->cs_type); 6518906SEric.Saxe@Sun.COM /* 6528906SEric.Saxe@Sun.COM * Allocate, initialize and install cstate kstat 6538906SEric.Saxe@Sun.COM */ 6548906SEric.Saxe@Sun.COM cstate->cs_ksp = kstat_create("cstate", CPU->cpu_id, 6558906SEric.Saxe@Sun.COM name, "misc", 6568906SEric.Saxe@Sun.COM KSTAT_TYPE_NAMED, 6578906SEric.Saxe@Sun.COM sizeof (cpu_idle_kstat) / sizeof (kstat_named_t), 6588906SEric.Saxe@Sun.COM KSTAT_FLAG_VIRTUAL); 6598906SEric.Saxe@Sun.COM 6608906SEric.Saxe@Sun.COM if (cstate->cs_ksp == NULL) { 6618906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "kstat_create(c_state) fail"); 6628906SEric.Saxe@Sun.COM } else { 6638906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_data = &cpu_idle_kstat; 6648906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_lock = &cpu_idle_mutex; 6658906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_update = cpu_idle_kstat_update; 6668906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_data_size += MAXNAMELEN; 6678906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_private = cstate; 6688906SEric.Saxe@Sun.COM kstat_install(cstate->cs_ksp); 6698906SEric.Saxe@Sun.COM cstate++; 6708906SEric.Saxe@Sun.COM } 6718906SEric.Saxe@Sun.COM } 6728906SEric.Saxe@Sun.COM 6738906SEric.Saxe@Sun.COM cpupm_alloc_domains(cp, CPUPM_C_STATES); 6748906SEric.Saxe@Sun.COM cpupm_alloc_ms_cstate(cp); 6758906SEric.Saxe@Sun.COM 6768906SEric.Saxe@Sun.COM if (cpu_deep_cstates_supported()) { 6778906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 6788906SEric.Saxe@Sun.COM if (cpu_deep_idle_callb_id == (callb_id_t)0) 6798906SEric.Saxe@Sun.COM cpu_deep_idle_callb_id = callb_add(&cpu_deep_idle_callb, 6808906SEric.Saxe@Sun.COM (void *)NULL, CB_CL_CPU_DEEP_IDLE, "cpu_deep_idle"); 6818906SEric.Saxe@Sun.COM if (cpu_idle_cpr_callb_id == (callb_id_t)0) 6828906SEric.Saxe@Sun.COM cpu_idle_cpr_callb_id = callb_add(&cpu_idle_cpr_callb, 6838906SEric.Saxe@Sun.COM (void *)NULL, CB_CL_CPR_PM, "cpu_idle_cpr"); 6848906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 6858906SEric.Saxe@Sun.COM } 6868906SEric.Saxe@Sun.COM 6878906SEric.Saxe@Sun.COM return (0); 6888906SEric.Saxe@Sun.COM } 6898906SEric.Saxe@Sun.COM 6908906SEric.Saxe@Sun.COM /* 6918906SEric.Saxe@Sun.COM * Free resources allocated by cpu_idle_init(). 6928906SEric.Saxe@Sun.COM */ 6938906SEric.Saxe@Sun.COM static void 6948906SEric.Saxe@Sun.COM cpu_idle_fini(cpu_t *cp) 6958906SEric.Saxe@Sun.COM { 6968906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6978906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 6988906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 6998906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate; 7008906SEric.Saxe@Sun.COM uint_t cpu_max_cstates, i; 7018906SEric.Saxe@Sun.COM 7028906SEric.Saxe@Sun.COM /* 7038906SEric.Saxe@Sun.COM * idle cpu points back to the generic one 7048906SEric.Saxe@Sun.COM */ 7058906SEric.Saxe@Sun.COM idle_cpu = CPU->cpu_m.mcpu_idle_cpu = non_deep_idle_cpu; 7068906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 7078906SEric.Saxe@Sun.COM 7088906SEric.Saxe@Sun.COM cstate = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 7098906SEric.Saxe@Sun.COM if (cstate) { 7108906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 7118906SEric.Saxe@Sun.COM 7128906SEric.Saxe@Sun.COM for (i = CPU_ACPI_C1; i <= cpu_max_cstates; i++) { 7138906SEric.Saxe@Sun.COM if (cstate->cs_ksp != NULL) 7148906SEric.Saxe@Sun.COM kstat_delete(cstate->cs_ksp); 7158906SEric.Saxe@Sun.COM cstate++; 7168906SEric.Saxe@Sun.COM } 7178906SEric.Saxe@Sun.COM } 7188906SEric.Saxe@Sun.COM 7198906SEric.Saxe@Sun.COM cpupm_free_ms_cstate(cp); 7208906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_cstate_domains); 7218906SEric.Saxe@Sun.COM cpu_acpi_free_cstate_data(handle); 7228906SEric.Saxe@Sun.COM 7238906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7248906SEric.Saxe@Sun.COM if (cpu_deep_idle_callb_id != (callb_id_t)0) { 7258906SEric.Saxe@Sun.COM (void) callb_delete(cpu_deep_idle_callb_id); 7268906SEric.Saxe@Sun.COM cpu_deep_idle_callb_id = (callb_id_t)0; 7278906SEric.Saxe@Sun.COM } 7288906SEric.Saxe@Sun.COM if (cpu_idle_cpr_callb_id != (callb_id_t)0) { 7298906SEric.Saxe@Sun.COM (void) callb_delete(cpu_idle_cpr_callb_id); 7308906SEric.Saxe@Sun.COM cpu_idle_cpr_callb_id = (callb_id_t)0; 7318906SEric.Saxe@Sun.COM } 7328906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 7338906SEric.Saxe@Sun.COM } 7348906SEric.Saxe@Sun.COM 7358906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7368906SEric.Saxe@Sun.COM static boolean_t 7378906SEric.Saxe@Sun.COM cpu_deep_idle_callb(void *arg, int code) 7388906SEric.Saxe@Sun.COM { 7398906SEric.Saxe@Sun.COM boolean_t rslt = B_TRUE; 7408906SEric.Saxe@Sun.COM 7418906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7428906SEric.Saxe@Sun.COM switch (code) { 7438906SEric.Saxe@Sun.COM case PM_DEFAULT_CPU_DEEP_IDLE: 7448906SEric.Saxe@Sun.COM /* 7458906SEric.Saxe@Sun.COM * Default policy is same as enable 7468906SEric.Saxe@Sun.COM */ 7478906SEric.Saxe@Sun.COM /*FALLTHROUGH*/ 7488906SEric.Saxe@Sun.COM case PM_ENABLE_CPU_DEEP_IDLE: 7498906SEric.Saxe@Sun.COM if ((cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) == 0) 7508906SEric.Saxe@Sun.COM break; 7518906SEric.Saxe@Sun.COM 752*9283SBill.Holler@Sun.COM if (cstate_timer_callback(PM_ENABLE_CPU_DEEP_IDLE)) { 7538906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 7548906SEric.Saxe@Sun.COM idle_cpu = cpu_idle_adaptive; 7558906SEric.Saxe@Sun.COM cpu_idle_cfg_state &= ~CPU_IDLE_DEEP_CFG; 7568906SEric.Saxe@Sun.COM } else { 7578906SEric.Saxe@Sun.COM rslt = B_FALSE; 7588906SEric.Saxe@Sun.COM } 7598906SEric.Saxe@Sun.COM break; 7608906SEric.Saxe@Sun.COM 7618906SEric.Saxe@Sun.COM case PM_DISABLE_CPU_DEEP_IDLE: 7628906SEric.Saxe@Sun.COM if (cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) 7638906SEric.Saxe@Sun.COM break; 7648906SEric.Saxe@Sun.COM 7658906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 766*9283SBill.Holler@Sun.COM if (cstate_timer_callback(PM_DISABLE_CPU_DEEP_IDLE)) { 7678906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 7688906SEric.Saxe@Sun.COM cpu_idle_cfg_state |= CPU_IDLE_DEEP_CFG; 7698906SEric.Saxe@Sun.COM } 7708906SEric.Saxe@Sun.COM break; 7718906SEric.Saxe@Sun.COM 7728906SEric.Saxe@Sun.COM default: 7738906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpu deep_idle_callb: invalid code %d\n", 7748906SEric.Saxe@Sun.COM code); 7758906SEric.Saxe@Sun.COM break; 7768906SEric.Saxe@Sun.COM } 7778906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 7788906SEric.Saxe@Sun.COM return (rslt); 7798906SEric.Saxe@Sun.COM } 7808906SEric.Saxe@Sun.COM 7818906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7828906SEric.Saxe@Sun.COM static boolean_t 7838906SEric.Saxe@Sun.COM cpu_idle_cpr_callb(void *arg, int code) 7848906SEric.Saxe@Sun.COM { 7858906SEric.Saxe@Sun.COM boolean_t rslt = B_TRUE; 7868906SEric.Saxe@Sun.COM 7878906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7888906SEric.Saxe@Sun.COM switch (code) { 7898906SEric.Saxe@Sun.COM case CB_CODE_CPR_RESUME: 790*9283SBill.Holler@Sun.COM if (cstate_timer_callback(CB_CODE_CPR_RESUME)) { 7918906SEric.Saxe@Sun.COM /* 7928906SEric.Saxe@Sun.COM * Do not enable dispatcher hooks if disabled by user. 7938906SEric.Saxe@Sun.COM */ 7948906SEric.Saxe@Sun.COM if (cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) 7958906SEric.Saxe@Sun.COM break; 7968906SEric.Saxe@Sun.COM 7978906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 7988906SEric.Saxe@Sun.COM idle_cpu = cpu_idle_adaptive; 7998906SEric.Saxe@Sun.COM } else { 8008906SEric.Saxe@Sun.COM rslt = B_FALSE; 8018906SEric.Saxe@Sun.COM } 8028906SEric.Saxe@Sun.COM break; 8038906SEric.Saxe@Sun.COM 8048906SEric.Saxe@Sun.COM case CB_CODE_CPR_CHKPT: 8058906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 8068906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 807*9283SBill.Holler@Sun.COM (void) cstate_timer_callback(CB_CODE_CPR_CHKPT); 8088906SEric.Saxe@Sun.COM break; 8098906SEric.Saxe@Sun.COM 8108906SEric.Saxe@Sun.COM default: 8118906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpudvr cpr_callb: invalid code %d\n", code); 8128906SEric.Saxe@Sun.COM break; 8138906SEric.Saxe@Sun.COM } 8148906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 8158906SEric.Saxe@Sun.COM return (rslt); 8168906SEric.Saxe@Sun.COM } 8178906SEric.Saxe@Sun.COM 8188906SEric.Saxe@Sun.COM /* 8198906SEric.Saxe@Sun.COM * handle _CST notification 8208906SEric.Saxe@Sun.COM */ 8218906SEric.Saxe@Sun.COM void 8228906SEric.Saxe@Sun.COM cpuidle_cstate_instance(cpu_t *cp) 8238906SEric.Saxe@Sun.COM { 8248906SEric.Saxe@Sun.COM #ifndef __xpv 8258906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 8268906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 8278906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 8288906SEric.Saxe@Sun.COM struct machcpu *mcpu; 8298906SEric.Saxe@Sun.COM cpuset_t dom_cpu_set; 8308906SEric.Saxe@Sun.COM kmutex_t *pm_lock; 8318906SEric.Saxe@Sun.COM int result = 0; 8328906SEric.Saxe@Sun.COM processorid_t cpu_id; 8338906SEric.Saxe@Sun.COM 8348906SEric.Saxe@Sun.COM if (mach_state == NULL) { 8358906SEric.Saxe@Sun.COM return; 8368906SEric.Saxe@Sun.COM } 8378906SEric.Saxe@Sun.COM 8388906SEric.Saxe@Sun.COM ASSERT(mach_state->ms_cstate.cma_domain != NULL); 8398906SEric.Saxe@Sun.COM dom_cpu_set = mach_state->ms_cstate.cma_domain->pm_cpus; 8408906SEric.Saxe@Sun.COM pm_lock = &mach_state->ms_cstate.cma_domain->pm_lock; 8418906SEric.Saxe@Sun.COM 8428906SEric.Saxe@Sun.COM /* 8438906SEric.Saxe@Sun.COM * Do for all the CPU's in the domain 8448906SEric.Saxe@Sun.COM */ 8458906SEric.Saxe@Sun.COM mutex_enter(pm_lock); 8468906SEric.Saxe@Sun.COM do { 8478906SEric.Saxe@Sun.COM CPUSET_FIND(dom_cpu_set, cpu_id); 8488906SEric.Saxe@Sun.COM if (cpu_id == CPUSET_NOTINSET) 8498906SEric.Saxe@Sun.COM break; 8508906SEric.Saxe@Sun.COM 8518906SEric.Saxe@Sun.COM ASSERT(cpu_id >= 0 && cpu_id < NCPU); 8528906SEric.Saxe@Sun.COM cp = cpu[cpu_id]; 8538906SEric.Saxe@Sun.COM mach_state = (cpupm_mach_state_t *) 8548906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_pm_mach_state; 8558906SEric.Saxe@Sun.COM if (!(mach_state->ms_caps & CPUPM_C_STATES)) { 8568906SEric.Saxe@Sun.COM mutex_exit(pm_lock); 8578906SEric.Saxe@Sun.COM return; 8588906SEric.Saxe@Sun.COM } 8598906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 8608906SEric.Saxe@Sun.COM ASSERT(handle != NULL); 8618906SEric.Saxe@Sun.COM 8628906SEric.Saxe@Sun.COM /* 8638906SEric.Saxe@Sun.COM * re-evaluate cstate object 8648906SEric.Saxe@Sun.COM */ 8658906SEric.Saxe@Sun.COM if (cpu_acpi_cache_cstate_data(handle) != 0) { 8668906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "Cannot re-evaluate the cpu c-state" 8678906SEric.Saxe@Sun.COM " object Instance: %d", cpu_id); 8688906SEric.Saxe@Sun.COM } 8698906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 8708906SEric.Saxe@Sun.COM mcpu = &(cp->cpu_m); 8718906SEric.Saxe@Sun.COM mcpu->max_cstates = cpu_acpi_get_max_cstates(handle); 8728906SEric.Saxe@Sun.COM if (mcpu->max_cstates > CPU_ACPI_C1) { 873*9283SBill.Holler@Sun.COM (void) cstate_timer_callback( 874*9283SBill.Holler@Sun.COM CST_EVENT_MULTIPLE_CSTATES); 8758906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 8768906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_idle_cpu = cpu_acpi_idle; 8778906SEric.Saxe@Sun.COM } else if (mcpu->max_cstates == CPU_ACPI_C1) { 8788906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 8798906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_idle_cpu = non_deep_idle_cpu; 880*9283SBill.Holler@Sun.COM (void) cstate_timer_callback(CST_EVENT_ONE_CSTATE); 8818906SEric.Saxe@Sun.COM } 8828906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 8838906SEric.Saxe@Sun.COM 8848906SEric.Saxe@Sun.COM CPUSET_ATOMIC_XDEL(dom_cpu_set, cpu_id, result); 8858906SEric.Saxe@Sun.COM mutex_exit(pm_lock); 8868906SEric.Saxe@Sun.COM } while (result < 0); 8878906SEric.Saxe@Sun.COM #endif 8888906SEric.Saxe@Sun.COM } 8898906SEric.Saxe@Sun.COM 8908906SEric.Saxe@Sun.COM /* 8918906SEric.Saxe@Sun.COM * handle the number or the type of available processor power states change 8928906SEric.Saxe@Sun.COM */ 8938906SEric.Saxe@Sun.COM void 8948906SEric.Saxe@Sun.COM cpuidle_manage_cstates(void *ctx) 8958906SEric.Saxe@Sun.COM { 8968906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 8978906SEric.Saxe@Sun.COM processorid_t cpu_id = cp->cpu_id; 8988906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 8998906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 9008906SEric.Saxe@Sun.COM boolean_t is_ready; 9018906SEric.Saxe@Sun.COM 9028906SEric.Saxe@Sun.COM if (mach_state == NULL) { 9038906SEric.Saxe@Sun.COM return; 9048906SEric.Saxe@Sun.COM } 9058906SEric.Saxe@Sun.COM 9068906SEric.Saxe@Sun.COM /* 9078906SEric.Saxe@Sun.COM * We currently refuse to power manage if the CPU is not ready to 9088906SEric.Saxe@Sun.COM * take cross calls (cross calls fail silently if CPU is not ready 9098906SEric.Saxe@Sun.COM * for it). 9108906SEric.Saxe@Sun.COM * 9118906SEric.Saxe@Sun.COM * Additionally, for x86 platforms we cannot power manage 9128906SEric.Saxe@Sun.COM * any one instance, until all instances have been initialized. 9138906SEric.Saxe@Sun.COM * That's because we don't know what the CPU domains look like 9148906SEric.Saxe@Sun.COM * until all instances have been initialized. 9158906SEric.Saxe@Sun.COM */ 9168906SEric.Saxe@Sun.COM is_ready = CPUPM_XCALL_IS_READY(cpu_id) && cpupm_cstate_ready(); 9178906SEric.Saxe@Sun.COM if (!is_ready) 9188906SEric.Saxe@Sun.COM return; 9198906SEric.Saxe@Sun.COM 9208906SEric.Saxe@Sun.COM cpuidle_cstate_instance(cp); 9218906SEric.Saxe@Sun.COM } 922