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