18906SEric.Saxe@Sun.COM /* 28906SEric.Saxe@Sun.COM * CDDL HEADER START 38906SEric.Saxe@Sun.COM * 48906SEric.Saxe@Sun.COM * The contents of this file are subject to the terms of the 58906SEric.Saxe@Sun.COM * Common Development and Distribution License (the "License"). 68906SEric.Saxe@Sun.COM * You may not use this file except in compliance with the License. 78906SEric.Saxe@Sun.COM * 88906SEric.Saxe@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98906SEric.Saxe@Sun.COM * or http://www.opensolaris.org/os/licensing. 108906SEric.Saxe@Sun.COM * See the License for the specific language governing permissions 118906SEric.Saxe@Sun.COM * and limitations under the License. 128906SEric.Saxe@Sun.COM * 138906SEric.Saxe@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148906SEric.Saxe@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158906SEric.Saxe@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168906SEric.Saxe@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178906SEric.Saxe@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188906SEric.Saxe@Sun.COM * 198906SEric.Saxe@Sun.COM * CDDL HEADER END 208906SEric.Saxe@Sun.COM */ 218906SEric.Saxe@Sun.COM /* 228906SEric.Saxe@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238906SEric.Saxe@Sun.COM * Use is subject to license terms. 248906SEric.Saxe@Sun.COM */ 258906SEric.Saxe@Sun.COM 268906SEric.Saxe@Sun.COM #include <sys/cpu_pm.h> 278906SEric.Saxe@Sun.COM #include <sys/x86_archext.h> 288906SEric.Saxe@Sun.COM #include <sys/sdt.h> 298906SEric.Saxe@Sun.COM #include <sys/spl.h> 308906SEric.Saxe@Sun.COM #include <sys/machsystm.h> 318906SEric.Saxe@Sun.COM #include <sys/hpet.h> 328906SEric.Saxe@Sun.COM #include <sys/cpupm.h> 338906SEric.Saxe@Sun.COM #include <sys/cpu_idle.h> 348906SEric.Saxe@Sun.COM #include <sys/cpu_acpi.h> 358906SEric.Saxe@Sun.COM #include <sys/cpupm_throttle.h> 368983SBill.Holler@Sun.COM #include <sys/dtrace.h> 378906SEric.Saxe@Sun.COM 388906SEric.Saxe@Sun.COM /* 398906SEric.Saxe@Sun.COM * This callback is used to build the PPM CPU domains once 408906SEric.Saxe@Sun.COM * all the CPU devices have been started. The callback is 418906SEric.Saxe@Sun.COM * initialized by the PPM driver to point to a routine that 428906SEric.Saxe@Sun.COM * will build the domains. 438906SEric.Saxe@Sun.COM */ 448906SEric.Saxe@Sun.COM void (*cpupm_rebuild_cpu_domains)(void); 458906SEric.Saxe@Sun.COM 468906SEric.Saxe@Sun.COM /* 478906SEric.Saxe@Sun.COM * This callback is used to reset the topspeed for all the 488906SEric.Saxe@Sun.COM * CPU devices. The callback is initialized by the PPM driver to 498906SEric.Saxe@Sun.COM * point to a routine that will reinitialize all the CPU devices 508906SEric.Saxe@Sun.COM * once all the CPU devices have been started and the CPU domains 518906SEric.Saxe@Sun.COM * built. 528906SEric.Saxe@Sun.COM */ 538906SEric.Saxe@Sun.COM void (*cpupm_init_topspeed)(void); 548906SEric.Saxe@Sun.COM 558906SEric.Saxe@Sun.COM /* 568906SEric.Saxe@Sun.COM * This callback is used to redefine the topspeed for a CPU device. 578906SEric.Saxe@Sun.COM * Since all CPUs in a domain should have identical properties, this 588906SEric.Saxe@Sun.COM * callback is initialized by the PPM driver to point to a routine 598906SEric.Saxe@Sun.COM * that will redefine the topspeed for all devices in a CPU domain. 608906SEric.Saxe@Sun.COM * This callback is exercised whenever an ACPI _PPC change notification 618906SEric.Saxe@Sun.COM * is received by the CPU driver. 628906SEric.Saxe@Sun.COM */ 638906SEric.Saxe@Sun.COM void (*cpupm_redefine_topspeed)(void *); 648906SEric.Saxe@Sun.COM 658906SEric.Saxe@Sun.COM /* 668906SEric.Saxe@Sun.COM * This callback is used by the PPM driver to call into the CPU driver 678906SEric.Saxe@Sun.COM * to find a CPU's current topspeed (i.e., it's current ACPI _PPC value). 688906SEric.Saxe@Sun.COM */ 698906SEric.Saxe@Sun.COM void (*cpupm_set_topspeed_callb)(void *, int); 708906SEric.Saxe@Sun.COM 718906SEric.Saxe@Sun.COM /* 728906SEric.Saxe@Sun.COM * This callback is used by the PPM driver to call into the CPU driver 738906SEric.Saxe@Sun.COM * to set a new topspeed for a CPU. 748906SEric.Saxe@Sun.COM */ 758906SEric.Saxe@Sun.COM int (*cpupm_get_topspeed_callb)(void *); 768906SEric.Saxe@Sun.COM 778906SEric.Saxe@Sun.COM static void cpupm_event_notify_handler(ACPI_HANDLE, UINT32, void *); 788906SEric.Saxe@Sun.COM static void cpupm_free_notify_handlers(cpu_t *); 798906SEric.Saxe@Sun.COM 808906SEric.Saxe@Sun.COM /* 818906SEric.Saxe@Sun.COM * Until proven otherwise, all power states are manageable. 828906SEric.Saxe@Sun.COM */ 838906SEric.Saxe@Sun.COM static uint32_t cpupm_enabled = CPUPM_ALL_STATES; 848906SEric.Saxe@Sun.COM 858906SEric.Saxe@Sun.COM /* 868906SEric.Saxe@Sun.COM * Until all CPUs have started, we do not allow 878906SEric.Saxe@Sun.COM * power management. 888906SEric.Saxe@Sun.COM */ 898906SEric.Saxe@Sun.COM static boolean_t cpupm_ready = B_FALSE; 908906SEric.Saxe@Sun.COM 918906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_pstate_domains = NULL; 928906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_tstate_domains = NULL; 938906SEric.Saxe@Sun.COM cpupm_state_domains_t *cpupm_cstate_domains = NULL; 948906SEric.Saxe@Sun.COM 958906SEric.Saxe@Sun.COM /* 968906SEric.Saxe@Sun.COM * c-state tunables 978906SEric.Saxe@Sun.COM * 988906SEric.Saxe@Sun.COM * cpupm_cs_idle_cost_tunable is the ratio of time CPU spends executing + idle 998906SEric.Saxe@Sun.COM * divided by time spent in the idle state transitions. 1008906SEric.Saxe@Sun.COM * A value of 10 means the CPU will not spend more than 1/10 of its time 1018906SEric.Saxe@Sun.COM * in idle latency. The worst case performance will be 90% of non Deep C-state 1028906SEric.Saxe@Sun.COM * kernel. 1038906SEric.Saxe@Sun.COM * 1048906SEric.Saxe@Sun.COM * cpupm_cs_idle_save_tunable is how long we must stay in a deeper C-state 1058906SEric.Saxe@Sun.COM * before it is worth going there. Expressed as a multiple of latency. 1068906SEric.Saxe@Sun.COM */ 1078906SEric.Saxe@Sun.COM uint32_t cpupm_cs_sample_tunable = 5; /* samples in decision period */ 1088906SEric.Saxe@Sun.COM uint32_t cpupm_cs_idle_cost_tunable = 10; /* work time / latency cost */ 1098906SEric.Saxe@Sun.COM uint32_t cpupm_cs_idle_save_tunable = 2; /* idle power savings */ 1108906SEric.Saxe@Sun.COM uint16_t cpupm_C2_idle_pct_tunable = 70; 1118906SEric.Saxe@Sun.COM uint16_t cpupm_C3_idle_pct_tunable = 80; 1128906SEric.Saxe@Sun.COM 1138906SEric.Saxe@Sun.COM #ifndef __xpv 1148906SEric.Saxe@Sun.COM extern boolean_t cpupm_intel_init(cpu_t *); 1158906SEric.Saxe@Sun.COM extern boolean_t cpupm_amd_init(cpu_t *); 1168906SEric.Saxe@Sun.COM 1178906SEric.Saxe@Sun.COM typedef struct cpupm_vendor { 1188906SEric.Saxe@Sun.COM boolean_t (*cpuv_init)(cpu_t *); 1198906SEric.Saxe@Sun.COM } cpupm_vendor_t; 1208906SEric.Saxe@Sun.COM 1218906SEric.Saxe@Sun.COM /* 1228906SEric.Saxe@Sun.COM * Table of supported vendors. 1238906SEric.Saxe@Sun.COM */ 1248906SEric.Saxe@Sun.COM static cpupm_vendor_t cpupm_vendors[] = { 1258906SEric.Saxe@Sun.COM cpupm_intel_init, 1268906SEric.Saxe@Sun.COM cpupm_amd_init, 1278906SEric.Saxe@Sun.COM NULL 1288906SEric.Saxe@Sun.COM }; 1298906SEric.Saxe@Sun.COM #endif 1308906SEric.Saxe@Sun.COM 1318906SEric.Saxe@Sun.COM /* 1328906SEric.Saxe@Sun.COM * Initialize the machine. 1338906SEric.Saxe@Sun.COM * See if a module exists for managing power for this CPU. 1348906SEric.Saxe@Sun.COM */ 1358906SEric.Saxe@Sun.COM /*ARGSUSED*/ 1368906SEric.Saxe@Sun.COM void 1378906SEric.Saxe@Sun.COM cpupm_init(cpu_t *cp) 1388906SEric.Saxe@Sun.COM { 1398906SEric.Saxe@Sun.COM #ifndef __xpv 1408906SEric.Saxe@Sun.COM cpupm_vendor_t *vendors; 1418906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 1428906SEric.Saxe@Sun.COM struct machcpu *mcpu = &(cp->cpu_m); 1438906SEric.Saxe@Sun.COM int *speeds; 1448906SEric.Saxe@Sun.COM uint_t nspeeds; 1458906SEric.Saxe@Sun.COM int ret; 1468906SEric.Saxe@Sun.COM 1478906SEric.Saxe@Sun.COM mach_state = cp->cpu_m.mcpu_pm_mach_state = 1488906SEric.Saxe@Sun.COM kmem_zalloc(sizeof (cpupm_mach_state_t), KM_SLEEP); 1498906SEric.Saxe@Sun.COM mach_state->ms_caps = CPUPM_NO_STATES; 1508906SEric.Saxe@Sun.COM mutex_init(&mach_state->ms_lock, NULL, MUTEX_DRIVER, NULL); 1518906SEric.Saxe@Sun.COM 1528906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle = cpu_acpi_init(cp); 1538906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle == NULL) { 1548906SEric.Saxe@Sun.COM cpupm_free(cp); 1558906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!cpupm_init: processor %d: " 1568906SEric.Saxe@Sun.COM "unable to get ACPI handle", cp->cpu_id); 1578906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!CPU power management will not function."); 1588906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 1598906SEric.Saxe@Sun.COM return; 1608906SEric.Saxe@Sun.COM } 1618906SEric.Saxe@Sun.COM 1628906SEric.Saxe@Sun.COM /* 1638906SEric.Saxe@Sun.COM * Loop through the CPU management module table and see if 1648906SEric.Saxe@Sun.COM * any of the modules implement CPU power management 1658906SEric.Saxe@Sun.COM * for this CPU. 1668906SEric.Saxe@Sun.COM */ 1678906SEric.Saxe@Sun.COM for (vendors = cpupm_vendors; vendors->cpuv_init != NULL; vendors++) { 1688906SEric.Saxe@Sun.COM if (vendors->cpuv_init(cp)) 1698906SEric.Saxe@Sun.COM break; 1708906SEric.Saxe@Sun.COM } 1718906SEric.Saxe@Sun.COM 1728906SEric.Saxe@Sun.COM /* 1738906SEric.Saxe@Sun.COM * Nope, we can't power manage this CPU. 1748906SEric.Saxe@Sun.COM */ 1758906SEric.Saxe@Sun.COM if (vendors == NULL) { 1768906SEric.Saxe@Sun.COM cpupm_free(cp); 1778906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 1788906SEric.Saxe@Sun.COM return; 1798906SEric.Saxe@Sun.COM } 1808906SEric.Saxe@Sun.COM 1818906SEric.Saxe@Sun.COM /* 1828906SEric.Saxe@Sun.COM * If P-state support exists for this system, then initialize it. 1838906SEric.Saxe@Sun.COM */ 1848906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_ops != NULL) { 1858906SEric.Saxe@Sun.COM ret = mach_state->ms_pstate.cma_ops->cpus_init(cp); 1868906SEric.Saxe@Sun.COM if (ret != 0) { 1878906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!cpupm_init: processor %d:" 1888906SEric.Saxe@Sun.COM " unable to initialize P-state support", 1898906SEric.Saxe@Sun.COM cp->cpu_id); 1908906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_ops = NULL; 1918906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_P_STATES); 1928906SEric.Saxe@Sun.COM } else { 1938906SEric.Saxe@Sun.COM nspeeds = cpupm_get_speeds(cp, &speeds); 1948906SEric.Saxe@Sun.COM if (nspeeds == 0) { 1958906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!cpupm_init: processor %d:" 1968906SEric.Saxe@Sun.COM " no speeds to manage", cp->cpu_id); 1978906SEric.Saxe@Sun.COM } else { 1988906SEric.Saxe@Sun.COM cpupm_set_supp_freqs(cp, speeds, nspeeds); 1998906SEric.Saxe@Sun.COM cpupm_free_speeds(speeds, nspeeds); 2008906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_P_STATES; 2018906SEric.Saxe@Sun.COM } 2028906SEric.Saxe@Sun.COM } 2038906SEric.Saxe@Sun.COM } 2048906SEric.Saxe@Sun.COM 2058906SEric.Saxe@Sun.COM if (mach_state->ms_tstate.cma_ops != NULL) { 2068906SEric.Saxe@Sun.COM ret = mach_state->ms_tstate.cma_ops->cpus_init(cp); 2078906SEric.Saxe@Sun.COM if (ret != 0) { 2089004SNapanda.Pemmaiah@Sun.COM char err_msg[128]; 2099004SNapanda.Pemmaiah@Sun.COM int p_res; 2109004SNapanda.Pemmaiah@Sun.COM p_res = snprintf(err_msg, sizeof (err_msg), 2119004SNapanda.Pemmaiah@Sun.COM "!cpupm_init: processor %d: unable to initialize " 2129004SNapanda.Pemmaiah@Sun.COM "T-state support", cp->cpu_id); 2139004SNapanda.Pemmaiah@Sun.COM if (p_res >= 0) 2149004SNapanda.Pemmaiah@Sun.COM DTRACE_PROBE1(cpu_ts_err_msg, char *, err_msg); 2158906SEric.Saxe@Sun.COM mach_state->ms_tstate.cma_ops = NULL; 2168906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_T_STATES); 2178906SEric.Saxe@Sun.COM } else { 2188906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_T_STATES; 2198906SEric.Saxe@Sun.COM } 2208906SEric.Saxe@Sun.COM } 2218906SEric.Saxe@Sun.COM 2228906SEric.Saxe@Sun.COM /* 2238906SEric.Saxe@Sun.COM * If C-states support exists for this system, then initialize it. 2248906SEric.Saxe@Sun.COM */ 2258906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_ops != NULL) { 2268906SEric.Saxe@Sun.COM ret = mach_state->ms_cstate.cma_ops->cpus_init(cp); 2278906SEric.Saxe@Sun.COM if (ret != 0) { 2288906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "!cpupm_init: processor %d:" 2298906SEric.Saxe@Sun.COM " unable to initialize C-state support", 2308906SEric.Saxe@Sun.COM cp->cpu_id); 2318906SEric.Saxe@Sun.COM mach_state->ms_cstate.cma_ops = NULL; 2328906SEric.Saxe@Sun.COM mcpu->max_cstates = CPU_ACPI_C1; 2338906SEric.Saxe@Sun.COM cpupm_disable(CPUPM_C_STATES); 2348906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 2358906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 2368906SEric.Saxe@Sun.COM } else if (cpu_deep_cstates_supported()) { 2378906SEric.Saxe@Sun.COM mcpu->max_cstates = cpu_acpi_get_max_cstates( 2388906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle); 2398906SEric.Saxe@Sun.COM if (mcpu->max_cstates > CPU_ACPI_C1) { 2408906SEric.Saxe@Sun.COM hpet.callback(CST_EVENT_MULTIPLE_CSTATES); 2418906SEric.Saxe@Sun.COM CPU->cpu_m.mcpu_idle_cpu = cpu_acpi_idle; 2428906SEric.Saxe@Sun.COM mcpu->mcpu_idle_type = CPU_ACPI_C1; 2438906SEric.Saxe@Sun.COM disp_enq_thread = cstate_wakeup; 2448906SEric.Saxe@Sun.COM } else { 2458906SEric.Saxe@Sun.COM hpet.callback(CST_EVENT_ONE_CSTATE); 2468906SEric.Saxe@Sun.COM } 2478906SEric.Saxe@Sun.COM mach_state->ms_caps |= CPUPM_C_STATES; 2488906SEric.Saxe@Sun.COM } else { 2498906SEric.Saxe@Sun.COM mcpu->max_cstates = CPU_ACPI_C1; 2508906SEric.Saxe@Sun.COM idle_cpu = non_deep_idle_cpu; 2518906SEric.Saxe@Sun.COM disp_enq_thread = non_deep_idle_disp_enq_thread; 2528906SEric.Saxe@Sun.COM } 2538906SEric.Saxe@Sun.COM } 2548906SEric.Saxe@Sun.COM 2558906SEric.Saxe@Sun.COM 2568906SEric.Saxe@Sun.COM if (mach_state->ms_caps == CPUPM_NO_STATES) { 2578906SEric.Saxe@Sun.COM cpupm_free(cp); 2588906SEric.Saxe@Sun.COM CPUPM_DISABLE(); 2598906SEric.Saxe@Sun.COM return; 2608906SEric.Saxe@Sun.COM } 2618906SEric.Saxe@Sun.COM 2628906SEric.Saxe@Sun.COM if ((mach_state->ms_caps & CPUPM_T_STATES) || 2638906SEric.Saxe@Sun.COM (mach_state->ms_caps & CPUPM_P_STATES) || 2648906SEric.Saxe@Sun.COM (mach_state->ms_caps & CPUPM_C_STATES)) 2658906SEric.Saxe@Sun.COM cpupm_add_notify_handler(cp, cpupm_event_notify_handler, cp); 2668906SEric.Saxe@Sun.COM #endif 2678906SEric.Saxe@Sun.COM } 2688906SEric.Saxe@Sun.COM 2698906SEric.Saxe@Sun.COM /* 2708906SEric.Saxe@Sun.COM * Free any resources allocated by cpupm_init(). 2718906SEric.Saxe@Sun.COM */ 2728906SEric.Saxe@Sun.COM /*ARGSUSED*/ 2738906SEric.Saxe@Sun.COM void 2748906SEric.Saxe@Sun.COM cpupm_free(cpu_t *cp) 2758906SEric.Saxe@Sun.COM { 2768906SEric.Saxe@Sun.COM #ifndef __xpv 2778906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 2788906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 2798906SEric.Saxe@Sun.COM 2808906SEric.Saxe@Sun.COM if (mach_state == NULL) 2818906SEric.Saxe@Sun.COM return; 2828906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_ops != NULL) { 2838906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_ops->cpus_fini(cp); 2848906SEric.Saxe@Sun.COM mach_state->ms_pstate.cma_ops = NULL; 2858906SEric.Saxe@Sun.COM } 2868906SEric.Saxe@Sun.COM 2878906SEric.Saxe@Sun.COM if (mach_state->ms_tstate.cma_ops != NULL) { 2888906SEric.Saxe@Sun.COM mach_state->ms_tstate.cma_ops->cpus_fini(cp); 2898906SEric.Saxe@Sun.COM mach_state->ms_tstate.cma_ops = NULL; 2908906SEric.Saxe@Sun.COM } 2918906SEric.Saxe@Sun.COM 2928906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_ops != NULL) { 2938906SEric.Saxe@Sun.COM mach_state->ms_cstate.cma_ops->cpus_fini(cp); 2948906SEric.Saxe@Sun.COM mach_state->ms_cstate.cma_ops = NULL; 2958906SEric.Saxe@Sun.COM } 2968906SEric.Saxe@Sun.COM 2978906SEric.Saxe@Sun.COM cpupm_free_notify_handlers(cp); 2988906SEric.Saxe@Sun.COM 2998906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle != NULL) { 3008906SEric.Saxe@Sun.COM cpu_acpi_fini(mach_state->ms_acpi_handle); 3018906SEric.Saxe@Sun.COM mach_state->ms_acpi_handle = NULL; 3028906SEric.Saxe@Sun.COM } 3038906SEric.Saxe@Sun.COM 3048906SEric.Saxe@Sun.COM mutex_destroy(&mach_state->ms_lock); 3058906SEric.Saxe@Sun.COM kmem_free(mach_state, sizeof (cpupm_mach_state_t)); 3068906SEric.Saxe@Sun.COM cp->cpu_m.mcpu_pm_mach_state = NULL; 3078906SEric.Saxe@Sun.COM #endif 3088906SEric.Saxe@Sun.COM } 3098906SEric.Saxe@Sun.COM 3108906SEric.Saxe@Sun.COM /* 3118906SEric.Saxe@Sun.COM * If all CPUs have started and at least one power state is manageable, 3128906SEric.Saxe@Sun.COM * then the CPUs are ready for power management. 3138906SEric.Saxe@Sun.COM */ 3148906SEric.Saxe@Sun.COM boolean_t 3158906SEric.Saxe@Sun.COM cpupm_is_ready() 3168906SEric.Saxe@Sun.COM { 3178906SEric.Saxe@Sun.COM #ifndef __xpv 3188906SEric.Saxe@Sun.COM if (cpupm_enabled == CPUPM_NO_STATES) 3198906SEric.Saxe@Sun.COM return (B_FALSE); 3208906SEric.Saxe@Sun.COM return (cpupm_ready); 3218906SEric.Saxe@Sun.COM #else 3228906SEric.Saxe@Sun.COM return (B_FALSE); 3238906SEric.Saxe@Sun.COM #endif 3248906SEric.Saxe@Sun.COM 3258906SEric.Saxe@Sun.COM } 3268906SEric.Saxe@Sun.COM 3278906SEric.Saxe@Sun.COM boolean_t 3288906SEric.Saxe@Sun.COM cpupm_is_enabled(uint32_t state) 3298906SEric.Saxe@Sun.COM { 3308906SEric.Saxe@Sun.COM return ((cpupm_enabled & state) == state); 3318906SEric.Saxe@Sun.COM } 3328906SEric.Saxe@Sun.COM 3338906SEric.Saxe@Sun.COM /* 3348906SEric.Saxe@Sun.COM * By default, all states are enabled. 3358906SEric.Saxe@Sun.COM */ 3368906SEric.Saxe@Sun.COM void 3378906SEric.Saxe@Sun.COM cpupm_disable(uint32_t state) 3388906SEric.Saxe@Sun.COM { 3398906SEric.Saxe@Sun.COM 3408906SEric.Saxe@Sun.COM if (state & CPUPM_P_STATES) { 3418906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_pstate_domains); 3428906SEric.Saxe@Sun.COM } 3438906SEric.Saxe@Sun.COM if (state & CPUPM_T_STATES) { 3448906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_tstate_domains); 3458906SEric.Saxe@Sun.COM } 3468906SEric.Saxe@Sun.COM if (state & CPUPM_C_STATES) { 3478906SEric.Saxe@Sun.COM cpupm_free_domains(&cpupm_cstate_domains); 3488906SEric.Saxe@Sun.COM } 3498906SEric.Saxe@Sun.COM cpupm_enabled &= ~state; 3508906SEric.Saxe@Sun.COM } 3518906SEric.Saxe@Sun.COM 3528906SEric.Saxe@Sun.COM /* 3538906SEric.Saxe@Sun.COM * Once all CPUs have been started, the PPM driver should build CPU 3548906SEric.Saxe@Sun.COM * domains and initialize the topspeed for all CPU devices. 3558906SEric.Saxe@Sun.COM */ 3568906SEric.Saxe@Sun.COM void 3578906SEric.Saxe@Sun.COM cpupm_post_startup() 3588906SEric.Saxe@Sun.COM { 3598906SEric.Saxe@Sun.COM #ifndef __xpv 3608906SEric.Saxe@Sun.COM /* 3618906SEric.Saxe@Sun.COM * The CPU domain built by the PPM during CPUs attaching 3628906SEric.Saxe@Sun.COM * should be rebuilt with the information retrieved from 3638906SEric.Saxe@Sun.COM * ACPI. 3648906SEric.Saxe@Sun.COM */ 3658906SEric.Saxe@Sun.COM if (cpupm_rebuild_cpu_domains != NULL) 3668906SEric.Saxe@Sun.COM (*cpupm_rebuild_cpu_domains)(); 3678906SEric.Saxe@Sun.COM 3688906SEric.Saxe@Sun.COM /* 3698906SEric.Saxe@Sun.COM * Only initialize the topspeed if P-states are enabled. 3708906SEric.Saxe@Sun.COM */ 3718906SEric.Saxe@Sun.COM if (cpupm_enabled & CPUPM_P_STATES && cpupm_init_topspeed != NULL) 3728906SEric.Saxe@Sun.COM (*cpupm_init_topspeed)(); 3738906SEric.Saxe@Sun.COM #endif 3748906SEric.Saxe@Sun.COM cpupm_ready = B_TRUE; 3758906SEric.Saxe@Sun.COM } 3768906SEric.Saxe@Sun.COM 3778906SEric.Saxe@Sun.COM /* 3788906SEric.Saxe@Sun.COM * Allocate power domains for C,P and T States 3798906SEric.Saxe@Sun.COM */ 3808906SEric.Saxe@Sun.COM void 3818906SEric.Saxe@Sun.COM cpupm_alloc_domains(cpu_t *cp, int state) 3828906SEric.Saxe@Sun.COM { 3838906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 3848906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 3858906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle = mach_state->ms_acpi_handle; 3868906SEric.Saxe@Sun.COM cpupm_state_domains_t **dom_ptr; 3878906SEric.Saxe@Sun.COM cpupm_state_domains_t *dptr; 3888906SEric.Saxe@Sun.COM cpupm_state_domains_t **mach_dom_state_ptr; 3898906SEric.Saxe@Sun.COM uint32_t domain; 3908906SEric.Saxe@Sun.COM uint32_t type; 3918906SEric.Saxe@Sun.COM 3928906SEric.Saxe@Sun.COM switch (state) { 3938906SEric.Saxe@Sun.COM case CPUPM_P_STATES: 3948906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_PSD_CACHED)) { 3958906SEric.Saxe@Sun.COM domain = CPU_ACPI_PSD(handle).sd_domain; 3968906SEric.Saxe@Sun.COM type = CPU_ACPI_PSD(handle).sd_type; 3978906SEric.Saxe@Sun.COM } else { 3988906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 3998906SEric.Saxe@Sun.COM domain = cpuid_get_chipid(cp); 4008906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4018906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4028906SEric.Saxe@Sun.COM } 4038906SEric.Saxe@Sun.COM dom_ptr = &cpupm_pstate_domains; 4048906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_pstate.cma_domain; 4058906SEric.Saxe@Sun.COM break; 4068906SEric.Saxe@Sun.COM case CPUPM_T_STATES: 4078906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_TSD_CACHED)) { 4088906SEric.Saxe@Sun.COM domain = CPU_ACPI_TSD(handle).sd_domain; 4098906SEric.Saxe@Sun.COM type = CPU_ACPI_TSD(handle).sd_type; 4108906SEric.Saxe@Sun.COM } else { 4118906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 4128906SEric.Saxe@Sun.COM domain = cpuid_get_chipid(cp); 4138906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4148906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4158906SEric.Saxe@Sun.COM } 4168906SEric.Saxe@Sun.COM dom_ptr = &cpupm_tstate_domains; 4178906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_tstate.cma_domain; 4188906SEric.Saxe@Sun.COM break; 4198906SEric.Saxe@Sun.COM case CPUPM_C_STATES: 4208906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_CSD_CACHED)) { 4218906SEric.Saxe@Sun.COM domain = CPU_ACPI_CSD(handle).sd_domain; 4228906SEric.Saxe@Sun.COM type = CPU_ACPI_CSD(handle).sd_type; 4238906SEric.Saxe@Sun.COM } else { 4248906SEric.Saxe@Sun.COM mutex_enter(&cpu_lock); 4258906SEric.Saxe@Sun.COM domain = cpuid_get_coreid(cp); 4268906SEric.Saxe@Sun.COM mutex_exit(&cpu_lock); 4278906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL; 4288906SEric.Saxe@Sun.COM } 4298906SEric.Saxe@Sun.COM dom_ptr = &cpupm_cstate_domains; 4308906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_cstate.cma_domain; 4318906SEric.Saxe@Sun.COM break; 4328906SEric.Saxe@Sun.COM default: 4338906SEric.Saxe@Sun.COM return; 4348906SEric.Saxe@Sun.COM } 4358906SEric.Saxe@Sun.COM 4368906SEric.Saxe@Sun.COM for (dptr = *dom_ptr; dptr != NULL; dptr = dptr->pm_next) { 4378906SEric.Saxe@Sun.COM if (dptr->pm_domain == domain) 4388906SEric.Saxe@Sun.COM break; 4398906SEric.Saxe@Sun.COM } 4408906SEric.Saxe@Sun.COM 4418906SEric.Saxe@Sun.COM /* new domain is created and linked at the head */ 4428906SEric.Saxe@Sun.COM if (dptr == NULL) { 4438906SEric.Saxe@Sun.COM dptr = kmem_zalloc(sizeof (cpupm_state_domains_t), KM_SLEEP); 4448906SEric.Saxe@Sun.COM dptr->pm_domain = domain; 4458906SEric.Saxe@Sun.COM dptr->pm_type = type; 4468906SEric.Saxe@Sun.COM dptr->pm_next = *dom_ptr; 4478906SEric.Saxe@Sun.COM mutex_init(&dptr->pm_lock, NULL, MUTEX_SPIN, 4488906SEric.Saxe@Sun.COM (void *)ipltospl(DISP_LEVEL)); 4498906SEric.Saxe@Sun.COM CPUSET_ZERO(dptr->pm_cpus); 4508906SEric.Saxe@Sun.COM *dom_ptr = dptr; 4518906SEric.Saxe@Sun.COM } 4528906SEric.Saxe@Sun.COM CPUSET_ADD(dptr->pm_cpus, cp->cpu_id); 4538906SEric.Saxe@Sun.COM *mach_dom_state_ptr = dptr; 4548906SEric.Saxe@Sun.COM } 4558906SEric.Saxe@Sun.COM 4568906SEric.Saxe@Sun.COM /* 4578906SEric.Saxe@Sun.COM * Free C, P or T state power domains 4588906SEric.Saxe@Sun.COM */ 4598906SEric.Saxe@Sun.COM void 4608906SEric.Saxe@Sun.COM cpupm_free_domains(cpupm_state_domains_t **dom_ptr) 4618906SEric.Saxe@Sun.COM { 4628906SEric.Saxe@Sun.COM cpupm_state_domains_t *this_domain, *next_domain; 4638906SEric.Saxe@Sun.COM 4648906SEric.Saxe@Sun.COM this_domain = *dom_ptr; 4658906SEric.Saxe@Sun.COM while (this_domain != NULL) { 4668906SEric.Saxe@Sun.COM next_domain = this_domain->pm_next; 4678906SEric.Saxe@Sun.COM mutex_destroy(&this_domain->pm_lock); 4688906SEric.Saxe@Sun.COM kmem_free((void *)this_domain, 4698906SEric.Saxe@Sun.COM sizeof (cpupm_state_domains_t)); 4708906SEric.Saxe@Sun.COM this_domain = next_domain; 4718906SEric.Saxe@Sun.COM } 4728906SEric.Saxe@Sun.COM *dom_ptr = NULL; 4738906SEric.Saxe@Sun.COM } 4748906SEric.Saxe@Sun.COM 4758906SEric.Saxe@Sun.COM void 4768906SEric.Saxe@Sun.COM cpupm_alloc_ms_cstate(cpu_t *cp) 4778906SEric.Saxe@Sun.COM { 4788906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 4798906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate; 4808906SEric.Saxe@Sun.COM 4818906SEric.Saxe@Sun.COM mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 4828906SEric.Saxe@Sun.COM ms_cstate = &mach_state->ms_cstate; 4838906SEric.Saxe@Sun.COM ASSERT(ms_cstate->cma_state.cstate == NULL); 4848906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = kmem_zalloc(sizeof (cma_c_state_t), 4858906SEric.Saxe@Sun.COM KM_SLEEP); 4868906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate->cs_next_cstate = CPU_ACPI_C1; 4878906SEric.Saxe@Sun.COM } 4888906SEric.Saxe@Sun.COM 4898906SEric.Saxe@Sun.COM void 4908906SEric.Saxe@Sun.COM cpupm_free_ms_cstate(cpu_t *cp) 4918906SEric.Saxe@Sun.COM { 4928906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 4938906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 4948906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate = &mach_state->ms_cstate; 4958906SEric.Saxe@Sun.COM 4968906SEric.Saxe@Sun.COM if (ms_cstate->cma_state.cstate != NULL) { 4978906SEric.Saxe@Sun.COM kmem_free(ms_cstate->cma_state.cstate, sizeof (cma_c_state_t)); 4988906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = NULL; 4998906SEric.Saxe@Sun.COM } 5008906SEric.Saxe@Sun.COM } 5018906SEric.Saxe@Sun.COM 5028906SEric.Saxe@Sun.COM void 5038906SEric.Saxe@Sun.COM cpupm_state_change(cpu_t *cp, int level, int state) 5048906SEric.Saxe@Sun.COM { 5058906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 5068906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 5078906SEric.Saxe@Sun.COM cpupm_state_ops_t *state_ops; 5088906SEric.Saxe@Sun.COM cpupm_state_domains_t *state_domain; 5098906SEric.Saxe@Sun.COM cpuset_t set; 5108906SEric.Saxe@Sun.COM 5118906SEric.Saxe@Sun.COM DTRACE_PROBE2(cpupm__state__change, cpu_t *, cp, int, level); 5128906SEric.Saxe@Sun.COM 5138906SEric.Saxe@Sun.COM if (mach_state == NULL) { 5148906SEric.Saxe@Sun.COM return; 5158906SEric.Saxe@Sun.COM } 5168906SEric.Saxe@Sun.COM 5178906SEric.Saxe@Sun.COM switch (state) { 5188906SEric.Saxe@Sun.COM case CPUPM_P_STATES: 5198906SEric.Saxe@Sun.COM state_ops = mach_state->ms_pstate.cma_ops; 5208906SEric.Saxe@Sun.COM state_domain = mach_state->ms_pstate.cma_domain; 5218906SEric.Saxe@Sun.COM break; 5228906SEric.Saxe@Sun.COM case CPUPM_T_STATES: 5238906SEric.Saxe@Sun.COM state_ops = mach_state->ms_tstate.cma_ops; 5248906SEric.Saxe@Sun.COM state_domain = mach_state->ms_tstate.cma_domain; 5258906SEric.Saxe@Sun.COM break; 5268906SEric.Saxe@Sun.COM default: 5278906SEric.Saxe@Sun.COM break; 5288906SEric.Saxe@Sun.COM } 5298906SEric.Saxe@Sun.COM 5308906SEric.Saxe@Sun.COM switch (state_domain->pm_type) { 5318906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ANY: 5328906SEric.Saxe@Sun.COM /* 5338906SEric.Saxe@Sun.COM * A request on any CPU in the domain transitions the domain 5348906SEric.Saxe@Sun.COM */ 5358906SEric.Saxe@Sun.COM CPUSET_ONLY(set, cp->cpu_id); 5368906SEric.Saxe@Sun.COM state_ops->cpus_change(set, level); 5378906SEric.Saxe@Sun.COM break; 5388906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ALL: 5398906SEric.Saxe@Sun.COM /* 5408906SEric.Saxe@Sun.COM * All CPUs in the domain must request the transition 5418906SEric.Saxe@Sun.COM */ 5428906SEric.Saxe@Sun.COM case CPU_ACPI_HW_ALL: 5438906SEric.Saxe@Sun.COM /* 5448906SEric.Saxe@Sun.COM * P/T-state transitions are coordinated by the hardware 5458906SEric.Saxe@Sun.COM * For now, request the transition on all CPUs in the domain, 5468906SEric.Saxe@Sun.COM * but looking ahead we can probably be smarter about this. 5478906SEric.Saxe@Sun.COM */ 5488906SEric.Saxe@Sun.COM mutex_enter(&state_domain->pm_lock); 5498906SEric.Saxe@Sun.COM state_ops->cpus_change(state_domain->pm_cpus, level); 5508906SEric.Saxe@Sun.COM mutex_exit(&state_domain->pm_lock); 5518906SEric.Saxe@Sun.COM break; 5528906SEric.Saxe@Sun.COM default: 5538906SEric.Saxe@Sun.COM cmn_err(CE_WARN, "Unknown domain coordination type: %d", 5548906SEric.Saxe@Sun.COM state_domain->pm_type); 5558906SEric.Saxe@Sun.COM } 5568906SEric.Saxe@Sun.COM } 5578906SEric.Saxe@Sun.COM 5588906SEric.Saxe@Sun.COM /* 5598906SEric.Saxe@Sun.COM * CPU PM interfaces exposed to the CPU power manager 5608906SEric.Saxe@Sun.COM */ 5618906SEric.Saxe@Sun.COM /*ARGSUSED*/ 5628906SEric.Saxe@Sun.COM id_t 5638906SEric.Saxe@Sun.COM cpupm_plat_domain_id(cpu_t *cp, cpupm_dtype_t type) 5648906SEric.Saxe@Sun.COM { 5658906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 5668906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 5678906SEric.Saxe@Sun.COM 5688906SEric.Saxe@Sun.COM if ((mach_state == NULL) || (!cpupm_is_enabled(CPUPM_P_STATES) && 5698906SEric.Saxe@Sun.COM !cpupm_is_enabled(CPUPM_C_STATES))) { 5708906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN); 5718906SEric.Saxe@Sun.COM } 5728906SEric.Saxe@Sun.COM if (type == CPUPM_DTYPE_ACTIVE) { 5738906SEric.Saxe@Sun.COM /* 5748906SEric.Saxe@Sun.COM * Return P-State domain for the specified CPU 5758906SEric.Saxe@Sun.COM */ 5768906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_domain) { 5778906SEric.Saxe@Sun.COM return (mach_state->ms_pstate.cma_domain->pm_domain); 5788906SEric.Saxe@Sun.COM } 5798906SEric.Saxe@Sun.COM } else if (type == CPUPM_DTYPE_IDLE) { 5808906SEric.Saxe@Sun.COM /* 5818906SEric.Saxe@Sun.COM * Return C-State domain for the specified CPU 5828906SEric.Saxe@Sun.COM */ 5838906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_domain) { 5848906SEric.Saxe@Sun.COM return (mach_state->ms_cstate.cma_domain->pm_domain); 5858906SEric.Saxe@Sun.COM } 5868906SEric.Saxe@Sun.COM } 5878906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN); 5888906SEric.Saxe@Sun.COM } 5898906SEric.Saxe@Sun.COM 5908906SEric.Saxe@Sun.COM /*ARGSUSED*/ 5918906SEric.Saxe@Sun.COM uint_t 5928906SEric.Saxe@Sun.COM cpupm_plat_state_enumerate(cpu_t *cp, cpupm_dtype_t type, 5938906SEric.Saxe@Sun.COM cpupm_state_t *states) 5948906SEric.Saxe@Sun.COM { 5958906SEric.Saxe@Sun.COM int *speeds; 5968906SEric.Saxe@Sun.COM uint_t nspeeds, i; 5978906SEric.Saxe@Sun.COM 5988906SEric.Saxe@Sun.COM /* 5998906SEric.Saxe@Sun.COM * Idle domain support unimplemented 6008906SEric.Saxe@Sun.COM */ 6018906SEric.Saxe@Sun.COM if (type != CPUPM_DTYPE_ACTIVE) { 6028906SEric.Saxe@Sun.COM return (0); 6038906SEric.Saxe@Sun.COM } 6048906SEric.Saxe@Sun.COM nspeeds = cpupm_get_speeds(cp, &speeds); 6058906SEric.Saxe@Sun.COM 6068906SEric.Saxe@Sun.COM /* 6078906SEric.Saxe@Sun.COM * If the caller passes NULL for states, just return the 6088906SEric.Saxe@Sun.COM * number of states. 6098906SEric.Saxe@Sun.COM */ 6108906SEric.Saxe@Sun.COM if (states != NULL) { 6118906SEric.Saxe@Sun.COM for (i = 0; i < nspeeds; i++) { 6128906SEric.Saxe@Sun.COM states[i].cps_speed = speeds[i]; 6138906SEric.Saxe@Sun.COM states[i].cps_handle = (cpupm_handle_t)i; 6148906SEric.Saxe@Sun.COM } 6158906SEric.Saxe@Sun.COM } 6168906SEric.Saxe@Sun.COM cpupm_free_speeds(speeds, nspeeds); 6178906SEric.Saxe@Sun.COM return (nspeeds); 6188906SEric.Saxe@Sun.COM } 6198906SEric.Saxe@Sun.COM 6208906SEric.Saxe@Sun.COM /*ARGSUSED*/ 6218906SEric.Saxe@Sun.COM int 6228906SEric.Saxe@Sun.COM cpupm_plat_change_state(cpu_t *cp, cpupm_state_t *state) 6238906SEric.Saxe@Sun.COM { 6248906SEric.Saxe@Sun.COM if (!cpupm_is_ready()) 6258906SEric.Saxe@Sun.COM return (-1); 6268906SEric.Saxe@Sun.COM 6278906SEric.Saxe@Sun.COM cpupm_state_change(cp, (int)state->cps_handle, CPUPM_P_STATES); 6288906SEric.Saxe@Sun.COM 6298906SEric.Saxe@Sun.COM return (0); 6308906SEric.Saxe@Sun.COM } 6318906SEric.Saxe@Sun.COM 6328906SEric.Saxe@Sun.COM /*ARGSUSED*/ 6338906SEric.Saxe@Sun.COM /* 6348906SEric.Saxe@Sun.COM * Note: It is the responsibility of the users of 6358906SEric.Saxe@Sun.COM * cpupm_get_speeds() to free the memory allocated 6368906SEric.Saxe@Sun.COM * for speeds using cpupm_free_speeds() 6378906SEric.Saxe@Sun.COM */ 6388906SEric.Saxe@Sun.COM uint_t 6398906SEric.Saxe@Sun.COM cpupm_get_speeds(cpu_t *cp, int **speeds) 6408906SEric.Saxe@Sun.COM { 6418906SEric.Saxe@Sun.COM #ifndef __xpv 6428906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6438906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 6448906SEric.Saxe@Sun.COM return (cpu_acpi_get_speeds(mach_state->ms_acpi_handle, speeds)); 6458906SEric.Saxe@Sun.COM #else 6468906SEric.Saxe@Sun.COM return (0); 6478906SEric.Saxe@Sun.COM #endif 6488906SEric.Saxe@Sun.COM } 6498906SEric.Saxe@Sun.COM 6508906SEric.Saxe@Sun.COM /*ARGSUSED*/ 6518906SEric.Saxe@Sun.COM void 6528906SEric.Saxe@Sun.COM cpupm_free_speeds(int *speeds, uint_t nspeeds) 6538906SEric.Saxe@Sun.COM { 6548906SEric.Saxe@Sun.COM #ifndef __xpv 6558906SEric.Saxe@Sun.COM cpu_acpi_free_speeds(speeds, nspeeds); 6568906SEric.Saxe@Sun.COM #endif 6578906SEric.Saxe@Sun.COM } 6588906SEric.Saxe@Sun.COM 6598906SEric.Saxe@Sun.COM /* 6608906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 6618906SEric.Saxe@Sun.COM */ 6628906SEric.Saxe@Sun.COM boolean_t 6638906SEric.Saxe@Sun.COM cpupm_power_ready(void) 6648906SEric.Saxe@Sun.COM { 6658906SEric.Saxe@Sun.COM return (cpupm_is_enabled(CPUPM_P_STATES) && cpupm_is_ready()); 6668906SEric.Saxe@Sun.COM } 6678906SEric.Saxe@Sun.COM 6688906SEric.Saxe@Sun.COM /* 6698906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 6708906SEric.Saxe@Sun.COM */ 6718906SEric.Saxe@Sun.COM boolean_t 6728906SEric.Saxe@Sun.COM cpupm_throttle_ready(void) 6738906SEric.Saxe@Sun.COM { 6748906SEric.Saxe@Sun.COM return (cpupm_is_enabled(CPUPM_T_STATES) && cpupm_is_ready()); 6758906SEric.Saxe@Sun.COM } 6768906SEric.Saxe@Sun.COM 6778906SEric.Saxe@Sun.COM /* 6788906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully. 6798906SEric.Saxe@Sun.COM */ 6808906SEric.Saxe@Sun.COM boolean_t 6818906SEric.Saxe@Sun.COM cpupm_cstate_ready(void) 6828906SEric.Saxe@Sun.COM { 6838906SEric.Saxe@Sun.COM return (cpupm_is_enabled(CPUPM_C_STATES) && cpupm_is_ready()); 6848906SEric.Saxe@Sun.COM } 6858906SEric.Saxe@Sun.COM 6868906SEric.Saxe@Sun.COM void 6878906SEric.Saxe@Sun.COM cpupm_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx) 6888906SEric.Saxe@Sun.COM { 6898906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 6908906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 6918906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 6928906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 6938906SEric.Saxe@Sun.COM 6948906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 6958906SEric.Saxe@Sun.COM for (entry = mach_state->ms_handlers; entry != NULL; 6968906SEric.Saxe@Sun.COM entry = entry->nq_next) { 6978906SEric.Saxe@Sun.COM entry->nq_handler(obj, val, entry->nq_ctx); 6988906SEric.Saxe@Sun.COM } 6998906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 7008906SEric.Saxe@Sun.COM } 7018906SEric.Saxe@Sun.COM 7028906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7038906SEric.Saxe@Sun.COM void 7048906SEric.Saxe@Sun.COM cpupm_add_notify_handler(cpu_t *cp, CPUPM_NOTIFY_HANDLER handler, void *ctx) 7058906SEric.Saxe@Sun.COM { 7068906SEric.Saxe@Sun.COM #ifndef __xpv 7078906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 7088906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 7098906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 7108906SEric.Saxe@Sun.COM 7118906SEric.Saxe@Sun.COM entry = kmem_zalloc(sizeof (cpupm_notification_t), KM_SLEEP); 7128906SEric.Saxe@Sun.COM entry->nq_handler = handler; 7138906SEric.Saxe@Sun.COM entry->nq_ctx = ctx; 7148906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 7158906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) { 7168906SEric.Saxe@Sun.COM entry->nq_next = NULL; 7178906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry; 7188906SEric.Saxe@Sun.COM cpu_acpi_install_notify_handler(mach_state->ms_acpi_handle, 7198906SEric.Saxe@Sun.COM cpupm_notify_handler, cp); 7208906SEric.Saxe@Sun.COM 7218906SEric.Saxe@Sun.COM } else { 7228906SEric.Saxe@Sun.COM entry->nq_next = mach_state->ms_handlers; 7238906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry; 7248906SEric.Saxe@Sun.COM } 7258906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 7268906SEric.Saxe@Sun.COM #endif 7278906SEric.Saxe@Sun.COM } 7288906SEric.Saxe@Sun.COM 7298906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7308906SEric.Saxe@Sun.COM static void 7318906SEric.Saxe@Sun.COM cpupm_free_notify_handlers(cpu_t *cp) 7328906SEric.Saxe@Sun.COM { 7338906SEric.Saxe@Sun.COM #ifndef __xpv 7348906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state = 7358906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 7368906SEric.Saxe@Sun.COM cpupm_notification_t *entry; 7378906SEric.Saxe@Sun.COM cpupm_notification_t *next; 7388906SEric.Saxe@Sun.COM 7398906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock); 7408906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) { 7418906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 7428906SEric.Saxe@Sun.COM return; 7438906SEric.Saxe@Sun.COM } 7448906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle != NULL) { 7458906SEric.Saxe@Sun.COM cpu_acpi_remove_notify_handler(mach_state->ms_acpi_handle, 7468906SEric.Saxe@Sun.COM cpupm_notify_handler); 7478906SEric.Saxe@Sun.COM } 7488906SEric.Saxe@Sun.COM entry = mach_state->ms_handlers; 7498906SEric.Saxe@Sun.COM while (entry != NULL) { 7508906SEric.Saxe@Sun.COM next = entry->nq_next; 7518906SEric.Saxe@Sun.COM kmem_free(entry, sizeof (cpupm_notification_t)); 7528906SEric.Saxe@Sun.COM entry = next; 7538906SEric.Saxe@Sun.COM } 7548906SEric.Saxe@Sun.COM mach_state->ms_handlers = NULL; 7558906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock); 7568906SEric.Saxe@Sun.COM #endif 7578906SEric.Saxe@Sun.COM } 7588906SEric.Saxe@Sun.COM 7598906SEric.Saxe@Sun.COM /* 7608906SEric.Saxe@Sun.COM * Get the current max speed from the ACPI _PPC object 7618906SEric.Saxe@Sun.COM */ 7628906SEric.Saxe@Sun.COM /*ARGSUSED*/ 7638906SEric.Saxe@Sun.COM int 7648906SEric.Saxe@Sun.COM cpupm_get_top_speed(cpu_t *cp) 7658906SEric.Saxe@Sun.COM { 7668906SEric.Saxe@Sun.COM #ifndef __xpv 7678906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state; 7688906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle; 7698906SEric.Saxe@Sun.COM int plat_level; 7708906SEric.Saxe@Sun.COM uint_t nspeeds; 7718906SEric.Saxe@Sun.COM int max_level; 7728906SEric.Saxe@Sun.COM 7738906SEric.Saxe@Sun.COM mach_state = 7748906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 7758906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle; 7768906SEric.Saxe@Sun.COM 7778906SEric.Saxe@Sun.COM cpu_acpi_cache_ppc(handle); 7788906SEric.Saxe@Sun.COM plat_level = CPU_ACPI_PPC(handle); 7798906SEric.Saxe@Sun.COM 7808906SEric.Saxe@Sun.COM nspeeds = CPU_ACPI_PSTATES_COUNT(handle); 7818906SEric.Saxe@Sun.COM 7828906SEric.Saxe@Sun.COM max_level = nspeeds - 1; 7838906SEric.Saxe@Sun.COM if ((plat_level < 0) || (plat_level > max_level)) { 7848906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpupm_get_top_speed: CPU %d: " 7858906SEric.Saxe@Sun.COM "_PPC out of range %d", cp->cpu_id, plat_level); 7868906SEric.Saxe@Sun.COM plat_level = 0; 7878906SEric.Saxe@Sun.COM } 7888906SEric.Saxe@Sun.COM 7898906SEric.Saxe@Sun.COM return (plat_level); 7908906SEric.Saxe@Sun.COM #else 7918906SEric.Saxe@Sun.COM return (0); 7928906SEric.Saxe@Sun.COM #endif 7938906SEric.Saxe@Sun.COM } 7948906SEric.Saxe@Sun.COM 7958906SEric.Saxe@Sun.COM /* 7968906SEric.Saxe@Sun.COM * This notification handler is called whenever the ACPI _PPC 7978906SEric.Saxe@Sun.COM * object changes. The _PPC is a sort of governor on power levels. 7988906SEric.Saxe@Sun.COM * It sets an upper threshold on which, _PSS defined, power levels 7998906SEric.Saxe@Sun.COM * are usuable. The _PPC value is dynamic and may change as properties 8008906SEric.Saxe@Sun.COM * (i.e., thermal or AC source) of the system change. 8018906SEric.Saxe@Sun.COM */ 8028906SEric.Saxe@Sun.COM 8038906SEric.Saxe@Sun.COM static void 8048906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(void *ctx) 8058906SEric.Saxe@Sun.COM { 8068906SEric.Saxe@Sun.COM cpu_t *cp = ctx; 8078906SEric.Saxe@Sun.COM int top_speed; 8088906SEric.Saxe@Sun.COM 8098906SEric.Saxe@Sun.COM top_speed = cpupm_get_top_speed(cp); 8108906SEric.Saxe@Sun.COM cpupm_redefine_max_activepwr_state(cp, top_speed); 8118906SEric.Saxe@Sun.COM } 8128906SEric.Saxe@Sun.COM 8138906SEric.Saxe@Sun.COM /* ARGSUSED */ 8148906SEric.Saxe@Sun.COM static void 8158906SEric.Saxe@Sun.COM cpupm_event_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx) 8168906SEric.Saxe@Sun.COM { 8178906SEric.Saxe@Sun.COM #ifndef __xpv 818*9040SMark.Haywood@Sun.COM 819*9040SMark.Haywood@Sun.COM cpu_t *cp = ctx; 820*9040SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state = 821*9040SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 822*9040SMark.Haywood@Sun.COM 823*9040SMark.Haywood@Sun.COM if (mach_state == NULL) 824*9040SMark.Haywood@Sun.COM return; 825*9040SMark.Haywood@Sun.COM 8268906SEric.Saxe@Sun.COM /* 8278906SEric.Saxe@Sun.COM * Currently, we handle _TPC,_CST and _PPC change notifications. 8288906SEric.Saxe@Sun.COM */ 829*9040SMark.Haywood@Sun.COM if (val == CPUPM_TPC_CHANGE_NOTIFICATION && 830*9040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_T_STATES) { 8318906SEric.Saxe@Sun.COM cpupm_throttle_manage_notification(ctx); 832*9040SMark.Haywood@Sun.COM } else if (val == CPUPM_CST_CHANGE_NOTIFICATION && 833*9040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_C_STATES) { 8348906SEric.Saxe@Sun.COM cpuidle_manage_cstates(ctx); 835*9040SMark.Haywood@Sun.COM } else if (val == CPUPM_PPC_CHANGE_NOTIFICATION && 836*9040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_P_STATES) { 8378906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(ctx); 8388906SEric.Saxe@Sun.COM } 8398906SEric.Saxe@Sun.COM #endif 8408906SEric.Saxe@Sun.COM } 8418906SEric.Saxe@Sun.COM 8428906SEric.Saxe@Sun.COM /* 8438906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU exits idle. 8448906SEric.Saxe@Sun.COM */ 8458906SEric.Saxe@Sun.COM void 8468906SEric.Saxe@Sun.COM cpupm_wakeup_cstate_data(cma_c_state_t *cs_data, hrtime_t end) 8478906SEric.Saxe@Sun.COM { 8488906SEric.Saxe@Sun.COM cs_data->cs_idle_exit = end; 8498906SEric.Saxe@Sun.COM } 8508906SEric.Saxe@Sun.COM 8518906SEric.Saxe@Sun.COM /* 8528906SEric.Saxe@Sun.COM * Determine next cstate based on cpupm data. 8538906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU goes idle. 8548906SEric.Saxe@Sun.COM * Do as much as possible in the idle state bookkeeping function because the 8558906SEric.Saxe@Sun.COM * performance impact while idle is minimal compared to in the wakeup function 8568906SEric.Saxe@Sun.COM * when there is real work to do. 8578906SEric.Saxe@Sun.COM */ 8588906SEric.Saxe@Sun.COM uint32_t 8598983SBill.Holler@Sun.COM cpupm_next_cstate(cma_c_state_t *cs_data, cpu_acpi_cstate_t *cstates, 8608983SBill.Holler@Sun.COM uint32_t cs_count, hrtime_t start) 8618906SEric.Saxe@Sun.COM { 8628983SBill.Holler@Sun.COM hrtime_t duration; 8638983SBill.Holler@Sun.COM hrtime_t ave_interval; 8648983SBill.Holler@Sun.COM hrtime_t ave_idle_time; 8658983SBill.Holler@Sun.COM uint32_t i; 8668906SEric.Saxe@Sun.COM 8678906SEric.Saxe@Sun.COM duration = cs_data->cs_idle_exit - cs_data->cs_idle_enter; 8688906SEric.Saxe@Sun.COM scalehrtime(&duration); 8698906SEric.Saxe@Sun.COM cs_data->cs_idle += duration; 8708906SEric.Saxe@Sun.COM cs_data->cs_idle_enter = start; 8718906SEric.Saxe@Sun.COM 8728906SEric.Saxe@Sun.COM ++cs_data->cs_cnt; 8738906SEric.Saxe@Sun.COM if (cs_data->cs_cnt > cpupm_cs_sample_tunable) { 8748906SEric.Saxe@Sun.COM cs_data->cs_smpl_len = start - cs_data->cs_smpl_start; 8758906SEric.Saxe@Sun.COM scalehrtime(&cs_data->cs_smpl_len); 8768906SEric.Saxe@Sun.COM cs_data->cs_smpl_len |= 1; /* protect from DIV 0 */ 8778906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle = cs_data->cs_idle; 8788906SEric.Saxe@Sun.COM cs_data->cs_idle = 0; 8798906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle_pct = ((100 * cs_data->cs_smpl_idle) / 8808906SEric.Saxe@Sun.COM cs_data->cs_smpl_len); 8818906SEric.Saxe@Sun.COM 8828906SEric.Saxe@Sun.COM cs_data->cs_smpl_start = start; 8838906SEric.Saxe@Sun.COM cs_data->cs_cnt = 0; 8848906SEric.Saxe@Sun.COM 8858906SEric.Saxe@Sun.COM /* 8868906SEric.Saxe@Sun.COM * Strand level C-state policy 8878983SBill.Holler@Sun.COM * The cpu_acpi_cstate_t *cstates array is not required to 8888983SBill.Holler@Sun.COM * have an entry for both CPU_ACPI_C2 and CPU_ACPI_C3. 8898983SBill.Holler@Sun.COM * There are cs_count entries in the cstates array. 8908983SBill.Holler@Sun.COM * cs_data->cs_next_cstate contains the index of the next 8918983SBill.Holler@Sun.COM * C-state this CPU should enter. 8928906SEric.Saxe@Sun.COM */ 8938983SBill.Holler@Sun.COM ASSERT(cstates[0].cs_type == CPU_ACPI_C1); 8948906SEric.Saxe@Sun.COM 8958906SEric.Saxe@Sun.COM /* 8968906SEric.Saxe@Sun.COM * Will CPU be idle long enough to save power? 8978906SEric.Saxe@Sun.COM */ 8988906SEric.Saxe@Sun.COM ave_idle_time = (cs_data->cs_smpl_idle / 8998906SEric.Saxe@Sun.COM cpupm_cs_sample_tunable) / 1000; 9008983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 9018983SBill.Holler@Sun.COM if (ave_idle_time < (cstates[i].cs_latency * 9028983SBill.Holler@Sun.COM cpupm_cs_idle_save_tunable)) { 9038983SBill.Holler@Sun.COM cs_count = i; 9048983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *, 9058983SBill.Holler@Sun.COM CPU, int, i); 9068983SBill.Holler@Sun.COM } 9078906SEric.Saxe@Sun.COM } 9088906SEric.Saxe@Sun.COM 9098906SEric.Saxe@Sun.COM /* 9108906SEric.Saxe@Sun.COM * Wakeup often (even when non-idle time is very short)? 9118906SEric.Saxe@Sun.COM * Some producer/consumer type loads fall into this category. 9128906SEric.Saxe@Sun.COM */ 9138906SEric.Saxe@Sun.COM ave_interval = (cs_data->cs_smpl_len / cpupm_cs_sample_tunable) 9148906SEric.Saxe@Sun.COM / 1000; 9158983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 9168983SBill.Holler@Sun.COM if (ave_interval <= (cstates[i].cs_latency * 9178983SBill.Holler@Sun.COM cpupm_cs_idle_cost_tunable)) { 9188983SBill.Holler@Sun.COM cs_count = i; 9198983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *, 9208983SBill.Holler@Sun.COM CPU, int, (CPU_MAX_CSTATES + i)); 9218983SBill.Holler@Sun.COM } 9228906SEric.Saxe@Sun.COM } 9238906SEric.Saxe@Sun.COM 9248906SEric.Saxe@Sun.COM /* 9258906SEric.Saxe@Sun.COM * Idle percent 9268906SEric.Saxe@Sun.COM */ 9278983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) { 9288983SBill.Holler@Sun.COM switch (cstates[i].cs_type) { 9298983SBill.Holler@Sun.COM case CPU_ACPI_C2: 9308983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct < 9318983SBill.Holler@Sun.COM cpupm_C2_idle_pct_tunable) { 9328983SBill.Holler@Sun.COM cs_count = i; 9338983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, 9348983SBill.Holler@Sun.COM cpu_t *, CPU, int, 9358983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i)); 9368983SBill.Holler@Sun.COM } 9378983SBill.Holler@Sun.COM break; 9388983SBill.Holler@Sun.COM 9398983SBill.Holler@Sun.COM case CPU_ACPI_C3: 9408983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct < 9418983SBill.Holler@Sun.COM cpupm_C3_idle_pct_tunable) { 9428983SBill.Holler@Sun.COM cs_count = i; 9438983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, 9448983SBill.Holler@Sun.COM cpu_t *, CPU, int, 9458983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i)); 9468983SBill.Holler@Sun.COM } 9478983SBill.Holler@Sun.COM break; 9488983SBill.Holler@Sun.COM } 9498906SEric.Saxe@Sun.COM } 9508983SBill.Holler@Sun.COM 9518983SBill.Holler@Sun.COM cs_data->cs_next_cstate = cs_count - 1; 9528906SEric.Saxe@Sun.COM } 9538906SEric.Saxe@Sun.COM 9548906SEric.Saxe@Sun.COM return (cs_data->cs_next_cstate); 9558906SEric.Saxe@Sun.COM } 956