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
cpupm_init(cpu_t * cp)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);
243*12004Sjiang.liu@intel.com cp->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
cpupm_free(cpu_t * cp,boolean_t cpupm_stop)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
cpupm_fini(cpu_t * cp)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
cpupm_start(cpu_t * cp)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
cpupm_stop(cpu_t * cp)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
cpupm_is_ready(cpu_t * cp)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
cpupm_is_enabled(uint32_t state)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
cpupm_disable(uint32_t state)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
cpupm_alloc_domains(cpu_t * cp,int state)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 {
442*12004Sjiang.liu@intel.com if (MUTEX_HELD(&cpu_lock)) {
443*12004Sjiang.liu@intel.com domain = cpuid_get_chipid(cp);
444*12004Sjiang.liu@intel.com } else {
445*12004Sjiang.liu@intel.com mutex_enter(&cpu_lock);
446*12004Sjiang.liu@intel.com domain = cpuid_get_chipid(cp);
447*12004Sjiang.liu@intel.com mutex_exit(&cpu_lock);
448*12004Sjiang.liu@intel.com }
4498906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL;
4508906SEric.Saxe@Sun.COM }
4518906SEric.Saxe@Sun.COM dom_ptr = &cpupm_pstate_domains;
4528906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_pstate.cma_domain;
4538906SEric.Saxe@Sun.COM break;
4548906SEric.Saxe@Sun.COM case CPUPM_T_STATES:
4558906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_TSD_CACHED)) {
4568906SEric.Saxe@Sun.COM domain = CPU_ACPI_TSD(handle).sd_domain;
4578906SEric.Saxe@Sun.COM type = CPU_ACPI_TSD(handle).sd_type;
4588906SEric.Saxe@Sun.COM } else {
459*12004Sjiang.liu@intel.com if (MUTEX_HELD(&cpu_lock)) {
460*12004Sjiang.liu@intel.com domain = cpuid_get_chipid(cp);
461*12004Sjiang.liu@intel.com } else {
462*12004Sjiang.liu@intel.com mutex_enter(&cpu_lock);
463*12004Sjiang.liu@intel.com domain = cpuid_get_chipid(cp);
464*12004Sjiang.liu@intel.com mutex_exit(&cpu_lock);
465*12004Sjiang.liu@intel.com }
4668906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL;
4678906SEric.Saxe@Sun.COM }
4688906SEric.Saxe@Sun.COM dom_ptr = &cpupm_tstate_domains;
4698906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_tstate.cma_domain;
4708906SEric.Saxe@Sun.COM break;
4718906SEric.Saxe@Sun.COM case CPUPM_C_STATES:
4728906SEric.Saxe@Sun.COM if (CPU_ACPI_IS_OBJ_CACHED(handle, CPU_ACPI_CSD_CACHED)) {
4738906SEric.Saxe@Sun.COM domain = CPU_ACPI_CSD(handle).sd_domain;
4748906SEric.Saxe@Sun.COM type = CPU_ACPI_CSD(handle).sd_type;
4758906SEric.Saxe@Sun.COM } else {
476*12004Sjiang.liu@intel.com if (MUTEX_HELD(&cpu_lock)) {
477*12004Sjiang.liu@intel.com domain = cpuid_get_coreid(cp);
478*12004Sjiang.liu@intel.com } else {
479*12004Sjiang.liu@intel.com mutex_enter(&cpu_lock);
480*12004Sjiang.liu@intel.com domain = cpuid_get_coreid(cp);
481*12004Sjiang.liu@intel.com mutex_exit(&cpu_lock);
482*12004Sjiang.liu@intel.com }
4838906SEric.Saxe@Sun.COM type = CPU_ACPI_HW_ALL;
4848906SEric.Saxe@Sun.COM }
4858906SEric.Saxe@Sun.COM dom_ptr = &cpupm_cstate_domains;
4868906SEric.Saxe@Sun.COM mach_dom_state_ptr = &mach_state->ms_cstate.cma_domain;
4878906SEric.Saxe@Sun.COM break;
4888906SEric.Saxe@Sun.COM default:
4898906SEric.Saxe@Sun.COM return;
4908906SEric.Saxe@Sun.COM }
4918906SEric.Saxe@Sun.COM
4928906SEric.Saxe@Sun.COM for (dptr = *dom_ptr; dptr != NULL; dptr = dptr->pm_next) {
4938906SEric.Saxe@Sun.COM if (dptr->pm_domain == domain)
4948906SEric.Saxe@Sun.COM break;
4958906SEric.Saxe@Sun.COM }
4968906SEric.Saxe@Sun.COM
4978906SEric.Saxe@Sun.COM /* new domain is created and linked at the head */
4988906SEric.Saxe@Sun.COM if (dptr == NULL) {
4998906SEric.Saxe@Sun.COM dptr = kmem_zalloc(sizeof (cpupm_state_domains_t), KM_SLEEP);
5008906SEric.Saxe@Sun.COM dptr->pm_domain = domain;
5018906SEric.Saxe@Sun.COM dptr->pm_type = type;
5028906SEric.Saxe@Sun.COM dptr->pm_next = *dom_ptr;
5038906SEric.Saxe@Sun.COM mutex_init(&dptr->pm_lock, NULL, MUTEX_SPIN,
5048906SEric.Saxe@Sun.COM (void *)ipltospl(DISP_LEVEL));
5058906SEric.Saxe@Sun.COM CPUSET_ZERO(dptr->pm_cpus);
5068906SEric.Saxe@Sun.COM *dom_ptr = dptr;
5078906SEric.Saxe@Sun.COM }
5088906SEric.Saxe@Sun.COM CPUSET_ADD(dptr->pm_cpus, cp->cpu_id);
5098906SEric.Saxe@Sun.COM *mach_dom_state_ptr = dptr;
5108906SEric.Saxe@Sun.COM }
5118906SEric.Saxe@Sun.COM
5128906SEric.Saxe@Sun.COM /*
5138906SEric.Saxe@Sun.COM * Free C, P or T state power domains
5148906SEric.Saxe@Sun.COM */
5158906SEric.Saxe@Sun.COM void
cpupm_free_domains(cpupm_state_domains_t ** dom_ptr)5168906SEric.Saxe@Sun.COM cpupm_free_domains(cpupm_state_domains_t **dom_ptr)
5178906SEric.Saxe@Sun.COM {
5188906SEric.Saxe@Sun.COM cpupm_state_domains_t *this_domain, *next_domain;
5198906SEric.Saxe@Sun.COM
5208906SEric.Saxe@Sun.COM this_domain = *dom_ptr;
5218906SEric.Saxe@Sun.COM while (this_domain != NULL) {
5228906SEric.Saxe@Sun.COM next_domain = this_domain->pm_next;
5238906SEric.Saxe@Sun.COM mutex_destroy(&this_domain->pm_lock);
5248906SEric.Saxe@Sun.COM kmem_free((void *)this_domain,
5258906SEric.Saxe@Sun.COM sizeof (cpupm_state_domains_t));
5268906SEric.Saxe@Sun.COM this_domain = next_domain;
5278906SEric.Saxe@Sun.COM }
5288906SEric.Saxe@Sun.COM *dom_ptr = NULL;
5298906SEric.Saxe@Sun.COM }
5308906SEric.Saxe@Sun.COM
53110488SMark.Haywood@Sun.COM /*
53210488SMark.Haywood@Sun.COM * Remove CPU from C, P or T state power domains
53310488SMark.Haywood@Sun.COM */
53410488SMark.Haywood@Sun.COM void
cpupm_remove_domains(cpu_t * cp,int state,cpupm_state_domains_t ** dom_ptr)53510488SMark.Haywood@Sun.COM cpupm_remove_domains(cpu_t *cp, int state, cpupm_state_domains_t **dom_ptr)
53610488SMark.Haywood@Sun.COM {
53710488SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state =
53810488SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
53910488SMark.Haywood@Sun.COM cpupm_state_domains_t *dptr;
54010488SMark.Haywood@Sun.COM uint32_t pm_domain;
54110488SMark.Haywood@Sun.COM
54210488SMark.Haywood@Sun.COM ASSERT(mach_state);
54310488SMark.Haywood@Sun.COM
54410488SMark.Haywood@Sun.COM switch (state) {
54510488SMark.Haywood@Sun.COM case CPUPM_P_STATES:
54610488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_pstate.cma_domain->pm_domain;
54710488SMark.Haywood@Sun.COM break;
54810488SMark.Haywood@Sun.COM case CPUPM_T_STATES:
54910488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_tstate.cma_domain->pm_domain;
55010488SMark.Haywood@Sun.COM break;
55110488SMark.Haywood@Sun.COM case CPUPM_C_STATES:
55210488SMark.Haywood@Sun.COM pm_domain = mach_state->ms_cstate.cma_domain->pm_domain;
55310488SMark.Haywood@Sun.COM break;
55410488SMark.Haywood@Sun.COM default:
55510488SMark.Haywood@Sun.COM return;
55610488SMark.Haywood@Sun.COM }
55710488SMark.Haywood@Sun.COM
55810488SMark.Haywood@Sun.COM /*
55910488SMark.Haywood@Sun.COM * Find the CPU C, P or T state power domain
56010488SMark.Haywood@Sun.COM */
56110488SMark.Haywood@Sun.COM for (dptr = *dom_ptr; dptr != NULL; dptr = dptr->pm_next) {
56210488SMark.Haywood@Sun.COM if (dptr->pm_domain == pm_domain)
56310488SMark.Haywood@Sun.COM break;
56410488SMark.Haywood@Sun.COM }
56510488SMark.Haywood@Sun.COM
56610488SMark.Haywood@Sun.COM /*
56710488SMark.Haywood@Sun.COM * return if no matched domain found
56810488SMark.Haywood@Sun.COM */
56910488SMark.Haywood@Sun.COM if (dptr == NULL)
57010488SMark.Haywood@Sun.COM return;
57110488SMark.Haywood@Sun.COM
57210488SMark.Haywood@Sun.COM /*
57310488SMark.Haywood@Sun.COM * We found one matched power domain, remove CPU from its cpuset.
57410961Saubrey.li@intel.com * pm_lock(spin lock) here to avoid the race conditions between
57510488SMark.Haywood@Sun.COM * event change notification and cpu remove.
57610488SMark.Haywood@Sun.COM */
57710488SMark.Haywood@Sun.COM mutex_enter(&dptr->pm_lock);
57810488SMark.Haywood@Sun.COM if (CPU_IN_SET(dptr->pm_cpus, cp->cpu_id))
57910488SMark.Haywood@Sun.COM CPUSET_DEL(dptr->pm_cpus, cp->cpu_id);
58010488SMark.Haywood@Sun.COM mutex_exit(&dptr->pm_lock);
58110488SMark.Haywood@Sun.COM }
58210488SMark.Haywood@Sun.COM
5838906SEric.Saxe@Sun.COM void
cpupm_alloc_ms_cstate(cpu_t * cp)5848906SEric.Saxe@Sun.COM cpupm_alloc_ms_cstate(cpu_t *cp)
5858906SEric.Saxe@Sun.COM {
5868906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state;
5878906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate;
5888906SEric.Saxe@Sun.COM
5898906SEric.Saxe@Sun.COM mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
5908906SEric.Saxe@Sun.COM ms_cstate = &mach_state->ms_cstate;
5918906SEric.Saxe@Sun.COM ASSERT(ms_cstate->cma_state.cstate == NULL);
5928906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = kmem_zalloc(sizeof (cma_c_state_t),
5938906SEric.Saxe@Sun.COM KM_SLEEP);
5948906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate->cs_next_cstate = CPU_ACPI_C1;
5958906SEric.Saxe@Sun.COM }
5968906SEric.Saxe@Sun.COM
5978906SEric.Saxe@Sun.COM void
cpupm_free_ms_cstate(cpu_t * cp)5988906SEric.Saxe@Sun.COM cpupm_free_ms_cstate(cpu_t *cp)
5998906SEric.Saxe@Sun.COM {
6008906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
6018906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
6028906SEric.Saxe@Sun.COM cpupm_mach_acpi_state_t *ms_cstate = &mach_state->ms_cstate;
6038906SEric.Saxe@Sun.COM
6048906SEric.Saxe@Sun.COM if (ms_cstate->cma_state.cstate != NULL) {
6058906SEric.Saxe@Sun.COM kmem_free(ms_cstate->cma_state.cstate, sizeof (cma_c_state_t));
6068906SEric.Saxe@Sun.COM ms_cstate->cma_state.cstate = NULL;
6078906SEric.Saxe@Sun.COM }
6088906SEric.Saxe@Sun.COM }
6098906SEric.Saxe@Sun.COM
6108906SEric.Saxe@Sun.COM void
cpupm_state_change(cpu_t * cp,int level,int state)6118906SEric.Saxe@Sun.COM cpupm_state_change(cpu_t *cp, int level, int state)
6128906SEric.Saxe@Sun.COM {
6138906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
6148906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
6158906SEric.Saxe@Sun.COM cpupm_state_ops_t *state_ops;
6168906SEric.Saxe@Sun.COM cpupm_state_domains_t *state_domain;
6178906SEric.Saxe@Sun.COM cpuset_t set;
6188906SEric.Saxe@Sun.COM
6198906SEric.Saxe@Sun.COM DTRACE_PROBE2(cpupm__state__change, cpu_t *, cp, int, level);
6208906SEric.Saxe@Sun.COM
6218906SEric.Saxe@Sun.COM if (mach_state == NULL) {
6228906SEric.Saxe@Sun.COM return;
6238906SEric.Saxe@Sun.COM }
6248906SEric.Saxe@Sun.COM
6258906SEric.Saxe@Sun.COM switch (state) {
6268906SEric.Saxe@Sun.COM case CPUPM_P_STATES:
6278906SEric.Saxe@Sun.COM state_ops = mach_state->ms_pstate.cma_ops;
6288906SEric.Saxe@Sun.COM state_domain = mach_state->ms_pstate.cma_domain;
6298906SEric.Saxe@Sun.COM break;
6308906SEric.Saxe@Sun.COM case CPUPM_T_STATES:
6318906SEric.Saxe@Sun.COM state_ops = mach_state->ms_tstate.cma_ops;
6328906SEric.Saxe@Sun.COM state_domain = mach_state->ms_tstate.cma_domain;
6338906SEric.Saxe@Sun.COM break;
6348906SEric.Saxe@Sun.COM default:
6358906SEric.Saxe@Sun.COM break;
6368906SEric.Saxe@Sun.COM }
6378906SEric.Saxe@Sun.COM
6388906SEric.Saxe@Sun.COM switch (state_domain->pm_type) {
6398906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ANY:
6408906SEric.Saxe@Sun.COM /*
6418906SEric.Saxe@Sun.COM * A request on any CPU in the domain transitions the domain
6428906SEric.Saxe@Sun.COM */
6438906SEric.Saxe@Sun.COM CPUSET_ONLY(set, cp->cpu_id);
6448906SEric.Saxe@Sun.COM state_ops->cpus_change(set, level);
6458906SEric.Saxe@Sun.COM break;
6468906SEric.Saxe@Sun.COM case CPU_ACPI_SW_ALL:
6478906SEric.Saxe@Sun.COM /*
6488906SEric.Saxe@Sun.COM * All CPUs in the domain must request the transition
6498906SEric.Saxe@Sun.COM */
6508906SEric.Saxe@Sun.COM case CPU_ACPI_HW_ALL:
6518906SEric.Saxe@Sun.COM /*
6528906SEric.Saxe@Sun.COM * P/T-state transitions are coordinated by the hardware
6538906SEric.Saxe@Sun.COM * For now, request the transition on all CPUs in the domain,
6548906SEric.Saxe@Sun.COM * but looking ahead we can probably be smarter about this.
6558906SEric.Saxe@Sun.COM */
6568906SEric.Saxe@Sun.COM mutex_enter(&state_domain->pm_lock);
6578906SEric.Saxe@Sun.COM state_ops->cpus_change(state_domain->pm_cpus, level);
6588906SEric.Saxe@Sun.COM mutex_exit(&state_domain->pm_lock);
6598906SEric.Saxe@Sun.COM break;
6608906SEric.Saxe@Sun.COM default:
66110075SMark.Haywood@Sun.COM cmn_err(CE_NOTE, "Unknown domain coordination type: %d",
6628906SEric.Saxe@Sun.COM state_domain->pm_type);
6638906SEric.Saxe@Sun.COM }
6648906SEric.Saxe@Sun.COM }
6658906SEric.Saxe@Sun.COM
6668906SEric.Saxe@Sun.COM /*
6678906SEric.Saxe@Sun.COM * CPU PM interfaces exposed to the CPU power manager
6688906SEric.Saxe@Sun.COM */
6698906SEric.Saxe@Sun.COM /*ARGSUSED*/
6708906SEric.Saxe@Sun.COM id_t
cpupm_plat_domain_id(cpu_t * cp,cpupm_dtype_t type)6718906SEric.Saxe@Sun.COM cpupm_plat_domain_id(cpu_t *cp, cpupm_dtype_t type)
6728906SEric.Saxe@Sun.COM {
6738906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
6748906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
6758906SEric.Saxe@Sun.COM
6768906SEric.Saxe@Sun.COM if ((mach_state == NULL) || (!cpupm_is_enabled(CPUPM_P_STATES) &&
6778906SEric.Saxe@Sun.COM !cpupm_is_enabled(CPUPM_C_STATES))) {
6788906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN);
6798906SEric.Saxe@Sun.COM }
6808906SEric.Saxe@Sun.COM if (type == CPUPM_DTYPE_ACTIVE) {
6818906SEric.Saxe@Sun.COM /*
6828906SEric.Saxe@Sun.COM * Return P-State domain for the specified CPU
6838906SEric.Saxe@Sun.COM */
6848906SEric.Saxe@Sun.COM if (mach_state->ms_pstate.cma_domain) {
6858906SEric.Saxe@Sun.COM return (mach_state->ms_pstate.cma_domain->pm_domain);
6868906SEric.Saxe@Sun.COM }
6878906SEric.Saxe@Sun.COM } else if (type == CPUPM_DTYPE_IDLE) {
6888906SEric.Saxe@Sun.COM /*
6898906SEric.Saxe@Sun.COM * Return C-State domain for the specified CPU
6908906SEric.Saxe@Sun.COM */
6918906SEric.Saxe@Sun.COM if (mach_state->ms_cstate.cma_domain) {
6928906SEric.Saxe@Sun.COM return (mach_state->ms_cstate.cma_domain->pm_domain);
6938906SEric.Saxe@Sun.COM }
6948906SEric.Saxe@Sun.COM }
6958906SEric.Saxe@Sun.COM return (CPUPM_NO_DOMAIN);
6968906SEric.Saxe@Sun.COM }
6978906SEric.Saxe@Sun.COM
6988906SEric.Saxe@Sun.COM /*ARGSUSED*/
6998906SEric.Saxe@Sun.COM uint_t
cpupm_plat_state_enumerate(cpu_t * cp,cpupm_dtype_t type,cpupm_state_t * states)7008906SEric.Saxe@Sun.COM cpupm_plat_state_enumerate(cpu_t *cp, cpupm_dtype_t type,
7018906SEric.Saxe@Sun.COM cpupm_state_t *states)
7028906SEric.Saxe@Sun.COM {
7038906SEric.Saxe@Sun.COM int *speeds;
7048906SEric.Saxe@Sun.COM uint_t nspeeds, i;
7058906SEric.Saxe@Sun.COM
7068906SEric.Saxe@Sun.COM /*
7078906SEric.Saxe@Sun.COM * Idle domain support unimplemented
7088906SEric.Saxe@Sun.COM */
7098906SEric.Saxe@Sun.COM if (type != CPUPM_DTYPE_ACTIVE) {
7108906SEric.Saxe@Sun.COM return (0);
7118906SEric.Saxe@Sun.COM }
7128906SEric.Saxe@Sun.COM nspeeds = cpupm_get_speeds(cp, &speeds);
7138906SEric.Saxe@Sun.COM
7148906SEric.Saxe@Sun.COM /*
7158906SEric.Saxe@Sun.COM * If the caller passes NULL for states, just return the
7168906SEric.Saxe@Sun.COM * number of states.
7178906SEric.Saxe@Sun.COM */
7188906SEric.Saxe@Sun.COM if (states != NULL) {
7198906SEric.Saxe@Sun.COM for (i = 0; i < nspeeds; i++) {
7208906SEric.Saxe@Sun.COM states[i].cps_speed = speeds[i];
7218906SEric.Saxe@Sun.COM states[i].cps_handle = (cpupm_handle_t)i;
7228906SEric.Saxe@Sun.COM }
7238906SEric.Saxe@Sun.COM }
7248906SEric.Saxe@Sun.COM cpupm_free_speeds(speeds, nspeeds);
7258906SEric.Saxe@Sun.COM return (nspeeds);
7268906SEric.Saxe@Sun.COM }
7278906SEric.Saxe@Sun.COM
7288906SEric.Saxe@Sun.COM /*ARGSUSED*/
7298906SEric.Saxe@Sun.COM int
cpupm_plat_change_state(cpu_t * cp,cpupm_state_t * state)7308906SEric.Saxe@Sun.COM cpupm_plat_change_state(cpu_t *cp, cpupm_state_t *state)
7318906SEric.Saxe@Sun.COM {
73210488SMark.Haywood@Sun.COM if (!cpupm_is_ready(cp))
7338906SEric.Saxe@Sun.COM return (-1);
7348906SEric.Saxe@Sun.COM
7358906SEric.Saxe@Sun.COM cpupm_state_change(cp, (int)state->cps_handle, CPUPM_P_STATES);
7368906SEric.Saxe@Sun.COM
7378906SEric.Saxe@Sun.COM return (0);
7388906SEric.Saxe@Sun.COM }
7398906SEric.Saxe@Sun.COM
7408906SEric.Saxe@Sun.COM /*ARGSUSED*/
7418906SEric.Saxe@Sun.COM /*
7428906SEric.Saxe@Sun.COM * Note: It is the responsibility of the users of
7438906SEric.Saxe@Sun.COM * cpupm_get_speeds() to free the memory allocated
7448906SEric.Saxe@Sun.COM * for speeds using cpupm_free_speeds()
7458906SEric.Saxe@Sun.COM */
7468906SEric.Saxe@Sun.COM uint_t
cpupm_get_speeds(cpu_t * cp,int ** speeds)7478906SEric.Saxe@Sun.COM cpupm_get_speeds(cpu_t *cp, int **speeds)
7488906SEric.Saxe@Sun.COM {
7498906SEric.Saxe@Sun.COM #ifndef __xpv
7508906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
7518906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
7528906SEric.Saxe@Sun.COM return (cpu_acpi_get_speeds(mach_state->ms_acpi_handle, speeds));
7538906SEric.Saxe@Sun.COM #else
7548906SEric.Saxe@Sun.COM return (0);
7558906SEric.Saxe@Sun.COM #endif
7568906SEric.Saxe@Sun.COM }
7578906SEric.Saxe@Sun.COM
7588906SEric.Saxe@Sun.COM /*ARGSUSED*/
7598906SEric.Saxe@Sun.COM void
cpupm_free_speeds(int * speeds,uint_t nspeeds)7608906SEric.Saxe@Sun.COM cpupm_free_speeds(int *speeds, uint_t nspeeds)
7618906SEric.Saxe@Sun.COM {
7628906SEric.Saxe@Sun.COM #ifndef __xpv
7638906SEric.Saxe@Sun.COM cpu_acpi_free_speeds(speeds, nspeeds);
7648906SEric.Saxe@Sun.COM #endif
7658906SEric.Saxe@Sun.COM }
7668906SEric.Saxe@Sun.COM
7678906SEric.Saxe@Sun.COM /*
7688906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully.
7698906SEric.Saxe@Sun.COM */
7708906SEric.Saxe@Sun.COM boolean_t
cpupm_power_ready(cpu_t * cp)77110488SMark.Haywood@Sun.COM cpupm_power_ready(cpu_t *cp)
7728906SEric.Saxe@Sun.COM {
77310488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_P_STATES) && cpupm_is_ready(cp));
7748906SEric.Saxe@Sun.COM }
7758906SEric.Saxe@Sun.COM
7768906SEric.Saxe@Sun.COM /*
7778906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully.
7788906SEric.Saxe@Sun.COM */
7798906SEric.Saxe@Sun.COM boolean_t
cpupm_throttle_ready(cpu_t * cp)78010488SMark.Haywood@Sun.COM cpupm_throttle_ready(cpu_t *cp)
7818906SEric.Saxe@Sun.COM {
78210488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_T_STATES) && cpupm_is_ready(cp));
7838906SEric.Saxe@Sun.COM }
7848906SEric.Saxe@Sun.COM
7858906SEric.Saxe@Sun.COM /*
7868906SEric.Saxe@Sun.COM * All CPU instances have been initialized successfully.
7878906SEric.Saxe@Sun.COM */
7888906SEric.Saxe@Sun.COM boolean_t
cpupm_cstate_ready(cpu_t * cp)78910488SMark.Haywood@Sun.COM cpupm_cstate_ready(cpu_t *cp)
7908906SEric.Saxe@Sun.COM {
79110488SMark.Haywood@Sun.COM return (cpupm_is_enabled(CPUPM_C_STATES) && cpupm_is_ready(cp));
7928906SEric.Saxe@Sun.COM }
7938906SEric.Saxe@Sun.COM
7948906SEric.Saxe@Sun.COM void
cpupm_notify_handler(ACPI_HANDLE obj,UINT32 val,void * ctx)7958906SEric.Saxe@Sun.COM cpupm_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx)
7968906SEric.Saxe@Sun.COM {
7978906SEric.Saxe@Sun.COM cpu_t *cp = ctx;
7988906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
7998906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
8008906SEric.Saxe@Sun.COM cpupm_notification_t *entry;
8018906SEric.Saxe@Sun.COM
8028906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock);
8038906SEric.Saxe@Sun.COM for (entry = mach_state->ms_handlers; entry != NULL;
8048906SEric.Saxe@Sun.COM entry = entry->nq_next) {
8058906SEric.Saxe@Sun.COM entry->nq_handler(obj, val, entry->nq_ctx);
8068906SEric.Saxe@Sun.COM }
8078906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock);
8088906SEric.Saxe@Sun.COM }
8098906SEric.Saxe@Sun.COM
8108906SEric.Saxe@Sun.COM /*ARGSUSED*/
8118906SEric.Saxe@Sun.COM void
cpupm_add_notify_handler(cpu_t * cp,CPUPM_NOTIFY_HANDLER handler,void * ctx)8128906SEric.Saxe@Sun.COM cpupm_add_notify_handler(cpu_t *cp, CPUPM_NOTIFY_HANDLER handler, void *ctx)
8138906SEric.Saxe@Sun.COM {
8148906SEric.Saxe@Sun.COM #ifndef __xpv
8158906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
8168906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
8178906SEric.Saxe@Sun.COM cpupm_notification_t *entry;
8188906SEric.Saxe@Sun.COM
8198906SEric.Saxe@Sun.COM entry = kmem_zalloc(sizeof (cpupm_notification_t), KM_SLEEP);
8208906SEric.Saxe@Sun.COM entry->nq_handler = handler;
8218906SEric.Saxe@Sun.COM entry->nq_ctx = ctx;
8228906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock);
8238906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) {
8248906SEric.Saxe@Sun.COM entry->nq_next = NULL;
8258906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry;
8268906SEric.Saxe@Sun.COM cpu_acpi_install_notify_handler(mach_state->ms_acpi_handle,
8278906SEric.Saxe@Sun.COM cpupm_notify_handler, cp);
8288906SEric.Saxe@Sun.COM
8298906SEric.Saxe@Sun.COM } else {
8308906SEric.Saxe@Sun.COM entry->nq_next = mach_state->ms_handlers;
8318906SEric.Saxe@Sun.COM mach_state->ms_handlers = entry;
8328906SEric.Saxe@Sun.COM }
8338906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock);
8348906SEric.Saxe@Sun.COM #endif
8358906SEric.Saxe@Sun.COM }
8368906SEric.Saxe@Sun.COM
8378906SEric.Saxe@Sun.COM /*ARGSUSED*/
8388906SEric.Saxe@Sun.COM static void
cpupm_free_notify_handlers(cpu_t * cp)8398906SEric.Saxe@Sun.COM cpupm_free_notify_handlers(cpu_t *cp)
8408906SEric.Saxe@Sun.COM {
8418906SEric.Saxe@Sun.COM #ifndef __xpv
8428906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state =
8438906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
8448906SEric.Saxe@Sun.COM cpupm_notification_t *entry;
8458906SEric.Saxe@Sun.COM cpupm_notification_t *next;
8468906SEric.Saxe@Sun.COM
8478906SEric.Saxe@Sun.COM mutex_enter(&mach_state->ms_lock);
8488906SEric.Saxe@Sun.COM if (mach_state->ms_handlers == NULL) {
8498906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock);
8508906SEric.Saxe@Sun.COM return;
8518906SEric.Saxe@Sun.COM }
8528906SEric.Saxe@Sun.COM if (mach_state->ms_acpi_handle != NULL) {
8538906SEric.Saxe@Sun.COM cpu_acpi_remove_notify_handler(mach_state->ms_acpi_handle,
8548906SEric.Saxe@Sun.COM cpupm_notify_handler);
8558906SEric.Saxe@Sun.COM }
8568906SEric.Saxe@Sun.COM entry = mach_state->ms_handlers;
8578906SEric.Saxe@Sun.COM while (entry != NULL) {
8588906SEric.Saxe@Sun.COM next = entry->nq_next;
8598906SEric.Saxe@Sun.COM kmem_free(entry, sizeof (cpupm_notification_t));
8608906SEric.Saxe@Sun.COM entry = next;
8618906SEric.Saxe@Sun.COM }
8628906SEric.Saxe@Sun.COM mach_state->ms_handlers = NULL;
8638906SEric.Saxe@Sun.COM mutex_exit(&mach_state->ms_lock);
8648906SEric.Saxe@Sun.COM #endif
8658906SEric.Saxe@Sun.COM }
8668906SEric.Saxe@Sun.COM
8678906SEric.Saxe@Sun.COM /*
8688906SEric.Saxe@Sun.COM * Get the current max speed from the ACPI _PPC object
8698906SEric.Saxe@Sun.COM */
8708906SEric.Saxe@Sun.COM /*ARGSUSED*/
8718906SEric.Saxe@Sun.COM int
cpupm_get_top_speed(cpu_t * cp)8728906SEric.Saxe@Sun.COM cpupm_get_top_speed(cpu_t *cp)
8738906SEric.Saxe@Sun.COM {
8748906SEric.Saxe@Sun.COM #ifndef __xpv
8758906SEric.Saxe@Sun.COM cpupm_mach_state_t *mach_state;
8768906SEric.Saxe@Sun.COM cpu_acpi_handle_t handle;
8778906SEric.Saxe@Sun.COM int plat_level;
8788906SEric.Saxe@Sun.COM uint_t nspeeds;
8798906SEric.Saxe@Sun.COM int max_level;
8808906SEric.Saxe@Sun.COM
8818906SEric.Saxe@Sun.COM mach_state =
8828906SEric.Saxe@Sun.COM (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
8838906SEric.Saxe@Sun.COM handle = mach_state->ms_acpi_handle;
8848906SEric.Saxe@Sun.COM
8858906SEric.Saxe@Sun.COM cpu_acpi_cache_ppc(handle);
8868906SEric.Saxe@Sun.COM plat_level = CPU_ACPI_PPC(handle);
8878906SEric.Saxe@Sun.COM
8888906SEric.Saxe@Sun.COM nspeeds = CPU_ACPI_PSTATES_COUNT(handle);
8898906SEric.Saxe@Sun.COM
8908906SEric.Saxe@Sun.COM max_level = nspeeds - 1;
8918906SEric.Saxe@Sun.COM if ((plat_level < 0) || (plat_level > max_level)) {
8928906SEric.Saxe@Sun.COM cmn_err(CE_NOTE, "!cpupm_get_top_speed: CPU %d: "
8938906SEric.Saxe@Sun.COM "_PPC out of range %d", cp->cpu_id, plat_level);
8948906SEric.Saxe@Sun.COM plat_level = 0;
8958906SEric.Saxe@Sun.COM }
8968906SEric.Saxe@Sun.COM
8978906SEric.Saxe@Sun.COM return (plat_level);
8988906SEric.Saxe@Sun.COM #else
8998906SEric.Saxe@Sun.COM return (0);
9008906SEric.Saxe@Sun.COM #endif
9018906SEric.Saxe@Sun.COM }
9028906SEric.Saxe@Sun.COM
9038906SEric.Saxe@Sun.COM /*
9048906SEric.Saxe@Sun.COM * This notification handler is called whenever the ACPI _PPC
9058906SEric.Saxe@Sun.COM * object changes. The _PPC is a sort of governor on power levels.
9068906SEric.Saxe@Sun.COM * It sets an upper threshold on which, _PSS defined, power levels
9078906SEric.Saxe@Sun.COM * are usuable. The _PPC value is dynamic and may change as properties
9088906SEric.Saxe@Sun.COM * (i.e., thermal or AC source) of the system change.
9098906SEric.Saxe@Sun.COM */
9108906SEric.Saxe@Sun.COM
9118906SEric.Saxe@Sun.COM static void
cpupm_power_manage_notifications(void * ctx)9128906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(void *ctx)
9138906SEric.Saxe@Sun.COM {
9148906SEric.Saxe@Sun.COM cpu_t *cp = ctx;
9158906SEric.Saxe@Sun.COM int top_speed;
9168906SEric.Saxe@Sun.COM
9178906SEric.Saxe@Sun.COM top_speed = cpupm_get_top_speed(cp);
9188906SEric.Saxe@Sun.COM cpupm_redefine_max_activepwr_state(cp, top_speed);
9198906SEric.Saxe@Sun.COM }
9208906SEric.Saxe@Sun.COM
9218906SEric.Saxe@Sun.COM /* ARGSUSED */
9228906SEric.Saxe@Sun.COM static void
cpupm_event_notify_handler(ACPI_HANDLE obj,UINT32 val,void * ctx)9238906SEric.Saxe@Sun.COM cpupm_event_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx)
9248906SEric.Saxe@Sun.COM {
9258906SEric.Saxe@Sun.COM #ifndef __xpv
9269040SMark.Haywood@Sun.COM
9279040SMark.Haywood@Sun.COM cpu_t *cp = ctx;
9289040SMark.Haywood@Sun.COM cpupm_mach_state_t *mach_state =
9299040SMark.Haywood@Sun.COM (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
9309040SMark.Haywood@Sun.COM
9319040SMark.Haywood@Sun.COM if (mach_state == NULL)
9329040SMark.Haywood@Sun.COM return;
9339040SMark.Haywood@Sun.COM
9348906SEric.Saxe@Sun.COM /*
9358906SEric.Saxe@Sun.COM * Currently, we handle _TPC,_CST and _PPC change notifications.
9368906SEric.Saxe@Sun.COM */
9379040SMark.Haywood@Sun.COM if (val == CPUPM_TPC_CHANGE_NOTIFICATION &&
9389040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_T_STATES) {
9398906SEric.Saxe@Sun.COM cpupm_throttle_manage_notification(ctx);
9409040SMark.Haywood@Sun.COM } else if (val == CPUPM_CST_CHANGE_NOTIFICATION &&
9419040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_C_STATES) {
9428906SEric.Saxe@Sun.COM cpuidle_manage_cstates(ctx);
9439040SMark.Haywood@Sun.COM } else if (val == CPUPM_PPC_CHANGE_NOTIFICATION &&
9449040SMark.Haywood@Sun.COM mach_state->ms_caps & CPUPM_P_STATES) {
9458906SEric.Saxe@Sun.COM cpupm_power_manage_notifications(ctx);
9468906SEric.Saxe@Sun.COM }
9478906SEric.Saxe@Sun.COM #endif
9488906SEric.Saxe@Sun.COM }
9498906SEric.Saxe@Sun.COM
9508906SEric.Saxe@Sun.COM /*
9518906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU exits idle.
9528906SEric.Saxe@Sun.COM */
9538906SEric.Saxe@Sun.COM void
cpupm_wakeup_cstate_data(cma_c_state_t * cs_data,hrtime_t end)9548906SEric.Saxe@Sun.COM cpupm_wakeup_cstate_data(cma_c_state_t *cs_data, hrtime_t end)
9558906SEric.Saxe@Sun.COM {
9568906SEric.Saxe@Sun.COM cs_data->cs_idle_exit = end;
9578906SEric.Saxe@Sun.COM }
9588906SEric.Saxe@Sun.COM
9598906SEric.Saxe@Sun.COM /*
9608906SEric.Saxe@Sun.COM * Determine next cstate based on cpupm data.
9618906SEric.Saxe@Sun.COM * Update cpupm cstate data each time CPU goes idle.
9628906SEric.Saxe@Sun.COM * Do as much as possible in the idle state bookkeeping function because the
9638906SEric.Saxe@Sun.COM * performance impact while idle is minimal compared to in the wakeup function
9648906SEric.Saxe@Sun.COM * when there is real work to do.
9658906SEric.Saxe@Sun.COM */
9668906SEric.Saxe@Sun.COM uint32_t
cpupm_next_cstate(cma_c_state_t * cs_data,cpu_acpi_cstate_t * cstates,uint32_t cs_count,hrtime_t start)9678983SBill.Holler@Sun.COM cpupm_next_cstate(cma_c_state_t *cs_data, cpu_acpi_cstate_t *cstates,
9688983SBill.Holler@Sun.COM uint32_t cs_count, hrtime_t start)
9698906SEric.Saxe@Sun.COM {
9708983SBill.Holler@Sun.COM hrtime_t duration;
9718983SBill.Holler@Sun.COM hrtime_t ave_interval;
9728983SBill.Holler@Sun.COM hrtime_t ave_idle_time;
97310353Saubrey.li@intel.com uint32_t i, smpl_cnt;
9748906SEric.Saxe@Sun.COM
9758906SEric.Saxe@Sun.COM duration = cs_data->cs_idle_exit - cs_data->cs_idle_enter;
9768906SEric.Saxe@Sun.COM scalehrtime(&duration);
9778906SEric.Saxe@Sun.COM cs_data->cs_idle += duration;
9788906SEric.Saxe@Sun.COM cs_data->cs_idle_enter = start;
9798906SEric.Saxe@Sun.COM
98010353Saubrey.li@intel.com smpl_cnt = ++cs_data->cs_cnt;
98110353Saubrey.li@intel.com cs_data->cs_smpl_len = start - cs_data->cs_smpl_start;
98210353Saubrey.li@intel.com scalehrtime(&cs_data->cs_smpl_len);
98310353Saubrey.li@intel.com if (cs_data->cs_smpl_len > cpupm_cs_sample_interval) {
9848906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle = cs_data->cs_idle;
9858906SEric.Saxe@Sun.COM cs_data->cs_idle = 0;
9868906SEric.Saxe@Sun.COM cs_data->cs_smpl_idle_pct = ((100 * cs_data->cs_smpl_idle) /
9878906SEric.Saxe@Sun.COM cs_data->cs_smpl_len);
9888906SEric.Saxe@Sun.COM
9898906SEric.Saxe@Sun.COM cs_data->cs_smpl_start = start;
9908906SEric.Saxe@Sun.COM cs_data->cs_cnt = 0;
9918906SEric.Saxe@Sun.COM
9928906SEric.Saxe@Sun.COM /*
9938906SEric.Saxe@Sun.COM * Strand level C-state policy
9948983SBill.Holler@Sun.COM * The cpu_acpi_cstate_t *cstates array is not required to
9958983SBill.Holler@Sun.COM * have an entry for both CPU_ACPI_C2 and CPU_ACPI_C3.
9968983SBill.Holler@Sun.COM * There are cs_count entries in the cstates array.
9978983SBill.Holler@Sun.COM * cs_data->cs_next_cstate contains the index of the next
9988983SBill.Holler@Sun.COM * C-state this CPU should enter.
9998906SEric.Saxe@Sun.COM */
10008983SBill.Holler@Sun.COM ASSERT(cstates[0].cs_type == CPU_ACPI_C1);
10018906SEric.Saxe@Sun.COM
10028906SEric.Saxe@Sun.COM /*
10038906SEric.Saxe@Sun.COM * Will CPU be idle long enough to save power?
10048906SEric.Saxe@Sun.COM */
100510353Saubrey.li@intel.com ave_idle_time = (cs_data->cs_smpl_idle / smpl_cnt) / 1000;
10068983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) {
10078983SBill.Holler@Sun.COM if (ave_idle_time < (cstates[i].cs_latency *
10088983SBill.Holler@Sun.COM cpupm_cs_idle_save_tunable)) {
10098983SBill.Holler@Sun.COM cs_count = i;
10108983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *,
10118983SBill.Holler@Sun.COM CPU, int, i);
10128983SBill.Holler@Sun.COM }
10138906SEric.Saxe@Sun.COM }
10148906SEric.Saxe@Sun.COM
10158906SEric.Saxe@Sun.COM /*
10168906SEric.Saxe@Sun.COM * Wakeup often (even when non-idle time is very short)?
10178906SEric.Saxe@Sun.COM * Some producer/consumer type loads fall into this category.
10188906SEric.Saxe@Sun.COM */
101910353Saubrey.li@intel.com ave_interval = (cs_data->cs_smpl_len / smpl_cnt) / 1000;
10208983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) {
10218983SBill.Holler@Sun.COM if (ave_interval <= (cstates[i].cs_latency *
10228983SBill.Holler@Sun.COM cpupm_cs_idle_cost_tunable)) {
10238983SBill.Holler@Sun.COM cs_count = i;
10248983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate, cpu_t *,
10258983SBill.Holler@Sun.COM CPU, int, (CPU_MAX_CSTATES + i));
10268983SBill.Holler@Sun.COM }
10278906SEric.Saxe@Sun.COM }
10288906SEric.Saxe@Sun.COM
10298906SEric.Saxe@Sun.COM /*
10308906SEric.Saxe@Sun.COM * Idle percent
10318906SEric.Saxe@Sun.COM */
10328983SBill.Holler@Sun.COM for (i = 1; i < cs_count; ++i) {
10338983SBill.Holler@Sun.COM switch (cstates[i].cs_type) {
10348983SBill.Holler@Sun.COM case CPU_ACPI_C2:
10358983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct <
10368983SBill.Holler@Sun.COM cpupm_C2_idle_pct_tunable) {
10378983SBill.Holler@Sun.COM cs_count = i;
10388983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate,
10398983SBill.Holler@Sun.COM cpu_t *, CPU, int,
10408983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i));
10418983SBill.Holler@Sun.COM }
10428983SBill.Holler@Sun.COM break;
10438983SBill.Holler@Sun.COM
10448983SBill.Holler@Sun.COM case CPU_ACPI_C3:
10458983SBill.Holler@Sun.COM if (cs_data->cs_smpl_idle_pct <
10468983SBill.Holler@Sun.COM cpupm_C3_idle_pct_tunable) {
10478983SBill.Holler@Sun.COM cs_count = i;
10488983SBill.Holler@Sun.COM DTRACE_PROBE2(cpupm__next__cstate,
10498983SBill.Holler@Sun.COM cpu_t *, CPU, int,
10508983SBill.Holler@Sun.COM ((2 * CPU_MAX_CSTATES) + i));
10518983SBill.Holler@Sun.COM }
10528983SBill.Holler@Sun.COM break;
10538983SBill.Holler@Sun.COM }
10548906SEric.Saxe@Sun.COM }
10558983SBill.Holler@Sun.COM
10568983SBill.Holler@Sun.COM cs_data->cs_next_cstate = cs_count - 1;
10578906SEric.Saxe@Sun.COM }
10588906SEric.Saxe@Sun.COM
10598906SEric.Saxe@Sun.COM return (cs_data->cs_next_cstate);
10608906SEric.Saxe@Sun.COM }
1061