xref: /onnv-gate/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 10075:254480e5218d)
18906SEric.Saxe@Sun.COM /*
28906SEric.Saxe@Sun.COM  * CDDL HEADER START
38906SEric.Saxe@Sun.COM  *
48906SEric.Saxe@Sun.COM  * The contents of this file are subject to the terms of the
58906SEric.Saxe@Sun.COM  * Common Development and Distribution License (the "License").
68906SEric.Saxe@Sun.COM  * You may not use this file except in compliance with the License.
78906SEric.Saxe@Sun.COM  *
88906SEric.Saxe@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98906SEric.Saxe@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108906SEric.Saxe@Sun.COM  * See the License for the specific language governing permissions
118906SEric.Saxe@Sun.COM  * and limitations under the License.
128906SEric.Saxe@Sun.COM  *
138906SEric.Saxe@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148906SEric.Saxe@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158906SEric.Saxe@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168906SEric.Saxe@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178906SEric.Saxe@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188906SEric.Saxe@Sun.COM  *
198906SEric.Saxe@Sun.COM  * CDDL HEADER END
208906SEric.Saxe@Sun.COM  */
218906SEric.Saxe@Sun.COM /*
228906SEric.Saxe@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238906SEric.Saxe@Sun.COM  * Use is subject to license terms.
248906SEric.Saxe@Sun.COM  */
258906SEric.Saxe@Sun.COM 
268906SEric.Saxe@Sun.COM #include <sys/x86_archext.h>
278906SEric.Saxe@Sun.COM #include <sys/machsystm.h>
288935Srafael.vanoni@sun.com #include <sys/archsystm.h>
298906SEric.Saxe@Sun.COM #include <sys/x_call.h>
308906SEric.Saxe@Sun.COM #include <sys/acpi/acpi.h>
318906SEric.Saxe@Sun.COM #include <sys/acpica.h>
328906SEric.Saxe@Sun.COM #include <sys/speedstep.h>
338906SEric.Saxe@Sun.COM #include <sys/cpu_acpi.h>
348906SEric.Saxe@Sun.COM #include <sys/cpupm.h>
358906SEric.Saxe@Sun.COM #include <sys/dtrace.h>
368906SEric.Saxe@Sun.COM #include <sys/sdt.h>
378906SEric.Saxe@Sun.COM 
388935Srafael.vanoni@sun.com /*
398935Srafael.vanoni@sun.com  * turbo related structure definitions
408935Srafael.vanoni@sun.com  */
418935Srafael.vanoni@sun.com typedef struct cpupm_turbo_info {
428935Srafael.vanoni@sun.com 	kstat_t		*turbo_ksp;		/* turbo kstat */
438935Srafael.vanoni@sun.com 	int		in_turbo;		/* in turbo? */
448935Srafael.vanoni@sun.com 	int		turbo_supported;	/* turbo flag */
458935Srafael.vanoni@sun.com 	uint64_t	t_mcnt;			/* turbo mcnt */
468935Srafael.vanoni@sun.com 	uint64_t	t_acnt;			/* turbo acnt */
478935Srafael.vanoni@sun.com } cpupm_turbo_info_t;
488935Srafael.vanoni@sun.com 
498935Srafael.vanoni@sun.com typedef struct turbo_kstat_s {
508935Srafael.vanoni@sun.com 	struct kstat_named	turbo_supported;	/* turbo flag */
518935Srafael.vanoni@sun.com 	struct kstat_named	t_mcnt;			/* IA32_MPERF_MSR */
528935Srafael.vanoni@sun.com 	struct kstat_named	t_acnt;			/* IA32_APERF_MSR */
538935Srafael.vanoni@sun.com } turbo_kstat_t;
548935Srafael.vanoni@sun.com 
558906SEric.Saxe@Sun.COM static int speedstep_init(cpu_t *);
568906SEric.Saxe@Sun.COM static void speedstep_fini(cpu_t *);
578906SEric.Saxe@Sun.COM static void speedstep_power(cpuset_t, uint32_t);
588935Srafael.vanoni@sun.com static boolean_t turbo_supported(void);
598935Srafael.vanoni@sun.com static int turbo_kstat_update(kstat_t *, int);
608935Srafael.vanoni@sun.com static void get_turbo_info(cpupm_turbo_info_t *);
618935Srafael.vanoni@sun.com static void reset_turbo_info(void);
628935Srafael.vanoni@sun.com static void record_turbo_info(cpupm_turbo_info_t *, uint32_t, uint32_t);
638935Srafael.vanoni@sun.com static void update_turbo_info(cpupm_turbo_info_t *);
648906SEric.Saxe@Sun.COM 
658906SEric.Saxe@Sun.COM /*
668906SEric.Saxe@Sun.COM  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
678906SEric.Saxe@Sun.COM  */
688906SEric.Saxe@Sun.COM cpupm_state_ops_t speedstep_ops = {
698906SEric.Saxe@Sun.COM 	"Enhanced SpeedStep Technology",
708906SEric.Saxe@Sun.COM 	speedstep_init,
718906SEric.Saxe@Sun.COM 	speedstep_fini,
728906SEric.Saxe@Sun.COM 	speedstep_power
738906SEric.Saxe@Sun.COM };
748906SEric.Saxe@Sun.COM 
758906SEric.Saxe@Sun.COM /*
768906SEric.Saxe@Sun.COM  * Error returns
778906SEric.Saxe@Sun.COM  */
788906SEric.Saxe@Sun.COM #define	ESS_RET_SUCCESS		0x00
798906SEric.Saxe@Sun.COM #define	ESS_RET_NO_PM		0x01
808906SEric.Saxe@Sun.COM #define	ESS_RET_UNSUP_STATE	0x02
818906SEric.Saxe@Sun.COM 
828906SEric.Saxe@Sun.COM /*
838906SEric.Saxe@Sun.COM  * MSR registers for changing and reading processor power state.
848906SEric.Saxe@Sun.COM  */
858906SEric.Saxe@Sun.COM #define	IA32_PERF_STAT_MSR		0x198
868906SEric.Saxe@Sun.COM #define	IA32_PERF_CTL_MSR		0x199
878906SEric.Saxe@Sun.COM 
888906SEric.Saxe@Sun.COM #define	IA32_CPUID_TSC_CONSTANT		0xF30
898906SEric.Saxe@Sun.COM #define	IA32_MISC_ENABLE_MSR		0x1A0
908906SEric.Saxe@Sun.COM #define	IA32_MISC_ENABLE_EST		(1<<16)
918906SEric.Saxe@Sun.COM #define	IA32_MISC_ENABLE_CXE		(1<<25)
928935Srafael.vanoni@sun.com 
938935Srafael.vanoni@sun.com #define	CPUID_TURBO_SUPPORT		(1 << 1)
948935Srafael.vanoni@sun.com #define	CPU_ACPI_P0			0
958935Srafael.vanoni@sun.com #define	CPU_IN_TURBO			1
968935Srafael.vanoni@sun.com 
978935Srafael.vanoni@sun.com /*
988935Srafael.vanoni@sun.com  * MSR for hardware coordination feedback mechanism
998935Srafael.vanoni@sun.com  *   - IA32_MPERF: increments in proportion to a fixed frequency
1008935Srafael.vanoni@sun.com  *   - IA32_APERF: increments in proportion to actual performance
1018935Srafael.vanoni@sun.com  */
1028935Srafael.vanoni@sun.com #define	IA32_MPERF_MSR			0xE7
1038935Srafael.vanoni@sun.com #define	IA32_APERF_MSR			0xE8
1048935Srafael.vanoni@sun.com 
1058906SEric.Saxe@Sun.COM /*
1068906SEric.Saxe@Sun.COM  * Debugging support
1078906SEric.Saxe@Sun.COM  */
1088906SEric.Saxe@Sun.COM #ifdef	DEBUG
1098906SEric.Saxe@Sun.COM volatile int ess_debug = 0;
1108906SEric.Saxe@Sun.COM #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
1118906SEric.Saxe@Sun.COM #else
1128906SEric.Saxe@Sun.COM #define	ESSDEBUG(arglist)
1138906SEric.Saxe@Sun.COM #endif
1148906SEric.Saxe@Sun.COM 
1158935Srafael.vanoni@sun.com static kmutex_t turbo_mutex;
1168935Srafael.vanoni@sun.com 
1178935Srafael.vanoni@sun.com turbo_kstat_t turbo_kstat = {
1188935Srafael.vanoni@sun.com 	{ "turbo_supported",	KSTAT_DATA_UINT32 },
1198935Srafael.vanoni@sun.com 	{ "turbo_mcnt",		KSTAT_DATA_UINT64 },
1208935Srafael.vanoni@sun.com 	{ "turbo_acnt",		KSTAT_DATA_UINT64 },
1218935Srafael.vanoni@sun.com };
1228935Srafael.vanoni@sun.com 
1238935Srafael.vanoni@sun.com /*
1248935Srafael.vanoni@sun.com  * kstat update function of the turbo mode info
1258935Srafael.vanoni@sun.com  */
1268935Srafael.vanoni@sun.com static int
1278935Srafael.vanoni@sun.com turbo_kstat_update(kstat_t *ksp, int flag)
1288935Srafael.vanoni@sun.com {
1298935Srafael.vanoni@sun.com 	cpupm_turbo_info_t *turbo_info = ksp->ks_private;
1308935Srafael.vanoni@sun.com 
1318935Srafael.vanoni@sun.com 	if (flag == KSTAT_WRITE) {
1328935Srafael.vanoni@sun.com 		return (EACCES);
1338935Srafael.vanoni@sun.com 	}
1348935Srafael.vanoni@sun.com 
1358935Srafael.vanoni@sun.com 	/*
1368935Srafael.vanoni@sun.com 	 * update the count in case CPU is in the turbo
1378935Srafael.vanoni@sun.com 	 * mode for a long time
1388935Srafael.vanoni@sun.com 	 */
1398935Srafael.vanoni@sun.com 	if (turbo_info->in_turbo == CPU_IN_TURBO)
1408935Srafael.vanoni@sun.com 		update_turbo_info(turbo_info);
1418935Srafael.vanoni@sun.com 
1428935Srafael.vanoni@sun.com 	turbo_kstat.turbo_supported.value.ui32 =
1438935Srafael.vanoni@sun.com 	    turbo_info->turbo_supported;
1448935Srafael.vanoni@sun.com 	turbo_kstat.t_mcnt.value.ui64 = turbo_info->t_mcnt;
1458935Srafael.vanoni@sun.com 	turbo_kstat.t_acnt.value.ui64 = turbo_info->t_acnt;
1468935Srafael.vanoni@sun.com 
1478935Srafael.vanoni@sun.com 	return (0);
1488935Srafael.vanoni@sun.com }
1498935Srafael.vanoni@sun.com 
1508935Srafael.vanoni@sun.com /*
1518935Srafael.vanoni@sun.com  * Get count of MPERF/APERF MSR
1528935Srafael.vanoni@sun.com  */
1538935Srafael.vanoni@sun.com static void
1548935Srafael.vanoni@sun.com get_turbo_info(cpupm_turbo_info_t *turbo_info)
1558935Srafael.vanoni@sun.com {
1568935Srafael.vanoni@sun.com 	ulong_t		iflag;
1578935Srafael.vanoni@sun.com 	uint64_t	mcnt, acnt;
1588935Srafael.vanoni@sun.com 
1598935Srafael.vanoni@sun.com 	iflag = intr_clear();
1608935Srafael.vanoni@sun.com 	mcnt = rdmsr(IA32_MPERF_MSR);
1618935Srafael.vanoni@sun.com 	acnt = rdmsr(IA32_APERF_MSR);
1628935Srafael.vanoni@sun.com 	turbo_info->t_mcnt += mcnt;
1638935Srafael.vanoni@sun.com 	turbo_info->t_acnt += acnt;
1648935Srafael.vanoni@sun.com 	intr_restore(iflag);
1658935Srafael.vanoni@sun.com }
1668935Srafael.vanoni@sun.com 
1678935Srafael.vanoni@sun.com /*
1688935Srafael.vanoni@sun.com  * Clear MPERF/APERF MSR
1698935Srafael.vanoni@sun.com  */
1708935Srafael.vanoni@sun.com static void
1718935Srafael.vanoni@sun.com reset_turbo_info(void)
1728935Srafael.vanoni@sun.com {
1738935Srafael.vanoni@sun.com 	ulong_t		iflag;
1748935Srafael.vanoni@sun.com 
1758935Srafael.vanoni@sun.com 	iflag = intr_clear();
1768935Srafael.vanoni@sun.com 	wrmsr(IA32_MPERF_MSR, 0);
1778935Srafael.vanoni@sun.com 	wrmsr(IA32_APERF_MSR, 0);
1788935Srafael.vanoni@sun.com 	intr_restore(iflag);
1798935Srafael.vanoni@sun.com }
1808935Srafael.vanoni@sun.com 
1818935Srafael.vanoni@sun.com /*
1828935Srafael.vanoni@sun.com  * sum up the count of one CPU_ACPI_P0 transition
1838935Srafael.vanoni@sun.com  */
1848935Srafael.vanoni@sun.com static void
1858935Srafael.vanoni@sun.com record_turbo_info(cpupm_turbo_info_t *turbo_info,
1868935Srafael.vanoni@sun.com     uint32_t cur_state, uint32_t req_state)
1878935Srafael.vanoni@sun.com {
1888935Srafael.vanoni@sun.com 	if (!turbo_info->turbo_supported)
1898935Srafael.vanoni@sun.com 		return;
1908935Srafael.vanoni@sun.com 	/*
1918935Srafael.vanoni@sun.com 	 * enter P0 state
1928935Srafael.vanoni@sun.com 	 */
1938935Srafael.vanoni@sun.com 	if (req_state == CPU_ACPI_P0) {
1948935Srafael.vanoni@sun.com 		reset_turbo_info();
1958935Srafael.vanoni@sun.com 		turbo_info->in_turbo = CPU_IN_TURBO;
1968935Srafael.vanoni@sun.com 	}
1978935Srafael.vanoni@sun.com 	/*
1988935Srafael.vanoni@sun.com 	 * Leave P0 state
1998935Srafael.vanoni@sun.com 	 */
2008935Srafael.vanoni@sun.com 	else if (cur_state == CPU_ACPI_P0) {
2018935Srafael.vanoni@sun.com 		turbo_info->in_turbo = 0;
2028935Srafael.vanoni@sun.com 		get_turbo_info(turbo_info);
2038935Srafael.vanoni@sun.com 	}
2048935Srafael.vanoni@sun.com }
2058935Srafael.vanoni@sun.com 
2068935Srafael.vanoni@sun.com /*
2078935Srafael.vanoni@sun.com  * update the sum of counts and clear MSRs
2088935Srafael.vanoni@sun.com  */
2098935Srafael.vanoni@sun.com static void
2108935Srafael.vanoni@sun.com update_turbo_info(cpupm_turbo_info_t *turbo_info)
2118935Srafael.vanoni@sun.com {
2128935Srafael.vanoni@sun.com 	ulong_t		iflag;
2138935Srafael.vanoni@sun.com 	uint64_t	mcnt, acnt;
2148935Srafael.vanoni@sun.com 
2158935Srafael.vanoni@sun.com 	iflag = intr_clear();
2168935Srafael.vanoni@sun.com 	mcnt = rdmsr(IA32_MPERF_MSR);
2178935Srafael.vanoni@sun.com 	acnt = rdmsr(IA32_APERF_MSR);
2188935Srafael.vanoni@sun.com 	wrmsr(IA32_MPERF_MSR, 0);
2198935Srafael.vanoni@sun.com 	wrmsr(IA32_APERF_MSR, 0);
2208935Srafael.vanoni@sun.com 	turbo_info->t_mcnt += mcnt;
2218935Srafael.vanoni@sun.com 	turbo_info->t_acnt += acnt;
2228935Srafael.vanoni@sun.com 	intr_restore(iflag);
2238935Srafael.vanoni@sun.com }
2248935Srafael.vanoni@sun.com 
2258906SEric.Saxe@Sun.COM /*
2268906SEric.Saxe@Sun.COM  * Write the ctrl register. How it is written, depends upon the _PCT
2278906SEric.Saxe@Sun.COM  * APCI object value.
2288906SEric.Saxe@Sun.COM  */
2298906SEric.Saxe@Sun.COM static void
2308906SEric.Saxe@Sun.COM write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
2318906SEric.Saxe@Sun.COM {
2328906SEric.Saxe@Sun.COM 	cpu_acpi_pct_t *pct_ctrl;
2338906SEric.Saxe@Sun.COM 	uint64_t reg;
2348906SEric.Saxe@Sun.COM 
2358906SEric.Saxe@Sun.COM 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
2368906SEric.Saxe@Sun.COM 
2378906SEric.Saxe@Sun.COM 	switch (pct_ctrl->cr_addrspace_id) {
2388906SEric.Saxe@Sun.COM 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
2398906SEric.Saxe@Sun.COM 		/*
2408906SEric.Saxe@Sun.COM 		 * Read current power state because reserved bits must be
2418906SEric.Saxe@Sun.COM 		 * preserved, compose new value, and write it.
2428906SEric.Saxe@Sun.COM 		 */
2438906SEric.Saxe@Sun.COM 		reg = rdmsr(IA32_PERF_CTL_MSR);
2448906SEric.Saxe@Sun.COM 		reg &= ~((uint64_t)0xFFFF);
2458906SEric.Saxe@Sun.COM 		reg |= ctrl;
2468906SEric.Saxe@Sun.COM 		wrmsr(IA32_PERF_CTL_MSR, reg);
2478906SEric.Saxe@Sun.COM 		break;
2488906SEric.Saxe@Sun.COM 
2498906SEric.Saxe@Sun.COM 	case ACPI_ADR_SPACE_SYSTEM_IO:
2508906SEric.Saxe@Sun.COM 		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
2518906SEric.Saxe@Sun.COM 		    pct_ctrl->cr_width);
2528906SEric.Saxe@Sun.COM 		break;
2538906SEric.Saxe@Sun.COM 
2548906SEric.Saxe@Sun.COM 	default:
2558906SEric.Saxe@Sun.COM 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
2568906SEric.Saxe@Sun.COM 		    pct_ctrl->cr_addrspace_id);
2578906SEric.Saxe@Sun.COM 		return;
2588906SEric.Saxe@Sun.COM 	}
2598906SEric.Saxe@Sun.COM 
2608906SEric.Saxe@Sun.COM 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
2618906SEric.Saxe@Sun.COM }
2628906SEric.Saxe@Sun.COM 
2638906SEric.Saxe@Sun.COM /*
2648906SEric.Saxe@Sun.COM  * Transition the current processor to the requested state.
2658906SEric.Saxe@Sun.COM  */
2668906SEric.Saxe@Sun.COM void
2678906SEric.Saxe@Sun.COM speedstep_pstate_transition(uint32_t req_state)
2688906SEric.Saxe@Sun.COM {
2698906SEric.Saxe@Sun.COM 	cpupm_mach_state_t *mach_state =
2708906SEric.Saxe@Sun.COM 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
2718906SEric.Saxe@Sun.COM 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
2728906SEric.Saxe@Sun.COM 	cpu_acpi_pstate_t *req_pstate;
2738906SEric.Saxe@Sun.COM 	uint32_t ctrl;
2748935Srafael.vanoni@sun.com 	cpupm_turbo_info_t *turbo_info =
2758935Srafael.vanoni@sun.com 	    (cpupm_turbo_info_t *)(mach_state->ms_vendor);
2768906SEric.Saxe@Sun.COM 
2778906SEric.Saxe@Sun.COM 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
2788906SEric.Saxe@Sun.COM 	req_pstate += req_state;
2798906SEric.Saxe@Sun.COM 
2808906SEric.Saxe@Sun.COM 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
2818906SEric.Saxe@Sun.COM 
2828906SEric.Saxe@Sun.COM 	/*
2838906SEric.Saxe@Sun.COM 	 * Initiate the processor p-state change.
2848906SEric.Saxe@Sun.COM 	 */
2858906SEric.Saxe@Sun.COM 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
2868906SEric.Saxe@Sun.COM 	write_ctrl(handle, ctrl);
2878906SEric.Saxe@Sun.COM 
2888935Srafael.vanoni@sun.com 	if (turbo_info)
2898935Srafael.vanoni@sun.com 		record_turbo_info(turbo_info,
2908935Srafael.vanoni@sun.com 		    mach_state->ms_pstate.cma_state.pstate, req_state);
2918935Srafael.vanoni@sun.com 
2928935Srafael.vanoni@sun.com 
2938906SEric.Saxe@Sun.COM 	mach_state->ms_pstate.cma_state.pstate = req_state;
2948906SEric.Saxe@Sun.COM 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
2958906SEric.Saxe@Sun.COM }
2968906SEric.Saxe@Sun.COM 
2978906SEric.Saxe@Sun.COM static void
2988906SEric.Saxe@Sun.COM speedstep_power(cpuset_t set, uint32_t req_state)
2998906SEric.Saxe@Sun.COM {
3008906SEric.Saxe@Sun.COM 	/*
3018906SEric.Saxe@Sun.COM 	 * If thread is already running on target CPU then just
3028906SEric.Saxe@Sun.COM 	 * make the transition request. Otherwise, we'll need to
3038906SEric.Saxe@Sun.COM 	 * make a cross-call.
3048906SEric.Saxe@Sun.COM 	 */
3058906SEric.Saxe@Sun.COM 	kpreempt_disable();
3068906SEric.Saxe@Sun.COM 	if (CPU_IN_SET(set, CPU->cpu_id)) {
3078906SEric.Saxe@Sun.COM 		speedstep_pstate_transition(req_state);
3088906SEric.Saxe@Sun.COM 		CPUSET_DEL(set, CPU->cpu_id);
3098906SEric.Saxe@Sun.COM 	}
3108906SEric.Saxe@Sun.COM 	if (!CPUSET_ISNULL(set)) {
3119489SJoe.Bonasera@sun.com 		xc_call((xc_arg_t)req_state, NULL, NULL, CPUSET2BV(set),
3128906SEric.Saxe@Sun.COM 		    (xc_func_t)speedstep_pstate_transition);
3138906SEric.Saxe@Sun.COM 	}
3148906SEric.Saxe@Sun.COM 	kpreempt_enable();
3158906SEric.Saxe@Sun.COM }
3168906SEric.Saxe@Sun.COM 
3178906SEric.Saxe@Sun.COM /*
3188906SEric.Saxe@Sun.COM  * Validate that this processor supports Speedstep and if so,
3198906SEric.Saxe@Sun.COM  * get the P-state data from ACPI and cache it.
3208906SEric.Saxe@Sun.COM  */
3218906SEric.Saxe@Sun.COM static int
3228906SEric.Saxe@Sun.COM speedstep_init(cpu_t *cp)
3238906SEric.Saxe@Sun.COM {
3248906SEric.Saxe@Sun.COM 	cpupm_mach_state_t *mach_state =
3258906SEric.Saxe@Sun.COM 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
3268906SEric.Saxe@Sun.COM 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
3278906SEric.Saxe@Sun.COM 	cpu_acpi_pct_t *pct_stat;
3288935Srafael.vanoni@sun.com 	cpupm_turbo_info_t *turbo_info;
3298906SEric.Saxe@Sun.COM 
3308906SEric.Saxe@Sun.COM 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
3318906SEric.Saxe@Sun.COM 
3328906SEric.Saxe@Sun.COM 	/*
3338906SEric.Saxe@Sun.COM 	 * Cache the P-state specific ACPI data.
3348906SEric.Saxe@Sun.COM 	 */
3358906SEric.Saxe@Sun.COM 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
336*10075SMark.Haywood@Sun.COM 		cmn_err(CE_NOTE, "!SpeedStep support is being "
337*10075SMark.Haywood@Sun.COM 		    "disabled due to errors parsing ACPI P-state objects "
338*10075SMark.Haywood@Sun.COM 		    "exported by BIOS.");
3398906SEric.Saxe@Sun.COM 		speedstep_fini(cp);
3408906SEric.Saxe@Sun.COM 		return (ESS_RET_NO_PM);
3418906SEric.Saxe@Sun.COM 	}
3428906SEric.Saxe@Sun.COM 
3438906SEric.Saxe@Sun.COM 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
3448906SEric.Saxe@Sun.COM 	switch (pct_stat->cr_addrspace_id) {
3458906SEric.Saxe@Sun.COM 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
3468906SEric.Saxe@Sun.COM 		ESSDEBUG(("Transitions will use fixed hardware\n"));
3478906SEric.Saxe@Sun.COM 		break;
3488906SEric.Saxe@Sun.COM 	case ACPI_ADR_SPACE_SYSTEM_IO:
3498906SEric.Saxe@Sun.COM 		ESSDEBUG(("Transitions will use system IO\n"));
3508906SEric.Saxe@Sun.COM 		break;
3518906SEric.Saxe@Sun.COM 	default:
3528906SEric.Saxe@Sun.COM 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
3538906SEric.Saxe@Sun.COM 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
3548906SEric.Saxe@Sun.COM 		cmn_err(CE_NOTE, "!CPU power management will not function.");
3558906SEric.Saxe@Sun.COM 		speedstep_fini(cp);
3568906SEric.Saxe@Sun.COM 		return (ESS_RET_NO_PM);
3578906SEric.Saxe@Sun.COM 	}
3588906SEric.Saxe@Sun.COM 
3598906SEric.Saxe@Sun.COM 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
3608906SEric.Saxe@Sun.COM 
3618935Srafael.vanoni@sun.com 	if (!turbo_supported()) {
3628935Srafael.vanoni@sun.com 		mach_state->ms_vendor = NULL;
3638935Srafael.vanoni@sun.com 		goto ess_ret_success;
3648935Srafael.vanoni@sun.com 	}
3658935Srafael.vanoni@sun.com 	/*
3668935Srafael.vanoni@sun.com 	 * turbo mode supported
3678935Srafael.vanoni@sun.com 	 */
3688935Srafael.vanoni@sun.com 	turbo_info = mach_state->ms_vendor =
3698935Srafael.vanoni@sun.com 	    kmem_zalloc(sizeof (cpupm_turbo_info_t), KM_SLEEP);
3708935Srafael.vanoni@sun.com 	turbo_info->turbo_supported = 1;
3718935Srafael.vanoni@sun.com 	turbo_info->turbo_ksp = kstat_create("turbo", cp->cpu_id,
3728935Srafael.vanoni@sun.com 	    "turbo", "misc", KSTAT_TYPE_NAMED,
3738935Srafael.vanoni@sun.com 	    sizeof (turbo_kstat) / sizeof (kstat_named_t),
3748935Srafael.vanoni@sun.com 	    KSTAT_FLAG_VIRTUAL);
3758935Srafael.vanoni@sun.com 
3768935Srafael.vanoni@sun.com 	if (turbo_info->turbo_ksp == NULL) {
3778935Srafael.vanoni@sun.com 		cmn_err(CE_NOTE, "kstat_create(turbo) fail");
3788935Srafael.vanoni@sun.com 	} else {
3798935Srafael.vanoni@sun.com 		turbo_info->turbo_ksp->ks_data = &turbo_kstat;
3808935Srafael.vanoni@sun.com 		turbo_info->turbo_ksp->ks_lock = &turbo_mutex;
3818935Srafael.vanoni@sun.com 		turbo_info->turbo_ksp->ks_update = turbo_kstat_update;
3828935Srafael.vanoni@sun.com 		turbo_info->turbo_ksp->ks_data_size += MAXNAMELEN;
3838935Srafael.vanoni@sun.com 		turbo_info->turbo_ksp->ks_private = turbo_info;
3848935Srafael.vanoni@sun.com 
3858935Srafael.vanoni@sun.com 		kstat_install(turbo_info->turbo_ksp);
3868935Srafael.vanoni@sun.com 	}
3878935Srafael.vanoni@sun.com 
3888935Srafael.vanoni@sun.com ess_ret_success:
3898935Srafael.vanoni@sun.com 
3908906SEric.Saxe@Sun.COM 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
3918906SEric.Saxe@Sun.COM 	return (ESS_RET_SUCCESS);
3928906SEric.Saxe@Sun.COM }
3938906SEric.Saxe@Sun.COM 
3948906SEric.Saxe@Sun.COM /*
3958906SEric.Saxe@Sun.COM  * Free resources allocated by speedstep_init().
3968906SEric.Saxe@Sun.COM  */
3978906SEric.Saxe@Sun.COM static void
3988906SEric.Saxe@Sun.COM speedstep_fini(cpu_t *cp)
3998906SEric.Saxe@Sun.COM {
4008906SEric.Saxe@Sun.COM 	cpupm_mach_state_t *mach_state =
4018906SEric.Saxe@Sun.COM 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
4028906SEric.Saxe@Sun.COM 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
4038935Srafael.vanoni@sun.com 	cpupm_turbo_info_t *turbo_info =
4048935Srafael.vanoni@sun.com 	    (cpupm_turbo_info_t *)(mach_state->ms_vendor);
4058906SEric.Saxe@Sun.COM 
4068906SEric.Saxe@Sun.COM 	cpupm_free_domains(&cpupm_pstate_domains);
4078906SEric.Saxe@Sun.COM 	cpu_acpi_free_pstate_data(handle);
4088935Srafael.vanoni@sun.com 
4098935Srafael.vanoni@sun.com 	if (turbo_info) {
4108935Srafael.vanoni@sun.com 		if (turbo_info->turbo_ksp != NULL)
4118935Srafael.vanoni@sun.com 			kstat_delete(turbo_info->turbo_ksp);
4128935Srafael.vanoni@sun.com 		kmem_free(turbo_info, sizeof (cpupm_turbo_info_t));
4138935Srafael.vanoni@sun.com 	}
4148906SEric.Saxe@Sun.COM }
4158906SEric.Saxe@Sun.COM 
4168906SEric.Saxe@Sun.COM boolean_t
4178906SEric.Saxe@Sun.COM speedstep_supported(uint_t family, uint_t model)
4188906SEric.Saxe@Sun.COM {
4198906SEric.Saxe@Sun.COM 	struct cpuid_regs cpu_regs;
4208906SEric.Saxe@Sun.COM 
4218906SEric.Saxe@Sun.COM 	/* Required features */
4228906SEric.Saxe@Sun.COM 	if (!(x86_feature & X86_CPUID) ||
4238906SEric.Saxe@Sun.COM 	    !(x86_feature & X86_MSR)) {
4248906SEric.Saxe@Sun.COM 		return (B_FALSE);
4258906SEric.Saxe@Sun.COM 	}
4268906SEric.Saxe@Sun.COM 
4278906SEric.Saxe@Sun.COM 	/*
4288906SEric.Saxe@Sun.COM 	 * We only support family/model combinations which
4298906SEric.Saxe@Sun.COM 	 * are P-state TSC invariant.
4308906SEric.Saxe@Sun.COM 	 */
4318906SEric.Saxe@Sun.COM 	if (!((family == 0xf && model >= 0x3) ||
4328906SEric.Saxe@Sun.COM 	    (family == 0x6 && model >= 0xe))) {
4338906SEric.Saxe@Sun.COM 		return (B_FALSE);
4348906SEric.Saxe@Sun.COM 	}
4358906SEric.Saxe@Sun.COM 
4368906SEric.Saxe@Sun.COM 	/*
4378906SEric.Saxe@Sun.COM 	 * Enhanced SpeedStep supported?
4388906SEric.Saxe@Sun.COM 	 */
4398906SEric.Saxe@Sun.COM 	cpu_regs.cp_eax = 0x1;
4408906SEric.Saxe@Sun.COM 	(void) __cpuid_insn(&cpu_regs);
4418906SEric.Saxe@Sun.COM 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
4428906SEric.Saxe@Sun.COM 		return (B_FALSE);
4438906SEric.Saxe@Sun.COM 	}
4448906SEric.Saxe@Sun.COM 
4458906SEric.Saxe@Sun.COM 	return (B_TRUE);
4468906SEric.Saxe@Sun.COM }
4478935Srafael.vanoni@sun.com 
4488935Srafael.vanoni@sun.com boolean_t
4498935Srafael.vanoni@sun.com turbo_supported(void)
4508935Srafael.vanoni@sun.com {
4518935Srafael.vanoni@sun.com 	struct cpuid_regs cpu_regs;
4528935Srafael.vanoni@sun.com 
4538935Srafael.vanoni@sun.com 	/* Required features */
4548935Srafael.vanoni@sun.com 	if (!(x86_feature & X86_CPUID) ||
4558935Srafael.vanoni@sun.com 	    !(x86_feature & X86_MSR)) {
4568935Srafael.vanoni@sun.com 		return (B_FALSE);
4578935Srafael.vanoni@sun.com 	}
4588935Srafael.vanoni@sun.com 
4598935Srafael.vanoni@sun.com 	/*
4608935Srafael.vanoni@sun.com 	 * turbo mode supported?
4618935Srafael.vanoni@sun.com 	 */
4628935Srafael.vanoni@sun.com 	cpu_regs.cp_eax = 0x6;
4638935Srafael.vanoni@sun.com 	(void) __cpuid_insn(&cpu_regs);
4648935Srafael.vanoni@sun.com 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
4658935Srafael.vanoni@sun.com 		return (B_FALSE);
4668935Srafael.vanoni@sun.com 	}
4678935Srafael.vanoni@sun.com 
4688935Srafael.vanoni@sun.com 	return (B_TRUE);
4698935Srafael.vanoni@sun.com }
470