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 */ 259283SBill.Holler@Sun.COM /* 269283SBill.Holler@Sun.COM * Copyright (c) 2009, Intel Corporation. 279283SBill.Holler@Sun.COM * All rights reserved. 289283SBill.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> 399637SRandy.Fishel@Sun.COM #include <sys/cpu_event.h> 408906SEric.Saxe@Sun.COM #include <sys/hpet.h> 418906SEric.Saxe@Sun.COM #include <sys/archsystm.h> 428906SEric.Saxe@Sun.COM #include <vm/hat_i86.h> 438906SEric.Saxe@Sun.COM #include <sys/dtrace.h> 448906SEric.Saxe@Sun.COM #include <sys/sdt.h> 458906SEric.Saxe@Sun.COM #include <sys/callb.h> 468906SEric.Saxe@Sun.COM 479283SBill.Holler@Sun.COM #define CSTATE_USING_HPET 1 489283SBill.Holler@Sun.COM #define CSTATE_USING_LAT 2 499283SBill.Holler@Sun.COM 508906SEric.Saxe@Sun.COM extern void cpu_idle_adaptive(void); 518983SBill.Holler@Sun.COM extern uint32_t cpupm_next_cstate(cma_c_state_t *cs_data, 528983SBill.Holler@Sun.COM cpu_acpi_cstate_t *cstates, uint32_t cs_count, hrtime_t start); 538906SEric.Saxe@Sun.COM 548906SEric.Saxe@Sun.COM static int cpu_idle_init(cpu_t *); 558906SEric.Saxe@Sun.COM static void cpu_idle_fini(cpu_t *); 568906SEric.Saxe@Sun.COM static boolean_t cpu_deep_idle_callb(void *arg, int code); 578906SEric.Saxe@Sun.COM static boolean_t cpu_idle_cpr_callb(void *arg, int code); 588906SEric.Saxe@Sun.COM static void acpi_cpu_cstate(cpu_acpi_cstate_t *cstate); 598906SEric.Saxe@Sun.COM 609283SBill.Holler@Sun.COM static boolean_t cstate_use_timer(hrtime_t *lapic_expire, int timer); 619283SBill.Holler@Sun.COM 629283SBill.Holler@Sun.COM /* 639283SBill.Holler@Sun.COM * the flag of always-running local APIC timer. 649283SBill.Holler@Sun.COM * the flag of HPET Timer use in deep cstate. 659283SBill.Holler@Sun.COM */ 669283SBill.Holler@Sun.COM static boolean_t cpu_cstate_arat = B_FALSE; 679283SBill.Holler@Sun.COM static boolean_t cpu_cstate_hpet = B_FALSE; 689283SBill.Holler@Sun.COM 698906SEric.Saxe@Sun.COM /* 708906SEric.Saxe@Sun.COM * Interfaces for modules implementing Intel's deep c-state. 718906SEric.Saxe@Sun.COM */ 728906SEric.Saxe@Sun.COM cpupm_state_ops_t cpu_idle_ops = { 738906SEric.Saxe@Sun.COM "Generic ACPI C-state Support", 748906SEric.Saxe@Sun.COM cpu_idle_init, 758906SEric.Saxe@Sun.COM cpu_idle_fini, 768906SEric.Saxe@Sun.COM NULL 778906SEric.Saxe@Sun.COM }; 788906SEric.Saxe@Sun.COM 798906SEric.Saxe@Sun.COM static kmutex_t cpu_idle_callb_mutex; 808906SEric.Saxe@Sun.COM static callb_id_t cpu_deep_idle_callb_id; 818906SEric.Saxe@Sun.COM static callb_id_t cpu_idle_cpr_callb_id; 828906SEric.Saxe@Sun.COM static uint_t cpu_idle_cfg_state; 838906SEric.Saxe@Sun.COM 848906SEric.Saxe@Sun.COM static kmutex_t cpu_idle_mutex; 858906SEric.Saxe@Sun.COM 868906SEric.Saxe@Sun.COM cpu_idle_kstat_t cpu_idle_kstat = { 878906SEric.Saxe@Sun.COM { "address_space_id", KSTAT_DATA_STRING }, 888906SEric.Saxe@Sun.COM { "latency", KSTAT_DATA_UINT32 }, 898906SEric.Saxe@Sun.COM { "power", KSTAT_DATA_UINT32 }, 908906SEric.Saxe@Sun.COM }; 918906SEric.Saxe@Sun.COM 928906SEric.Saxe@Sun.COM /* 938906SEric.Saxe@Sun.COM * kstat update function of the c-state info 948906SEric.Saxe@Sun.COM */ 958906SEric.Saxe@Sun.COM static int 968906SEric.Saxe@Sun.COM cpu_idle_kstat_update(kstat_t *ksp, int flag) 978906SEric.Saxe@Sun.COM { 988906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate = ksp->ks_private; 998906SEric.Saxe@Sun.COM 1008906SEric.Saxe@Sun.COM if (flag == KSTAT_WRITE) { 1018906SEric.Saxe@Sun.COM return (EACCES); 1028906SEric.Saxe@Sun.COM } 1038906SEric.Saxe@Sun.COM 1048906SEric.Saxe@Sun.COM if (cstate->cs_addrspace_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { 1058906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1068906SEric.Saxe@Sun.COM "FFixedHW"); 1078906SEric.Saxe@Sun.COM } else if (cstate->cs_addrspace_id == ACPI_ADR_SPACE_SYSTEM_IO) { 1088906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1098906SEric.Saxe@Sun.COM "SystemIO"); 1108906SEric.Saxe@Sun.COM } else { 1118906SEric.Saxe@Sun.COM kstat_named_setstr(&cpu_idle_kstat.addr_space_id, 1128906SEric.Saxe@Sun.COM "Unsupported"); 1138906SEric.Saxe@Sun.COM } 1148906SEric.Saxe@Sun.COM 1158906SEric.Saxe@Sun.COM cpu_idle_kstat.cs_latency.value.ui32 = cstate->cs_latency; 1168906SEric.Saxe@Sun.COM cpu_idle_kstat.cs_power.value.ui32 = cstate->cs_power; 1178906SEric.Saxe@Sun.COM 1188906SEric.Saxe@Sun.COM return (0); 1198906SEric.Saxe@Sun.COM } 1208906SEric.Saxe@Sun.COM 1218906SEric.Saxe@Sun.COM /* 1229283SBill.Holler@Sun.COM * Used during configuration callbacks to manage implementation specific 1239283SBill.Holler@Sun.COM * details of the hardware timer used during Deep C-state. 1249283SBill.Holler@Sun.COM */ 1259283SBill.Holler@Sun.COM boolean_t 1269283SBill.Holler@Sun.COM cstate_timer_callback(int code) 1279283SBill.Holler@Sun.COM { 1289283SBill.Holler@Sun.COM if (cpu_cstate_arat) { 1299283SBill.Holler@Sun.COM return (B_TRUE); 1309283SBill.Holler@Sun.COM } else if (cpu_cstate_hpet) { 1319283SBill.Holler@Sun.COM return (hpet.callback(code)); 1329283SBill.Holler@Sun.COM } 1339283SBill.Holler@Sun.COM return (B_FALSE); 1349283SBill.Holler@Sun.COM } 1359283SBill.Holler@Sun.COM 1369283SBill.Holler@Sun.COM /* 1379283SBill.Holler@Sun.COM * Some Local APIC Timers do not work during Deep C-states. 1389283SBill.Holler@Sun.COM * The Deep C-state idle function uses this function to ensure it is using a 1399283SBill.Holler@Sun.COM * hardware timer that works during Deep C-states. This function also 1409283SBill.Holler@Sun.COM * switches the timer back to the LACPI Timer after Deep C-state. 1419283SBill.Holler@Sun.COM */ 1429283SBill.Holler@Sun.COM static boolean_t 1439283SBill.Holler@Sun.COM cstate_use_timer(hrtime_t *lapic_expire, int timer) 1449283SBill.Holler@Sun.COM { 1459283SBill.Holler@Sun.COM if (cpu_cstate_arat) 1469283SBill.Holler@Sun.COM return (B_TRUE); 1479283SBill.Holler@Sun.COM 1489283SBill.Holler@Sun.COM /* 1499283SBill.Holler@Sun.COM * We have to return B_FALSE if no arat or hpet support 1509283SBill.Holler@Sun.COM */ 1519283SBill.Holler@Sun.COM if (!cpu_cstate_hpet) 1529283SBill.Holler@Sun.COM return (B_FALSE); 1539283SBill.Holler@Sun.COM 1549283SBill.Holler@Sun.COM switch (timer) { 1559283SBill.Holler@Sun.COM case CSTATE_USING_HPET: 1569283SBill.Holler@Sun.COM return (hpet.use_hpet_timer(lapic_expire)); 1579283SBill.Holler@Sun.COM case CSTATE_USING_LAT: 1589283SBill.Holler@Sun.COM hpet.use_lapic_timer(*lapic_expire); 1599283SBill.Holler@Sun.COM return (B_TRUE); 1609283SBill.Holler@Sun.COM default: 1619283SBill.Holler@Sun.COM return (B_FALSE); 1629283SBill.Holler@Sun.COM } 1639283SBill.Holler@Sun.COM } 1649283SBill.Holler@Sun.COM 1659283SBill.Holler@Sun.COM /* 1668906SEric.Saxe@Sun.COM * c-state wakeup function. 1678906SEric.Saxe@Sun.COM * Similar to cpu_wakeup and cpu_wakeup_mwait except this function deals 1688906SEric.Saxe@Sun.COM * with CPUs asleep in MWAIT, HLT, or ACPI Deep C-State. 1698906SEric.Saxe@Sun.COM */ 1708906SEric.Saxe@Sun.COM void 1718906SEric.Saxe@Sun.COM cstate_wakeup(cpu_t *cp, int bound) 1728906SEric.Saxe@Sun.COM { 1738906SEric.Saxe@Sun.COM struct machcpu *mcpu = &(cp->cpu_m); 1748906SEric.Saxe@Sun.COM volatile uint32_t *mcpu_mwait = mcpu->mcpu_mwait; 1758906SEric.Saxe@Sun.COM cpupart_t *cpu_part; 1768906SEric.Saxe@Sun.COM uint_t cpu_found; 1778906SEric.Saxe@Sun.COM processorid_t cpu_sid; 1788906SEric.Saxe@Sun.COM 1798906SEric.Saxe@Sun.COM cpu_part = cp->cpu_part; 1808906SEric.Saxe@Sun.COM cpu_sid = cp->cpu_seqid; 1818906SEric.Saxe@Sun.COM /* 1828906SEric.Saxe@Sun.COM * Clear the halted bit for that CPU since it will be woken up 1838906SEric.Saxe@Sun.COM * in a moment. 1848906SEric.Saxe@Sun.COM */ 1858906SEric.Saxe@Sun.COM if (bitset_in_set(&cpu_part->cp_haltset, cpu_sid)) { 1868906SEric.Saxe@Sun.COM /* 1878906SEric.Saxe@Sun.COM * Clear the halted bit for that CPU since it will be 1888906SEric.Saxe@Sun.COM * poked in a moment. 1898906SEric.Saxe@Sun.COM */ 1908906SEric.Saxe@Sun.COM bitset_atomic_del(&cpu_part->cp_haltset, cpu_sid); 1918906SEric.Saxe@Sun.COM 1928906SEric.Saxe@Sun.COM /* 1938906SEric.Saxe@Sun.COM * We may find the current CPU present in the halted cpuset 1948906SEric.Saxe@Sun.COM * if we're in the context of an interrupt that occurred 1958906SEric.Saxe@Sun.COM * before we had a chance to clear our bit in cpu_idle(). 1968906SEric.Saxe@Sun.COM * Waking ourself is obviously unnecessary, since if 1978906SEric.Saxe@Sun.COM * we're here, we're not halted. 1988906SEric.Saxe@Sun.COM */ 1998906SEric.Saxe@Sun.COM if (cp != CPU) { 2008906SEric.Saxe@Sun.COM /* 2018906SEric.Saxe@Sun.COM * Use correct wakeup mechanism 2028906SEric.Saxe@Sun.COM */ 2038906SEric.Saxe@Sun.COM if ((mcpu_mwait != NULL) && 2048906SEric.Saxe@Sun.COM (*mcpu_mwait == MWAIT_HALTED)) 2058906SEric.Saxe@Sun.COM MWAIT_WAKEUP(cp); 2068906SEric.Saxe@Sun.COM else 2078906SEric.Saxe@Sun.COM poke_cpu(cp->cpu_id); 2088906SEric.Saxe@Sun.COM } 2098906SEric.Saxe@Sun.COM return; 2108906SEric.Saxe@Sun.COM } else { 2118906SEric.Saxe@Sun.COM /* 2128906SEric.Saxe@Sun.COM * This cpu isn't halted, but it's idle or undergoing a 2138906SEric.Saxe@Sun.COM * context switch. No need to awaken anyone else. 2148906SEric.Saxe@Sun.COM */ 2158906SEric.Saxe@Sun.COM if (cp->cpu_thread == cp->cpu_idle_thread || 2168906SEric.Saxe@Sun.COM cp->cpu_disp_flags & CPU_DISP_DONTSTEAL) 2178906SEric.Saxe@Sun.COM return; 2188906SEric.Saxe@Sun.COM } 2198906SEric.Saxe@Sun.COM 2208906SEric.Saxe@Sun.COM /* 2218906SEric.Saxe@Sun.COM * No need to wake up other CPUs if the thread we just enqueued 2228906SEric.Saxe@Sun.COM * is bound. 2238906SEric.Saxe@Sun.COM */ 2248906SEric.Saxe@Sun.COM if (bound) 2258906SEric.Saxe@Sun.COM return; 2268906SEric.Saxe@Sun.COM 2278906SEric.Saxe@Sun.COM 2288906SEric.Saxe@Sun.COM /* 2298906SEric.Saxe@Sun.COM * See if there's any other halted CPUs. If there are, then 2308906SEric.Saxe@Sun.COM * select one, and awaken it. 2318906SEric.Saxe@Sun.COM * It's possible that after we find a CPU, somebody else 2328906SEric.Saxe@Sun.COM * will awaken it before we get the chance. 2338906SEric.Saxe@Sun.COM * In that case, look again. 2348906SEric.Saxe@Sun.COM */ 2358906SEric.Saxe@Sun.COM do { 2368906SEric.Saxe@Sun.COM cpu_found = bitset_find(&cpu_part->cp_haltset); 2378906SEric.Saxe@Sun.COM if (cpu_found == (uint_t)-1) 2388906SEric.Saxe@Sun.COM return; 2398906SEric.Saxe@Sun.COM 2408906SEric.Saxe@Sun.COM } while (bitset_atomic_test_and_del(&cpu_part->cp_haltset, 2418906SEric.Saxe@Sun.COM cpu_found) < 0); 2428906SEric.Saxe@Sun.COM 2438906SEric.Saxe@Sun.COM /* 2448906SEric.Saxe@Sun.COM * Must use correct wakeup mechanism to avoid lost wakeup of 2458906SEric.Saxe@Sun.COM * alternate cpu. 2468906SEric.Saxe@Sun.COM */ 2478906SEric.Saxe@Sun.COM if (cpu_found != CPU->cpu_seqid) { 2488906SEric.Saxe@Sun.COM mcpu_mwait = cpu[cpu_found]->cpu_m.mcpu_mwait; 2498906SEric.Saxe@Sun.COM if ((mcpu_mwait != NULL) && (*mcpu_mwait == MWAIT_HALTED)) 2508906SEric.Saxe@Sun.COM MWAIT_WAKEUP(cpu_seq[cpu_found]); 2518906SEric.Saxe@Sun.COM else 2528906SEric.Saxe@Sun.COM poke_cpu(cpu_seq[cpu_found]->cpu_id); 2538906SEric.Saxe@Sun.COM } 2548906SEric.Saxe@Sun.COM } 2558906SEric.Saxe@Sun.COM 2568906SEric.Saxe@Sun.COM /* 2579637SRandy.Fishel@Sun.COM * Function called by CPU idle notification framework to check whether CPU 2589637SRandy.Fishel@Sun.COM * has been awakened. It will be called with interrupt disabled. 2599637SRandy.Fishel@Sun.COM * If CPU has been awakened, call cpu_idle_exit() to notify CPU idle 2609637SRandy.Fishel@Sun.COM * notification framework. 2619637SRandy.Fishel@Sun.COM */ 2629637SRandy.Fishel@Sun.COM static void 2639637SRandy.Fishel@Sun.COM acpi_cpu_mwait_check_wakeup(void *arg) 2649637SRandy.Fishel@Sun.COM { 2659637SRandy.Fishel@Sun.COM volatile uint32_t *mcpu_mwait = (volatile uint32_t *)arg; 2669637SRandy.Fishel@Sun.COM 2679637SRandy.Fishel@Sun.COM ASSERT(arg != NULL); 2689637SRandy.Fishel@Sun.COM if (*mcpu_mwait != MWAIT_HALTED) { 2699637SRandy.Fishel@Sun.COM /* 2709637SRandy.Fishel@Sun.COM * CPU has been awakened, notify CPU idle notification system. 2719637SRandy.Fishel@Sun.COM */ 2729637SRandy.Fishel@Sun.COM cpu_idle_exit(CPU_IDLE_CB_FLAG_IDLE); 2739637SRandy.Fishel@Sun.COM } else { 2749637SRandy.Fishel@Sun.COM /* 2759637SRandy.Fishel@Sun.COM * Toggle interrupt flag to detect pending interrupts. 2769637SRandy.Fishel@Sun.COM * If interrupt happened, do_interrupt() will notify CPU idle 2779637SRandy.Fishel@Sun.COM * notification framework so no need to call cpu_idle_exit() 2789637SRandy.Fishel@Sun.COM * here. 2799637SRandy.Fishel@Sun.COM */ 2809637SRandy.Fishel@Sun.COM sti(); 2819637SRandy.Fishel@Sun.COM SMT_PAUSE(); 2829637SRandy.Fishel@Sun.COM cli(); 2839637SRandy.Fishel@Sun.COM } 2849637SRandy.Fishel@Sun.COM } 2859637SRandy.Fishel@Sun.COM 2869637SRandy.Fishel@Sun.COM static void 2879637SRandy.Fishel@Sun.COM acpi_cpu_mwait_ipi_check_wakeup(void *arg) 2889637SRandy.Fishel@Sun.COM { 2899637SRandy.Fishel@Sun.COM volatile uint32_t *mcpu_mwait = (volatile uint32_t *)arg; 2909637SRandy.Fishel@Sun.COM 2919637SRandy.Fishel@Sun.COM ASSERT(arg != NULL); 2929637SRandy.Fishel@Sun.COM if (*mcpu_mwait != MWAIT_WAKEUP_IPI) { 2939637SRandy.Fishel@Sun.COM /* 2949637SRandy.Fishel@Sun.COM * CPU has been awakened, notify CPU idle notification system. 2959637SRandy.Fishel@Sun.COM */ 2969637SRandy.Fishel@Sun.COM cpu_idle_exit(CPU_IDLE_CB_FLAG_IDLE); 2979637SRandy.Fishel@Sun.COM } else { 2989637SRandy.Fishel@Sun.COM /* 2999637SRandy.Fishel@Sun.COM * Toggle interrupt flag to detect pending interrupts. 3009637SRandy.Fishel@Sun.COM * If interrupt happened, do_interrupt() will notify CPU idle 3019637SRandy.Fishel@Sun.COM * notification framework so no need to call cpu_idle_exit() 3029637SRandy.Fishel@Sun.COM * here. 3039637SRandy.Fishel@Sun.COM */ 3049637SRandy.Fishel@Sun.COM sti(); 3059637SRandy.Fishel@Sun.COM SMT_PAUSE(); 3069637SRandy.Fishel@Sun.COM cli(); 3079637SRandy.Fishel@Sun.COM } 3089637SRandy.Fishel@Sun.COM } 3099637SRandy.Fishel@Sun.COM 3109637SRandy.Fishel@Sun.COM /*ARGSUSED*/ 3119637SRandy.Fishel@Sun.COM static void 3129637SRandy.Fishel@Sun.COM acpi_cpu_check_wakeup(void *arg) 3139637SRandy.Fishel@Sun.COM { 3149637SRandy.Fishel@Sun.COM /* 3159637SRandy.Fishel@Sun.COM * Toggle interrupt flag to detect pending interrupts. 3169637SRandy.Fishel@Sun.COM * If interrupt happened, do_interrupt() will notify CPU idle 3179637SRandy.Fishel@Sun.COM * notification framework so no need to call cpu_idle_exit() here. 3189637SRandy.Fishel@Sun.COM */ 3199637SRandy.Fishel@Sun.COM sti(); 3209637SRandy.Fishel@Sun.COM SMT_PAUSE(); 3219637SRandy.Fishel@Sun.COM cli(); 3229637SRandy.Fishel@Sun.COM } 3239637SRandy.Fishel@Sun.COM 3249637SRandy.Fishel@Sun.COM /* 3258906SEric.Saxe@Sun.COM * enter deep c-state handler 3268906SEric.Saxe@Sun.COM */ 3278906SEric.Saxe@Sun.COM static void 3288906SEric.Saxe@Sun.COM acpi_cpu_cstate(cpu_acpi_cstate_t *cstate) 3298906SEric.Saxe@Sun.COM { 3308906SEric.Saxe@Sun.COM volatile uint32_t *mcpu_mwait = CPU->cpu_m.mcpu_mwait; 3318906SEric.Saxe@Sun.COM cpu_t *cpup = CPU; 3328906SEric.Saxe@Sun.COM processorid_t cpu_sid = cpup->cpu_seqid; 3338906SEric.Saxe@Sun.COM cpupart_t *cp = cpup->cpu_part; 3348906SEric.Saxe@Sun.COM hrtime_t lapic_expire; 3358906SEric.Saxe@Sun.COM uint8_t type = cstate->cs_addrspace_id; 3368906SEric.Saxe@Sun.COM uint32_t cs_type = cstate->cs_type; 3378906SEric.Saxe@Sun.COM int hset_update = 1; 3389283SBill.Holler@Sun.COM boolean_t using_timer; 3399637SRandy.Fishel@Sun.COM cpu_idle_check_wakeup_t check_func = &acpi_cpu_check_wakeup; 3408906SEric.Saxe@Sun.COM 3418906SEric.Saxe@Sun.COM /* 3428906SEric.Saxe@Sun.COM * Set our mcpu_mwait here, so we can tell if anyone tries to 3438906SEric.Saxe@Sun.COM * wake us between now and when we call mwait. No other cpu will 3448906SEric.Saxe@Sun.COM * attempt to set our mcpu_mwait until we add ourself to the haltset. 3458906SEric.Saxe@Sun.COM */ 3468906SEric.Saxe@Sun.COM if (mcpu_mwait) { 3479637SRandy.Fishel@Sun.COM if (type == ACPI_ADR_SPACE_SYSTEM_IO) { 3488906SEric.Saxe@Sun.COM *mcpu_mwait = MWAIT_WAKEUP_IPI; 3499637SRandy.Fishel@Sun.COM check_func = &acpi_cpu_mwait_ipi_check_wakeup; 3509637SRandy.Fishel@Sun.COM } else { 3518906SEric.Saxe@Sun.COM *mcpu_mwait = MWAIT_HALTED; 3529637SRandy.Fishel@Sun.COM check_func = &acpi_cpu_mwait_check_wakeup; 3539637SRandy.Fishel@Sun.COM } 3548906SEric.Saxe@Sun.COM } 3558906SEric.Saxe@Sun.COM 3568906SEric.Saxe@Sun.COM /* 3578906SEric.Saxe@Sun.COM * If this CPU is online, and there are multiple CPUs 3588906SEric.Saxe@Sun.COM * in the system, then we should note our halting 3598906SEric.Saxe@Sun.COM * by adding ourselves to the partition's halted CPU 3608906SEric.Saxe@Sun.COM * bitmap. This allows other CPUs to find/awaken us when 3618906SEric.Saxe@Sun.COM * work becomes available. 3628906SEric.Saxe@Sun.COM */ 3638906SEric.Saxe@Sun.COM if (cpup->cpu_flags & CPU_OFFLINE || ncpus == 1) 3648906SEric.Saxe@Sun.COM hset_update = 0; 3658906SEric.Saxe@Sun.COM 3668906SEric.Saxe@Sun.COM /* 3678906SEric.Saxe@Sun.COM * Add ourselves to the partition's halted CPUs bitmask 3688906SEric.Saxe@Sun.COM * and set our HALTED flag, if necessary. 3698906SEric.Saxe@Sun.COM * 3708906SEric.Saxe@Sun.COM * When a thread becomes runnable, it is placed on the queue 3718906SEric.Saxe@Sun.COM * and then the halted cpuset is checked to determine who 3728906SEric.Saxe@Sun.COM * (if anyone) should be awakened. We therefore need to first 3738906SEric.Saxe@Sun.COM * add ourselves to the halted cpuset, and and then check if there 3748906SEric.Saxe@Sun.COM * is any work available. 3758906SEric.Saxe@Sun.COM * 3768906SEric.Saxe@Sun.COM * Note that memory barriers after updating the HALTED flag 3778906SEric.Saxe@Sun.COM * are not necessary since an atomic operation (updating the bitmap) 3788906SEric.Saxe@Sun.COM * immediately follows. On x86 the atomic operation acts as a 3798906SEric.Saxe@Sun.COM * memory barrier for the update of cpu_disp_flags. 3808906SEric.Saxe@Sun.COM */ 3818906SEric.Saxe@Sun.COM if (hset_update) { 3828906SEric.Saxe@Sun.COM cpup->cpu_disp_flags |= CPU_DISP_HALTED; 3838906SEric.Saxe@Sun.COM bitset_atomic_add(&cp->cp_haltset, cpu_sid); 3848906SEric.Saxe@Sun.COM } 3858906SEric.Saxe@Sun.COM 3868906SEric.Saxe@Sun.COM /* 3878906SEric.Saxe@Sun.COM * Check to make sure there's really nothing to do. 3888906SEric.Saxe@Sun.COM * Work destined for this CPU may become available after 3898906SEric.Saxe@Sun.COM * this check. We'll be notified through the clearing of our 3908906SEric.Saxe@Sun.COM * bit in the halted CPU bitmask, and a write to our mcpu_mwait. 3918906SEric.Saxe@Sun.COM * 3928906SEric.Saxe@Sun.COM * disp_anywork() checks disp_nrunnable, so we do not have to later. 3938906SEric.Saxe@Sun.COM */ 3948906SEric.Saxe@Sun.COM if (disp_anywork()) { 3958906SEric.Saxe@Sun.COM if (hset_update) { 3968906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 3978906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 3988906SEric.Saxe@Sun.COM } 3998906SEric.Saxe@Sun.COM return; 4008906SEric.Saxe@Sun.COM } 4018906SEric.Saxe@Sun.COM 4028906SEric.Saxe@Sun.COM /* 4038906SEric.Saxe@Sun.COM * We're on our way to being halted. 4048906SEric.Saxe@Sun.COM * 4058906SEric.Saxe@Sun.COM * The local APIC timer can stop in ACPI C2 and deeper c-states. 4069283SBill.Holler@Sun.COM * Try to program the HPET hardware to substitute for this CPU's 4079283SBill.Holler@Sun.COM * LAPIC timer. 4089283SBill.Holler@Sun.COM * cstate_use_timer() could disable the LAPIC Timer. Make sure 4099283SBill.Holler@Sun.COM * to start the LAPIC Timer again before leaving this function. 4108906SEric.Saxe@Sun.COM * 4119283SBill.Holler@Sun.COM * Disable interrupts here so we will awaken immediately after halting 4129283SBill.Holler@Sun.COM * if someone tries to poke us between now and the time we actually 4139283SBill.Holler@Sun.COM * halt. 4148906SEric.Saxe@Sun.COM */ 4159283SBill.Holler@Sun.COM cli(); 4169283SBill.Holler@Sun.COM using_timer = cstate_use_timer(&lapic_expire, CSTATE_USING_HPET); 4178906SEric.Saxe@Sun.COM 4188906SEric.Saxe@Sun.COM /* 4198906SEric.Saxe@Sun.COM * We check for the presence of our bit after disabling interrupts. 4208906SEric.Saxe@Sun.COM * If it's cleared, we'll return. If the bit is cleared after 4218906SEric.Saxe@Sun.COM * we check then the cstate_wakeup() will pop us out of the halted 4228906SEric.Saxe@Sun.COM * state. 4238906SEric.Saxe@Sun.COM * 4248906SEric.Saxe@Sun.COM * This means that the ordering of the cstate_wakeup() and the clearing 4258906SEric.Saxe@Sun.COM * of the bit by cpu_wakeup is important. 4268906SEric.Saxe@Sun.COM * cpu_wakeup() must clear our mc_haltset bit, and then call 4278906SEric.Saxe@Sun.COM * cstate_wakeup(). 4288906SEric.Saxe@Sun.COM * acpi_cpu_cstate() must disable interrupts, then check for the bit. 4298906SEric.Saxe@Sun.COM */ 4308906SEric.Saxe@Sun.COM if (hset_update && bitset_in_set(&cp->cp_haltset, cpu_sid) == 0) { 4319283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 4329283SBill.Holler@Sun.COM CSTATE_USING_LAT); 4339283SBill.Holler@Sun.COM sti(); 4348906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 4358906SEric.Saxe@Sun.COM return; 4368906SEric.Saxe@Sun.COM } 4378906SEric.Saxe@Sun.COM 4388906SEric.Saxe@Sun.COM /* 4398906SEric.Saxe@Sun.COM * The check for anything locally runnable is here for performance 4408906SEric.Saxe@Sun.COM * and isn't needed for correctness. disp_nrunnable ought to be 4418906SEric.Saxe@Sun.COM * in our cache still, so it's inexpensive to check, and if there 4428906SEric.Saxe@Sun.COM * is anything runnable we won't have to wait for the poke. 4438906SEric.Saxe@Sun.COM */ 4448906SEric.Saxe@Sun.COM if (cpup->cpu_disp->disp_nrunnable != 0) { 4459283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 4469283SBill.Holler@Sun.COM CSTATE_USING_LAT); 4479283SBill.Holler@Sun.COM sti(); 4488906SEric.Saxe@Sun.COM if (hset_update) { 4498906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 4508906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 4518906SEric.Saxe@Sun.COM } 4528906SEric.Saxe@Sun.COM return; 4538906SEric.Saxe@Sun.COM } 4548906SEric.Saxe@Sun.COM 4559283SBill.Holler@Sun.COM if (using_timer == B_FALSE) { 4568906SEric.Saxe@Sun.COM 4579283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, 4589283SBill.Holler@Sun.COM CSTATE_USING_LAT); 4599283SBill.Holler@Sun.COM sti(); 4608906SEric.Saxe@Sun.COM 4618906SEric.Saxe@Sun.COM /* 4628906SEric.Saxe@Sun.COM * We are currently unable to program the HPET to act as this 4639283SBill.Holler@Sun.COM * CPU's proxy LAPIC timer. This CPU cannot enter C2 or deeper 4649283SBill.Holler@Sun.COM * because no timer is set to wake it up while its LAPIC timer 4658906SEric.Saxe@Sun.COM * stalls in deep C-States. 4668906SEric.Saxe@Sun.COM * Enter C1 instead. 4678906SEric.Saxe@Sun.COM * 4688906SEric.Saxe@Sun.COM * cstate_wake_cpu() will wake this CPU with an IPI which 4698906SEric.Saxe@Sun.COM * works with MWAIT. 4708906SEric.Saxe@Sun.COM */ 4718906SEric.Saxe@Sun.COM i86_monitor(mcpu_mwait, 0, 0); 4728906SEric.Saxe@Sun.COM if ((*mcpu_mwait & ~MWAIT_WAKEUP_IPI) == MWAIT_HALTED) { 4739637SRandy.Fishel@Sun.COM if (cpu_idle_enter(IDLE_STATE_C1, 0, 4749637SRandy.Fishel@Sun.COM check_func, (void *)mcpu_mwait) == 0) { 4759637SRandy.Fishel@Sun.COM if ((*mcpu_mwait & ~MWAIT_WAKEUP_IPI) == 4769637SRandy.Fishel@Sun.COM MWAIT_HALTED) { 4779637SRandy.Fishel@Sun.COM i86_mwait(0, 0); 4789637SRandy.Fishel@Sun.COM } 4799637SRandy.Fishel@Sun.COM cpu_idle_exit(CPU_IDLE_CB_FLAG_IDLE); 4809637SRandy.Fishel@Sun.COM } 4818906SEric.Saxe@Sun.COM } 4828906SEric.Saxe@Sun.COM 4838906SEric.Saxe@Sun.COM /* 4848906SEric.Saxe@Sun.COM * We're no longer halted 4858906SEric.Saxe@Sun.COM */ 4868906SEric.Saxe@Sun.COM if (hset_update) { 4878906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 4888906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 4898906SEric.Saxe@Sun.COM } 4908906SEric.Saxe@Sun.COM return; 4918906SEric.Saxe@Sun.COM } 4928906SEric.Saxe@Sun.COM 4938906SEric.Saxe@Sun.COM if (type == ACPI_ADR_SPACE_FIXED_HARDWARE) { 4948906SEric.Saxe@Sun.COM /* 4958906SEric.Saxe@Sun.COM * We're on our way to being halted. 4968906SEric.Saxe@Sun.COM * To avoid a lost wakeup, arm the monitor before checking 4978906SEric.Saxe@Sun.COM * if another cpu wrote to mcpu_mwait to wake us up. 4988906SEric.Saxe@Sun.COM */ 4998906SEric.Saxe@Sun.COM i86_monitor(mcpu_mwait, 0, 0); 5008906SEric.Saxe@Sun.COM if (*mcpu_mwait == MWAIT_HALTED) { 5019637SRandy.Fishel@Sun.COM if (cpu_idle_enter((uint_t)cs_type, 0, 5029637SRandy.Fishel@Sun.COM check_func, (void *)mcpu_mwait) == 0) { 5039637SRandy.Fishel@Sun.COM if (*mcpu_mwait == MWAIT_HALTED) { 5049637SRandy.Fishel@Sun.COM i86_mwait(cstate->cs_address, 1); 5059637SRandy.Fishel@Sun.COM } 5069637SRandy.Fishel@Sun.COM cpu_idle_exit(CPU_IDLE_CB_FLAG_IDLE); 5079637SRandy.Fishel@Sun.COM } 5088906SEric.Saxe@Sun.COM } 5098906SEric.Saxe@Sun.COM } else if (type == ACPI_ADR_SPACE_SYSTEM_IO) { 5108906SEric.Saxe@Sun.COM uint32_t value; 5118906SEric.Saxe@Sun.COM ACPI_TABLE_FADT *gbl_FADT; 5128906SEric.Saxe@Sun.COM 5138906SEric.Saxe@Sun.COM if (*mcpu_mwait == MWAIT_WAKEUP_IPI) { 5149637SRandy.Fishel@Sun.COM if (cpu_idle_enter((uint_t)cs_type, 0, 5159637SRandy.Fishel@Sun.COM check_func, (void *)mcpu_mwait) == 0) { 5169637SRandy.Fishel@Sun.COM if (*mcpu_mwait == MWAIT_WAKEUP_IPI) { 5179637SRandy.Fishel@Sun.COM (void) cpu_acpi_read_port( 5189637SRandy.Fishel@Sun.COM cstate->cs_address, &value, 8); 5199637SRandy.Fishel@Sun.COM acpica_get_global_FADT(&gbl_FADT); 5209637SRandy.Fishel@Sun.COM (void) cpu_acpi_read_port( 5219637SRandy.Fishel@Sun.COM gbl_FADT->XPmTimerBlock.Address, 5229637SRandy.Fishel@Sun.COM &value, 32); 5239637SRandy.Fishel@Sun.COM } 5249637SRandy.Fishel@Sun.COM cpu_idle_exit(CPU_IDLE_CB_FLAG_IDLE); 5259637SRandy.Fishel@Sun.COM } 5268906SEric.Saxe@Sun.COM } 5278906SEric.Saxe@Sun.COM } 5288906SEric.Saxe@Sun.COM 5298906SEric.Saxe@Sun.COM /* 5309283SBill.Holler@Sun.COM * The LAPIC timer may have stopped in deep c-state. 5319283SBill.Holler@Sun.COM * Reprogram this CPU's LAPIC here before enabling interrupts. 5328906SEric.Saxe@Sun.COM */ 5339283SBill.Holler@Sun.COM (void) cstate_use_timer(&lapic_expire, CSTATE_USING_LAT); 5349283SBill.Holler@Sun.COM sti(); 5358906SEric.Saxe@Sun.COM 5368906SEric.Saxe@Sun.COM /* 5378906SEric.Saxe@Sun.COM * We're no longer halted 5388906SEric.Saxe@Sun.COM */ 5398906SEric.Saxe@Sun.COM if (hset_update) { 5408906SEric.Saxe@Sun.COM cpup->cpu_disp_flags &= ~CPU_DISP_HALTED; 5418906SEric.Saxe@Sun.COM bitset_atomic_del(&cp->cp_haltset, cpu_sid); 5428906SEric.Saxe@Sun.COM } 5438906SEric.Saxe@Sun.COM } 5448906SEric.Saxe@Sun.COM 5458906SEric.Saxe@Sun.COM /* 5468906SEric.Saxe@Sun.COM * Idle the present CPU, deep c-state is supported 5478906SEric.Saxe@Sun.COM */ 5488906SEric.Saxe@Sun.COM void 5498906SEric.Saxe@Sun.COM cpu_acpi_idle(void) 5508906SEric.Saxe@Sun.COM { 5518906SEric.Saxe@Sun.COM cpu_t *cp = CPU; 5528906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 5538906SEric.Saxe@Sun.COM cma_c_state_t *cs_data; 5548983SBill.Holler@Sun.COM cpu_acpi_cstate_t *cstates; 5558906SEric.Saxe@Sun.COM hrtime_t start, end; 5568906SEric.Saxe@Sun.COM int cpu_max_cstates; 5578983SBill.Holler@Sun.COM uint32_t cs_indx; 5588983SBill.Holler@Sun.COM uint16_t cs_type; 5598906SEric.Saxe@Sun.COM 5608906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 5618906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 5628906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 5638906SEric.Saxe@Sun.COM ASSERT(CPU_ACPI_CSTATES(handle) != NULL); 5648906SEric.Saxe@Sun.COM 5658906SEric.Saxe@Sun.COM cs_data = mach_state->ms_cstate.cma_state.cstate; 5668983SBill.Holler@Sun.COM cstates = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 5678983SBill.Holler@Sun.COM ASSERT(cstates != NULL); 5688906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 5698906SEric.Saxe@Sun.COM if (cpu_max_cstates > CPU_MAX_CSTATES) 5708906SEric.Saxe@Sun.COM cpu_max_cstates = CPU_MAX_CSTATES; 5718983SBill.Holler@Sun.COM if (cpu_max_cstates == 1) { /* no ACPI c-state data */ 5728983SBill.Holler@Sun.COM (*non_deep_idle_cpu)(); 5738983SBill.Holler@Sun.COM return; 5748983SBill.Holler@Sun.COM } 5758906SEric.Saxe@Sun.COM 5768906SEric.Saxe@Sun.COM start = gethrtime_unscaled(); 5778906SEric.Saxe@Sun.COM 5788983SBill.Holler@Sun.COM cs_indx = cpupm_next_cstate(cs_data, cstates, cpu_max_cstates, start); 5798906SEric.Saxe@Sun.COM 5808983SBill.Holler@Sun.COM cs_type = cstates[cs_indx].cs_type; 5818906SEric.Saxe@Sun.COM 5828906SEric.Saxe@Sun.COM switch (cs_type) { 5838906SEric.Saxe@Sun.COM default: 5848906SEric.Saxe@Sun.COM /* FALLTHROUGH */ 5858906SEric.Saxe@Sun.COM case CPU_ACPI_C1: 5868906SEric.Saxe@Sun.COM (*non_deep_idle_cpu)(); 5878906SEric.Saxe@Sun.COM break; 5888906SEric.Saxe@Sun.COM 5898906SEric.Saxe@Sun.COM case CPU_ACPI_C2: 5908983SBill.Holler@Sun.COM acpi_cpu_cstate(&cstates[cs_indx]); 5918906SEric.Saxe@Sun.COM break; 5928906SEric.Saxe@Sun.COM 5938906SEric.Saxe@Sun.COM case CPU_ACPI_C3: 5948906SEric.Saxe@Sun.COM /* 595*10447SBill.Holler@Sun.COM * All supported Intel processors maintain cache coherency 596*10447SBill.Holler@Sun.COM * during C3. Currently when entering C3 processors flush 597*10447SBill.Holler@Sun.COM * core caches to higher level shared cache. The shared cache 598*10447SBill.Holler@Sun.COM * maintains state and supports probes during C3. 599*10447SBill.Holler@Sun.COM * Consequently there is no need to handle cache coherency 600*10447SBill.Holler@Sun.COM * and Bus Master activity here with the cache flush, BM_RLD 601*10447SBill.Holler@Sun.COM * bit, BM_STS bit, nor PM2_CNT.ARB_DIS mechanisms described 602*10447SBill.Holler@Sun.COM * in section 8.1.4 of the ACPI Specification 4.0. 6038906SEric.Saxe@Sun.COM */ 6048983SBill.Holler@Sun.COM acpi_cpu_cstate(&cstates[cs_indx]); 6058906SEric.Saxe@Sun.COM break; 6068906SEric.Saxe@Sun.COM } 6078906SEric.Saxe@Sun.COM 6088906SEric.Saxe@Sun.COM end = gethrtime_unscaled(); 6098906SEric.Saxe@Sun.COM 6108906SEric.Saxe@Sun.COM /* 6118906SEric.Saxe@Sun.COM * Update statistics 6128906SEric.Saxe@Sun.COM */ 6138906SEric.Saxe@Sun.COM cpupm_wakeup_cstate_data(cs_data, end); 6148906SEric.Saxe@Sun.COM } 6158906SEric.Saxe@Sun.COM 6168906SEric.Saxe@Sun.COM boolean_t 6178906SEric.Saxe@Sun.COM cpu_deep_cstates_supported(void) 6188906SEric.Saxe@Sun.COM { 6198906SEric.Saxe@Sun.COM extern int idle_cpu_no_deep_c; 6208906SEric.Saxe@Sun.COM 6218906SEric.Saxe@Sun.COM if (idle_cpu_no_deep_c) 6228906SEric.Saxe@Sun.COM return (B_FALSE); 6238906SEric.Saxe@Sun.COM 6248906SEric.Saxe@Sun.COM if (!cpuid_deep_cstates_supported()) 6258906SEric.Saxe@Sun.COM return (B_FALSE); 6268906SEric.Saxe@Sun.COM 6279283SBill.Holler@Sun.COM if (cpuid_arat_supported()) { 6289283SBill.Holler@Sun.COM cpu_cstate_arat = B_TRUE; 6299283SBill.Holler@Sun.COM return (B_TRUE); 6309283SBill.Holler@Sun.COM } 6318906SEric.Saxe@Sun.COM 6329283SBill.Holler@Sun.COM if ((hpet.supported == HPET_FULL_SUPPORT) && 6339283SBill.Holler@Sun.COM hpet.install_proxy()) { 6349283SBill.Holler@Sun.COM cpu_cstate_hpet = B_TRUE; 6359283SBill.Holler@Sun.COM return (B_TRUE); 6369283SBill.Holler@Sun.COM } 6379283SBill.Holler@Sun.COM 6389283SBill.Holler@Sun.COM return (B_FALSE); 6398906SEric.Saxe@Sun.COM } 6408906SEric.Saxe@Sun.COM 6418906SEric.Saxe@Sun.COM /* 6428906SEric.Saxe@Sun.COM * Validate that this processor supports deep cstate and if so, 6438906SEric.Saxe@Sun.COM * get the c-state data from ACPI and cache it. 6448906SEric.Saxe@Sun.COM */ 6458906SEric.Saxe@Sun.COM static int 6468906SEric.Saxe@Sun.COM cpu_idle_init(cpu_t *cp) 6478906SEric.Saxe@Sun.COM { 6488906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6498906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 6508906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 6518906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate; 6528906SEric.Saxe@Sun.COM char name[KSTAT_STRLEN]; 6538906SEric.Saxe@Sun.COM int cpu_max_cstates, i; 65410075SMark.Haywood@Sun.COM int ret; 6558906SEric.Saxe@Sun.COM 6568906SEric.Saxe@Sun.COM /* 6578906SEric.Saxe@Sun.COM * Cache the C-state specific ACPI data. 6588906SEric.Saxe@Sun.COM */ 65910075SMark.Haywood@Sun.COM if ((ret = cpu_acpi_cache_cstate_data(handle)) != 0) { 66010075SMark.Haywood@Sun.COM if (ret < 0) 66110075SMark.Haywood@Sun.COM cmn_err(CE_NOTE, 66210075SMark.Haywood@Sun.COM "!Support for CPU deep idle states is being " 66310075SMark.Haywood@Sun.COM "disabled due to errors parsing ACPI C-state " 66410075SMark.Haywood@Sun.COM "objects exported by BIOS."); 6658906SEric.Saxe@Sun.COM cpu_idle_fini(cp); 6668906SEric.Saxe@Sun.COM return (-1); 6678906SEric.Saxe@Sun.COM } 6688906SEric.Saxe@Sun.COM 6698906SEric.Saxe@Sun.COM cstate = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 6708906SEric.Saxe@Sun.COM 6718906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 6728906SEric.Saxe@Sun.COM 6738906SEric.Saxe@Sun.COM for (i = CPU_ACPI_C1; i <= cpu_max_cstates; i++) { 6748906SEric.Saxe@Sun.COM (void) snprintf(name, KSTAT_STRLEN - 1, "c%d", cstate->cs_type); 6758906SEric.Saxe@Sun.COM /* 6768906SEric.Saxe@Sun.COM * Allocate, initialize and install cstate kstat 6778906SEric.Saxe@Sun.COM */ 6788906SEric.Saxe@Sun.COM cstate->cs_ksp = kstat_create("cstate", CPU->cpu_id, 6798906SEric.Saxe@Sun.COM name, "misc", 6808906SEric.Saxe@Sun.COM KSTAT_TYPE_NAMED, 6818906SEric.Saxe@Sun.COM sizeof (cpu_idle_kstat) / sizeof (kstat_named_t), 6828906SEric.Saxe@Sun.COM KSTAT_FLAG_VIRTUAL); 6838906SEric.Saxe@Sun.COM 6848906SEric.Saxe@Sun.COM if (cstate->cs_ksp == NULL) { 6858906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "kstat_create(c_state) fail"); 6868906SEric.Saxe@Sun.COM } else { 6878906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_data = &cpu_idle_kstat; 6888906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_lock = &cpu_idle_mutex; 6898906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_update = cpu_idle_kstat_update; 6908906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_data_size += MAXNAMELEN; 6918906SEric.Saxe@Sun.COM cstate->cs_ksp->ks_private = cstate; 6928906SEric.Saxe@Sun.COM kstat_install(cstate->cs_ksp); 6938906SEric.Saxe@Sun.COM cstate++; 6948906SEric.Saxe@Sun.COM } 6958906SEric.Saxe@Sun.COM } 6968906SEric.Saxe@Sun.COM 6978906SEric.Saxe@Sun.COM cpupm_alloc_domains(cp, CPUPM_C_STATES); 6988906SEric.Saxe@Sun.COM cpupm_alloc_ms_cstate(cp); 6998906SEric.Saxe@Sun.COM 7008906SEric.Saxe@Sun.COM if (cpu_deep_cstates_supported()) { 701*10447SBill.Holler@Sun.COM uint32_t value; 702*10447SBill.Holler@Sun.COM 7038906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7048906SEric.Saxe@Sun.COM if (cpu_deep_idle_callb_id == (callb_id_t)0) 7058906SEric.Saxe@Sun.COM cpu_deep_idle_callb_id = callb_add(&cpu_deep_idle_callb, 7068906SEric.Saxe@Sun.COM (void *)NULL, CB_CL_CPU_DEEP_IDLE, "cpu_deep_idle"); 7078906SEric.Saxe@Sun.COM if (cpu_idle_cpr_callb_id == (callb_id_t)0) 7088906SEric.Saxe@Sun.COM cpu_idle_cpr_callb_id = callb_add(&cpu_idle_cpr_callb, 7098906SEric.Saxe@Sun.COM (void *)NULL, CB_CL_CPR_PM, "cpu_idle_cpr"); 7108906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 711*10447SBill.Holler@Sun.COM 712*10447SBill.Holler@Sun.COM 713*10447SBill.Holler@Sun.COM /* 714*10447SBill.Holler@Sun.COM * All supported CPUs (Nehalem and later) will remain in C3 715*10447SBill.Holler@Sun.COM * during Bus Master activity. 716*10447SBill.Holler@Sun.COM * All CPUs set ACPI_BITREG_BUS_MASTER_RLD to 0 here if it 717*10447SBill.Holler@Sun.COM * is not already 0 before enabling Deeper C-states. 718*10447SBill.Holler@Sun.COM */ 719*10447SBill.Holler@Sun.COM cpu_acpi_get_register(ACPI_BITREG_BUS_MASTER_RLD, &value); 720*10447SBill.Holler@Sun.COM if (value & 1) 721*10447SBill.Holler@Sun.COM cpu_acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0); 7228906SEric.Saxe@Sun.COM } 7238906SEric.Saxe@Sun.COM 7248906SEric.Saxe@Sun.COM return (0); 7258906SEric.Saxe@Sun.COM } 7268906SEric.Saxe@Sun.COM 7278906SEric.Saxe@Sun.COM /* 7288906SEric.Saxe@Sun.COM * Free resources allocated by cpu_idle_init(). 7298906SEric.Saxe@Sun.COM */ 7308906SEric.Saxe@Sun.COM static void 7318906SEric.Saxe@Sun.COM cpu_idle_fini(cpu_t *cp) 7328906SEric.Saxe@Sun.COM { 7338906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 7348906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 7358906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 7368906SEric.Saxe@Sun.COM cpu_acpi_cstate_t *cstate; 7378906SEric.Saxe@Sun.COM uint_t cpu_max_cstates, i; 7388906SEric.Saxe@Sun.COM 7398906SEric.Saxe@Sun.COM /* 7408906SEric.Saxe@Sun.COM * idle cpu points back to the generic one 7418906SEric.Saxe@Sun.COM */ 7428906SEric.Saxe@Sun.COM idle_cpu = CPU->cpu_m.mcpu_idle_cpu = non_deep_idle_cpu; 7438906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 7448906SEric.Saxe@Sun.COM 7458906SEric.Saxe@Sun.COM cstate = (cpu_acpi_cstate_t *)CPU_ACPI_CSTATES(handle); 7468906SEric.Saxe@Sun.COM if (cstate) { 7478906SEric.Saxe@Sun.COM cpu_max_cstates = cpu_acpi_get_max_cstates(handle); 7488906SEric.Saxe@Sun.COM 7498906SEric.Saxe@Sun.COM for (i = CPU_ACPI_C1; i <= cpu_max_cstates; i++) { 7508906SEric.Saxe@Sun.COM if (cstate->cs_ksp != NULL) 7518906SEric.Saxe@Sun.COM kstat_delete(cstate->cs_ksp); 7528906SEric.Saxe@Sun.COM cstate++; 7538906SEric.Saxe@Sun.COM } 7548906SEric.Saxe@Sun.COM } 7558906SEric.Saxe@Sun.COM 7568906SEric.Saxe@Sun.COM cpupm_free_ms_cstate(cp); 7578906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_cstate_domains); 7588906SEric.Saxe@Sun.COM cpu_acpi_free_cstate_data(handle); 7598906SEric.Saxe@Sun.COM 7608906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7618906SEric.Saxe@Sun.COM if (cpu_deep_idle_callb_id != (callb_id_t)0) { 7628906SEric.Saxe@Sun.COM (void) callb_delete(cpu_deep_idle_callb_id); 7638906SEric.Saxe@Sun.COM cpu_deep_idle_callb_id = (callb_id_t)0; 7648906SEric.Saxe@Sun.COM } 7658906SEric.Saxe@Sun.COM if (cpu_idle_cpr_callb_id != (callb_id_t)0) { 7668906SEric.Saxe@Sun.COM (void) callb_delete(cpu_idle_cpr_callb_id); 7678906SEric.Saxe@Sun.COM cpu_idle_cpr_callb_id = (callb_id_t)0; 7688906SEric.Saxe@Sun.COM } 7698906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 7708906SEric.Saxe@Sun.COM } 7718906SEric.Saxe@Sun.COM 7728906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7738906SEric.Saxe@Sun.COM static boolean_t 7748906SEric.Saxe@Sun.COM cpu_deep_idle_callb(void *arg, int code) 7758906SEric.Saxe@Sun.COM { 7768906SEric.Saxe@Sun.COM boolean_t rslt = B_TRUE; 7778906SEric.Saxe@Sun.COM 7788906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 7798906SEric.Saxe@Sun.COM switch (code) { 7808906SEric.Saxe@Sun.COM case PM_DEFAULT_CPU_DEEP_IDLE: 7818906SEric.Saxe@Sun.COM /* 7828906SEric.Saxe@Sun.COM * Default policy is same as enable 7838906SEric.Saxe@Sun.COM */ 7848906SEric.Saxe@Sun.COM /*FALLTHROUGH*/ 7858906SEric.Saxe@Sun.COM case PM_ENABLE_CPU_DEEP_IDLE: 7868906SEric.Saxe@Sun.COM if ((cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) == 0) 7878906SEric.Saxe@Sun.COM break; 7888906SEric.Saxe@Sun.COM 7899283SBill.Holler@Sun.COM if (cstate_timer_callback(PM_ENABLE_CPU_DEEP_IDLE)) { 7908906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 7918906SEric.Saxe@Sun.COM idle_cpu = cpu_idle_adaptive; 7928906SEric.Saxe@Sun.COM cpu_idle_cfg_state &= ~CPU_IDLE_DEEP_CFG; 7938906SEric.Saxe@Sun.COM } else { 7948906SEric.Saxe@Sun.COM rslt = B_FALSE; 7958906SEric.Saxe@Sun.COM } 7968906SEric.Saxe@Sun.COM break; 7978906SEric.Saxe@Sun.COM 7988906SEric.Saxe@Sun.COM case PM_DISABLE_CPU_DEEP_IDLE: 7998906SEric.Saxe@Sun.COM if (cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) 8008906SEric.Saxe@Sun.COM break; 8018906SEric.Saxe@Sun.COM 8028906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 8039283SBill.Holler@Sun.COM if (cstate_timer_callback(PM_DISABLE_CPU_DEEP_IDLE)) { 8048906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 8058906SEric.Saxe@Sun.COM cpu_idle_cfg_state |= CPU_IDLE_DEEP_CFG; 8068906SEric.Saxe@Sun.COM } 8078906SEric.Saxe@Sun.COM break; 8088906SEric.Saxe@Sun.COM 8098906SEric.Saxe@Sun.COM default: 8108906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpu deep_idle_callb: invalid code %d\n", 8118906SEric.Saxe@Sun.COM 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 /*ARGSUSED*/ 8198906SEric.Saxe@Sun.COM static boolean_t 8208906SEric.Saxe@Sun.COM cpu_idle_cpr_callb(void *arg, int code) 8218906SEric.Saxe@Sun.COM { 8228906SEric.Saxe@Sun.COM boolean_t rslt = B_TRUE; 8238906SEric.Saxe@Sun.COM 8248906SEric.Saxe@Sun.COM mutex_enter(&cpu_idle_callb_mutex); 8258906SEric.Saxe@Sun.COM switch (code) { 8268906SEric.Saxe@Sun.COM case CB_CODE_CPR_RESUME: 8279283SBill.Holler@Sun.COM if (cstate_timer_callback(CB_CODE_CPR_RESUME)) { 8288906SEric.Saxe@Sun.COM /* 8298906SEric.Saxe@Sun.COM * Do not enable dispatcher hooks if disabled by user. 8308906SEric.Saxe@Sun.COM */ 8318906SEric.Saxe@Sun.COM if (cpu_idle_cfg_state & CPU_IDLE_DEEP_CFG) 8328906SEric.Saxe@Sun.COM break; 8338906SEric.Saxe@Sun.COM 8348906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 8358906SEric.Saxe@Sun.COM idle_cpu = cpu_idle_adaptive; 8368906SEric.Saxe@Sun.COM } else { 8378906SEric.Saxe@Sun.COM rslt = B_FALSE; 8388906SEric.Saxe@Sun.COM } 8398906SEric.Saxe@Sun.COM break; 8408906SEric.Saxe@Sun.COM 8418906SEric.Saxe@Sun.COM case CB_CODE_CPR_CHKPT: 8428906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 8438906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 8449283SBill.Holler@Sun.COM (void) cstate_timer_callback(CB_CODE_CPR_CHKPT); 8458906SEric.Saxe@Sun.COM break; 8468906SEric.Saxe@Sun.COM 8478906SEric.Saxe@Sun.COM default: 8488906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpudvr cpr_callb: invalid code %d\n", code); 8498906SEric.Saxe@Sun.COM break; 8508906SEric.Saxe@Sun.COM } 8518906SEric.Saxe@Sun.COM mutex_exit(&cpu_idle_callb_mutex); 8528906SEric.Saxe@Sun.COM return (rslt); 8538906SEric.Saxe@Sun.COM } 8548906SEric.Saxe@Sun.COM 8558906SEric.Saxe@Sun.COM /* 8568906SEric.Saxe@Sun.COM * handle _CST notification 8578906SEric.Saxe@Sun.COM */ 8588906SEric.Saxe@Sun.COM void 8598906SEric.Saxe@Sun.COM cpuidle_cstate_instance(cpu_t *cp) 8608906SEric.Saxe@Sun.COM { 8618906SEric.Saxe@Sun.COM #ifndef __xpv 8628906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 8638906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 8648906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 8658906SEric.Saxe@Sun.COM struct machcpu *mcpu; 8668906SEric.Saxe@Sun.COM cpuset_t dom_cpu_set; 8678906SEric.Saxe@Sun.COM kmutex_t *pm_lock; 8688906SEric.Saxe@Sun.COM int result = 0; 8698906SEric.Saxe@Sun.COM processorid_t cpu_id; 8708906SEric.Saxe@Sun.COM 8718906SEric.Saxe@Sun.COM if (mach_state == NULL) { 8728906SEric.Saxe@Sun.COM return; 8738906SEric.Saxe@Sun.COM } 8748906SEric.Saxe@Sun.COM 8758906SEric.Saxe@Sun.COM ASSERT(mach_state->ms_cstate.cma_domain != NULL); 8768906SEric.Saxe@Sun.COM dom_cpu_set = mach_state->ms_cstate.cma_domain->pm_cpus; 8778906SEric.Saxe@Sun.COM pm_lock = &mach_state->ms_cstate.cma_domain->pm_lock; 8788906SEric.Saxe@Sun.COM 8798906SEric.Saxe@Sun.COM /* 8808906SEric.Saxe@Sun.COM * Do for all the CPU's in the domain 8818906SEric.Saxe@Sun.COM */ 8828906SEric.Saxe@Sun.COM mutex_enter(pm_lock); 8838906SEric.Saxe@Sun.COM do { 8848906SEric.Saxe@Sun.COM CPUSET_FIND(dom_cpu_set, cpu_id); 8858906SEric.Saxe@Sun.COM if (cpu_id == CPUSET_NOTINSET) 8868906SEric.Saxe@Sun.COM break; 8878906SEric.Saxe@Sun.COM 8888906SEric.Saxe@Sun.COM ASSERT(cpu_id >= 0 && cpu_id < NCPU); 8898906SEric.Saxe@Sun.COM cp = cpu[cpu_id]; 8908906SEric.Saxe@Sun.COM mach_state = (cpupm_mach_state_t *) 8918906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_pm_mach_state; 8928906SEric.Saxe@Sun.COM if (!(mach_state->ms_caps & CPUPM_C_STATES)) { 8938906SEric.Saxe@Sun.COM mutex_exit(pm_lock); 8948906SEric.Saxe@Sun.COM return; 8958906SEric.Saxe@Sun.COM } 8968906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 8978906SEric.Saxe@Sun.COM ASSERT(handle != NULL); 8988906SEric.Saxe@Sun.COM 8998906SEric.Saxe@Sun.COM /* 9008906SEric.Saxe@Sun.COM * re-evaluate cstate object 9018906SEric.Saxe@Sun.COM */ 9028906SEric.Saxe@Sun.COM if (cpu_acpi_cache_cstate_data(handle) != 0) { 9038906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "Cannot re-evaluate the cpu c-state" 9048906SEric.Saxe@Sun.COM " object Instance: %d", cpu_id); 9058906SEric.Saxe@Sun.COM } 9068906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 9078906SEric.Saxe@Sun.COM mcpu = &(cp->cpu_m); 9088906SEric.Saxe@Sun.COM mcpu->max_cstates = cpu_acpi_get_max_cstates(handle); 9098906SEric.Saxe@Sun.COM if (mcpu->max_cstates > CPU_ACPI_C1) { 9109283SBill.Holler@Sun.COM (void) cstate_timer_callback( 9119283SBill.Holler@Sun.COM CST_EVENT_MULTIPLE_CSTATES); 9128906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 9138906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_idle_cpu = cpu_acpi_idle; 9148906SEric.Saxe@Sun.COM } else if (mcpu->max_cstates == CPU_ACPI_C1) { 9158906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 9168906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_idle_cpu = non_deep_idle_cpu; 9179283SBill.Holler@Sun.COM (void) cstate_timer_callback(CST_EVENT_ONE_CSTATE); 9188906SEric.Saxe@Sun.COM } 9198906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 9208906SEric.Saxe@Sun.COM 9218906SEric.Saxe@Sun.COM CPUSET_ATOMIC_XDEL(dom_cpu_set, cpu_id, result); 9228906SEric.Saxe@Sun.COM mutex_exit(pm_lock); 9238906SEric.Saxe@Sun.COM } while (result < 0); 9248906SEric.Saxe@Sun.COM #endif 9258906SEric.Saxe@Sun.COM } 9268906SEric.Saxe@Sun.COM 9278906SEric.Saxe@Sun.COM /* 9288906SEric.Saxe@Sun.COM * handle the number or the type of available processor power states change 9298906SEric.Saxe@Sun.COM */ 9308906SEric.Saxe@Sun.COM void 9318906SEric.Saxe@Sun.COM cpuidle_manage_cstates(void *ctx) 9328906SEric.Saxe@Sun.COM { 9338906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 9348906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 9358906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 9368906SEric.Saxe@Sun.COM boolean_t is_ready; 9378906SEric.Saxe@Sun.COM 9388906SEric.Saxe@Sun.COM if (mach_state == NULL) { 9398906SEric.Saxe@Sun.COM return; 9408906SEric.Saxe@Sun.COM } 9418906SEric.Saxe@Sun.COM 9428906SEric.Saxe@Sun.COM /* 9438906SEric.Saxe@Sun.COM * We currently refuse to power manage if the CPU is not ready to 9448906SEric.Saxe@Sun.COM * take cross calls (cross calls fail silently if CPU is not ready 9458906SEric.Saxe@Sun.COM * for it). 9468906SEric.Saxe@Sun.COM * 9478906SEric.Saxe@Sun.COM * Additionally, for x86 platforms we cannot power manage 9488906SEric.Saxe@Sun.COM * any one instance, until all instances have been initialized. 9498906SEric.Saxe@Sun.COM * That's because we don't know what the CPU domains look like 9508906SEric.Saxe@Sun.COM * until all instances have been initialized. 9518906SEric.Saxe@Sun.COM */ 9529489SJoe.Bonasera@sun.com is_ready = (cp->cpu_flags & CPU_READY) && cpupm_cstate_ready(); 9538906SEric.Saxe@Sun.COM if (!is_ready) 9548906SEric.Saxe@Sun.COM return; 9558906SEric.Saxe@Sun.COM 9568906SEric.Saxe@Sun.COM cpuidle_cstate_instance(cp); 9578906SEric.Saxe@Sun.COM } 958