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/cpu_pm.h> 318906SEric.Saxe@Sun.COM #include <sys/x86_archext.h> 328906SEric.Saxe@Sun.COM #include <sys/sdt.h> 338906SEric.Saxe@Sun.COM #include <sys/spl.h> 348906SEric.Saxe@Sun.COM #include <sys/machsystm.h> 3510488SMark.Haywood@Sun.COM #include <sys/archsystm.h> 368906SEric.Saxe@Sun.COM #include <sys/hpet.h> 3710147SMark.Haywood@Sun.COM #include <sys/acpi/acpi.h> 3810147SMark.Haywood@Sun.COM #include <sys/acpica.h> 398906SEric.Saxe@Sun.COM #include <sys/cpupm.h> 408906SEric.Saxe@Sun.COM #include <sys/cpu_idle.h> 418906SEric.Saxe@Sun.COM #include <sys/cpu_acpi.h> 428906SEric.Saxe@Sun.COM #include <sys/cpupm_throttle.h> 438983SBill.Holler@Sun.COM #include <sys/dtrace.h> 4410488SMark.Haywood@Sun.COM #include <sys/note.h> 458906SEric.Saxe@Sun.COM 468906SEric.Saxe@Sun.COM /* 478906SEric.Saxe@Sun.COM * This callback is used to build the PPM CPU domains once 4810488SMark.Haywood@Sun.COM * a CPU device has been started. The callback is initialized 4910488SMark.Haywood@Sun.COM * by the PPM driver to point to a routine that will build the 5010488SMark.Haywood@Sun.COM * domains. 518906SEric.Saxe@Sun.COM */ 5210488SMark.Haywood@Sun.COM void (*cpupm_ppm_alloc_pstate_domains)(cpu_t *); 538906SEric.Saxe@Sun.COM 548906SEric.Saxe@Sun.COM /* 5510488SMark.Haywood@Sun.COM * This callback is used to remove CPU from the PPM CPU domains 5610488SMark.Haywood@Sun.COM * when the cpu driver is detached. The callback is initialized 5710488SMark.Haywood@Sun.COM * by the PPM driver to point to a routine that will remove CPU 5810488SMark.Haywood@Sun.COM * from the domains. 598906SEric.Saxe@Sun.COM */ 6010488SMark.Haywood@Sun.COM void (*cpupm_ppm_free_pstate_domains)(cpu_t *); 618906SEric.Saxe@Sun.COM 628906SEric.Saxe@Sun.COM /* 638906SEric.Saxe@Sun.COM * This callback is used to redefine the topspeed for a CPU device. 648906SEric.Saxe@Sun.COM * Since all CPUs in a domain should have identical properties, this 658906SEric.Saxe@Sun.COM * callback is initialized by the PPM driver to point to a routine 668906SEric.Saxe@Sun.COM * that will redefine the topspeed for all devices in a CPU domain. 678906SEric.Saxe@Sun.COM * This callback is exercised whenever an ACPI _PPC change notification 688906SEric.Saxe@Sun.COM * is received by the CPU driver. 698906SEric.Saxe@Sun.COM */ 708906SEric.Saxe@Sun.COM void (*cpupm_redefine_topspeed)(void *); 718906SEric.Saxe@Sun.COM 728906SEric.Saxe@Sun.COM /* 738906SEric.Saxe@Sun.COM * This callback is used by the PPM driver to call into the CPU driver 748906SEric.Saxe@Sun.COM * to find a CPU's current topspeed (i.e., it's current ACPI _PPC value). 758906SEric.Saxe@Sun.COM */ 768906SEric.Saxe@Sun.COM void (*cpupm_set_topspeed_callb)(void *, int); 778906SEric.Saxe@Sun.COM 788906SEric.Saxe@Sun.COM /* 798906SEric.Saxe@Sun.COM * This callback is used by the PPM driver to call into the CPU driver 808906SEric.Saxe@Sun.COM * to set a new topspeed for a CPU. 818906SEric.Saxe@Sun.COM */ 828906SEric.Saxe@Sun.COM int (*cpupm_get_topspeed_callb)(void *); 838906SEric.Saxe@Sun.COM 848906SEric.Saxe@Sun.COM static void cpupm_event_notify_handler(ACPI_HANDLE, UINT32, void *); 858906SEric.Saxe@Sun.COM static void cpupm_free_notify_handlers(cpu_t *); 8610786SMark.Haywood@Sun.COM static void cpupm_power_manage_notifications(void *); 878906SEric.Saxe@Sun.COM 888906SEric.Saxe@Sun.COM /* 898906SEric.Saxe@Sun.COM * Until proven otherwise, all power states are manageable. 908906SEric.Saxe@Sun.COM */ 918906SEric.Saxe@Sun.COM static uint32_t cpupm_enabled = CPUPM_ALL_STATES; 928906SEric.Saxe@Sun.COM 938906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_pstate_domains = NULL; 948906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_tstate_domains = NULL; 958906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_cstate_domains = NULL; 968906SEric.Saxe@Sun.COM 978906SEric.Saxe@Sun.COM /* 988906SEric.Saxe@Sun.COM * c-state tunables 998906SEric.Saxe@Sun.COM * 10010353Saubrey.li@intel.com * cpupm_cs_sample_interval is the length of time we wait before 10110353Saubrey.li@intel.com * recalculating c-state statistics. When a CPU goes idle it checks 10210353Saubrey.li@intel.com * to see if it has been longer than cpupm_cs_sample_interval since it last 10310353Saubrey.li@intel.com * caculated which C-state to go to. 10410353Saubrey.li@intel.com * 1058906SEric.Saxe@Sun.COM * cpupm_cs_idle_cost_tunable is the ratio of time CPU spends executing + idle 1068906SEric.Saxe@Sun.COM * divided by time spent in the idle state transitions. 1078906SEric.Saxe@Sun.COM * A value of 10 means the CPU will not spend more than 1/10 of its time 1088906SEric.Saxe@Sun.COM * in idle latency. The worst case performance will be 90% of non Deep C-state 1098906SEric.Saxe@Sun.COM * kernel. 1108906SEric.Saxe@Sun.COM * 1118906SEric.Saxe@Sun.COM * cpupm_cs_idle_save_tunable is how long we must stay in a deeper C-state 1128906SEric.Saxe@Sun.COM * before it is worth going there. Expressed as a multiple of latency. 1138906SEric.Saxe@Sun.COM */ 11410353Saubrey.li@intel.com uint32_t cpupm_cs_sample_interval = 100*1000*1000; /* 100 milliseconds */ 1158906SEric.Saxe@Sun.COM uint32_t cpupm_cs_idle_cost_tunable = 10; /* work time / latency cost */ 1168906SEric.Saxe@Sun.COM uint32_t cpupm_cs_idle_save_tunable = 2; /* idle power savings */ 1178906SEric.Saxe@Sun.COM uint16_t cpupm_C2_idle_pct_tunable = 70; 1188906SEric.Saxe@Sun.COM uint16_t cpupm_C3_idle_pct_tunable = 80; 1198906SEric.Saxe@Sun.COM 1208906SEric.Saxe@Sun.COM #ifndef __xpv 1218906SEric.Saxe@Sun.COM extern boolean_t cpupm_intel_init(cpu_t *); 1228906SEric.Saxe@Sun.COM extern boolean_t cpupm_amd_init(cpu_t *); 1238906SEric.Saxe@Sun.COM 1248906SEric.Saxe@Sun.COM typedef struct cpupm_vendor { 1258906SEric.Saxe@Sun.COM boolean_t (*cpuv_init)(cpu_t *); 1268906SEric.Saxe@Sun.COM } cpupm_vendor_t; 1278906SEric.Saxe@Sun.COM 1288906SEric.Saxe@Sun.COM /* 1298906SEric.Saxe@Sun.COM * Table of supported vendors. 1308906SEric.Saxe@Sun.COM */ 1318906SEric.Saxe@Sun.COM static cpupm_vendor_t cpupm_vendors[] = { 1328906SEric.Saxe@Sun.COM cpupm_intel_init, 1338906SEric.Saxe@Sun.COM cpupm_amd_init, 1348906SEric.Saxe@Sun.COM NULL 1358906SEric.Saxe@Sun.COM }; 1368906SEric.Saxe@Sun.COM #endif 1378906SEric.Saxe@Sun.COM 1388906SEric.Saxe@Sun.COM /* 1398906SEric.Saxe@Sun.COM * Initialize the machine. 1408906SEric.Saxe@Sun.COM * See if a module exists for managing power for this CPU. 1418906SEric.Saxe@Sun.COM */ 1428906SEric.Saxe@Sun.COM /*ARGSUSED*/ 1438906SEric.Saxe@Sun.COM void 1448906SEric.Saxe@Sun.COM cpupm_init(cpu_t *cp) 1458906SEric.Saxe@Sun.COM { 1468906SEric.Saxe@Sun.COM #ifndef __xpv 1478906SEric.Saxe@Sun.COM cpupm_vendor_t *vendors; 1488906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 1498906SEric.Saxe@Sun.COM struct machcpu *mcpu = &(cp->cpu_m); 15010147SMark.Haywood@Sun.COM static boolean_t first = B_TRUE; 1518906SEric.Saxe@Sun.COM int *speeds; 1528906SEric.Saxe@Sun.COM uint_t nspeeds; 1538906SEric.Saxe@Sun.COM int ret; 1548906SEric.Saxe@Sun.COM 1558906SEric.Saxe@Sun.COM mach_state = cp->cpu_m.mcpu_pm_mach_state = 1568906SEric.Saxe@Sun.COM kmem_zalloc(sizeof (cpupm_mach_state_t), KM_SLEEP); 1578906SEric.Saxe@Sun.COM mach_state->ms_caps = CPUPM_NO_STATES; 1588906SEric.Saxe@Sun.COM mutex_init(&mach_state->ms_lock, NULL, MUTEX_DRIVER, NULL); 1598906SEric.Saxe@Sun.COM 1608906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle = cpu_acpi_init(cp); 1618906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle == NULL) { 16210488SMark.Haywood@Sun.COM cpupm_fini(cp); 1638906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!cpupm_init: processor %d: " 1648906SEric.Saxe@Sun.COM "unable to get ACPI handle", cp->cpu_id); 1658906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!CPU power management will not function."); 1668906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 16710147SMark.Haywood@Sun.COM first = B_FALSE; 1688906SEric.Saxe@Sun.COM return; 1698906SEric.Saxe@Sun.COM } 1708906SEric.Saxe@Sun.COM 1718906SEric.Saxe@Sun.COM /* 1728906SEric.Saxe@Sun.COM * Loop through the CPU management module table and see if 1738906SEric.Saxe@Sun.COM * any of the modules implement CPU power management 1748906SEric.Saxe@Sun.COM * for this CPU. 1758906SEric.Saxe@Sun.COM */ 1768906SEric.Saxe@Sun.COM for (vendors = cpupm_vendors; vendors->cpuv_init != NULL; vendors++) { 1778906SEric.Saxe@Sun.COM if (vendors->cpuv_init(cp)) 1788906SEric.Saxe@Sun.COM break; 1798906SEric.Saxe@Sun.COM } 1808906SEric.Saxe@Sun.COM 1818906SEric.Saxe@Sun.COM /* 1828906SEric.Saxe@Sun.COM * Nope, we can't power manage this CPU. 1838906SEric.Saxe@Sun.COM */ 1848906SEric.Saxe@Sun.COM if (vendors == NULL) { 18510488SMark.Haywood@Sun.COM cpupm_fini(cp); 1868906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 18710147SMark.Haywood@Sun.COM first = B_FALSE; 1888906SEric.Saxe@Sun.COM return; 1898906SEric.Saxe@Sun.COM } 1908906SEric.Saxe@Sun.COM 1918906SEric.Saxe@Sun.COM /* 1928906SEric.Saxe@Sun.COM * If P-state support exists for this system, then initialize it. 1938906SEric.Saxe@Sun.COM */ 1948906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_ops != NULL) { 1958906SEric.Saxe@Sun.COM ret = mach_state->ms_pstate.cma_ops->cpus_init(cp); 1968906SEric.Saxe@Sun.COM if (ret != 0) { 1978906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_ops = NULL; 1988906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_P_STATES); 1998906SEric.Saxe@Sun.COM } else { 2008906SEric.Saxe@Sun.COM nspeeds = cpupm_get_speeds(cp, &speeds); 2018906SEric.Saxe@Sun.COM if (nspeeds == 0) { 20210075SMark.Haywood@Sun.COM cmn_err(CE_NOTE, "!cpupm_init: processor %d:" 2038906SEric.Saxe@Sun.COM " no speeds to manage", cp->cpu_id); 2048906SEric.Saxe@Sun.COM } else { 2058906SEric.Saxe@Sun.COM cpupm_set_supp_freqs(cp, speeds, nspeeds); 2068906SEric.Saxe@Sun.COM cpupm_free_speeds(speeds, nspeeds); 2078906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_P_STATES; 2088906SEric.Saxe@Sun.COM } 2098906SEric.Saxe@Sun.COM } 21010848SNapanda.Pemmaiah@Sun.COM } else { 21110848SNapanda.Pemmaiah@Sun.COM cpupm_disable(CPUPM_P_STATES); 2128906SEric.Saxe@Sun.COM } 2138906SEric.Saxe@Sun.COM 2148906SEric.Saxe@Sun.COM if (mach_state->ms_tstate.cma_ops != NULL) { 2158906SEric.Saxe@Sun.COM ret = mach_state->ms_tstate.cma_ops->cpus_init(cp); 2168906SEric.Saxe@Sun.COM if (ret != 0) { 2178906SEric.Saxe@Sun.COM mach_state->ms_tstate.cma_ops = NULL; 2188906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_T_STATES); 2198906SEric.Saxe@Sun.COM } else { 2208906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_T_STATES; 2218906SEric.Saxe@Sun.COM } 22210848SNapanda.Pemmaiah@Sun.COM } else { 22310848SNapanda.Pemmaiah@Sun.COM cpupm_disable(CPUPM_T_STATES); 2248906SEric.Saxe@Sun.COM } 2258906SEric.Saxe@Sun.COM 2268906SEric.Saxe@Sun.COM /* 2278906SEric.Saxe@Sun.COM * If C-states support exists for this system, then initialize it. 2288906SEric.Saxe@Sun.COM */ 2298906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_ops != NULL) { 2308906SEric.Saxe@Sun.COM ret = mach_state->ms_cstate.cma_ops->cpus_init(cp); 2318906SEric.Saxe@Sun.COM if (ret != 0) { 2328906SEric.Saxe@Sun.COM mach_state->ms_cstate.cma_ops = NULL; 2338906SEric.Saxe@Sun.COM mcpu->max_cstates = CPU_ACPI_C1; 2348906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_C_STATES); 2358906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 2368906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 2378906SEric.Saxe@Sun.COM } else if (cpu_deep_cstates_supported()) { 2388906SEric.Saxe@Sun.COM mcpu->max_cstates = cpu_acpi_get_max_cstates( 2398906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle); 2408906SEric.Saxe@Sun.COM if (mcpu->max_cstates > CPU_ACPI_C1) { 2419283SBill.Holler@Sun.COM (void) cstate_timer_callback( 2429283SBill.Holler@Sun.COM CST_EVENT_MULTIPLE_CSTATES); 2438906SEric.Saxe@Sun.COM CPU->cpu_m.mcpu_idle_cpu = cpu_acpi_idle; 2448906SEric.Saxe@Sun.COM mcpu->mcpu_idle_type = CPU_ACPI_C1; 2458906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 2468906SEric.Saxe@Sun.COM } else { 2479283SBill.Holler@Sun.COM (void) cstate_timer_callback( 2489283SBill.Holler@Sun.COM CST_EVENT_ONE_CSTATE); 2498906SEric.Saxe@Sun.COM } 2508906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_C_STATES; 2518906SEric.Saxe@Sun.COM } else { 2528906SEric.Saxe@Sun.COM mcpu->max_cstates = CPU_ACPI_C1; 2538906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 2548906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 2558906SEric.Saxe@Sun.COM } 25610848SNapanda.Pemmaiah@Sun.COM } else { 25710848SNapanda.Pemmaiah@Sun.COM cpupm_disable(CPUPM_C_STATES); 2588906SEric.Saxe@Sun.COM } 2598906SEric.Saxe@Sun.COM 2608906SEric.Saxe@Sun.COM 2618906SEric.Saxe@Sun.COM if (mach_state->ms_caps == CPUPM_NO_STATES) { 26210488SMark.Haywood@Sun.COM cpupm_fini(cp); 2638906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 26410147SMark.Haywood@Sun.COM first = B_FALSE; 2658906SEric.Saxe@Sun.COM return; 2668906SEric.Saxe@Sun.COM } 2678906SEric.Saxe@Sun.COM 2688906SEric.Saxe@Sun.COM if ((mach_state->ms_caps & CPUPM_T_STATES) || 2698906SEric.Saxe@Sun.COM (mach_state->ms_caps & CPUPM_P_STATES) || 27010147SMark.Haywood@Sun.COM (mach_state->ms_caps & CPUPM_C_STATES)) { 27110147SMark.Haywood@Sun.COM if (first) { 27210147SMark.Haywood@Sun.COM acpica_write_cpupm_capabilities( 27310147SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_P_STATES, 27410147SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_C_STATES); 27510147SMark.Haywood@Sun.COM } 27610848SNapanda.Pemmaiah@Sun.COM if (mach_state->ms_caps & CPUPM_T_STATES) { 27710848SNapanda.Pemmaiah@Sun.COM cpupm_throttle_manage_notification(cp); 27810848SNapanda.Pemmaiah@Sun.COM } 27910848SNapanda.Pemmaiah@Sun.COM if (mach_state->ms_caps & CPUPM_C_STATES) { 28010848SNapanda.Pemmaiah@Sun.COM cpuidle_manage_cstates(cp); 28110848SNapanda.Pemmaiah@Sun.COM } 28210848SNapanda.Pemmaiah@Sun.COM if (mach_state->ms_caps & CPUPM_P_STATES) { 28310848SNapanda.Pemmaiah@Sun.COM cpupm_power_manage_notifications(cp); 28410848SNapanda.Pemmaiah@Sun.COM } 28510786SMark.Haywood@Sun.COM cpupm_add_notify_handler(cp, cpupm_event_notify_handler, cp); 28610147SMark.Haywood@Sun.COM } 28710147SMark.Haywood@Sun.COM first = B_FALSE; 2888906SEric.Saxe@Sun.COM #endif 2898906SEric.Saxe@Sun.COM } 2908906SEric.Saxe@Sun.COM 2918906SEric.Saxe@Sun.COM /* 29210488SMark.Haywood@Sun.COM * Free any resources allocated during cpupm initialization or cpupm start. 2938906SEric.Saxe@Sun.COM */ 2948906SEric.Saxe@Sun.COM /*ARGSUSED*/ 2958906SEric.Saxe@Sun.COM void 29610488SMark.Haywood@Sun.COM cpupm_free(cpu_t *cp, boolean_t cpupm_stop) 2978906SEric.Saxe@Sun.COM { 2988906SEric.Saxe@Sun.COM #ifndef __xpv 2998906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 3008906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 3018906SEric.Saxe@Sun.COM 3028906SEric.Saxe@Sun.COM if (mach_state == NULL) 3038906SEric.Saxe@Sun.COM return; 30410488SMark.Haywood@Sun.COM 3058906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_ops != NULL) { 30610488SMark.Haywood@Sun.COM if (cpupm_stop) 30710488SMark.Haywood@Sun.COM mach_state->ms_pstate.cma_ops->cpus_stop(cp); 30810488SMark.Haywood@Sun.COM else 30910488SMark.Haywood@Sun.COM mach_state->ms_pstate.cma_ops->cpus_fini(cp); 3108906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_ops = NULL; 3118906SEric.Saxe@Sun.COM } 3128906SEric.Saxe@Sun.COM 3138906SEric.Saxe@Sun.COM if (mach_state->ms_tstate.cma_ops != NULL) { 31410488SMark.Haywood@Sun.COM if (cpupm_stop) 31510488SMark.Haywood@Sun.COM mach_state->ms_tstate.cma_ops->cpus_stop(cp); 31610488SMark.Haywood@Sun.COM else 31710488SMark.Haywood@Sun.COM mach_state->ms_tstate.cma_ops->cpus_fini(cp); 3188906SEric.Saxe@Sun.COM mach_state->ms_tstate.cma_ops = NULL; 3198906SEric.Saxe@Sun.COM } 3208906SEric.Saxe@Sun.COM 3218906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_ops != NULL) { 32210488SMark.Haywood@Sun.COM if (cpupm_stop) 32310488SMark.Haywood@Sun.COM mach_state->ms_cstate.cma_ops->cpus_stop(cp); 32410488SMark.Haywood@Sun.COM else 32510488SMark.Haywood@Sun.COM mach_state->ms_cstate.cma_ops->cpus_fini(cp); 32610488SMark.Haywood@Sun.COM 3278906SEric.Saxe@Sun.COM mach_state->ms_cstate.cma_ops = NULL; 3288906SEric.Saxe@Sun.COM } 3298906SEric.Saxe@Sun.COM 3308906SEric.Saxe@Sun.COM cpupm_free_notify_handlers(cp); 3318906SEric.Saxe@Sun.COM 3328906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle != NULL) { 3338906SEric.Saxe@Sun.COM cpu_acpi_fini(mach_state->ms_acpi_handle); 3348906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle = NULL; 3358906SEric.Saxe@Sun.COM } 3368906SEric.Saxe@Sun.COM 3378906SEric.Saxe@Sun.COM mutex_destroy(&mach_state->ms_lock); 3388906SEric.Saxe@Sun.COM kmem_free(mach_state, sizeof (cpupm_mach_state_t)); 3398906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_pm_mach_state = NULL; 3408906SEric.Saxe@Sun.COM #endif 3418906SEric.Saxe@Sun.COM } 3428906SEric.Saxe@Sun.COM 34310488SMark.Haywood@Sun.COM void 34410488SMark.Haywood@Sun.COM cpupm_fini(cpu_t *cp) 34510488SMark.Haywood@Sun.COM { 34610488SMark.Haywood@Sun.COM /* 34710488SMark.Haywood@Sun.COM * call (*cpus_fini)() ops to release the cpupm resource 34810488SMark.Haywood@Sun.COM * in the P/C/T-state driver 34910488SMark.Haywood@Sun.COM */ 35010488SMark.Haywood@Sun.COM cpupm_free(cp, B_FALSE); 35110488SMark.Haywood@Sun.COM } 35210488SMark.Haywood@Sun.COM 35310488SMark.Haywood@Sun.COM void 35410488SMark.Haywood@Sun.COM cpupm_start(cpu_t *cp) 35510488SMark.Haywood@Sun.COM { 35610488SMark.Haywood@Sun.COM cpupm_init(cp); 35710488SMark.Haywood@Sun.COM } 35810488SMark.Haywood@Sun.COM 35910488SMark.Haywood@Sun.COM void 36010488SMark.Haywood@Sun.COM cpupm_stop(cpu_t *cp) 36110488SMark.Haywood@Sun.COM { 36210488SMark.Haywood@Sun.COM /* 36310488SMark.Haywood@Sun.COM * call (*cpus_stop)() ops to reclaim the cpupm resource 36410488SMark.Haywood@Sun.COM * in the P/C/T-state driver 36510488SMark.Haywood@Sun.COM */ 36610488SMark.Haywood@Sun.COM cpupm_free(cp, B_TRUE); 36710488SMark.Haywood@Sun.COM } 36810488SMark.Haywood@Sun.COM 3698906SEric.Saxe@Sun.COM /* 37010488SMark.Haywood@Sun.COM * If A CPU has started and at least one power state is manageable, 37110488SMark.Haywood@Sun.COM * then the CPU is ready for power management. 3728906SEric.Saxe@Sun.COM */ 3738906SEric.Saxe@Sun.COM boolean_t 37410488SMark.Haywood@Sun.COM cpupm_is_ready(cpu_t *cp) 3758906SEric.Saxe@Sun.COM { 3768906SEric.Saxe@Sun.COM #ifndef __xpv 37710488SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state = 37810488SMark.Haywood@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 37910488SMark.Haywood@Sun.COM uint32_t cpupm_caps = mach_state->ms_caps; 38010488SMark.Haywood@Sun.COM 3818906SEric.Saxe@Sun.COM if (cpupm_enabled == CPUPM_NO_STATES) 3828906SEric.Saxe@Sun.COM return (B_FALSE); 38310488SMark.Haywood@Sun.COM 38410488SMark.Haywood@Sun.COM if ((cpupm_caps & CPUPM_T_STATES) || 38510488SMark.Haywood@Sun.COM (cpupm_caps & CPUPM_P_STATES) || 38610488SMark.Haywood@Sun.COM (cpupm_caps & CPUPM_C_STATES)) 38710488SMark.Haywood@Sun.COM 38810488SMark.Haywood@Sun.COM return (B_TRUE); 38910488SMark.Haywood@Sun.COM return (B_FALSE); 3908906SEric.Saxe@Sun.COM #else 39110488SMark.Haywood@Sun.COM _NOTE(ARGUNUSED(cp)); 3928906SEric.Saxe@Sun.COM return (B_FALSE); 3938906SEric.Saxe@Sun.COM #endif 3948906SEric.Saxe@Sun.COM } 3958906SEric.Saxe@Sun.COM 3968906SEric.Saxe@Sun.COM boolean_t 3978906SEric.Saxe@Sun.COM cpupm_is_enabled(uint32_t state) 3988906SEric.Saxe@Sun.COM { 3998906SEric.Saxe@Sun.COM return ((cpupm_enabled & state) == state); 4008906SEric.Saxe@Sun.COM } 4018906SEric.Saxe@Sun.COM 4028906SEric.Saxe@Sun.COM /* 4038906SEric.Saxe@Sun.COM * By default, all states are enabled. 4048906SEric.Saxe@Sun.COM */ 4058906SEric.Saxe@Sun.COM void 4068906SEric.Saxe@Sun.COM cpupm_disable(uint32_t state) 4078906SEric.Saxe@Sun.COM { 4088906SEric.Saxe@Sun.COM 4098906SEric.Saxe@Sun.COM if (state & CPUPM_P_STATES) { 4108906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_pstate_domains); 4118906SEric.Saxe@Sun.COM } 4128906SEric.Saxe@Sun.COM if (state & CPUPM_T_STATES) { 4138906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_tstate_domains); 4148906SEric.Saxe@Sun.COM } 4158906SEric.Saxe@Sun.COM if (state & CPUPM_C_STATES) { 4168906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_cstate_domains); 4178906SEric.Saxe@Sun.COM } 4188906SEric.Saxe@Sun.COM cpupm_enabled &= ~state; 4198906SEric.Saxe@Sun.COM } 4208906SEric.Saxe@Sun.COM 4218906SEric.Saxe@Sun.COM /* 4228906SEric.Saxe@Sun.COM * Allocate power domains for C,P and T States 4238906SEric.Saxe@Sun.COM */ 4248906SEric.Saxe@Sun.COM void 4258906SEric.Saxe@Sun.COM cpupm_alloc_domains(cpu_t *cp, int state) 4268906SEric.Saxe@Sun.COM { 4278906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 4288906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 4298906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 4308906SEric.Saxe@Sun.COM cpupm_state_domains_t **dom_ptr; 4318906SEric.Saxe@Sun.COM cpupm_state_domains_t *dptr; 4328906SEric.Saxe@Sun.COM cpupm_state_domains_t **mach_dom_state_ptr; 4338906SEric.Saxe@Sun.COM uint32_t domain; 4348906SEric.Saxe@Sun.COM uint32_t type; 4358906SEric.Saxe@Sun.COM 4368906SEric.Saxe@Sun.COM switch (state) { 4378906SEric.Saxe@Sun.COM case CPUPM_P_STATES: 4388906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_PSD_CACHED)) { 4398906SEric.Saxe@Sun.COM domain = CPU_ACPI_PSD(handle).sd_domain; 4408906SEric.Saxe@Sun.COM type = CPU_ACPI_PSD(handle).sd_type; 4418906SEric.Saxe@Sun.COM } else { 4428906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 4438906SEric.Saxe@Sun.COM domain = cpuid_get_chipid(cp); 4448906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4458906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4468906SEric.Saxe@Sun.COM } 4478906SEric.Saxe@Sun.COM dom_ptr = &cpupm_pstate_domains; 4488906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_pstate.cma_domain; 4498906SEric.Saxe@Sun.COM break; 4508906SEric.Saxe@Sun.COM case CPUPM_T_STATES: 4518906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_TSD_CACHED)) { 4528906SEric.Saxe@Sun.COM domain = CPU_ACPI_TSD(handle).sd_domain; 4538906SEric.Saxe@Sun.COM type = CPU_ACPI_TSD(handle).sd_type; 4548906SEric.Saxe@Sun.COM } else { 4558906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 4568906SEric.Saxe@Sun.COM domain = cpuid_get_chipid(cp); 4578906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4588906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4598906SEric.Saxe@Sun.COM } 4608906SEric.Saxe@Sun.COM dom_ptr = &cpupm_tstate_domains; 4618906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_tstate.cma_domain; 4628906SEric.Saxe@Sun.COM break; 4638906SEric.Saxe@Sun.COM case CPUPM_C_STATES: 4648906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_CSD_CACHED)) { 4658906SEric.Saxe@Sun.COM domain = CPU_ACPI_CSD(handle).sd_domain; 4668906SEric.Saxe@Sun.COM type = CPU_ACPI_CSD(handle).sd_type; 4678906SEric.Saxe@Sun.COM } else { 4688906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 4698906SEric.Saxe@Sun.COM domain = cpuid_get_coreid(cp); 4708906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4718906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4728906SEric.Saxe@Sun.COM } 4738906SEric.Saxe@Sun.COM dom_ptr = &cpupm_cstate_domains; 4748906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_cstate.cma_domain; 4758906SEric.Saxe@Sun.COM break; 4768906SEric.Saxe@Sun.COM default: 4778906SEric.Saxe@Sun.COM return; 4788906SEric.Saxe@Sun.COM } 4798906SEric.Saxe@Sun.COM 4808906SEric.Saxe@Sun.COM for (dptr = *dom_ptr; dptr != NULL; dptr = dptr->pm_next) { 4818906SEric.Saxe@Sun.COM if (dptr->pm_domain == domain) 4828906SEric.Saxe@Sun.COM break; 4838906SEric.Saxe@Sun.COM } 4848906SEric.Saxe@Sun.COM 4858906SEric.Saxe@Sun.COM /* new domain is created and linked at the head */ 4868906SEric.Saxe@Sun.COM if (dptr == NULL) { 4878906SEric.Saxe@Sun.COM dptr = kmem_zalloc(sizeof (cpupm_state_domains_t), KM_SLEEP); 4888906SEric.Saxe@Sun.COM dptr->pm_domain = domain; 4898906SEric.Saxe@Sun.COM dptr->pm_type = type; 4908906SEric.Saxe@Sun.COM dptr->pm_next = *dom_ptr; 4918906SEric.Saxe@Sun.COM mutex_init(&dptr->pm_lock, NULL, MUTEX_SPIN, 4928906SEric.Saxe@Sun.COM (void *)ipltospl(DISP_LEVEL)); 4938906SEric.Saxe@Sun.COM CPUSET_ZERO(dptr->pm_cpus); 4948906SEric.Saxe@Sun.COM *dom_ptr = dptr; 4958906SEric.Saxe@Sun.COM } 4968906SEric.Saxe@Sun.COM CPUSET_ADD(dptr->pm_cpus, cp->cpu_id); 4978906SEric.Saxe@Sun.COM *mach_dom_state_ptr = dptr; 4988906SEric.Saxe@Sun.COM } 4998906SEric.Saxe@Sun.COM 5008906SEric.Saxe@Sun.COM /* 5018906SEric.Saxe@Sun.COM * Free C, P or T state power domains 5028906SEric.Saxe@Sun.COM */ 5038906SEric.Saxe@Sun.COM void 5048906SEric.Saxe@Sun.COM cpupm_free_domains(cpupm_state_domains_t **dom_ptr) 5058906SEric.Saxe@Sun.COM { 5068906SEric.Saxe@Sun.COM cpupm_state_domains_t *this_domain, *next_domain; 5078906SEric.Saxe@Sun.COM 5088906SEric.Saxe@Sun.COM this_domain = *dom_ptr; 5098906SEric.Saxe@Sun.COM while (this_domain != NULL) { 5108906SEric.Saxe@Sun.COM next_domain = this_domain->pm_next; 5118906SEric.Saxe@Sun.COM mutex_destroy(&this_domain->pm_lock); 5128906SEric.Saxe@Sun.COM kmem_free((void *)this_domain, 5138906SEric.Saxe@Sun.COM sizeof (cpupm_state_domains_t)); 5148906SEric.Saxe@Sun.COM this_domain = next_domain; 5158906SEric.Saxe@Sun.COM } 5168906SEric.Saxe@Sun.COM *dom_ptr = NULL; 5178906SEric.Saxe@Sun.COM } 5188906SEric.Saxe@Sun.COM 51910488SMark.Haywood@Sun.COM /* 52010488SMark.Haywood@Sun.COM * Remove CPU from C, P or T state power domains 52110488SMark.Haywood@Sun.COM */ 52210488SMark.Haywood@Sun.COM void 52310488SMark.Haywood@Sun.COM cpupm_remove_domains(cpu_t *cp, int state, cpupm_state_domains_t **dom_ptr) 52410488SMark.Haywood@Sun.COM { 52510488SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state = 52610488SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 52710488SMark.Haywood@Sun.COM cpupm_state_domains_t *dptr; 52810488SMark.Haywood@Sun.COM uint32_t pm_domain; 52910488SMark.Haywood@Sun.COM 53010488SMark.Haywood@Sun.COM ASSERT(mach_state); 53110488SMark.Haywood@Sun.COM 53210488SMark.Haywood@Sun.COM switch (state) { 53310488SMark.Haywood@Sun.COM case CPUPM_P_STATES: 53410488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_pstate.cma_domain->pm_domain; 53510488SMark.Haywood@Sun.COM break; 53610488SMark.Haywood@Sun.COM case CPUPM_T_STATES: 53710488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_tstate.cma_domain->pm_domain; 53810488SMark.Haywood@Sun.COM break; 53910488SMark.Haywood@Sun.COM case CPUPM_C_STATES: 54010488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_cstate.cma_domain->pm_domain; 54110488SMark.Haywood@Sun.COM break; 54210488SMark.Haywood@Sun.COM default: 54310488SMark.Haywood@Sun.COM return; 54410488SMark.Haywood@Sun.COM } 54510488SMark.Haywood@Sun.COM 54610488SMark.Haywood@Sun.COM /* 54710488SMark.Haywood@Sun.COM * Find the CPU C, P or T state power domain 54810488SMark.Haywood@Sun.COM */ 54910488SMark.Haywood@Sun.COM for (dptr = *dom_ptr; dptr != NULL; dptr = dptr->pm_next) { 55010488SMark.Haywood@Sun.COM if (dptr->pm_domain == pm_domain) 55110488SMark.Haywood@Sun.COM break; 55210488SMark.Haywood@Sun.COM } 55310488SMark.Haywood@Sun.COM 55410488SMark.Haywood@Sun.COM /* 55510488SMark.Haywood@Sun.COM * return if no matched domain found 55610488SMark.Haywood@Sun.COM */ 55710488SMark.Haywood@Sun.COM if (dptr == NULL) 55810488SMark.Haywood@Sun.COM return; 55910488SMark.Haywood@Sun.COM 56010488SMark.Haywood@Sun.COM /* 56110488SMark.Haywood@Sun.COM * We found one matched power domain, remove CPU from its cpuset. 562*10961Saubrey.li@intel.com * pm_lock(spin lock) here to avoid the race conditions between 56310488SMark.Haywood@Sun.COM * event change notification and cpu remove. 56410488SMark.Haywood@Sun.COM */ 56510488SMark.Haywood@Sun.COM mutex_enter(&dptr->pm_lock); 56610488SMark.Haywood@Sun.COM if (CPU_IN_SET(dptr->pm_cpus, cp->cpu_id)) 56710488SMark.Haywood@Sun.COM CPUSET_DEL(dptr->pm_cpus, cp->cpu_id); 56810488SMark.Haywood@Sun.COM mutex_exit(&dptr->pm_lock); 56910488SMark.Haywood@Sun.COM } 57010488SMark.Haywood@Sun.COM 5718906SEric.Saxe@Sun.COM void 5728906SEric.Saxe@Sun.COM cpupm_alloc_ms_cstate(cpu_t *cp) 5738906SEric.Saxe@Sun.COM { 5748906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 5758906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate; 5768906SEric.Saxe@Sun.COM 5778906SEric.Saxe@Sun.COM mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 5788906SEric.Saxe@Sun.COM ms_cstate = &mach_state->ms_cstate; 5798906SEric.Saxe@Sun.COM ASSERT(ms_cstate->cma_state.cstate == NULL); 5808906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = kmem_zalloc(sizeof (cma_c_state_t), 5818906SEric.Saxe@Sun.COM KM_SLEEP); 5828906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate->cs_next_cstate = CPU_ACPI_C1; 5838906SEric.Saxe@Sun.COM } 5848906SEric.Saxe@Sun.COM 5858906SEric.Saxe@Sun.COM void 5868906SEric.Saxe@Sun.COM cpupm_free_ms_cstate(cpu_t *cp) 5878906SEric.Saxe@Sun.COM { 5888906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 5898906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 5908906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate = &mach_state->ms_cstate; 5918906SEric.Saxe@Sun.COM 5928906SEric.Saxe@Sun.COM if (ms_cstate->cma_state.cstate != NULL) { 5938906SEric.Saxe@Sun.COM kmem_free(ms_cstate->cma_state.cstate, sizeof (cma_c_state_t)); 5948906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = NULL; 5958906SEric.Saxe@Sun.COM } 5968906SEric.Saxe@Sun.COM } 5978906SEric.Saxe@Sun.COM 5988906SEric.Saxe@Sun.COM void 5998906SEric.Saxe@Sun.COM cpupm_state_change(cpu_t *cp, int level, int state) 6008906SEric.Saxe@Sun.COM { 6018906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6028906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 6038906SEric.Saxe@Sun.COM cpupm_state_ops_t *state_ops; 6048906SEric.Saxe@Sun.COM cpupm_state_domains_t *state_domain; 6058906SEric.Saxe@Sun.COM cpuset_t set; 6068906SEric.Saxe@Sun.COM 6078906SEric.Saxe@Sun.COM DTRACE_PROBE2(cpupm__state__change, cpu_t *, cp, int, level); 6088906SEric.Saxe@Sun.COM 6098906SEric.Saxe@Sun.COM if (mach_state == NULL) { 6108906SEric.Saxe@Sun.COM return; 6118906SEric.Saxe@Sun.COM } 6128906SEric.Saxe@Sun.COM 6138906SEric.Saxe@Sun.COM switch (state) { 6148906SEric.Saxe@Sun.COM case CPUPM_P_STATES: 6158906SEric.Saxe@Sun.COM state_ops = mach_state->ms_pstate.cma_ops; 6168906SEric.Saxe@Sun.COM state_domain = mach_state->ms_pstate.cma_domain; 6178906SEric.Saxe@Sun.COM break; 6188906SEric.Saxe@Sun.COM case CPUPM_T_STATES: 6198906SEric.Saxe@Sun.COM state_ops = mach_state->ms_tstate.cma_ops; 6208906SEric.Saxe@Sun.COM state_domain = mach_state->ms_tstate.cma_domain; 6218906SEric.Saxe@Sun.COM break; 6228906SEric.Saxe@Sun.COM default: 6238906SEric.Saxe@Sun.COM break; 6248906SEric.Saxe@Sun.COM } 6258906SEric.Saxe@Sun.COM 6268906SEric.Saxe@Sun.COM switch (state_domain->pm_type) { 6278906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ANY: 6288906SEric.Saxe@Sun.COM /* 6298906SEric.Saxe@Sun.COM * A request on any CPU in the domain transitions the domain 6308906SEric.Saxe@Sun.COM */ 6318906SEric.Saxe@Sun.COM CPUSET_ONLY(set, cp->cpu_id); 6328906SEric.Saxe@Sun.COM state_ops->cpus_change(set, level); 6338906SEric.Saxe@Sun.COM break; 6348906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ALL: 6358906SEric.Saxe@Sun.COM /* 6368906SEric.Saxe@Sun.COM * All CPUs in the domain must request the transition 6378906SEric.Saxe@Sun.COM */ 6388906SEric.Saxe@Sun.COM case CPU_ACPI_HW_ALL: 6398906SEric.Saxe@Sun.COM /* 6408906SEric.Saxe@Sun.COM * P/T-state transitions are coordinated by the hardware 6418906SEric.Saxe@Sun.COM * For now, request the transition on all CPUs in the domain, 6428906SEric.Saxe@Sun.COM * but looking ahead we can probably be smarter about this. 6438906SEric.Saxe@Sun.COM */ 6448906SEric.Saxe@Sun.COM mutex_enter(&state_domain->pm_lock); 6458906SEric.Saxe@Sun.COM state_ops->cpus_change(state_domain->pm_cpus, level); 6468906SEric.Saxe@Sun.COM mutex_exit(&state_domain->pm_lock); 6478906SEric.Saxe@Sun.COM break; 6488906SEric.Saxe@Sun.COM default: 64910075SMark.Haywood@Sun.COM cmn_err(CE_NOTE, "Unknown domain coordination type: %d", 6508906SEric.Saxe@Sun.COM state_domain->pm_type); 6518906SEric.Saxe@Sun.COM } 6528906SEric.Saxe@Sun.COM } 6538906SEric.Saxe@Sun.COM 6548906SEric.Saxe@Sun.COM /* 6558906SEric.Saxe@Sun.COM * CPU PM interfaces exposed to the CPU power manager 6568906SEric.Saxe@Sun.COM */ 6578906SEric.Saxe@Sun.COM /*ARGSUSED*/ 6588906SEric.Saxe@Sun.COM id_t 6598906SEric.Saxe@Sun.COM cpupm_plat_domain_id(cpu_t *cp, cpupm_dtype_t type) 6608906SEric.Saxe@Sun.COM { 6618906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6628906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 6638906SEric.Saxe@Sun.COM 6648906SEric.Saxe@Sun.COM if ((mach_state == NULL) || (!cpupm_is_enabled(CPUPM_P_STATES) && 6658906SEric.Saxe@Sun.COM !cpupm_is_enabled(CPUPM_C_STATES))) { 6668906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN); 6678906SEric.Saxe@Sun.COM } 6688906SEric.Saxe@Sun.COM if (type == CPUPM_DTYPE_ACTIVE) { 6698906SEric.Saxe@Sun.COM /* 6708906SEric.Saxe@Sun.COM * Return P-State domain for the specified CPU 6718906SEric.Saxe@Sun.COM */ 6728906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_domain) { 6738906SEric.Saxe@Sun.COM return (mach_state->ms_pstate.cma_domain->pm_domain); 6748906SEric.Saxe@Sun.COM } 6758906SEric.Saxe@Sun.COM } else if (type == CPUPM_DTYPE_IDLE) { 6768906SEric.Saxe@Sun.COM /* 6778906SEric.Saxe@Sun.COM * Return C-State domain for the specified CPU 6788906SEric.Saxe@Sun.COM */ 6798906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_domain) { 6808906SEric.Saxe@Sun.COM return (mach_state->ms_cstate.cma_domain->pm_domain); 6818906SEric.Saxe@Sun.COM } 6828906SEric.Saxe@Sun.COM } 6838906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN); 6848906SEric.Saxe@Sun.COM } 6858906SEric.Saxe@Sun.COM 6868906SEric.Saxe@Sun.COM /*ARGSUSED*/ 6878906SEric.Saxe@Sun.COM uint_t 6888906SEric.Saxe@Sun.COM cpupm_plat_state_enumerate(cpu_t *cp, cpupm_dtype_t type, 6898906SEric.Saxe@Sun.COM cpupm_state_t *states) 6908906SEric.Saxe@Sun.COM { 6918906SEric.Saxe@Sun.COM int *speeds; 6928906SEric.Saxe@Sun.COM uint_t nspeeds, i; 6938906SEric.Saxe@Sun.COM 6948906SEric.Saxe@Sun.COM /* 6958906SEric.Saxe@Sun.COM * Idle domain support unimplemented 6968906SEric.Saxe@Sun.COM */ 6978906SEric.Saxe@Sun.COM if (type != CPUPM_DTYPE_ACTIVE) { 6988906SEric.Saxe@Sun.COM return (0); 6998906SEric.Saxe@Sun.COM } 7008906SEric.Saxe@Sun.COM nspeeds = cpupm_get_speeds(cp, &speeds); 7018906SEric.Saxe@Sun.COM 7028906SEric.Saxe@Sun.COM /* 7038906SEric.Saxe@Sun.COM * If the caller passes NULL for states, just return the 7048906SEric.Saxe@Sun.COM * number of states. 7058906SEric.Saxe@Sun.COM */ 7068906SEric.Saxe@Sun.COM if (states != NULL) { 7078906SEric.Saxe@Sun.COM for (i = 0; i < nspeeds; i++) { 7088906SEric.Saxe@Sun.COM states[i].cps_speed = speeds[i]; 7098906SEric.Saxe@Sun.COM states[i].cps_handle = (cpupm_handle_t)i; 7108906SEric.Saxe@Sun.COM } 7118906SEric.Saxe@Sun.COM } 7128906SEric.Saxe@Sun.COM cpupm_free_speeds(speeds, nspeeds); 7138906SEric.Saxe@Sun.COM return (nspeeds); 7148906SEric.Saxe@Sun.COM } 7158906SEric.Saxe@Sun.COM 7168906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7178906SEric.Saxe@Sun.COM int 7188906SEric.Saxe@Sun.COM cpupm_plat_change_state(cpu_t *cp, cpupm_state_t *state) 7198906SEric.Saxe@Sun.COM { 72010488SMark.Haywood@Sun.COM if (!cpupm_is_ready(cp)) 7218906SEric.Saxe@Sun.COM return (-1); 7228906SEric.Saxe@Sun.COM 7238906SEric.Saxe@Sun.COM cpupm_state_change(cp, (int)state->cps_handle, CPUPM_P_STATES); 7248906SEric.Saxe@Sun.COM 7258906SEric.Saxe@Sun.COM return (0); 7268906SEric.Saxe@Sun.COM } 7278906SEric.Saxe@Sun.COM 7288906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7298906SEric.Saxe@Sun.COM /* 7308906SEric.Saxe@Sun.COM * Note: It is the responsibility of the users of 7318906SEric.Saxe@Sun.COM * cpupm_get_speeds() to free the memory allocated 7328906SEric.Saxe@Sun.COM * for speeds using cpupm_free_speeds() 7338906SEric.Saxe@Sun.COM */ 7348906SEric.Saxe@Sun.COM uint_t 7358906SEric.Saxe@Sun.COM cpupm_get_speeds(cpu_t *cp, int **speeds) 7368906SEric.Saxe@Sun.COM { 7378906SEric.Saxe@Sun.COM #ifndef __xpv 7388906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 7398906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 7408906SEric.Saxe@Sun.COM return (cpu_acpi_get_speeds(mach_state->ms_acpi_handle, speeds)); 7418906SEric.Saxe@Sun.COM #else 7428906SEric.Saxe@Sun.COM return (0); 7438906SEric.Saxe@Sun.COM #endif 7448906SEric.Saxe@Sun.COM } 7458906SEric.Saxe@Sun.COM 7468906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7478906SEric.Saxe@Sun.COM void 7488906SEric.Saxe@Sun.COM cpupm_free_speeds(int *speeds, uint_t nspeeds) 7498906SEric.Saxe@Sun.COM { 7508906SEric.Saxe@Sun.COM #ifndef __xpv 7518906SEric.Saxe@Sun.COM cpu_acpi_free_speeds(speeds, nspeeds); 7528906SEric.Saxe@Sun.COM #endif 7538906SEric.Saxe@Sun.COM } 7548906SEric.Saxe@Sun.COM 7558906SEric.Saxe@Sun.COM /* 7568906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 7578906SEric.Saxe@Sun.COM */ 7588906SEric.Saxe@Sun.COM boolean_t 75910488SMark.Haywood@Sun.COM cpupm_power_ready(cpu_t *cp) 7608906SEric.Saxe@Sun.COM { 76110488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_P_STATES) && cpupm_is_ready(cp)); 7628906SEric.Saxe@Sun.COM } 7638906SEric.Saxe@Sun.COM 7648906SEric.Saxe@Sun.COM /* 7658906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 7668906SEric.Saxe@Sun.COM */ 7678906SEric.Saxe@Sun.COM boolean_t 76810488SMark.Haywood@Sun.COM cpupm_throttle_ready(cpu_t *cp) 7698906SEric.Saxe@Sun.COM { 77010488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_T_STATES) && cpupm_is_ready(cp)); 7718906SEric.Saxe@Sun.COM } 7728906SEric.Saxe@Sun.COM 7738906SEric.Saxe@Sun.COM /* 7748906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 7758906SEric.Saxe@Sun.COM */ 7768906SEric.Saxe@Sun.COM boolean_t 77710488SMark.Haywood@Sun.COM cpupm_cstate_ready(cpu_t *cp) 7788906SEric.Saxe@Sun.COM { 77910488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_C_STATES) && cpupm_is_ready(cp)); 7808906SEric.Saxe@Sun.COM } 7818906SEric.Saxe@Sun.COM 7828906SEric.Saxe@Sun.COM void 7838906SEric.Saxe@Sun.COM cpupm_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx) 7848906SEric.Saxe@Sun.COM { 7858906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 7868906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 7878906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 7888906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 7898906SEric.Saxe@Sun.COM 7908906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 7918906SEric.Saxe@Sun.COM for (entry = mach_state->ms_handlers; entry != NULL; 7928906SEric.Saxe@Sun.COM entry = entry->nq_next) { 7938906SEric.Saxe@Sun.COM entry->nq_handler(obj, val, entry->nq_ctx); 7948906SEric.Saxe@Sun.COM } 7958906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 7968906SEric.Saxe@Sun.COM } 7978906SEric.Saxe@Sun.COM 7988906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7998906SEric.Saxe@Sun.COM void 8008906SEric.Saxe@Sun.COM cpupm_add_notify_handler(cpu_t *cp, CPUPM_NOTIFY_HANDLER handler, void *ctx) 8018906SEric.Saxe@Sun.COM { 8028906SEric.Saxe@Sun.COM #ifndef __xpv 8038906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 8048906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 8058906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 8068906SEric.Saxe@Sun.COM 8078906SEric.Saxe@Sun.COM entry = kmem_zalloc(sizeof (cpupm_notification_t), KM_SLEEP); 8088906SEric.Saxe@Sun.COM entry->nq_handler = handler; 8098906SEric.Saxe@Sun.COM entry->nq_ctx = ctx; 8108906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 8118906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) { 8128906SEric.Saxe@Sun.COM entry->nq_next = NULL; 8138906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry; 8148906SEric.Saxe@Sun.COM cpu_acpi_install_notify_handler(mach_state->ms_acpi_handle, 8158906SEric.Saxe@Sun.COM cpupm_notify_handler, cp); 8168906SEric.Saxe@Sun.COM 8178906SEric.Saxe@Sun.COM } else { 8188906SEric.Saxe@Sun.COM entry->nq_next = mach_state->ms_handlers; 8198906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry; 8208906SEric.Saxe@Sun.COM } 8218906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 8228906SEric.Saxe@Sun.COM #endif 8238906SEric.Saxe@Sun.COM } 8248906SEric.Saxe@Sun.COM 8258906SEric.Saxe@Sun.COM /*ARGSUSED*/ 8268906SEric.Saxe@Sun.COM static void 8278906SEric.Saxe@Sun.COM cpupm_free_notify_handlers(cpu_t *cp) 8288906SEric.Saxe@Sun.COM { 8298906SEric.Saxe@Sun.COM #ifndef __xpv 8308906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 8318906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 8328906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 8338906SEric.Saxe@Sun.COM cpupm_notification_t *next; 8348906SEric.Saxe@Sun.COM 8358906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 8368906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) { 8378906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 8388906SEric.Saxe@Sun.COM return; 8398906SEric.Saxe@Sun.COM } 8408906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle != NULL) { 8418906SEric.Saxe@Sun.COM cpu_acpi_remove_notify_handler(mach_state->ms_acpi_handle, 8428906SEric.Saxe@Sun.COM cpupm_notify_handler); 8438906SEric.Saxe@Sun.COM } 8448906SEric.Saxe@Sun.COM entry = mach_state->ms_handlers; 8458906SEric.Saxe@Sun.COM while (entry != NULL) { 8468906SEric.Saxe@Sun.COM next = entry->nq_next; 8478906SEric.Saxe@Sun.COM kmem_free(entry, sizeof (cpupm_notification_t)); 8488906SEric.Saxe@Sun.COM entry = next; 8498906SEric.Saxe@Sun.COM } 8508906SEric.Saxe@Sun.COM mach_state->ms_handlers = NULL; 8518906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 8528906SEric.Saxe@Sun.COM #endif 8538906SEric.Saxe@Sun.COM } 8548906SEric.Saxe@Sun.COM 8558906SEric.Saxe@Sun.COM /* 8568906SEric.Saxe@Sun.COM * Get the current max speed from the ACPI _PPC object 8578906SEric.Saxe@Sun.COM */ 8588906SEric.Saxe@Sun.COM /*ARGSUSED*/ 8598906SEric.Saxe@Sun.COM int 8608906SEric.Saxe@Sun.COM cpupm_get_top_speed(cpu_t *cp) 8618906SEric.Saxe@Sun.COM { 8628906SEric.Saxe@Sun.COM #ifndef __xpv 8638906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 8648906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 8658906SEric.Saxe@Sun.COM int plat_level; 8668906SEric.Saxe@Sun.COM uint_t nspeeds; 8678906SEric.Saxe@Sun.COM int max_level; 8688906SEric.Saxe@Sun.COM 8698906SEric.Saxe@Sun.COM mach_state = 8708906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 8718906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 8728906SEric.Saxe@Sun.COM 8738906SEric.Saxe@Sun.COM cpu_acpi_cache_ppc(handle); 8748906SEric.Saxe@Sun.COM plat_level = CPU_ACPI_PPC(handle); 8758906SEric.Saxe@Sun.COM 8768906SEric.Saxe@Sun.COM nspeeds = CPU_ACPI_PSTATES_COUNT(handle); 8778906SEric.Saxe@Sun.COM 8788906SEric.Saxe@Sun.COM max_level = nspeeds - 1; 8798906SEric.Saxe@Sun.COM if ((plat_level < 0) || (plat_level > max_level)) { 8808906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpupm_get_top_speed: CPU %d: " 8818906SEric.Saxe@Sun.COM "_PPC out of range %d", cp->cpu_id, plat_level); 8828906SEric.Saxe@Sun.COM plat_level = 0; 8838906SEric.Saxe@Sun.COM } 8848906SEric.Saxe@Sun.COM 8858906SEric.Saxe@Sun.COM return (plat_level); 8868906SEric.Saxe@Sun.COM #else 8878906SEric.Saxe@Sun.COM return (0); 8888906SEric.Saxe@Sun.COM #endif 8898906SEric.Saxe@Sun.COM } 8908906SEric.Saxe@Sun.COM 8918906SEric.Saxe@Sun.COM /* 8928906SEric.Saxe@Sun.COM * This notification handler is called whenever the ACPI _PPC 8938906SEric.Saxe@Sun.COM * object changes. The _PPC is a sort of governor on power levels. 8948906SEric.Saxe@Sun.COM * It sets an upper threshold on which, _PSS defined, power levels 8958906SEric.Saxe@Sun.COM * are usuable. The _PPC value is dynamic and may change as properties 8968906SEric.Saxe@Sun.COM * (i.e., thermal or AC source) of the system change. 8978906SEric.Saxe@Sun.COM */ 8988906SEric.Saxe@Sun.COM 8998906SEric.Saxe@Sun.COM static void 9008906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(void *ctx) 9018906SEric.Saxe@Sun.COM { 9028906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 9038906SEric.Saxe@Sun.COM int top_speed; 9048906SEric.Saxe@Sun.COM 9058906SEric.Saxe@Sun.COM top_speed = cpupm_get_top_speed(cp); 9068906SEric.Saxe@Sun.COM cpupm_redefine_max_activepwr_state(cp, top_speed); 9078906SEric.Saxe@Sun.COM } 9088906SEric.Saxe@Sun.COM 9098906SEric.Saxe@Sun.COM /* ARGSUSED */ 9108906SEric.Saxe@Sun.COM static void 9118906SEric.Saxe@Sun.COM cpupm_event_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx) 9128906SEric.Saxe@Sun.COM { 9138906SEric.Saxe@Sun.COM #ifndef __xpv 9149040SMark.Haywood@Sun.COM 9159040SMark.Haywood@Sun.COM cpu_t *cp = ctx; 9169040SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state = 9179040SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 9189040SMark.Haywood@Sun.COM 9199040SMark.Haywood@Sun.COM if (mach_state == NULL) 9209040SMark.Haywood@Sun.COM return; 9219040SMark.Haywood@Sun.COM 9228906SEric.Saxe@Sun.COM /* 9238906SEric.Saxe@Sun.COM * Currently, we handle _TPC,_CST and _PPC change notifications. 9248906SEric.Saxe@Sun.COM */ 9259040SMark.Haywood@Sun.COM if (val == CPUPM_TPC_CHANGE_NOTIFICATION && 9269040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_T_STATES) { 9278906SEric.Saxe@Sun.COM cpupm_throttle_manage_notification(ctx); 9289040SMark.Haywood@Sun.COM } else if (val == CPUPM_CST_CHANGE_NOTIFICATION && 9299040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_C_STATES) { 9308906SEric.Saxe@Sun.COM cpuidle_manage_cstates(ctx); 9319040SMark.Haywood@Sun.COM } else if (val == CPUPM_PPC_CHANGE_NOTIFICATION && 9329040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_P_STATES) { 9338906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(ctx); 9348906SEric.Saxe@Sun.COM } 9358906SEric.Saxe@Sun.COM #endif 9368906SEric.Saxe@Sun.COM } 9378906SEric.Saxe@Sun.COM 9388906SEric.Saxe@Sun.COM /* 9398906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU exits idle. 9408906SEric.Saxe@Sun.COM */ 9418906SEric.Saxe@Sun.COM void 9428906SEric.Saxe@Sun.COM cpupm_wakeup_cstate_data(cma_c_state_t *cs_data, hrtime_t end) 9438906SEric.Saxe@Sun.COM { 9448906SEric.Saxe@Sun.COM cs_data->cs_idle_exit = end; 9458906SEric.Saxe@Sun.COM } 9468906SEric.Saxe@Sun.COM 9478906SEric.Saxe@Sun.COM /* 9488906SEric.Saxe@Sun.COM * Determine next cstate based on cpupm data. 9498906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU goes idle. 9508906SEric.Saxe@Sun.COM * Do as much as possible in the idle state bookkeeping function because the 9518906SEric.Saxe@Sun.COM * performance impact while idle is minimal compared to in the wakeup function 9528906SEric.Saxe@Sun.COM * when there is real work to do. 9538906SEric.Saxe@Sun.COM */ 9548906SEric.Saxe@Sun.COM uint32_t 9558983SBill.Holler@Sun.COM cpupm_next_cstate(cma_c_state_t *cs_data, cpu_acpi_cstate_t *cstates, 9568983SBill.Holler@Sun.COM uint32_t cs_count, hrtime_t start) 9578906SEric.Saxe@Sun.COM { 9588983SBill.Holler@Sun.COM hrtime_t duration; 9598983SBill.Holler@Sun.COM hrtime_t ave_interval; 9608983SBill.Holler@Sun.COM hrtime_t ave_idle_time; 96110353Saubrey.li@intel.com uint32_t i, smpl_cnt; 9628906SEric.Saxe@Sun.COM 9638906SEric.Saxe@Sun.COM duration = cs_data->cs_idle_exit - cs_data->cs_idle_enter; 9648906SEric.Saxe@Sun.COM scalehrtime(&duration); 9658906SEric.Saxe@Sun.COM cs_data->cs_idle += duration; 9668906SEric.Saxe@Sun.COM cs_data->cs_idle_enter = start; 9678906SEric.Saxe@Sun.COM 96810353Saubrey.li@intel.com smpl_cnt = ++cs_data->cs_cnt; 96910353Saubrey.li@intel.com cs_data->cs_smpl_len = start - cs_data->cs_smpl_start; 97010353Saubrey.li@intel.com scalehrtime(&cs_data->cs_smpl_len); 97110353Saubrey.li@intel.com if (cs_data->cs_smpl_len > cpupm_cs_sample_interval) { 9728906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle = cs_data->cs_idle; 9738906SEric.Saxe@Sun.COM cs_data->cs_idle = 0; 9748906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle_pct = ((100 * cs_data->cs_smpl_idle) / 9758906SEric.Saxe@Sun.COM cs_data->cs_smpl_len); 9768906SEric.Saxe@Sun.COM 9778906SEric.Saxe@Sun.COM cs_data->cs_smpl_start = start; 9788906SEric.Saxe@Sun.COM cs_data->cs_cnt = 0; 9798906SEric.Saxe@Sun.COM 9808906SEric.Saxe@Sun.COM /* 9818906SEric.Saxe@Sun.COM * Strand level C-state policy 9828983SBill.Holler@Sun.COM * The cpu_acpi_cstate_t *cstates array is not required to 9838983SBill.Holler@Sun.COM * have an entry for both CPU_ACPI_C2 and CPU_ACPI_C3. 9848983SBill.Holler@Sun.COM * There are cs_count entries in the cstates array. 9858983SBill.Holler@Sun.COM * cs_data->cs_next_cstate contains the index of the next 9868983SBill.Holler@Sun.COM * C-state this CPU should enter. 9878906SEric.Saxe@Sun.COM */ 9888983SBill.Holler@Sun.COM ASSERT(cstates[0].cs_type == CPU_ACPI_C1); 9898906SEric.Saxe@Sun.COM 9908906SEric.Saxe@Sun.COM /* 9918906SEric.Saxe@Sun.COM * Will CPU be idle long enough to save power? 9928906SEric.Saxe@Sun.COM */ 99310353Saubrey.li@intel.com ave_idle_time = (cs_data->cs_smpl_idle / smpl_cnt) / 1000; 9948983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 9958983SBill.Holler@Sun.COM if (ave_idle_time < (cstates[i].cs_latency * 9968983SBill.Holler@Sun.COM cpupm_cs_idle_save_tunable)) { 9978983SBill.Holler@Sun.COM cs_count = i; 9988983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *, 9998983SBill.Holler@Sun.COM CPU, int, i); 10008983SBill.Holler@Sun.COM } 10018906SEric.Saxe@Sun.COM } 10028906SEric.Saxe@Sun.COM 10038906SEric.Saxe@Sun.COM /* 10048906SEric.Saxe@Sun.COM * Wakeup often (even when non-idle time is very short)? 10058906SEric.Saxe@Sun.COM * Some producer/consumer type loads fall into this category. 10068906SEric.Saxe@Sun.COM */ 100710353Saubrey.li@intel.com ave_interval = (cs_data->cs_smpl_len / smpl_cnt) / 1000; 10088983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 10098983SBill.Holler@Sun.COM if (ave_interval <= (cstates[i].cs_latency * 10108983SBill.Holler@Sun.COM cpupm_cs_idle_cost_tunable)) { 10118983SBill.Holler@Sun.COM cs_count = i; 10128983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *, 10138983SBill.Holler@Sun.COM CPU, int, (CPU_MAX_CSTATES + i)); 10148983SBill.Holler@Sun.COM } 10158906SEric.Saxe@Sun.COM } 10168906SEric.Saxe@Sun.COM 10178906SEric.Saxe@Sun.COM /* 10188906SEric.Saxe@Sun.COM * Idle percent 10198906SEric.Saxe@Sun.COM */ 10208983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 10218983SBill.Holler@Sun.COM switch (cstates[i].cs_type) { 10228983SBill.Holler@Sun.COM case CPU_ACPI_C2: 10238983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct < 10248983SBill.Holler@Sun.COM cpupm_C2_idle_pct_tunable) { 10258983SBill.Holler@Sun.COM cs_count = i; 10268983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, 10278983SBill.Holler@Sun.COM cpu_t *, CPU, int, 10288983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i)); 10298983SBill.Holler@Sun.COM } 10308983SBill.Holler@Sun.COM break; 10318983SBill.Holler@Sun.COM 10328983SBill.Holler@Sun.COM case CPU_ACPI_C3: 10338983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct < 10348983SBill.Holler@Sun.COM cpupm_C3_idle_pct_tunable) { 10358983SBill.Holler@Sun.COM cs_count = i; 10368983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, 10378983SBill.Holler@Sun.COM cpu_t *, CPU, int, 10388983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i)); 10398983SBill.Holler@Sun.COM } 10408983SBill.Holler@Sun.COM break; 10418983SBill.Holler@Sun.COM } 10428906SEric.Saxe@Sun.COM } 10438983SBill.Holler@Sun.COM 10448983SBill.Holler@Sun.COM cs_data->cs_next_cstate = cs_count - 1; 10458906SEric.Saxe@Sun.COM } 10468906SEric.Saxe@Sun.COM 10478906SEric.Saxe@Sun.COM return (cs_data->cs_next_cstate); 10488906SEric.Saxe@Sun.COM } 1049