xref: /titanic_52/usr/src/uts/i86pc/os/cpupm/speedstep.c (revision 511588bb13d2462265d682dc1cb7ba5c7a27a771)
10e751525SEric Saxe /*
20e751525SEric Saxe  * CDDL HEADER START
30e751525SEric Saxe  *
40e751525SEric Saxe  * The contents of this file are subject to the terms of the
50e751525SEric Saxe  * Common Development and Distribution License (the "License").
60e751525SEric Saxe  * You may not use this file except in compliance with the License.
70e751525SEric Saxe  *
80e751525SEric Saxe  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90e751525SEric Saxe  * or http://www.opensolaris.org/os/licensing.
100e751525SEric Saxe  * See the License for the specific language governing permissions
110e751525SEric Saxe  * and limitations under the License.
120e751525SEric Saxe  *
130e751525SEric Saxe  * When distributing Covered Code, include this CDDL HEADER in each
140e751525SEric Saxe  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150e751525SEric Saxe  * If applicable, add the following below this CDDL HEADER, with the
160e751525SEric Saxe  * fields enclosed by brackets "[]" replaced with your own identifying
170e751525SEric Saxe  * information: Portions Copyright [yyyy] [name of copyright owner]
180e751525SEric Saxe  *
190e751525SEric Saxe  * CDDL HEADER END
200e751525SEric Saxe  */
210e751525SEric Saxe /*
227417cfdeSKuriakose Kuruvilla  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
230e751525SEric Saxe  */
24444f66e7SMark Haywood /*
25444f66e7SMark Haywood  * Copyright (c) 2009,  Intel Corporation.
26444f66e7SMark Haywood  * All Rights Reserved.
27444f66e7SMark Haywood  */
280e751525SEric Saxe 
290e751525SEric Saxe #include <sys/x86_archext.h>
300e751525SEric Saxe #include <sys/machsystm.h>
31e5bbdc06SRafael Vanoni #include <sys/archsystm.h>
320e751525SEric Saxe #include <sys/x_call.h>
330e751525SEric Saxe #include <sys/acpi/acpi.h>
340e751525SEric Saxe #include <sys/acpica.h>
350e751525SEric Saxe #include <sys/speedstep.h>
360e751525SEric Saxe #include <sys/cpu_acpi.h>
370e751525SEric Saxe #include <sys/cpupm.h>
380e751525SEric Saxe #include <sys/dtrace.h>
390e751525SEric Saxe #include <sys/sdt.h>
400e751525SEric Saxe 
410e751525SEric Saxe static int speedstep_init(cpu_t *);
420e751525SEric Saxe static void speedstep_fini(cpu_t *);
430e751525SEric Saxe static void speedstep_power(cpuset_t, uint32_t);
44444f66e7SMark Haywood static void speedstep_stop(cpu_t *);
455951ced0SHans Rosenfeld static boolean_t speedstep_turbo_supported(void);
460e751525SEric Saxe 
470e751525SEric Saxe /*
480e751525SEric Saxe  * Interfaces for modules implementing Intel's Enhanced SpeedStep.
490e751525SEric Saxe  */
500e751525SEric Saxe cpupm_state_ops_t speedstep_ops = {
510e751525SEric Saxe 	"Enhanced SpeedStep Technology",
520e751525SEric Saxe 	speedstep_init,
530e751525SEric Saxe 	speedstep_fini,
54444f66e7SMark Haywood 	speedstep_power,
55444f66e7SMark Haywood 	speedstep_stop
560e751525SEric Saxe };
570e751525SEric Saxe 
580e751525SEric Saxe /*
590e751525SEric Saxe  * Error returns
600e751525SEric Saxe  */
610e751525SEric Saxe #define	ESS_RET_SUCCESS		0x00
620e751525SEric Saxe #define	ESS_RET_NO_PM		0x01
630e751525SEric Saxe #define	ESS_RET_UNSUP_STATE	0x02
640e751525SEric Saxe 
650e751525SEric Saxe /*
660e751525SEric Saxe  * MSR registers for changing and reading processor power state.
670e751525SEric Saxe  */
680e751525SEric Saxe #define	IA32_PERF_STAT_MSR		0x198
690e751525SEric Saxe #define	IA32_PERF_CTL_MSR		0x199
700e751525SEric Saxe 
710e751525SEric Saxe #define	IA32_CPUID_TSC_CONSTANT		0xF30
720e751525SEric Saxe #define	IA32_MISC_ENABLE_MSR		0x1A0
730e751525SEric Saxe #define	IA32_MISC_ENABLE_EST		(1<<16)
740e751525SEric Saxe #define	IA32_MISC_ENABLE_CXE		(1<<25)
75e5bbdc06SRafael Vanoni 
76e5bbdc06SRafael Vanoni #define	CPUID_TURBO_SUPPORT		(1 << 1)
77e5bbdc06SRafael Vanoni 
780e751525SEric Saxe /*
790e751525SEric Saxe  * Debugging support
800e751525SEric Saxe  */
810e751525SEric Saxe #ifdef	DEBUG
820e751525SEric Saxe volatile int ess_debug = 0;
830e751525SEric Saxe #define	ESSDEBUG(arglist) if (ess_debug) printf arglist;
840e751525SEric Saxe #else
850e751525SEric Saxe #define	ESSDEBUG(arglist)
860e751525SEric Saxe #endif
870e751525SEric Saxe 
880e751525SEric Saxe /*
890e751525SEric Saxe  * Write the ctrl register. How it is written, depends upon the _PCT
900e751525SEric Saxe  * APCI object value.
910e751525SEric Saxe  */
920e751525SEric Saxe static void
930e751525SEric Saxe write_ctrl(cpu_acpi_handle_t handle, uint32_t ctrl)
940e751525SEric Saxe {
950e751525SEric Saxe 	cpu_acpi_pct_t *pct_ctrl;
960e751525SEric Saxe 	uint64_t reg;
970e751525SEric Saxe 
980e751525SEric Saxe 	pct_ctrl = CPU_ACPI_PCT_CTRL(handle);
990e751525SEric Saxe 
1000e751525SEric Saxe 	switch (pct_ctrl->cr_addrspace_id) {
1010e751525SEric Saxe 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
1020e751525SEric Saxe 		/*
1030e751525SEric Saxe 		 * Read current power state because reserved bits must be
1040e751525SEric Saxe 		 * preserved, compose new value, and write it.
1050e751525SEric Saxe 		 */
1060e751525SEric Saxe 		reg = rdmsr(IA32_PERF_CTL_MSR);
1070e751525SEric Saxe 		reg &= ~((uint64_t)0xFFFF);
1080e751525SEric Saxe 		reg |= ctrl;
1090e751525SEric Saxe 		wrmsr(IA32_PERF_CTL_MSR, reg);
1100e751525SEric Saxe 		break;
1110e751525SEric Saxe 
1120e751525SEric Saxe 	case ACPI_ADR_SPACE_SYSTEM_IO:
1130e751525SEric Saxe 		(void) cpu_acpi_write_port(pct_ctrl->cr_address, ctrl,
1140e751525SEric Saxe 		    pct_ctrl->cr_width);
1150e751525SEric Saxe 		break;
1160e751525SEric Saxe 
1170e751525SEric Saxe 	default:
1180e751525SEric Saxe 		DTRACE_PROBE1(ess_ctrl_unsupported_type, uint8_t,
1190e751525SEric Saxe 		    pct_ctrl->cr_addrspace_id);
1200e751525SEric Saxe 		return;
1210e751525SEric Saxe 	}
1220e751525SEric Saxe 
1230e751525SEric Saxe 	DTRACE_PROBE1(ess_ctrl_write, uint32_t, ctrl);
1240e751525SEric Saxe }
1250e751525SEric Saxe 
1260e751525SEric Saxe /*
1270e751525SEric Saxe  * Transition the current processor to the requested state.
1280e751525SEric Saxe  */
1290e751525SEric Saxe void
1300e751525SEric Saxe speedstep_pstate_transition(uint32_t req_state)
1310e751525SEric Saxe {
1320e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
1330e751525SEric Saxe 	    (cpupm_mach_state_t *)CPU->cpu_m.mcpu_pm_mach_state;
1340e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
1350e751525SEric Saxe 	cpu_acpi_pstate_t *req_pstate;
1360e751525SEric Saxe 	uint32_t ctrl;
1370e751525SEric Saxe 
1380e751525SEric Saxe 	req_pstate = (cpu_acpi_pstate_t *)CPU_ACPI_PSTATES(handle);
1390e751525SEric Saxe 	req_pstate += req_state;
1400e751525SEric Saxe 
1410e751525SEric Saxe 	DTRACE_PROBE1(ess_transition, uint32_t, CPU_ACPI_FREQ(req_pstate));
1420e751525SEric Saxe 
1430e751525SEric Saxe 	/*
1440e751525SEric Saxe 	 * Initiate the processor p-state change.
1450e751525SEric Saxe 	 */
1460e751525SEric Saxe 	ctrl = CPU_ACPI_PSTATE_CTRL(req_pstate);
1470e751525SEric Saxe 	write_ctrl(handle, ctrl);
1480e751525SEric Saxe 
1495951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
1505951ced0SHans Rosenfeld 		cpupm_record_turbo_info(mach_state->ms_turbo,
151e5bbdc06SRafael Vanoni 		    mach_state->ms_pstate.cma_state.pstate, req_state);
152e5bbdc06SRafael Vanoni 
1530e751525SEric Saxe 	mach_state->ms_pstate.cma_state.pstate = req_state;
1540e751525SEric Saxe 	cpu_set_curr_clock(((uint64_t)CPU_ACPI_FREQ(req_pstate) * 1000000));
1550e751525SEric Saxe }
1560e751525SEric Saxe 
1570e751525SEric Saxe static void
1580e751525SEric Saxe speedstep_power(cpuset_t set, uint32_t req_state)
1590e751525SEric Saxe {
1600e751525SEric Saxe 	/*
1610e751525SEric Saxe 	 * If thread is already running on target CPU then just
1620e751525SEric Saxe 	 * make the transition request. Otherwise, we'll need to
1630e751525SEric Saxe 	 * make a cross-call.
1640e751525SEric Saxe 	 */
1650e751525SEric Saxe 	kpreempt_disable();
1660e751525SEric Saxe 	if (CPU_IN_SET(set, CPU->cpu_id)) {
1670e751525SEric Saxe 		speedstep_pstate_transition(req_state);
1680e751525SEric Saxe 		CPUSET_DEL(set, CPU->cpu_id);
1690e751525SEric Saxe 	}
1700e751525SEric Saxe 	if (!CPUSET_ISNULL(set)) {
171f34a7178SJoe Bonasera 		xc_call((xc_arg_t)req_state, NULL, NULL, CPUSET2BV(set),
1720e751525SEric Saxe 		    (xc_func_t)speedstep_pstate_transition);
1730e751525SEric Saxe 	}
1740e751525SEric Saxe 	kpreempt_enable();
1750e751525SEric Saxe }
1760e751525SEric Saxe 
1770e751525SEric Saxe /*
1780e751525SEric Saxe  * Validate that this processor supports Speedstep and if so,
1790e751525SEric Saxe  * get the P-state data from ACPI and cache it.
1800e751525SEric Saxe  */
1810e751525SEric Saxe static int
1820e751525SEric Saxe speedstep_init(cpu_t *cp)
1830e751525SEric Saxe {
1840e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
1850e751525SEric Saxe 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
1860e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
1870e751525SEric Saxe 	cpu_acpi_pct_t *pct_stat;
188*511588bbSYuri Pankov 	static int logged = 0;
1890e751525SEric Saxe 
1900e751525SEric Saxe 	ESSDEBUG(("speedstep_init: processor %d\n", cp->cpu_id));
1910e751525SEric Saxe 
1920e751525SEric Saxe 	/*
1930e751525SEric Saxe 	 * Cache the P-state specific ACPI data.
1940e751525SEric Saxe 	 */
1950e751525SEric Saxe 	if (cpu_acpi_cache_pstate_data(handle) != 0) {
196*511588bbSYuri Pankov 		if (!logged) {
19700f97612SMark Haywood 			cmn_err(CE_NOTE, "!SpeedStep support is being "
198*511588bbSYuri Pankov 			    "disabled due to errors parsing ACPI P-state "
199*511588bbSYuri Pankov 			    "objects exported by BIOS.");
200*511588bbSYuri Pankov 			logged = 1;
201*511588bbSYuri Pankov 		}
2020e751525SEric Saxe 		speedstep_fini(cp);
2030e751525SEric Saxe 		return (ESS_RET_NO_PM);
2040e751525SEric Saxe 	}
2050e751525SEric Saxe 
2060e751525SEric Saxe 	pct_stat = CPU_ACPI_PCT_STATUS(handle);
2070e751525SEric Saxe 	switch (pct_stat->cr_addrspace_id) {
2080e751525SEric Saxe 	case ACPI_ADR_SPACE_FIXED_HARDWARE:
2090e751525SEric Saxe 		ESSDEBUG(("Transitions will use fixed hardware\n"));
2100e751525SEric Saxe 		break;
2110e751525SEric Saxe 	case ACPI_ADR_SPACE_SYSTEM_IO:
2120e751525SEric Saxe 		ESSDEBUG(("Transitions will use system IO\n"));
2130e751525SEric Saxe 		break;
2140e751525SEric Saxe 	default:
2150e751525SEric Saxe 		cmn_err(CE_WARN, "!_PCT conifgured for unsupported "
2160e751525SEric Saxe 		    "addrspace = %d.", pct_stat->cr_addrspace_id);
2170e751525SEric Saxe 		cmn_err(CE_NOTE, "!CPU power management will not function.");
2180e751525SEric Saxe 		speedstep_fini(cp);
2190e751525SEric Saxe 		return (ESS_RET_NO_PM);
2200e751525SEric Saxe 	}
2210e751525SEric Saxe 
2220e751525SEric Saxe 	cpupm_alloc_domains(cp, CPUPM_P_STATES);
2230e751525SEric Saxe 
2245951ced0SHans Rosenfeld 	if (speedstep_turbo_supported())
2255951ced0SHans Rosenfeld 		mach_state->ms_turbo = cpupm_turbo_init(cp);
226e5bbdc06SRafael Vanoni 
2270e751525SEric Saxe 	ESSDEBUG(("Processor %d succeeded.\n", cp->cpu_id))
2280e751525SEric Saxe 	return (ESS_RET_SUCCESS);
2290e751525SEric Saxe }
2300e751525SEric Saxe 
2310e751525SEric Saxe /*
2320e751525SEric Saxe  * Free resources allocated by speedstep_init().
2330e751525SEric Saxe  */
2340e751525SEric Saxe static void
2350e751525SEric Saxe speedstep_fini(cpu_t *cp)
2360e751525SEric Saxe {
2370e751525SEric Saxe 	cpupm_mach_state_t *mach_state =
2380e751525SEric Saxe 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
2390e751525SEric Saxe 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
2400e751525SEric Saxe 
2410e751525SEric Saxe 	cpupm_free_domains(&cpupm_pstate_domains);
2420e751525SEric Saxe 	cpu_acpi_free_pstate_data(handle);
243e5bbdc06SRafael Vanoni 
2445951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
2455951ced0SHans Rosenfeld 		cpupm_turbo_fini(mach_state->ms_turbo);
2465951ced0SHans Rosenfeld 	mach_state->ms_turbo = NULL;
2470e751525SEric Saxe }
2480e751525SEric Saxe 
249444f66e7SMark Haywood static void
250444f66e7SMark Haywood speedstep_stop(cpu_t *cp)
251444f66e7SMark Haywood {
252444f66e7SMark Haywood 	cpupm_mach_state_t *mach_state =
253444f66e7SMark Haywood 	    (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
254444f66e7SMark Haywood 	cpu_acpi_handle_t handle = mach_state->ms_acpi_handle;
255444f66e7SMark Haywood 
256444f66e7SMark Haywood 	cpupm_remove_domains(cp, CPUPM_P_STATES, &cpupm_pstate_domains);
257444f66e7SMark Haywood 	cpu_acpi_free_pstate_data(handle);
258444f66e7SMark Haywood 
2595951ced0SHans Rosenfeld 	if (mach_state->ms_turbo != NULL)
2605951ced0SHans Rosenfeld 		cpupm_turbo_fini(mach_state->ms_turbo);
2615951ced0SHans Rosenfeld 	mach_state->ms_turbo = NULL;
262444f66e7SMark Haywood }
263444f66e7SMark Haywood 
2640e751525SEric Saxe boolean_t
2650e751525SEric Saxe speedstep_supported(uint_t family, uint_t model)
2660e751525SEric Saxe {
2670e751525SEric Saxe 	struct cpuid_regs cpu_regs;
2680e751525SEric Saxe 
2690e751525SEric Saxe 	/* Required features */
2707417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
2717417cfdeSKuriakose Kuruvilla 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
2720e751525SEric Saxe 		return (B_FALSE);
2730e751525SEric Saxe 	}
2740e751525SEric Saxe 
2750e751525SEric Saxe 	/*
2760e751525SEric Saxe 	 * We only support family/model combinations which
2770e751525SEric Saxe 	 * are P-state TSC invariant.
2780e751525SEric Saxe 	 */
2790e751525SEric Saxe 	if (!((family == 0xf && model >= 0x3) ||
2800e751525SEric Saxe 	    (family == 0x6 && model >= 0xe))) {
2810e751525SEric Saxe 		return (B_FALSE);
2820e751525SEric Saxe 	}
2830e751525SEric Saxe 
2840e751525SEric Saxe 	/*
2850e751525SEric Saxe 	 * Enhanced SpeedStep supported?
2860e751525SEric Saxe 	 */
2870e751525SEric Saxe 	cpu_regs.cp_eax = 0x1;
2880e751525SEric Saxe 	(void) __cpuid_insn(&cpu_regs);
2890e751525SEric Saxe 	if (!(cpu_regs.cp_ecx & CPUID_INTC_ECX_EST)) {
2900e751525SEric Saxe 		return (B_FALSE);
2910e751525SEric Saxe 	}
2920e751525SEric Saxe 
2930e751525SEric Saxe 	return (B_TRUE);
2940e751525SEric Saxe }
295e5bbdc06SRafael Vanoni 
296e5bbdc06SRafael Vanoni boolean_t
2975951ced0SHans Rosenfeld speedstep_turbo_supported(void)
298e5bbdc06SRafael Vanoni {
299e5bbdc06SRafael Vanoni 	struct cpuid_regs cpu_regs;
300e5bbdc06SRafael Vanoni 
301e5bbdc06SRafael Vanoni 	/* Required features */
3027417cfdeSKuriakose Kuruvilla 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID) ||
3037417cfdeSKuriakose Kuruvilla 	    !is_x86_feature(x86_featureset, X86FSET_MSR)) {
304e5bbdc06SRafael Vanoni 		return (B_FALSE);
305e5bbdc06SRafael Vanoni 	}
306e5bbdc06SRafael Vanoni 
307e5bbdc06SRafael Vanoni 	/*
308e5bbdc06SRafael Vanoni 	 * turbo mode supported?
309e5bbdc06SRafael Vanoni 	 */
310e5bbdc06SRafael Vanoni 	cpu_regs.cp_eax = 0x6;
311e5bbdc06SRafael Vanoni 	(void) __cpuid_insn(&cpu_regs);
312e5bbdc06SRafael Vanoni 	if (!(cpu_regs.cp_eax & CPUID_TURBO_SUPPORT)) {
313e5bbdc06SRafael Vanoni 		return (B_FALSE);
314e5bbdc06SRafael Vanoni 	}
315e5bbdc06SRafael Vanoni 
316e5bbdc06SRafael Vanoni 	return (B_TRUE);
317e5bbdc06SRafael Vanoni }
318